config SND_SOC_WM1250_EV1
tristate
+config SND_SOC_WL1273_UART
+ tristate
+
+config SND_SOC_IFX1130_MODEM_CODEC
+ tristate
+
+config SND_SOC_ES305
+ tristate
+
config SND_SOC_WM8350
tristate
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
+snd-soc-wl1273-uart-objs := wl1273_uart.o
+snd-soc-ifx-modem-objs := ifx_modem_codec.o
+snd-soc-es305-objs := es305_codec.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
+obj-$(CONFIG_SND_SOC_WL1273_UART) += snd-soc-wl1273-uart.o
+obj-$(CONFIG_SND_SOC_IFX1130_MODEM_CODEC) +=ifx_modem_codec.o
+obj-$(CONFIG_SND_SOC_ES305) +=es305_codec.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
--- /dev/null
+/*
+ * es305_codec.c - ALSA ASOC [dummy] Codec driver for Audience ES305
+ *
+ * Copyright (C) 2011 Intel Corp
+ * Author:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#define FORMAT(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+/*
+ *
+ * DUMMY SOC CODEC DRIVER for es305
+ *
+ */
+
+static struct snd_soc_dai_driver es305_dai[] = {
+ {
+ .name = "es305 Voice",
+ .id = 0,
+ .playback = {
+ .stream_name = "es305_Downlink",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "es305_Uplink",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = NULL,
+ },
+
+};
+
+static struct snd_soc_codec_driver soc_codec_es305 = {
+ .probe = NULL,
+ .remove = NULL,
+};
+
+
+static int __devinit es305_device_probe(struct platform_device *pdev)
+{
+ pr_info("es305: codec device probe called for %s\n",
+ dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_es305,
+ es305_dai, ARRAY_SIZE(es305_dai));
+}
+
+static int __devexit es305_device_remove(struct platform_device *pdev)
+{
+ pr_info("es305: codec device remove called\n");
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver es305_codec_driver = {
+ .driver = {
+ .name = "es305_codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = es305_device_probe,
+ .remove = es305_device_remove,
+};
+
+static int __init es305_init(void)
+{
+ pr_info("called\n");
+ return platform_driver_register(&es305_codec_driver);
+}
+module_init(es305_init);
+
+static void __exit es305_exit(void)
+{
+ pr_info("called\n");
+ platform_driver_unregister(&es305_codec_driver);
+}
+module_exit(es305_exit);
+
+MODULE_DESCRIPTION("ASoC es305 codec driver");
+MODULE_AUTHOR("Selma Bensaid <selma.bensaid@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:es305");
+
--- /dev/null
+/*
+ * ifx_modem.c - ALSA ASOC driver for IFX1130 in Combined Interface Mode
+ *
+ * Copyright (C) 2011 Intel Corp
+ * Author:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#define FORMAT(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+
+static struct snd_soc_dai_driver ifx_modem_dai[] = {
+ {
+ .name = "IFX_Modem_Mixing",
+ .id = 0,
+ .playback = {
+ .stream_name = "IFX_Modem_Mix",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "IFX_Modem_Record",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = NULL,
+ },
+
+};
+
+static struct snd_soc_codec_driver soc_codec_ifx_modem = {
+ .probe = NULL,
+ .remove = NULL,
+};
+
+
+static int __devinit ifx_modem_device_probe(struct platform_device *pdev)
+{
+ pr_info("codec device probe called for %s\n",
+ dev_name(&pdev->dev));
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_ifx_modem,
+ ifx_modem_dai, ARRAY_SIZE(ifx_modem_dai));
+}
+
+static int __devexit ifx_modem_device_remove(struct platform_device *pdev)
+{
+ pr_info("codec device remove called\n");
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver ifx_modem_codec_driver = {
+ .driver = {
+ .name = "ifx_modem",
+ .owner = THIS_MODULE,
+ },
+ .probe = ifx_modem_device_probe,
+ .remove = ifx_modem_device_remove,
+};
+
+static int __init ifx_modem_init(void)
+{
+ pr_info("called\n");
+ return platform_driver_register(&ifx_modem_codec_driver);
+}
+module_init(ifx_modem_init);
+
+static void __exit ifx_modem_exit(void)
+{
+ pr_info("called\n");
+ platform_driver_unregister(&ifx_modem_codec_driver);
+}
+module_exit(ifx_modem_exit);
+
+MODULE_DESCRIPTION("ASoC IFX Modem codec driver");
+MODULE_AUTHOR("Selma Bensaid <selma.bensaid@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ifx-modem");
+
--- /dev/null
+/*
+ * wl1273_uart.c - ALSA ASOC driver for WL1273 in Combined Interface Mode
+ *
+ * Copyright (C) 2012 Intel Corp
+ * Author:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#define FORMAT(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#define WL1273_BT_CODEC_NAME "wl1273_BT"
+#define WL1273_FM_CODEC_NAME "wl1273_FM"
+
+static struct snd_soc_codec_driver soc_codec_wl1273_uart = {
+ .probe = NULL,
+ .remove = NULL,
+};
+
+static int wl1273_uart_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ /*
+ * The FM and BT stream cannot be open in //
+ */
+ if (!strcmp(codec_dai->name, WL1273_FM_CODEC_NAME)) {
+ if (codec->active) {
+ WARN(1, "WL1273 UART: Open %s unsupported Config\n",
+ codec_dai->name);
+ return -EBUSY;
+ }
+ } else if (!strcmp(codec_dai->name, WL1273_BT_CODEC_NAME)) {
+ if (codec->active &&
+ (!codec_dai->playback_active) &&
+ (!codec_dai->capture_active)) {
+ WARN(1, "WL1273 UART: Open %s unsupported Config\n",
+ codec_dai->name);
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+static struct snd_soc_dai_ops wl1273_uart_dai_ops = {
+ .startup = wl1273_uart_startup,
+};
+
+static struct snd_soc_dai_driver wl1273_uart_dai[] = {
+ {
+ .name = WL1273_BT_CODEC_NAME,
+ .id = 0,
+ .playback = {
+ .stream_name = "BT_Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "BT_Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wl1273_uart_dai_ops,
+ },
+ {
+ .name = WL1273_FM_CODEC_NAME,
+ .id = 1,
+ .playback = {
+ .stream_name = "FM_Playback",
+ .channels_min = 0,
+ .channels_max = 0,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "FM_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &wl1273_uart_dai_ops,
+ },
+};
+
+static int __devinit wl1273_uart_device_probe(struct platform_device *pdev)
+{
+ pr_info("codec device probe called for %s\n",
+ dev_name(&pdev->dev));
+
+ return snd_soc_register_codec(&pdev->dev, &soc_codec_wl1273_uart,
+ wl1273_uart_dai, ARRAY_SIZE(wl1273_uart_dai));
+}
+
+static int __devexit wl1273_uart_device_remove(struct platform_device *pdev)
+{
+ pr_info("codec device remove called\n");
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wl1273_uart_codec_driver = {
+ .driver = {
+ .name = "wl1273_uart",
+ .owner = THIS_MODULE,
+ },
+ .probe = wl1273_uart_device_probe,
+ .remove = wl1273_uart_device_remove,
+};
+
+static int __init wl1273_uart_init(void)
+{
+ pr_info("FCT %s called\n", __func__);
+ return platform_driver_register(&wl1273_uart_codec_driver);
+}
+module_init(wl1273_uart_init);
+
+static void __exit wl1273_uart_exit(void)
+{
+ pr_info("FCT %s called\n", __func__);
+ platform_driver_unregister(&wl1273_uart_codec_driver);
+}
+module_exit(wl1273_uart_exit);
+
+MODULE_DESCRIPTION("ASoC TI WL1273 codec driver");
+MODULE_AUTHOR("Selma Bensaid <selma.bensaid@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:wl1273-uart");
+
Say Y if you have device with mono speaker. If you say N support for stereo speakers
will be enabled.
If unsure select "N".
+
+
+config SND_MFLD_COMMS_MACHINE
+ tristate "Audio Comms SOC Machine Audio driver for Intel Medfield MID platform"
+ depends on EXPERIMENTAL && SND_INTEL_MID_I2S
+ select SND_SOC_WL1273_UART
+ select SND_SOC_IFX1130_MODEM_CODEC
+ select SND_SOC_ES305
+ select SND_SOC_SSP
+ help
+ Say Y here to include support for ALSA SSP soundcard.
+ To compile this driver as a module, choose M here: the module
+ will be called snd-alsa-ssp.ko
+ This is a sound card driver for TI LNW1273 BT/FM Board
+ This ALSA driver offers:
+ - BT device with 1 capture and 1 playback substreams,
+ for BT SCO voice memp record and playback
+ - FM device with 1 capture substream.
+ - VOIP device with 1 capture and 1 playback substreams
+ - IFX Modem device with 1 capture and 1 playback substreams,
+ for audio/tone/alert mixing during Voice Call and Voice Call record
+
+config SND_SOC_SSP
+ tristate
# DSP driver
obj-$(CONFIG_SND_INTEL_SST) += sst/
+
+# Audio Comms
+snd-soc-comms-machine-objs := mfld_comms_machine.o
+snd-soc-ssp-objs := mfld_ssp.o
+obj-$(CONFIG_SND_MFLD_COMMS_MACHINE) += mfld_comms_machine.o
+obj-$(CONFIG_SND_SOC_SSP) += mid_ssp.o
--- /dev/null
+/*
+ * mfld_ssp_wl1273_machine.c - ASoc Machine driver for
+ *
+ * Copyright (C) 2012 Intel Corp
+ * Author: Selma Bensaid <selma.bensaidl@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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define FORMAT(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include "mfld_comms_machine.h"
+
+/*****************************************/
+/* Global Variables */
+/*****************************************/
+
+static struct snd_soc_ops mfld_comms_dai_link_ops = {
+ .startup = mfld_comms_dai_link_startup,
+ .hw_params = mfld_comms_dai_link_hw_params,
+};
+
+/*
+ * MIXER CONTROLS for BT SCO stream
+ */
+static const char * const ssp_master_mode_text[] = {"disabled", "enabled"};
+static const struct soc_enum ssp_master_mode_enum =
+ SOC_ENUM_SINGLE_EXT(2, ssp_master_mode_text);
+
+
+static int get_ssp_master_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct comms_mc_private *comms_ctx =
+ snd_soc_card_get_drvdata(codec->card);
+ ucontrol->value.integer.value[0] = comms_ctx->ssp_master_mode;
+ return 0;
+}
+
+static int set_ssp_master_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct comms_mc_private *comms_ctx =
+ snd_soc_card_get_drvdata(codec->card);
+
+ if (ucontrol->value.integer.value[0] == comms_ctx->ssp_master_mode)
+ return 0;
+
+ comms_ctx->ssp_master_mode = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+
+static const struct snd_kcontrol_new ssp_snd_controls[] = {
+ SOC_ENUM_EXT("SSP Master Mode", ssp_master_mode_enum,
+ get_ssp_master_mode, set_ssp_master_mode),
+};
+
+
+
+static int mfld_comms_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_codec *codec = runtime->codec;
+ struct comms_mc_private *comms_ctx =
+ snd_soc_card_get_drvdata(codec->card);
+ int ret_val;
+
+ comms_ctx->ssp_master_mode = false;
+
+ ret_val = snd_soc_add_controls(codec, ssp_snd_controls,
+ ARRAY_SIZE(ssp_snd_controls));
+ if (ret_val)
+ pr_err("MFLD Comms Machine: Init failed %d",
+ ret_val);
+
+
+ return ret_val;
+}
+
+/*
+ * DAI LINK DEFINITIONS
+ */
+
+
+struct snd_soc_dai_link mfld_comms_dailink[] = {
+ [BT_SCO_DEV] = {
+ .name = "BT SCO",
+ .stream_name = "BTSCO",
+ .cpu_dai_name = SSP_BT_DAI_NAME,
+ .codec_dai_name = "wl1273_BT",
+ .codec_name = "wl1273_uart",
+ .platform_name = "mid-ssp-dai",
+ .init = mfld_comms_init,
+ .ops = &mfld_comms_dai_link_ops,
+ },
+ [FM_DEV] = {
+ .name = "FM",
+ .stream_name = "FM",
+ .cpu_dai_name = SSP_BT_DAI_NAME,
+ .codec_dai_name = "wl1273_FM",
+ .codec_name = "wl1273_uart",
+ .platform_name = "mid-ssp-dai",
+ .init = mfld_comms_init,
+ .ops = &mfld_comms_dai_link_ops,
+ },
+ [MSIC_VOIP_DEV] = {
+ .name = "MSIC VOIP",
+ .stream_name = "VOIP",
+ .cpu_dai_name = SSP_BT_DAI_NAME,
+ .codec_dai_name = "es305 Voice",
+ .codec_name = "es305_codec",
+ .platform_name = "mid-ssp-dai",
+ .init = mfld_comms_init,
+ .ops = &mfld_comms_dai_link_ops,
+ },
+ [IFX_MODEM_DEV] = {
+ .name = "IFX MODEM",
+ .stream_name = "IFX_MODEM_MIXING",
+ .cpu_dai_name = SSP_MODEM_DAI_NAME,
+ .codec_dai_name = "IFX_Modem_Mixing",
+ .codec_name = "ifx_modem",
+ .platform_name = "mid-ssp-dai",
+ .init = mfld_comms_init,
+ .ops = &mfld_comms_dai_link_ops,
+ },
+};
+
+/*
+ * DAI LINK OPS
+ */
+
+static int mfld_comms_dai_link_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *str_runtime;
+
+ str_runtime = substream->runtime;
+
+ WARN(!substream->pcm, "MFLD Comms Machine: ERROR "
+ "NULL substream->pcm\n");
+
+ if (!substream->pcm)
+ return -EINVAL;
+
+ /* set the runtime hw parameter with local snd_pcm_hardware struct */
+ switch (substream->pcm->device) {
+ case BT_SCO_DEV:
+ str_runtime->hw = BT_soc_hw_param;
+ break;
+ case FM_DEV:
+ str_runtime->hw = FM_soc_hw_param;
+ break;
+ case MSIC_VOIP_DEV:
+ str_runtime->hw = VOIP_alsa_hw_param;
+ break;
+ case IFX_MODEM_DEV:
+ str_runtime->hw = IFX_modem_alsa_hw_param;
+ break;
+ default:
+ pr_err("MFLD Comms Machine: bad PCM Device = %d\n",
+ substream->pcm->device);
+ }
+ return snd_pcm_hw_constraint_integer(str_runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+
+static int mfld_comms_dai_link_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 *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct comms_mc_private *comms_ctx =
+ snd_soc_card_get_drvdata(soc_card);
+
+ int ret = 0;
+ unsigned int tx_mask, rx_mask;
+ unsigned int nb_slot = 0;
+ unsigned int slot_width = 0;
+ unsigned int tristate_offset = 0;
+ unsigned int device = substream->pcm->device;
+
+
+ switch (device) {
+ case BT_SCO_DEV:
+ /*
+ * set cpu DAI configuration
+ * frame_format = PSP_FORMAT
+ * ssp_serial_clk_mode = SSP_CLK_MODE_1
+ * sspslclk_direction = SSPSCLK_MASTER_MODE
+ * sspsfrm_direction = SSPSFRM_MASTER_MODE
+ */
+
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_IF |
+ (comms_ctx->ssp_master_mode ?
+ SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBS_CFS));
+
+ if (ret < 0) {
+ pr_err("MFLD Comms Machine: Set FMT Fails %d\n", ret);
+ return -EINVAL;
+ }
+
+ /*
+ * BT SCO SSP Config
+ * ssp_active_tx_slots_map = 0x01
+ * ssp_active_rx_slots_map = 0x01
+ * frame_rate_divider_control = 1
+ * data_size = 16
+ * tristate = 1
+ * ssp_frmsync_timing_bit = 0
+ * (NEXT_FRMS_ASS_AFTER_END_OF_T4)
+ * ssp_frmsync_timing_bit = 1
+ * (NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM)
+ * ssp_psp_T2 = 1
+ * (Dummy start offset = 1 bit clock period)
+ */
+ nb_slot = SSP_BT_SLOT_NB_SLOT;
+ slot_width = SSP_BT_SLOT_WIDTH;
+ tx_mask = SSP_BT_SLOT_TX_MASK;
+ rx_mask = SSP_BT_SLOT_RX_MASK;
+
+ if (comms_ctx->ssp_master_mode)
+ tristate_offset = BIT(TRISTATE_BIT) |
+ BIT(DUMMY_START_ONE_PERIOD_OFFSET);
+ else
+ tristate_offset = BIT(FRAME_SYNC_RELATIVE_TIMING_BIT);
+ break;
+ case FM_DEV:
+ /*
+ * set cpu DAI configuration
+ * frame_format = PSP_FORMAT
+ * ssp_serial_clk_mode = SSP_CLK_MODE_1
+ * sspslclk_direction = SSPSCLK_MASTER_MODE
+ * sspsfrm_direction = SSPSFRM_MASTER_MODE
+ */
+
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ (comms_ctx->ssp_master_mode ?
+ SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBS_CFS));
+
+ if (ret < 0) {
+ pr_err("MFLD Comms Machine: Set FMT Fails %d\n", ret);
+ return -EINVAL;
+ }
+
+ /*
+ * FM SSP Config
+ * ssp_active_tx_slots_map = 0x00
+ * ssp_active_rx_slots_map = 0x01
+ * frame_rate_divider_control = 1
+ * data_size = 32
+ * tristate = 1
+ * ssp_frmsync_timing_bit = 1
+ * (NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM)
+ *
+ */
+ nb_slot = SSP_FM_SLOT_NB_SLOT;
+ slot_width = SSP_FM_SLOT_WIDTH;
+ tx_mask = SSP_FM_SLOT_TX_MASK;
+ rx_mask = SSP_FM_SLOT_RX_MASK;
+ tristate_offset = BIT(TRISTATE_BIT) |
+ BIT(DUMMY_START_ONE_PERIOD_OFFSET);
+ break;
+ case MSIC_VOIP_DEV:
+ /*
+ * set cpu DAI configuration
+ * frame_format = PSP_FORMAT
+ * ssp_serial_clk_mode = SSP_CLK_MODE_1
+ * sspslclk_direction = SSPSCLK_SLAVE_MODE
+ * sspsfrm_direction = SSPSFRM_SLAVE_MODE
+ */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ (comms_ctx->ssp_master_mode ?
+ SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBS_CFS));
+
+ if (ret < 0) {
+ pr_err("MFLD Comms Machine: Set FMT Fails %d\n",
+ ret);
+ return -EINVAL;
+ }
+
+ /*
+ * MSIC VOIP SSP Config
+ * ssp_active_tx_slots_map = 0x01
+ * ssp_active_rx_slots_map = 0x01
+ * frame_rate_divider_control = 1
+ * data_size = 32
+ * tristate = 1
+ * ssp_frmsync_timing_bit = 0, for SLAVE
+ * (NEXT_FRMS_ASS_AFTER_END_OF_T4)
+ * ssp_frmsync_timing_bit = 1, for MASTER
+ * (NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM)
+ *
+ *
+ */
+ nb_slot = SSP_VOIP_SLOT_NB_SLOT;
+ slot_width = SSP_VOIP_SLOT_WIDTH;
+ tx_mask = SSP_VOIP_SLOT_TX_MASK;
+ rx_mask = SSP_VOIP_SLOT_RX_MASK;
+
+ if (comms_ctx->ssp_master_mode)
+ tristate_offset = BIT(TRISTATE_BIT) |
+ BIT(FRAME_SYNC_RELATIVE_TIMING_BIT);
+ else
+ tristate_offset = BIT(FRAME_SYNC_RELATIVE_TIMING_BIT);
+ break;
+ case IFX_MODEM_DEV:
+ /*
+ * set cpu DAI configuration
+ * frame_format = PSP_FORMAT
+ * ssp_serial_clk_mode = SSP_CLK_MODE_1
+ * Master:
+ * sspslclk_direction = SSPSCLK_MASTER_MODE
+ * sspsfrm_direction = SSPSFRM_MASTER_MODE
+ * Slave:
+ * sspslclk_direction = SSPSCLK_SLAVE_MODE
+ * sspsfrm_direction = SSPSFRM_SLAVE_MODE
+ */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
+ (comms_ctx->ssp_master_mode ?
+ SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBS_CFS));
+ if (ret < 0) {
+ pr_err("MFLD Comms Machine: Set FMT Fails %d\n", ret);
+ return -EINVAL;
+ }
+
+ /*
+ * IFX Modem Mixing SSP Config
+ * ssp_active_tx_slots_map = 0x01
+ * ssp_active_rx_slots_map = 0x01
+ * frame_rate_divider_control = 1
+ * data_size = 32
+ * Master:
+ * tristate = 3
+ * ssp_frmsync_timing_bit = 1, for MASTER
+ * (NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM)
+ * Slave:
+ * tristate = 1
+ * ssp_frmsync_timing_bit = 0, for SLAVE
+ * (NEXT_FRMS_ASS_AFTER_END_OF_T4)
+ *
+ */
+ nb_slot = SSP_IFX_SLOT_NB_SLOT;
+ slot_width = SSP_IFX_SLOT_WIDTH;
+ tx_mask = SSP_IFX_SLOT_TX_MASK;
+ rx_mask = SSP_IFX_SLOT_RX_MASK;
+
+ if (comms_ctx->ssp_master_mode)
+ tristate_offset = BIT(TRISTATE_BIT) |
+ BIT(FRAME_SYNC_RELATIVE_TIMING_BIT);
+ else
+ tristate_offset = BIT(TRISTATE_BIT);
+ break;
+ default:
+ pr_err("MFLD Comms Machine: bad PCM Device ID = %d\n", device);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_mask,
+ rx_mask, nb_slot, slot_width);
+
+ if (ret < 0) {
+ pr_err("MFLD Comms Machine: Set TDM Slot Fails %d\n", ret);
+ return -EINVAL;
+ }
+
+ /* select clock source (if master) */
+ /* BT SCO: CPU DAI is master */
+ /* FM: CPU DAI is master */
+ /* BT_VOIP: CPU DAI is master */
+ if ((device == BT_SCO_DEV && comms_ctx->ssp_master_mode) ||
+ (device == FM_DEV) ||
+ ((device == MSIC_VOIP_DEV) && comms_ctx->ssp_master_mode) ||
+ ((device == IFX_MODEM_DEV) && comms_ctx->ssp_master_mode)) {
+
+ snd_soc_dai_set_sysclk(cpu_dai, SSP_CLK_ONCHIP,
+ substream->runtime->hw.rates, 0);
+ }
+
+
+ pr_info("MFLD Comms Machine: slot_width = %d\n",
+ slot_width);
+ pr_info("MFLD Comms Machine: tx_mask = %d\n",
+ tx_mask);
+ pr_info("MFLD Comms Machine: rx_mask = %d\n",
+ rx_mask);
+ pr_info("MFLD Comms Machine: tristate_offset = %d\n",
+ tristate_offset);
+
+ ret = snd_soc_dai_set_tristate(cpu_dai, tristate_offset);
+ if (ret < 0) {
+ pr_err("MFLD Comms Machine: Set Tristate Fails %d\n",
+ ret);
+ return -EINVAL;
+ }
+
+
+ return 0;
+} /* mfld_comms_dai_link_hw_params*/
+
+/* SoC card */
+static struct snd_soc_card snd_comms_card_mfld = {
+ .name = "SspCommsAudio",
+ .dai_link = mfld_comms_dailink,
+ .num_links = ARRAY_SIZE(mfld_comms_dailink),
+};
+
+static int __devinit snd_mfld_comms_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+ struct comms_mc_private *comms_ctx;
+
+ pr_info("MFLD Comms Machine: FCT %s enters\n",
+ __func__);
+
+ /* register the soc card */
+ snd_comms_card_mfld.dev = &pdev->dev;
+
+ snd_soc_initialize_card_lists(&snd_comms_card_mfld);
+
+ ret_val = snd_soc_register_card(&snd_comms_card_mfld);
+ if (ret_val) {
+ pr_err("MFLD Comms Machine: card "
+ "registeration failed %d\n",
+ ret_val);
+ return -EBUSY;
+ }
+
+ comms_ctx = kzalloc(sizeof(struct comms_mc_private), GFP_KERNEL);
+ if (comms_ctx == NULL) {
+ pr_err("Unable to allocate comms_ctx\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, &snd_comms_card_mfld);
+ snd_soc_card_set_drvdata(&snd_comms_card_mfld, comms_ctx);
+
+ return 0;
+} /* snd_mfld_comms_probe */
+
+static int __devexit snd_mfld_comms_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *soc_card = platform_get_drvdata(pdev);
+ struct comms_mc_private *comms_ctx = snd_soc_card_get_drvdata(soc_card);
+
+ pr_info("MFLD Comms Machine: FCT %s enters\n",
+ __func__);
+ kfree(comms_ctx);
+ snd_soc_card_set_drvdata(soc_card, NULL);
+ snd_soc_unregister_card(&snd_comms_card_mfld);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+} /* snd_mfld_comms_remove */
+
+
+static struct platform_driver snd_mfld_comms_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mfld-comms-audio",
+ },
+ .probe = snd_mfld_comms_probe,
+ .remove = __devexit_p(snd_mfld_comms_remove),
+};
+
+static int __init snd_mfld_comms_init(void)
+{
+ pr_info("MFLD Comms Machine: FCT %s enters\n",
+ __func__);
+ return platform_driver_register(&snd_mfld_comms_driver);
+}
+module_init(snd_mfld_comms_init);
+
+static void __exit snd_mfld_comms_exit(void)
+{
+ pr_info("MFLD Comms Machine: FCT %s enters\n",
+ __func__);
+ platform_driver_unregister(&snd_mfld_comms_driver);
+}
+module_exit(snd_mfld_comms_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
+MODULE_AUTHOR("Selma Bensaid");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ssp-bt-fm-audio");
--- /dev/null
+/*
+ * mfld_ssp_wl1273_machine.c - ASoC Machine driver for
+ * Intel Medfield MID platform
+ *
+ * Copyright (C) 2011 Intel Corp
+ * Author: Selma Bensaid <selma.bensaidl@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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef MFLD_SSP_WL1273_MACHINE_H_
+#define MFLD_SSP_WL1273_MACHINE_H_
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "mid_ssp.h"
+
+/*
+ * Structures Definition
+ */
+
+struct comms_mc_private {
+ bool ssp_master_mode;
+};
+
+static int mfld_comms_dai_link_startup(struct snd_pcm_substream *substream);
+static int mfld_comms_dai_link_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params);
+
+/* Data path functionalities */
+struct snd_pcm_hardware BT_soc_hw_param = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = (320*1024),
+ .period_bytes_min = 32,
+ .period_bytes_max = (320*1024),
+ .periods_min = 2,
+ .periods_max = (1024*2),
+ .fifo_size = 0,
+};
+
+struct snd_pcm_hardware FM_soc_hw_param = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = (SNDRV_PCM_RATE_48000),
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (640*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (640*1024),
+ .periods_min = 2,
+ .periods_max = (1024*2),
+ .fifo_size = 0,
+};
+
+/* Data path functionalities */
+struct snd_pcm_hardware IFX_modem_alsa_hw_param = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = (SNDRV_PCM_RATE_48000),
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (640*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (640*1024),
+ .periods_min = 2,
+ .periods_max = (1024*2),
+ .fifo_size = 0,
+};
+
+/* Data path functionalities */
+struct snd_pcm_hardware VOIP_alsa_hw_param = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
+ .rates = (SNDRV_PCM_RATE_48000),
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (640*1024),
+ .period_bytes_min = 64,
+ .period_bytes_max = (640*1024),
+ .periods_min = 2,
+ .periods_max = (1024*2),
+ .fifo_size = 0,
+};
+
+
+enum {
+ BT_SCO_DEV = 0,
+ FM_DEV,
+ MSIC_VOIP_DEV,
+ IFX_MODEM_DEV,
+};
+
+/*
+ * For FM 1 slot of 32 bits is used
+ * to transfer stereo 16 bits PCM samples
+ */
+#define SSP_FM_SLOT_NB_SLOT 1
+#define SSP_FM_SLOT_WIDTH 32
+#define SSP_FM_SLOT_RX_MASK 0x1
+#define SSP_FM_SLOT_TX_MASK 0x0
+/*
+ * For BT SCO 1 slot of 16 bits is used
+ * to transfer mono 16 bits PCM samples
+ */
+#define SSP_BT_SLOT_NB_SLOT 1
+#define SSP_BT_SLOT_WIDTH 16
+#define SSP_BT_SLOT_RX_MASK 0x1
+#define SSP_BT_SLOT_TX_MASK 0x1
+
+/*
+ * For VoIP 1 slot of 32 bits is used
+ * to transfer stereo 16 bits PCM samples
+ */
+#define SSP_VOIP_SLOT_NB_SLOT 1
+#define SSP_VOIP_SLOT_WIDTH 16
+#define SSP_VOIP_SLOT_RX_MASK 0x1
+#define SSP_VOIP_SLOT_TX_MASK 0x1
+
+/*
+ * For Modem IFX 1 slot of 32 bits is used
+ * to transfer stereo 16 bits PCM samples
+ */
+#define SSP_IFX_SLOT_NB_SLOT 1
+#define SSP_IFX_SLOT_WIDTH 32
+#define SSP_IFX_SLOT_RX_MASK 0x1
+#define SSP_IFX_SLOT_TX_MASK 0x1
+
+
+#endif /* MFLD_SSP_WL1273_MACHINE_H_ */
--- /dev/null
+/*
+ * mid_ssp.c - ASoC CPU DAI driver for
+ *
+ * Copyright (C) 2012 Intel Corp
+ * Author:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#define FORMAT(fmt) "%s: " fmt, __func__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include "mid_ssp.h"
+
+/*
+ * Default I2S configuration
+ */
+/*
+ * TO BE DONE: use mixer to make it more flexible
+ */
+const struct intel_mid_i2s_settings ssp_platform_i2s_config = {
+ .master_mode_clk_selection = SSP_MASTER_CLOCK_UNDEFINED,
+ .master_mode_standard_freq = 0xFFFF,
+ .tx_tristate_phase = TXD_TRISTATE_LAST_PHASE_OFF,
+ .slave_clk_free_running_status =
+ SLAVE_SSPCLK_ON_DURING_TRANSFER_ONLY,
+ .ssp_duplex_mode = RX_AND_TX_MODE,
+ .ssp_trailing_byte_mode = SSP_TRAILING_BYTE_HDL_BY_IA,
+ .ssp_tx_dma = SSP_TX_DMA_ENABLE,
+ .ssp_rx_dma = SSP_RX_DMA_ENABLE,
+ .rx_fifo_interrupt = SSP_RX_FIFO_OVER_INT_ENABLE,
+ .tx_fifo_interrupt = SSP_TX_FIFO_UNDER_INT_ENABLE,
+ .ssp_rx_timeout_interrupt_status = SSP_RX_TIMEOUT_INT_DISABLE,
+ .ssp_trailing_byte_interrupt_status =
+ SSP_TRAILING_BYTE_INT_ENABLE,
+ .ssp_loopback_mode_status = SSP_LOOPBACK_OFF,
+ .ssp_rx_fifo_threshold = MID_SSP_RX_FIFO_THRESHOLD,
+ .ssp_tx_fifo_threshold = MID_SSP_TX_FIFO_THRESHOLD,
+ .ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_HIGH,
+ .ssp_end_transfer_state =
+ SSP_END_DATA_TRANSFER_STATE_LOW,
+ .ssp_psp_T1 = 0,
+ .ssp_psp_T2 = 0,
+ .ssp_psp_T4 = 0,
+ .ssp_psp_T5 = 0,
+ .ssp_psp_T6 = 1,
+};
+
+/*
+ * SSP DAI Internal functions
+ */
+
+/**
+ * ssp_dma_req - This function programs a write or read request
+ * to the Intel I2S driver
+ *
+ * @param substream Pointer to stream structure
+ * return ret_val Status
+ */
+static int ssp_dma_req(struct snd_pcm_substream *substream)
+{
+
+ struct intel_alsa_ssp_stream_info *str_info;
+ struct intel_ssp_config *ssp_config;
+ struct snd_pcm_runtime *pl_runtime;
+ u32 *dma_addr;
+ int ret;
+ WARN(!substream, "SSP DAI: "
+ "ERROR NULL substream\n");
+ if (!substream)
+ return -EINVAL;
+
+
+ pl_runtime = substream->runtime;
+
+ str_info = pl_runtime->private_data;
+
+ WARN(!str_info, "SSP DAI: "
+ "ERROR NULL str_info\n");
+ if (!str_info)
+ return -EINVAL;
+
+ ssp_config = str_info->ssp_config;
+
+ WARN(!ssp_config, "SSP DAI: "
+ "ERROR NULL ssp_config\n");
+ if (!ssp_config)
+ return -EINVAL;
+
+
+ WARN(!ssp_config->i2s_handle, "SSP DAI: "
+ "ERROR, trying to play a stream however "
+ "ssp_config->i2s_handle is NULL\n");
+
+ if (!ssp_config->i2s_handle)
+ return -EINVAL;
+
+ str_info->length = frames_to_bytes(pl_runtime, pl_runtime->period_size);
+
+ str_info->addr = substream->runtime->dma_area;
+ pr_debug("SSP DAI: FCT %s substream->runtime->dma_area = %p",
+ __func__, substream->runtime->dma_area);
+
+ dma_addr = (u32 *)(str_info->addr +
+ str_info->length * str_info->period_req_index);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = intel_mid_i2s_wr_req(ssp_config->i2s_handle, dma_addr,
+ str_info->length, substream);
+ else
+ ret = intel_mid_i2s_rd_req(ssp_config->i2s_handle, dma_addr,
+ str_info->length, substream);
+
+ if (ret == 0) {
+
+ intel_mid_i2s_command(ssp_config->i2s_handle,
+ SSP_CMD_ENABLE_SSP, NULL);
+
+ if (test_and_set_bit(INTEL_ALSA_SSP_STREAM_RUNNING,
+ &str_info->stream_status)) {
+
+ pr_err("SSP DAI: "
+ "ERROR previous requested not handled\n");
+ return -EBUSY;
+ }
+
+ if (++(str_info->period_req_index) >= pl_runtime->periods)
+ str_info->period_req_index = 0;
+ return 0;
+ } else {
+ pr_err("SSP DAI: FCT %s read/write req ERROR\n",
+ __func__);
+ return -EINVAL;
+ }
+} /* ssp_dma_req */
+
+/**
+ * ssp_dma_complete - End of capture or playback callback
+ * called in DMA Complete Tasklet context
+ * This Callback has in charge of re-programming a new read or write
+ * request to Intel MID I2S Driver if the stream has not been Closed.
+ * It calls also the snd_pcm_period_elapsed if the stream is not
+ * PAUSED or SUSPENDED to inform ALSA Kernel that the Ring Buffer
+ * period has been sent or received properly
+ *
+ * @param param Pointer to a structure
+ * return status
+ */
+static int ssp_dma_complete(void *param)
+{
+ struct snd_pcm_substream *substream;
+ struct intel_alsa_ssp_stream_info *str_info;
+ struct snd_pcm_runtime *pl_runtime;
+ bool call_back = false;
+ bool reset_index = false;
+
+ substream = (struct snd_pcm_substream *)param;
+ pl_runtime = substream->runtime;
+ str_info = substream->runtime->private_data;
+
+ WARN(!str_info, "SSP DAI: ERROR NULL str_info\n");
+ if (!str_info)
+ return -EINVAL;
+
+ if (test_and_clear_bit(INTEL_ALSA_SSP_STREAM_RUNNING,
+ &str_info->stream_status)) {
+ bool dropped = test_and_clear_bit(INTEL_ALSA_SSP_STREAM_DROPPED,
+ &str_info->stream_status);
+ bool started = test_bit(INTEL_ALSA_SSP_STREAM_STARTED,
+ &str_info->stream_status);
+
+ if (started) {
+ /*
+ * Whatever dropped or not,
+ * the stream is on going
+ */
+ call_back = true;
+ }
+ if (started && dropped) {
+ /*
+ * the stream has been dropped and restarted
+ * before the callback occurs
+ * in this case the we have to reprogram the
+ * requests to SSP driver
+ * and reset the stream's indexes
+ */
+ reset_index = true;
+ }
+
+ if (!started && !dropped) {
+ pr_err("SSP DAI: FCT %s neither started nor dropped",
+ __func__);
+ return -EBUSY;
+ }
+
+ } else {
+ pr_err("SSP DAI: FCT %s called while not running ",
+ __func__);
+ return -EBUSY;
+ }
+
+ if (call_back == true) {
+ pr_debug("SSP DAI: playback/capture (REQ=%d,CB=%d): "
+ "DMA_REQ_COMPLETE\n",
+ str_info->period_req_index,
+ str_info->period_cb_index);
+
+ if (reset_index) {
+
+ str_info->period_cb_index = 0;
+ str_info->period_req_index = 0;
+
+ } else if (++(str_info->period_cb_index) >= pl_runtime->periods)
+ str_info->period_cb_index = 0;
+
+ /*
+ * Launch the next Capture/Playback request if
+ * no CLOSE has been requested
+ */
+ ssp_dma_req(substream);
+
+ /*
+ * Call the snd_pcm_period_elapsed to inform ALSA kernel
+ * that a ringbuffer period has been played
+ */
+ snd_pcm_period_elapsed(substream);
+
+ }
+ return 0;
+} /* ssp_dma_complete */
+
+/**
+ * intel_mid_ssp_transfer_data - send data buffers
+ *
+ * @param work Pointer to stream structure
+ * return void
+ */
+void intel_mid_ssp_transfer_data(struct work_struct *work)
+{
+ struct intel_alsa_ssp_stream_info *str_info;
+ struct snd_pcm_substream *substream;
+
+ BUG_ON(!work);
+
+ str_info = container_of(work, struct intel_alsa_ssp_stream_info,
+ ssp_ws);
+
+ BUG_ON(!str_info);
+
+ substream = str_info->substream;
+
+ BUG_ON(!substream);
+
+ ssp_dma_req(substream);
+
+} /* intel_mid_ssp_transfer_data */
+
+/*
+ * SSP PLATFORM
+ */
+
+/*
+ * SSP Platform functions
+ */
+static int ssp_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
+{
+ int retval = 0;
+ struct snd_soc_dai *dai;
+ struct snd_pcm *pcm;
+
+ pr_debug("SSP DAI: FCT %s enters\n",
+ __func__);
+ /*
+ * Do pre-allocation to all substreams of the given pcm for the
+ * specified DMA type.
+ *
+ */
+ dai = soc_runtime->cpu_dai;
+ pcm = soc_runtime->pcm;
+
+ 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),
+ SSP_MIN_BUFFER, SSP_MAX_BUFFER);
+
+ if (retval) {
+ pr_err("DMA buffer allocation fail\n");
+ return retval;
+ }
+ }
+ return retval;
+} /* ssp_platform_pcm_new */
+
+static void ssp_platform_pcm_free(struct snd_pcm *pcm)
+{
+ pr_debug("SSP DAI: FCT %s enter\n",
+ __func__);
+ /*
+ * release all pre-allocated buffers on the pcm
+ *
+ */
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+
+} /* ssp_platform_pcm_free */
+
+/**
+ * ssp_platform_hw_params - Allocate memory for Ring Buffer according
+ * to hw_params.
+ * It's called in a non-atomic context
+ *
+ * @param substream Substream for which the stream function is called
+ * @param hw_params Stream command thats requested from upper layer
+ * return status 0 ==> OK
+ *
+ */
+static int ssp_platform_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int ret_val;
+
+ /*
+ * Allocates the DMA buffer for the substream
+ * This callback could be called several time
+ * snd_pcm_lib_malloc_pages allows to avoid memory leak
+ * as it release already allocated memory when already allocated
+ */
+ ret_val = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+
+ if (ret_val < 0)
+ return ret_val;
+
+ memset(substream->runtime->dma_area, 0, params_buffer_bytes(hw_params));
+
+ return 0;
+} /* ssp_platform_hw_params */
+
+/*
+ * ssp_platform_pointer- to send the current buffer pointer
+ * processed by HW
+ * This function is called by ALSA framework to get the current HW buffer ptr
+ * to check the Ring Buffer Status
+ *
+ * @param substream Pointer to the substream for which the function
+ * is called
+ *
+ * return pcm_pointer Indicates the number of samples played
+ *
+ */
+static
+snd_pcm_uframes_t ssp_platform_pointer(struct snd_pcm_substream *substream)
+{
+ struct intel_alsa_ssp_stream_info *str_info;
+ unsigned long pcm_pointer = 0;
+
+ str_info = substream->runtime->private_data;
+
+ WARN(!str_info, "SSP DAI: ERROR NULL str_info\n");
+ if (!str_info)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pcm_pointer = (unsigned long) (str_info->period_cb_index
+ * substream->runtime->period_size);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_pointer = (unsigned long) (str_info->period_cb_index
+ * substream->runtime->period_size);
+
+ pr_debug("SSP DAI: FCT %s Frame bits:: %d "
+ "period_size :: %d periods :: %d\n",
+ __func__,
+ (int) substream->runtime->frame_bits,
+ (int) substream->runtime->period_size,
+ (int) substream->runtime->periods);
+
+ pr_debug("SSP DAI: FCT %s returns %ld\n",
+ __func__, pcm_pointer);
+
+ return pcm_pointer;
+} /* ssp_platform_pointer */
+
+static struct snd_pcm_ops ssp_platform_ops = {
+ .open = NULL,
+ .close = NULL,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = ssp_platform_hw_params,
+ .hw_free = NULL,
+ .prepare = NULL,
+ .trigger = NULL,
+ .pointer = ssp_platform_pointer,
+};
+
+struct snd_soc_platform_driver soc_ssp_platform_drv = {
+ .ops = &ssp_platform_ops,
+ .pcm_new = ssp_platform_pcm_new,
+ .pcm_free = ssp_platform_pcm_free,
+};
+
+/*
+ * SND SOC DAI OPs
+ */
+static int ssp_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct intel_ssp_config *ssp_config;
+
+ pr_info("SSP DAI: FCT %s enters for CPU_DAI %d\n",
+ __func__, cpu_dai->id);
+
+ ssp_config = kzalloc(sizeof(struct intel_ssp_config), GFP_KERNEL);
+
+
+ if (ssp_config == NULL) {
+ pr_err("Unable to allocate ssp_config\n");
+ return -ENOMEM;
+ }
+
+ ssp_config->intel_mid_dma_alloc = false;
+
+ ssp_config->i2s_settings = ssp_platform_i2s_config;
+ pr_info("SSP DAI: FCT %s ssp_config %p\n",
+ __func__, ssp_config);
+
+ cpu_dai->playback_dma_data = cpu_dai->capture_dma_data = ssp_config;
+
+ return 0;
+
+} /* ssp_probe */
+
+static int ssp_remove(struct snd_soc_dai *cpu_dai)
+{
+ struct intel_ssp_config *ssp_config;
+
+ WARN(!cpu_dai, "SSP DAI: "
+ "ERROR NULL cpu_dai\n");
+ if (!cpu_dai)
+ return -EINVAL;
+
+ ssp_config = cpu_dai->playback_dma_data;
+
+ kfree(ssp_config);
+
+ return 0;
+} /* ssp_remove */
+
+static int ssp_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct intel_ssp_config *ssp_config;
+ struct snd_pcm_runtime *pl_runtime;
+ struct intel_alsa_ssp_stream_info *str_info;
+ struct snd_soc_dai_driver *cpudai_drv = cpu_dai->driver;
+ unsigned int device;
+ int ret = 0;
+ struct workqueue_struct *ssp_dai_wq;
+
+ pr_info("SSP DAI: FCT %s enters for DAI Id = %d\n",
+ __func__, cpu_dai->driver->id);
+
+
+ pl_runtime = substream->runtime;
+
+ ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ WARN(!ssp_config, "SSP DAI: "
+ "FCT %s ERROR NULL ssp_config\n",
+ __func__);
+ if (!ssp_config)
+ return -EINVAL;
+
+ ssp_dai_wq = dev_get_drvdata(cpu_dai->dev);
+
+ WARN(!cpu_dai->driver, "SSP DAI: "
+ "FCT %s ERROR NULL cpu_dai->driver\n",
+ __func__);
+ if (!cpu_dai->driver)
+ return -EINVAL;
+
+ device = cpu_dai->driver->id;
+
+ /*
+ * setup the internal data structure stream pointers based on it being
+ * playback or capture stream
+ */
+ str_info = kzalloc(sizeof(*str_info), GFP_KERNEL);
+
+ if (!str_info) {
+ pr_err("SSP DAI: str_info alloc failure\n");
+ return -EINVAL;
+
+ }
+ str_info->substream = substream;
+ str_info->ssp_config = ssp_config;
+ str_info->stream_status = 0;
+
+ INIT_WORK(&str_info->ssp_ws, intel_mid_ssp_transfer_data);
+
+ /*
+ * Initialize SSPx [x=0,1] driver
+ * Store the Stream information
+ */
+ pl_runtime->private_data = str_info;
+
+ pr_debug("SSP DAI: FCT %s enters cpu_dai->card->name = %s\n",
+ __func__, cpu_dai->card->name);
+
+ if (!cpu_dai->active) {
+ if (!strcmp(cpudai_drv->name, SSP_BT_DAI_NAME)) {
+ ssp_config->i2s_handle =
+ intel_mid_i2s_open(SSP_USAGE_BLUETOOTH_FM);
+ pr_debug("opening the CPU_DAI for SSP_USAGE_BLUETOOTH_FM, i2s_handle = %p\n",
+ ssp_config->i2s_handle);
+
+ } else if (!strcmp(cpudai_drv->name, SSP_MODEM_DAI_NAME)) {
+ ssp_config->i2s_handle =
+ intel_mid_i2s_open(SSP_USAGE_MODEM);
+ pr_debug("opening the CPU_DAI for SSP_USAGE_MODEM, i2s_handle = %p\n",
+ ssp_config->i2s_handle);
+
+ } else {
+ pr_err("non Valid SOC CARD\n");
+ return -EINVAL;
+ }
+
+ /* Set the Write Callback */
+ ret = intel_mid_i2s_set_wr_cb(ssp_config->i2s_handle,
+ ssp_dma_complete);
+ if (ret)
+ return ret;
+
+ /* Set the Default Read Callback */
+ ret = intel_mid_i2s_set_rd_cb(ssp_config->i2s_handle,
+ ssp_dma_complete);
+ if (ret)
+ return ret;
+
+ } else {
+ /*
+ * do nothing because already Open by sibling substream
+ */
+ pr_debug("SSP DAI: FCT %s Open DO NOTHING\n",
+ __func__);
+ }
+ return 0;
+} /* ssp_dai_startup */
+
+static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct intel_ssp_config *ssp_config;
+ struct intel_alsa_ssp_stream_info *str_info;
+ struct snd_pcm_runtime *runtime;
+
+ ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ BUG_ON(!ssp_config);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ /*
+ * Only Free Tx channel if no playback streams are active
+ * Shutdown can be called right after a startup if something
+ * failed (as a concurrency issue
+ * so this case can happen
+ */
+ if ((!cpu_dai->playback_active) &&
+ (ssp_config->i2s_settings.ssp_active_tx_slots_map)) {
+ intel_mid_i2s_command(ssp_config->i2s_handle,
+ SSP_CMD_FREE_TX, NULL);
+ pr_debug("SSP DAI: FCT %s TX DMA Channel released\n",
+ __func__);
+ }
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ /*
+ * Only Free Rx channel if no capture streams are active
+ */
+ if ((!cpu_dai->capture_active) &&
+ (ssp_config->i2s_settings.ssp_active_rx_slots_map)) {
+ intel_mid_i2s_command(ssp_config->i2s_handle,
+ SSP_CMD_FREE_RX, NULL);
+ pr_debug("SSP DAI: FCT %s RX DMA Channel released\n",
+ __func__);
+ }
+ break;
+
+ default:
+ pr_err("SSP DAI: FCT %s Bad stream_dir: %d\n",
+ __func__, substream->stream);
+ break;
+ }
+
+ runtime = substream->runtime;
+ str_info = runtime->private_data;
+
+ /* Cancel pending work */
+ cancel_work_sync(&str_info->ssp_ws);
+
+ kfree(str_info);
+
+ if (!cpu_dai->active) {
+ pr_info("SSP DAI: FCT %s closing I2S\n",
+ __func__);
+ /*
+ * Close the Intel MID I2S connection
+ */
+ intel_mid_i2s_close(ssp_config->i2s_handle);
+
+ ssp_config->i2s_handle = NULL;
+ ssp_config->intel_mid_dma_alloc = false;
+ }
+
+} /* ssp_dai_shutdown */
+
+static int ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct intel_ssp_config *ssp_config;
+ struct intel_mid_i2s_settings *i2s_config;
+
+ ssp_config = cpu_dai->playback_dma_data;
+
+ WARN(!ssp_config, "SSP DAI: FCT %s ssp_config=NULL\n",
+ __func__);
+ if (!ssp_config)
+ return -EINVAL;
+
+ pr_debug("SSP DAI: FCT %s fmt = %d\n",
+ __func__, fmt);
+
+ i2s_config = &(ssp_config->i2s_settings);
+
+ /*
+ * SSP CLK Direction
+ * SSP FRMSYNC Direction
+ */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2s_config->sspslclk_direction = SSPSCLK_MASTER_MODE;
+ i2s_config->sspsfrm_direction = SSPSCLK_MASTER_MODE;
+ /*
+ * Mandatory to be able to perform only RX without TX
+ * in SSP CLK Master Mode
+ *
+ */
+ i2s_config->ssp_duplex_mode = RX_WITHOUT_TX_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ i2s_config->sspslclk_direction = SSPSCLK_MASTER_MODE;
+ i2s_config->sspsfrm_direction = SSPSCLK_SLAVE_MODE;
+ /*
+ * Mandatory to be able to perform only RX without TX
+ * in SSP CLK Master Mode
+ *
+ */
+ i2s_config->ssp_duplex_mode = RX_WITHOUT_TX_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s_config->sspslclk_direction = SSPSCLK_SLAVE_MODE;
+ i2s_config->sspsfrm_direction = SSPSCLK_SLAVE_MODE;
+ i2s_config->ssp_duplex_mode = RX_AND_TX_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ i2s_config->sspslclk_direction = SSPSCLK_SLAVE_MODE;
+ i2s_config->sspsfrm_direction = SSPSCLK_MASTER_MODE;
+ i2s_config->ssp_duplex_mode = RX_AND_TX_MODE;
+ break;
+ default:
+ pr_err("SSP DAI: %s Bad DAI CLK/FS Mode=%d\n",
+ __func__,
+ (fmt & SND_SOC_DAIFMT_MASTER_MASK));
+ return -EINVAL;
+ }
+ /*
+ * SSP Signal Inversion Mode
+ */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_2;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_3;
+ break;
+ default:
+ pr_err("SSP DAI: %s Bad DAI Signal Inversion Mode=%d\n",
+ __func__,
+ (fmt & SND_SOC_DAIFMT_INV_MASK));
+ return -EINVAL;
+ }
+
+ /*
+ * SSP Format Mode
+ */
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ i2s_config->frame_format = PSP_FORMAT;
+ break;
+
+ default:
+ pr_err("SSP DAI: %s Bad DAI format Mode=%d\n",
+ __func__,
+ (fmt & SND_SOC_DAIFMT_FORMAT_MASK));
+ return -EINVAL;
+ }
+ return 0;
+} /* ssp_set_dai_fmt */
+
+static int ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct intel_ssp_config *ssp_config;
+ struct intel_mid_i2s_settings *i2s_config;
+
+ ssp_config = cpu_dai->playback_dma_data;
+
+ WARN(!ssp_config, "SSP DAI: FCT %s ssp_config=NULL\n",
+ __func__);
+ if (!ssp_config)
+ return -EINVAL;
+
+
+ i2s_config = &(ssp_config->i2s_settings);
+
+ i2s_config->frame_rate_divider_control = slots;
+ i2s_config->data_size = slot_width;
+ i2s_config->mode = SSP_IN_NETWORK_MODE;
+ i2s_config->ssp_active_tx_slots_map = tx_mask;
+ i2s_config->ssp_active_rx_slots_map = rx_mask;
+
+ pr_debug("i2s_config->frame_rate_divider_control = %d\n",
+ i2s_config->frame_rate_divider_control);
+ pr_debug("i2s_config->data_size = %d\n",
+ i2s_config->data_size);
+ pr_debug("i2s_config->mode = %d\n",
+ i2s_config->mode);
+ pr_debug("i2s_config->ssp_active_tx_slots_map = %d\n",
+ i2s_config->ssp_active_tx_slots_map);
+ pr_debug("i2s_config->ssp_active_rx_slots_map = %d\n",
+ i2s_config->ssp_active_rx_slots_map);
+
+ return 0;
+}
+
+static int ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct intel_ssp_config *ssp_config;
+ struct intel_mid_i2s_settings *i2s_config;
+
+ ssp_config = cpu_dai->playback_dma_data;
+
+ BUG_ON(!ssp_config);
+
+ i2s_config = &(ssp_config->i2s_settings);
+
+ pr_debug("SSP DAI: FCT %s clk_id = %d\n",
+ __func__, clk_id);
+
+ switch (clk_id) {
+ case SSP_CLK_ONCHIP:
+ i2s_config->master_mode_clk_selection = SSP_ONCHIP_CLOCK;
+ break;
+ case SSP_CLK_NET:
+ i2s_config->master_mode_clk_selection = SSP_NETWORK_CLOCK;
+ break;
+ case SSP_CLK_EXT:
+ i2s_config->master_mode_clk_selection = SSP_EXTERNAL_CLOCK;
+ break;
+ case SSP_CLK_AUDIO:
+ i2s_config->master_mode_clk_selection = SSP_ONCHIP_AUDIO_CLOCK;
+ break;
+ default:
+ i2s_config->master_mode_standard_freq =
+ SSP_MASTER_CLOCK_UNDEFINED;
+ pr_err("SSP DAI: %s Bad clk_id=%d\n",
+ __func__,
+ clk_id);
+ return -EINVAL;
+ }
+
+ pr_debug("SSP DAI:FCT %s freq = %d\n",
+ __func__, freq);
+
+ switch (freq) {
+ case SNDRV_PCM_RATE_48000:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_48_000;
+ break;
+ case SNDRV_PCM_RATE_44100:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_44_100;
+ break;
+ case SNDRV_PCM_RATE_22050:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_22_050;
+ break;
+ case SNDRV_PCM_RATE_16000:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_16_000;
+ break;
+ case SNDRV_PCM_RATE_11025:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_11_025;
+ break;
+ case SNDRV_PCM_RATE_8000:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_8_000;
+ break;
+ default:
+ i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_UNDEFINED;
+ pr_err("SSP DAI: %s Bad freq_out=%d\n",
+ __func__,
+ freq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
+ int tristate)
+{
+ struct intel_ssp_config *ssp_config;
+ struct intel_mid_i2s_settings *i2s_config;
+
+ ssp_config = cpu_dai->playback_dma_data;
+
+ BUG_ON(!ssp_config);
+
+ i2s_config = &(ssp_config->i2s_settings);
+
+ if (IS_TRISTATE_ENABLED(tristate))
+ i2s_config->tx_tristate_enable = TXD_TRISTATE_ON;
+ else
+ i2s_config->tx_tristate_enable = TXD_TRISTATE_OFF;
+
+ if (IS_NEXT_FRMS_ASSERTED_WITH_LSB_PREVIOUS_FRM(tristate))
+ i2s_config->ssp_frmsync_timing_bit =
+ NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM;
+ else
+ i2s_config->ssp_frmsync_timing_bit =
+ NEXT_FRMS_ASS_AFTER_END_OF_T4;
+
+ i2s_config->ssp_psp_T2 = IS_DUMMY_START_ONE_PERIOD_OFFSET(tristate);
+ pr_debug("FCT %s tristate %x\n", __func__, tristate);
+
+ return 0;
+}
+
+/**
+ * ssp_dai_trigger- stream activities are handled here
+ * This function is called whenever a stream activity is invoked
+ * The Trigger function is called in an atomic context
+ *
+ * @param substream Substream for which the stream function is called
+ * @param cmd The stream command thats requested from upper layer
+ * return status 0 ==> OK
+ *
+ */
+static int ssp_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ int ret_val = 0;
+ struct intel_alsa_ssp_stream_info *str_info;
+ struct snd_pcm_runtime *pl_runtime;
+ struct workqueue_struct *ssp_dai_wq;
+ bool trigger_start = true;
+ int stream = 0;
+
+ pr_debug("SSP DAI: FCT %s enters\n",
+ __func__);
+
+ stream = substream->stream;
+
+
+ pl_runtime = substream->runtime;
+
+ WARN(!pl_runtime->private_data, "SSP DAI: ERROR "
+ "NULL pl_runtime->private_data\n");
+ if (!pl_runtime->private_data)
+ return -EINVAL;
+
+ WARN(!cpu_dai, "SSP DAI: ERROR NULL cpu_dai\n");
+ if (!cpu_dai)
+ return -EINVAL;
+
+ WARN(!cpu_dai->dev, "SSP DAI: ERROR NULL cpu_dai->dev\n");
+ if (!cpu_dai->dev)
+ return -EINVAL;
+
+ ssp_dai_wq = dev_get_drvdata(cpu_dai->dev);
+
+
+ WARN(!ssp_dai_wq, "SSP DAI: ERROR NULL ssp_dai_wq\n");
+ if (!ssp_dai_wq)
+ return -EINVAL;
+
+ str_info = pl_runtime->private_data;
+
+ pr_debug("SSP DAI: FCT %s CMD = 0x%04X\n",
+ __func__, cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!test_and_set_bit(INTEL_ALSA_SSP_STREAM_STARTED,
+ &str_info->stream_status)) {
+ if (test_bit(INTEL_ALSA_SSP_STREAM_DROPPED,
+ &str_info->stream_status)) {
+ pr_debug("SSP DAI: FCT %s do not restart "
+ "the trigger stream running "
+ "already\n", __func__);
+ trigger_start = false;
+ } else
+ trigger_start = true;
+ } else {
+ pr_err("SSP DAI: ERROR 2 consecutive TRIGGER_START\n");
+ return -EBUSY;
+ }
+
+ /* Store the substream locally */
+ if (trigger_start) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ pr_debug("SSP DAI: queue Playback Work\n");
+ queue_work(ssp_dai_wq, &str_info->ssp_ws);
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ pr_debug("SSP DAI: queue Capture Work\n");
+ queue_work(ssp_dai_wq, &str_info->ssp_ws);
+ } else {
+ pr_err("SSP DAI: SNDRV_PCM_TRIGGER_START Bad Stream: %d\n",
+ substream->stream);
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (test_and_clear_bit(INTEL_ALSA_SSP_STREAM_STARTED,
+ &str_info->stream_status))
+
+ set_bit(INTEL_ALSA_SSP_STREAM_DROPPED,
+ &str_info->stream_status);
+ else {
+ pr_err("SSP DAI: trigger START/STOP mismatch\n");
+ return -EBUSY;
+ }
+ break;
+
+ default:
+ pr_err("SSP DAI: snd_i2s_alsa_pcm_trigger Bad Command\n");
+ return -EINVAL;
+ }
+ return ret_val;
+} /* ssp_dai_trigger */
+
+/**
+ * ssp_dai_hw_params - Allocate memory for Ring Buffer according
+ * to hw_params.
+ * It's called in a non-atomic context
+ *
+ * @param substream Substream for which the stream function is called
+ * @param hw_params Stream command thats requested from upper layer
+ * @param cpu_dai Pointer to the CPU DAI that is used
+ * return status 0 ==> OK
+ *
+ */
+static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct intel_ssp_config *ssp_config;
+
+ pr_info("SSP DAI: FCT %s enters\n",
+ __func__);
+
+ WARN(!cpu_dai, "SSP DAI: ERROR NULL cpu_dai\n");
+ if (!cpu_dai)
+ return -EINVAL;
+
+ ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ pr_info("SSP DAI: FCT %s ssp_config %p\n",
+ __func__,
+ ssp_config);
+
+ /*
+ * The set HW Config is only once for a CPU DAI
+ */
+
+ if (cpu_dai->active == 1) {
+ intel_mid_i2s_command(ssp_config->i2s_handle,
+ SSP_CMD_SET_HW_CONFIG,
+ &(ssp_config->i2s_settings));
+
+ }
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ if (intel_mid_i2s_command(ssp_config->i2s_handle,
+ SSP_CMD_ALLOC_TX, NULL)) {
+ pr_err("can not alloc TX DMA Channel\n");
+ return -EBUSY;
+ }
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if (intel_mid_i2s_command(ssp_config->i2s_handle,
+ SSP_CMD_ALLOC_RX, NULL)) {
+ pr_err("can not alloc RX DMA Channel\n");
+ return -EBUSY;
+ }
+ break;
+
+ default:
+ pr_err("SSP DAI: FCT %s Bad stream_dir: %d\n",
+ __func__, substream->stream);
+ return -EINVAL;
+ }
+
+ ssp_config->intel_mid_dma_alloc = true;
+
+ pr_debug("SSP DAI: FCT %s leaves\n",
+ __func__);
+
+ return 0;
+}
+
+/* BT/FM */
+static struct snd_soc_dai_ops ssp_dai_ops = {
+ .startup = ssp_dai_startup,
+ .shutdown = ssp_dai_shutdown,
+ .trigger = ssp_dai_trigger,
+ .hw_params = ssp_dai_hw_params,
+ .set_sysclk = ssp_set_dai_sysclk,
+ .set_pll = NULL,
+ .set_fmt = ssp_set_dai_fmt,
+ .set_tdm_slot = ssp_set_dai_tdm_slot,
+ .set_tristate = ssp_set_dai_tristate,
+};
+
+#define SSP_SUPPORTED_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define SSP_SUPPORTED_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8)
+
+struct snd_soc_dai_driver intel_ssp_platform_dai[] = {
+{
+ .name = SSP_MODEM_DAI_NAME,
+ .id = 0,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SSP_SUPPORTED_RATES,
+ .formats = SSP_SUPPORTED_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SSP_SUPPORTED_RATES,
+ .formats = SSP_SUPPORTED_FORMATS,
+ },
+ .ops = &ssp_dai_ops,
+ .probe = ssp_probe,
+ .remove = ssp_remove,
+},
+{
+ .name = SSP_BT_DAI_NAME,
+ .id = 1,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SSP_SUPPORTED_RATES,
+ .formats = SSP_SUPPORTED_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SSP_SUPPORTED_RATES,
+ .formats = SSP_SUPPORTED_FORMATS,
+ },
+ .ops = &ssp_dai_ops,
+ .probe = ssp_probe,
+ .remove = ssp_remove,
+},
+};
+
+static int ssp_dai_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct workqueue_struct *ssp_dai_wq;
+
+ pr_debug("SSP DAI: FCT %s enters\n",
+ __func__);
+
+ ret = snd_soc_register_platform(&pdev->dev,
+ &soc_ssp_platform_drv);
+ if (ret) {
+ pr_err("registering SSP PLATFORM failed\n");
+ snd_soc_unregister_dai(&pdev->dev);
+ return -EBUSY;
+ }
+
+ ret = snd_soc_register_dais(&pdev->dev,
+ intel_ssp_platform_dai,
+ ARRAY_SIZE(intel_ssp_platform_dai));
+
+ if (ret) {
+ pr_err("registering cpu DAIs failed\n");
+ snd_soc_unregister_dai(&pdev->dev);
+ return -EBUSY;
+ }
+
+ ssp_dai_wq = create_workqueue("ssp_transfer_data");
+
+ if (!ssp_dai_wq) {
+ pr_err("work queue failed\n");
+ snd_soc_unregister_dai(&pdev->dev);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, ssp_dai_wq);
+
+ pr_debug("SSP DAI: FCT %s leaves %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int ssp_dai_remove(struct platform_device *pdev)
+{
+ struct workqueue_struct *ssp_dai_wq = platform_get_drvdata(pdev);
+
+ pr_debug("SSP DAI: FCT %s enters\n",
+ __func__);
+
+ flush_workqueue(ssp_dai_wq);
+
+ destroy_workqueue(ssp_dai_wq);
+
+ platform_set_drvdata(pdev, NULL);
+
+ snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(intel_ssp_platform_dai));
+
+ snd_soc_unregister_platform(&pdev->dev);
+
+ pr_debug("SSP DAI: FCT %s leaves\n",
+ __func__);
+
+ return 0;
+}
+
+static struct platform_driver intel_ssp_dai_driver = {
+ .driver = {
+ .name = "mid-ssp-dai",
+ .owner = THIS_MODULE,
+ },
+ .probe = ssp_dai_probe,
+ .remove = ssp_dai_remove,
+};
+
+
+static int __init ssp_soc_dai_init(void)
+{
+ pr_debug("SSP DAI: FCT %s called\n",
+ __func__);
+
+ return platform_driver_register(&intel_ssp_dai_driver);
+}
+module_init(ssp_soc_dai_init);
+
+static void __exit ssp_soc_dai_exit(void)
+{
+ pr_debug("SSP DAI: FCT %s called\n",
+ __func__);
+
+ platform_driver_unregister(&intel_ssp_dai_driver);
+
+}
+module_exit(ssp_soc_dai_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Selma Bensaid");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ssp-cpu-dai");
--- /dev/null
+/*
+ * mfld_ssp.h - ASoC CPU DAI driver for
+ *
+ * Copyright (C) 2012 Intel Corp
+ * Authors: Selma Bensaid <selma.bensaid@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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef MID_SSP_H_
+#define MID_SSP_H_
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <linux/intel_mid_i2s_if.h>
+
+#define SSP_MODEM_DAI_NAME "ssp-modem-cpu-dai"
+#define SSP_BT_DAI_NAME "ssp-bt-cpu-dai"
+
+#define SSP_MAX_BUFFER (640*1024)
+#define SSP_MIN_BUFFER (640*1024)
+
+#define TRISTATE_BIT 0
+#define FRAME_SYNC_RELATIVE_TIMING_BIT 1
+#define DUMMY_START_ONE_PERIOD_OFFSET 2
+#define DUMMY_START_ONE_PERIOD_MASK 0x3
+
+#define IS_TRISTATE_ENABLED(x) (x & BIT(TRISTATE_BIT))
+#define IS_NEXT_FRMS_ASSERTED_WITH_LSB_PREVIOUS_FRM(x) \
+ ((x & BIT(FRAME_SYNC_RELATIVE_TIMING_BIT)) \
+ >> FRAME_SYNC_RELATIVE_TIMING_BIT)
+#define IS_DUMMY_START_ONE_PERIOD_OFFSET(x) \
+ ((x >> DUMMY_START_ONE_PERIOD_OFFSET) \
+ & DUMMY_START_ONE_PERIOD_MASK)
+
+#define MID_SSP_RX_FIFO_THRESHOLD 8
+#define MID_SSP_TX_FIFO_THRESHOLD 7
+
+/*
+ * Structures Definition
+ */
+
+
+struct intel_ssp_config {
+ struct intel_mid_i2s_hdl *i2s_handle;
+ struct intel_mid_i2s_settings i2s_settings;
+ bool intel_mid_dma_alloc;
+};
+
+struct intel_alsa_ssp_stream_info {
+ struct snd_pcm_substream *substream;
+ struct work_struct ssp_ws;
+ struct intel_ssp_config *ssp_config;
+ unsigned long stream_status;
+ u32 period_req_index;
+ s32 period_cb_index;
+ u8 *addr;
+ int length;
+};
+
+
+/*
+ * Enum Definition
+ */
+
+enum intel_alsa_ssp_stream_status {
+ INTEL_ALSA_SSP_STREAM_INIT = 0,
+ INTEL_ALSA_SSP_STREAM_STARTED,
+ INTEL_ALSA_SSP_STREAM_RUNNING,
+ INTEL_ALSA_SSP_STREAM_PAUSED,
+ INTEL_ALSA_SSP_STREAM_DROPPED,
+};
+enum ssp_clk_def {
+ SSP_CLK_ONCHIP = 0x0,
+ SSP_CLK_NET,
+ SSP_CLK_EXT,
+ SSP_CLK_AUDIO
+};
+
+
+#endif /* MID_SSP_H_ */