int intel_scu_ipc_osc_clk(u8 clk, unsigned int khz);
+enum clk0_mode {
+ CLK0_AUDIENCE = 0x4,
+ CLK0_VIBRA1 = 0x8,
+ CLK0_VIBRA2 = 0x10,
+ CLK0_MSIC = 0x20,
+ CLK0_QUERY = 0x1000,
+};
+
+int intel_scu_ipc_set_osc_clk0(unsigned int enable, enum clk0_mode mode);
+
extern struct blocking_notifier_head intel_scu_notifier;
static inline void intel_scu_notifier_add(struct notifier_block *nb)
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/gpio.h>
-#include <sound/intel_sst.h>
-
+/* FIXME: remove once vibra becomes PCI driver */
+#include "../../../sound/soc/mid-x86/sst_platform.h"
#define VIBRA_ENABLE_GPIO 40
#define PWM_ENABLE_GPIO 49
-
struct vibra_info {
struct mutex lock;
struct device *dev;
struct device_attribute *attr, const char *buf, size_t len)
{
long vibrator_enable;
- int ret;
struct platform_device *pdev = to_platform_device(dev);
struct vibra_info *info = platform_get_drvdata(pdev);
config A1026
tristate "Audience a1026/eS305 Voice Processor"
- depends on I2C
- depends on SND_INTEL_SST
+ depends on I2C && INTEL_SCU_IPC
help
Provides an interface to the Audience voice processing
unit via an I2C bus interface.
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/firmware.h>
-#include <sound/intel_sst.h>
+#include <asm/intel_scu_ipc.h>
#include <linux/a1026.h>
#include <linux/i2c.h>
vp->suspended = 1;
msleep(120); /* 120 defined by fig 2 of eS305 as the time to wait
before clock gating */
- rc = intel_sst_set_pll(false, SST_PLL_AUDIENCE);
+ pr_debug("A1026 suspend\n");
+ rc = intel_scu_ipc_set_osc_clk0(false, CLK0_AUDIENCE);
if (rc)
pr_err("ipc clk disable command failed: %d\n", rc);
case A1026_ENABLE_CLOCK:
pr_debug("%s:ipc clk enable command\n", __func__);
mutex_lock(&the_vp->mutex);
- rc = intel_sst_set_pll(true, SST_PLL_AUDIENCE);
+ rc = intel_scu_ipc_set_osc_clk0(true, CLK0_AUDIENCE);
mutex_unlock(&the_vp->mutex);
if (rc) {
pr_err("ipc clk enable command failed: %d\n", rc);
/* PM Qos struct */
static struct pm_qos_request_list *qos;
+/* Mode for Audio clock */
+static DEFINE_MUTEX(osc_clk0_lock);
+static unsigned int osc_clk0_mode;
+
/*
* Command Register (Write Only):
* A write to this register results in an interrupt to the SCU core processor
EXPORT_SYMBOL_GPL(intel_scu_ipc_osc_clk);
/*
+ * OSC_CLK_AUDIO is connected to the MSIC as well as Audience, so it should be
+ * turned on if any one of them requests it to be on and it should be turned off
+ * only if no one needs it on.
+ */
+int intel_scu_ipc_set_osc_clk0(unsigned int enable, enum clk0_mode mode)
+{
+ int ret = 0, clk_enable;
+ static const unsigned int clk_khz = 19200;
+
+ pr_debug("set_clk0 request %s for Mode 0x%x\n",
+ enable ? "ON" : "OFF", mode);
+ mutex_lock(&osc_clk0_lock);
+ if (mode == CLK0_QUERY) {
+ ret = osc_clk0_mode;
+ goto out;
+ }
+ if (enable) {
+ /* if clock is already on, just add new user */
+ if (osc_clk0_mode) {
+ osc_clk0_mode |= mode;
+ goto out;
+ }
+ osc_clk0_mode |= mode;
+ pr_debug("set_clk0: enabling clk, mode 0x%x\n", osc_clk0_mode);
+ clk_enable = 1;
+ } else {
+ osc_clk0_mode &= ~mode;
+ pr_debug("set_clk0: disabling clk, mode 0x%x\n", osc_clk0_mode);
+ /* others using the clock, cannot turn it of */
+ if (osc_clk0_mode)
+ goto out;
+ clk_enable = 0;
+ }
+ pr_debug("configuring OSC_CLK_AUDIO now\n");
+ ret = intel_scu_ipc_osc_clk(OSC_CLK_AUDIO, clk_enable ? clk_khz : 0);
+out:
+ mutex_unlock(&osc_clk0_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_set_osc_clk0);
+
+/*
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
* When ioc bit is set to 1, caller api must wait for interrupt handler called
* which in turn unlocks the caller api. Currently this is not used
+++ /dev/null
-#ifndef __INTEL_SST_H__
-#define __INTEL_SST_H__
-/*
- * intel_sst.h - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-10 Intel Corporation
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@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.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This driver exposes the audio engine functionalities to the ALSA
- * and middleware.
- * This file is shared between the SST and MAD drivers
- */
-#include <sound/intel_sst_ioctl.h>
-#include <sound/jack.h>
-
-#define SST_CARD_NAMES "intel_mid_card"
-#define SST_PLL_DELAY 2000
-
-#define MFLD_MAX_HW_CH 4
-/* control list Pmic & Lpe */
-/* Input controls */
-enum port_status {
- ACTIVATE = 1,
- DEACTIVATE,
-};
-
-/* Card states */
-enum sst_card_states {
- SND_CARD_UN_INIT = 0,
- SND_CARD_INIT_DONE,
-};
-
-enum sst_controls {
- SST_SND_ALLOC = 0x1000,
- SST_SND_PAUSE = 0x1001,
- SST_SND_RESUME = 0x1002,
- SST_SND_DROP = 0x1003,
- SST_SND_FREE = 0x1004,
- SST_SND_BUFFER_POINTER = 0x1005,
- SST_SND_STREAM_INIT = 0x1006,
- SST_SND_START = 0x1007,
- SST_SND_STREAM_PROCESS = 0x1008,
- SST_CONTROL_BASE = 0x1000,
- SST_ENABLE_RX_TIME_SLOT = 0x1009,
- SST_SND_DEVICE_SUSPEND = 0x1010,
- SST_VMIC_CHANNEL_SELECT = 0x1011,
- SST_SND_DEVICE_RESUME = 0x1012,
- SST_SND_DEVICE_RESUME_SYNC = 0x1013,
- SST_SET_RUNTIME_PARAMS = 0x1014,
- SST_SET_ALGO_PARAMS = 0x1015,
- SST_MAX_CONTROLS = 0x1015,
-};
-
-enum SND_CARDS {
- SND_FS = 0,
- SND_MX,
- SND_NC,
- SND_MSIC
-};
-
-struct pcm_stream_info {
- int str_id;
- void *mad_substream;
- void (*period_elapsed) (void *mad_substream);
- unsigned long long buffer_ptr;
- unsigned long long pcm_delay;
- int sfreq;
-};
-
-struct snd_pmic_ops {
- int card_status;
- int master_mute;
- int num_channel;
- int input_dev_id;
- int mute_status;
- struct mutex lock;
- int pb_on, pbhs_on;
- int cap_on;
- int output_dev_id;
- int lineout_dev_id, lineout_names_cnt;
- int prev_lineout_dev_id;
- bool jack_interrupt_status;
- void (*pmic_irq_cb) (void *cb_data, u8 value);
- void (*pmic_irq_enable)(void *data);
- int (*pmic_get_mic_bias)(void *intelmaddata);
- int (*pmic_set_headset_state)(int state);
-
- unsigned int hw_dmic_map[MFLD_MAX_HW_CH];
- unsigned int available_dmics;
- int (*set_hw_dmic_route) (u8 index);
-};
-
-extern void sst_mad_send_jack_report(struct snd_jack *jack,
- int buttonpressevent,
- int status);
-
-
-int intemad_set_headset_state(int state);
-int intelmad_get_mic_bias(void);
-
-struct intel_sst_pcm_control {
- 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);
-};
-struct intel_sst_card_ops {
- unsigned int vendor_id;
- struct intel_sst_pcm_control *pcm_control;
-};
-
-/* modified for generic access */
-struct sc_reg_access {
- u16 reg_addr;
- u8 value;
- u8 mask;
-};
-enum sc_reg_access_type {
- PMIC_READ = 0,
- PMIC_WRITE,
- PMIC_READ_MODIFY,
-};
-
-enum intel_sst_pll_mode {
- SST_PLL_VOICE = 0x1,
- SST_PLL_AUDIO = 0x2,
- SST_PLL_AUDIENCE = 0x4,
- SST_PLL_VIBRA1 = 0x8,
- SST_PLL_VIBRA2 = 0x10,
- SST_PLL_MSIC = 0x20,
-};
-
-enum lpe_param_types_mixer {
- SST_ALGO_PARAM_MIXER_STREAM_CFG = 0x801,
-};
-
-
-int register_sst_card(struct intel_sst_card_ops *card);
-void unregister_sst_card(struct intel_sst_card_ops *card);
-int intel_sst_set_pll(unsigned int enable, enum intel_sst_pll_mode mode);
-int intel_sst_get_pll(void);
-void intel_sst_pwm_suspend(unsigned int suspend);
-#endif /* __INTEL_SST_H__ */
#include <linux/types.h>
enum sst_codec_types {
-/* AUDIO/MUSIC CODEC Type Definitions */
+ /* AUDIO/MUSIC CODEC Type Definitions */
SST_CODEC_TYPE_UNKNOWN = 0,
SST_CODEC_TYPE_PCM, /* Pass through Audio codec */
SST_CODEC_TYPE_MP3,
SST_CODEC_OEM2 = 0xC9,
};
-enum snd_sst_stream_ops {
- STREAM_OPS_PLAYBACK = 0, /* Decode */
- STREAM_OPS_CAPTURE, /* Encode */
- STREAM_OPS_PLAYBACK_DRM, /* Play Audio/Voice */
- STREAM_OPS_PLAYBACK_ALERT, /* Play Audio/Voice */
- STREAM_OPS_CAPTURE_VOICE_CALL, /* CSV Voice recording */
-};
-
enum stream_mode {
SST_STREAM_MODE_NONE = 0,
SST_STREAM_MODE_DNR = 1,
SST_STREAM_TYPE_LOW_LATENCY = 4,
};
-enum snd_sst_audio_device_type {
- SND_SST_DEVICE_HEADSET = 1,
- SND_SST_DEVICE_IHF,
- SND_SST_DEVICE_VIBRA,
- SND_SST_DEVICE_HAPTIC,
- SND_SST_DEVICE_CAPTURE,
-};
-
-enum snd_sst_input_stream {
- SST_INPUT_STREAM_PCM = 0x2,
- SST_INPUT_STREAM_COMPRESS = 0x8,
- SST_INPUT_STREAM_MIXED = 0xA,
-};
-
-enum snd_sst_stream_type {
- SST_STREAM_DEVICE_HS = 32,
- SST_STREAM_DEVICE_IHF = 33,
- SST_STREAM_DEVICE_MIC0 = 34,
- SST_STREAM_DEVICE_MIC1 = 35,
-};
-
/* Firmware Version info */
struct snd_sst_fw_version {
__u8 build; /* build number*/
__u8 minor; /* minor number*/
__u8 major; /* major number*/
- __u8 type; /* build type */
+ __u8 type; /* build type */
};
/* Port info structure */
};
enum snd_sst_device_mode {
-
SND_SST_DEV_MODE_PCM_MODE1 = 1, /*(16-bit word, bit-length frame sync)*/
SND_SST_DEV_MODE_PCM_MODE2,
SND_SST_DEV_MODE_PCM_MODE3,
enum stream_param_type {
SST_SET_TIME_SLOT = 0,
SST_SET_CHANNEL_INFO = 1,
- OTHERS = 2, /*reserved for other params that need to be set in future*/
+ OTHERS = 2, /*reserved for future params*/
};
/* Target selection per device structure */
__u8 size;
__u8 rsvd;
__u64 addr;
-} __attribute__ ((packed));
+} __packed;
struct snd_sst_runtime_params {
__u8 type;
__u8 size;
__u8 rsvd;
void *addr;
-} __attribute__ ((packed));
+} __packed;
+
/*IOCTL defined here */
/*SST MMF IOCTLS only */
#define SNDRV_SST_STREAM_SET_PARAMS _IOWR('L', 0x00, \
int dev_power;
struct list_head list;
+ int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_dapm;
#endif
enum snd_soc_bias_level level);
void (*seq_notifier)(struct snd_soc_dapm_context *,
enum snd_soc_dapm_type, int);
- int (*stream_event)(struct snd_soc_dapm_context *,
- enum snd_soc_bias_level level);
+
+ /* codec stream completion event */
+ int (*stream_event)(struct snd_soc_dapm_context *, int level);
};
/* SoC platform interface */
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/jack.h>
-#include <sound/intel_sst.h>
#include "sn95031.h"
#define SN95031_RATES (SNDRV_PCM_RATE_8000_96000)
static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
{
pr_debug("enable mic bias\n");
- pr_debug("codec %p\n", codec);
mutex_lock(&codec->mutex);
/* GI board has amic bias swapped, we need to enable
Mic2 bias for jack */
return ret;
}
-static void sn95031_configure_pll(struct snd_soc_codec *codec, int operation)
+void sn95031_configure_pll(struct snd_soc_codec *codec, int operation)
{
struct sn95031_priv *sn95031_ctx;
sn95031_ctx = snd_soc_codec_get_drvdata(codec);
- if (operation) {
- pr_debug("enabling PLL\n");
+ if (sn95031_ctx->pll_state == PLL_ENABLE_PENDING
+ && operation == ENABLE_PLL) {
+ pr_debug("setting PLL to 0x%x\n", sn95031_ctx->clk_src);
+ snd_soc_write(codec, SN95031_AUDPLLCTRL, 0x20);
+ udelay(1000);
/* PLL takes few msec to stabilize
Refer sec2.3 MFLD Audio Interface Doc-rev0.7 */
snd_soc_write(codec, SN95031_AUDPLLCTRL,
snd_soc_update_bits(codec, SN95031_AUDPLLCTRL, BIT(5), BIT(5));
udelay(1000);
sn95031_ctx->pll_state = PLL_ENABLED;
- } else {
+ } else if (operation == DISABLE_PLL) {
pr_debug("disabling PLL\n");
sn95031_ctx->clk_src = SN95031_INVALID;
sn95031_ctx->pll_state = PLL_DISABLED;
snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+ } else {
+ pr_debug("PLL configure state: op=0x%x, state=0x%x\n",
+ operation, sn95031_ctx->pll_state);
}
}
+EXPORT_SYMBOL_GPL(sn95031_configure_pll);
-static int sn95031_codec_stream_event(struct snd_soc_dapm_context *dapm,
- int event)
-{
- pr_debug("%s:Event=%d\n", __func__, event);
-
- if (event == SND_SOC_DAPM_STREAM_STOP) {
- /* disable the MSIC PLL only if no other active streams */
- if (dapm->codec->active == 0) {
- sn95031_configure_pll(dapm->codec, DISABLE_PLL);
- /* disable PLLIN source clock */
- intel_sst_set_pll(false, SST_PLL_MSIC);
- }
- }
- return 0;
-}
static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct sn95031_priv *sn95031_ctx;
sn95031_ctx = snd_soc_codec_get_drvdata(codec);
+ pr_debug("%s: 0x%x\n", __func__, level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- pr_debug("bias_prepare\n");
- /* enable msic pll only when any codec dai is active,
- not other use cases like static vibra etc */
- if (codec->active) {
- pr_debug("vaud_bias powering up pll\n");
- intel_sst_set_pll(true, SST_PLL_MSIC);
- /* allow few ms to stabilize the clock before
- enabling the MSIC PLL */
- usleep_range(5000, 6000);
- sn95031_configure_pll(codec, ENABLE_PLL);
- }
+ pr_debug("vaud_bias PREPARE\n");
break;
case SND_SOC_BIAS_STANDBY:
pr_debug("vaud_bias power up rail\n");
/* power up the rail, on in normal and aoac mode */
snd_soc_write(codec, SN95031_VAUD, 0x2D);
- msleep(1);
+ usleep_range(1000, 1100);
} else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
- pr_debug("vaud_bias standby\n");
+ pr_debug("vaud_bias STANDBY\n");
}
break;
case SND_SOC_BIAS_OFF:
- pr_debug("vaud_bias _OFF doing rail shutdown\n");
+ pr_debug("vaud_bias OFF, doing rail shutdown\n");
+ sn95031_configure_pll(codec, DISABLE_PLL);
/*
* off mode is 100, and we need AOAC as off as well,
* so 100100b ie 24
return 0;
}
-static int sn95031_enable_pnw_clk(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- int clk_id = 0;
-
- if (!strcmp(w->name, "Vibra1Clock"))
- clk_id = SST_PLL_VIBRA1;
- else if (!strcmp(w->name, "Vibra2Clock"))
- clk_id = SST_PLL_VIBRA2;
-
- if (SND_SOC_DAPM_EVENT_ON(event))
- intel_sst_set_pll(true, clk_id);
- else if (SND_SOC_DAPM_EVENT_OFF(event))
- intel_sst_set_pll(false, clk_id);
- return 0;
-}
-
-/* Callback to set volume for *VOLCTRL regs. Needs to be implemented separately
- * since clock and VAUDA need to be on before value can be written to the regs
- */
-static int sn95031_set_vol_2r(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- int err;
- unsigned int val, val2, val_mask;
- int sst_pll_mode_saved;
-
- val_mask = mask << shift;
- val = (ucontrol->value.integer.value[0] & mask);
- val2 = (ucontrol->value.integer.value[1] & mask);
-
- if (invert) {
- val = max - val;
- val2 = max - val2;
- }
-
- val = val << shift;
- val2 = val2 << shift;
-
- pr_debug("enabling PLL and VAUDA to change volume\n");
- mutex_lock(&codec->mutex);
- sst_pll_mode_saved = intel_sst_get_pll();
- intel_sst_set_pll(true, SST_PLL_MSIC);
- udelay(SST_PLL_DELAY);
- snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
- snd_soc_dapm_sync(&codec->dapm);
-
- err = snd_soc_update_bits(codec, reg, val_mask, val);
- if (err < 0)
- goto restore_state;
-
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
-restore_state:
- snd_soc_dapm_disable_pin(&codec->dapm, "VirtBias");
- snd_soc_dapm_sync(&codec->dapm);
- if ((sst_pll_mode_saved & SST_PLL_MSIC) == 0)
- intel_sst_set_pll(false, SST_PLL_MSIC);
- mutex_unlock(&codec->mutex);
- return err;
-}
-
/* mux controls */
static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" };
2, 2, TLV_DB_SCALE_ITEM(2100, 0, 0),
3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
};
-/* -62db to 9 db in 1db steps*/
-static const DECLARE_TLV_DB_SCALE(out_tlv, -6200, 100, 0);
static const struct soc_enum sn95031_micmode1_enum =
SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text);
2, 3, 0, mic_tlv),
SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2,
2, 3, 0, mic_tlv),
- /* Add digital volume and mute controls for Headphone/Headset*/
- SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", SN95031_HSLVOLCTRL,
- SN95031_HSRVOLCTRL, 0, 71, 1,
- snd_soc_get_volsw_2r, sn95031_set_vol_2r,
- out_tlv),
SOC_DOUBLE_R("Headphone Playback Switch", SN95031_HSLVOLCTRL,
SN95031_HSRVOLCTRL, 7, 1, 0),
/* Add digital volume and mute controls for Speaker*/
- SOC_DOUBLE_R_EXT_TLV("Speaker Playback Volume", SN95031_IHFLVOLCTRL,
- SN95031_IHFRVOLCTRL, 0, 71, 1,
- snd_soc_get_volsw_2r, sn95031_set_vol_2r,
- out_tlv),
SOC_DOUBLE_R("Speaker Playback Switch", SN95031_IHFLVOLCTRL,
SN95031_IHFRVOLCTRL, 7, 1, 0),
SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0),
SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0),
SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0),
- /* Dummy widget to trigger VAUDA on/off */
- SND_SOC_DAPM_MICBIAS("VirtBias", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0,
sn95031_dmic12_event,
SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
sn95031_vihf_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("Vibra1Clock", SND_SOC_NOPM, 0, 0,
- sn95031_enable_pnw_clk,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("Vibra2Clock", SND_SOC_NOPM, 0, 0,
- sn95031_enable_pnw_clk,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* playback path driver enables */
SND_SOC_DAPM_OUT_DRV("Headset Left Playback",
static const struct snd_soc_dapm_route sn95031_audio_map[] = {
/* headset and earpiece map */
- { "HPOUTL", NULL, "Headset Rail"},
- { "HPOUTR", NULL, "Headset Rail"},
{ "HPOUTL", NULL, "Headset Left Playback" },
{ "HPOUTR", NULL, "Headset Right Playback" },
{ "EPOUT", NULL, "Earpiece Playback" },
{ "Vibra1 Playback", NULL, "Vibra1 Enable Mux"},
{ "Vibra1 Enable Mux", "PWM", "Vibra1 DAC"},
{ "Vibra1 Enable Mux", "SPI", "VIB1SPI"},
- { "VIB1SPI", NULL, "Vibra1Clock"},
{ "VIB2OUT", NULL, "Vibra2 Playback"},
{ "Vibra2 Playback", NULL, "Vibra2 Enable Mux"},
{ "Vibra2 Enable Mux", "PWM", "Vibra2 DAC"},
{ "Vibra2 Enable Mux", "SPI", "VIB2SPI"},
- { "VIB2SPI", NULL, "Vibra2Clock"},
/* lineout */
{ "LINEOUTL", NULL, "Lineout Left Playback"},
return 0;
}
-static int sn95031_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+static int sn95031_codec_set_pll(struct snd_soc_codec *codec, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
- int mode, target_clk_src;
- struct snd_soc_codec *codec = codec_dai->codec;
+ int retval = 0;
struct sn95031_priv *sn95031_ctx;
- sn95031_ctx = snd_soc_codec_get_drvdata(codec_dai->codec);
-
- pr_debug("set_dai_pll\n");
+ sn95031_ctx = snd_soc_codec_get_drvdata(codec);
+ pr_debug("%s: 0x%x\n", __func__, source);
mutex_lock(&codec->mutex);
if (!freq_in || !freq_out) {
/* disable PLL */
+ pr_debug("request to disable pll\n");
sn95031_configure_pll(codec, DISABLE_PLL);
- mutex_unlock(&codec->mutex);
- return 0;
- }
- mode = snd_soc_read(codec, SN95031_PCM1C3) >> 7;
- if (!mode && (!strcmp(codec_dai->name, "SN95031 Voice"))) {
- target_clk_src = SN95031_PCM1BCLK;
- snd_soc_write(codec, SN95031_PCM1C2, 0x04);
- } else {
- target_clk_src = SN95031_PLLIN;
- snd_soc_write(codec, SN95031_PCM1C2, 0x00);
+ retval = 0;
+ goto out;
}
- /* clock source is same, so don't do anything */
- if (sn95031_ctx->clk_src == target_clk_src) {
- pr_debug("clk src is same, no action\n");
- mutex_unlock(&codec->mutex);
- return 0;
- }
- sn95031_ctx->clk_src = target_clk_src;
- if (codec->dapm.bias_level >= SND_SOC_BIAS_PREPARE) {
- pr_debug("bias_level is active, enabling pll\n");
- intel_sst_set_pll(true, SST_PLL_MSIC);
- /* allow few ms to stabilize the clock before
- enabling the MSIC PLL */
- usleep_range(5000, 6000);
- sn95031_configure_pll(codec, ENABLE_PLL);
- } else
+ if (sn95031_ctx->clk_src != source) {
sn95031_ctx->pll_state = PLL_ENABLE_PENDING;
-
+ sn95031_ctx->clk_src = source;
+ }
+ if (source == SN95031_INVALID)
+ sn95031_ctx->pll_state = PLL_DISABLED;
+out:
mutex_unlock(&codec->mutex);
- return 0;
+ return retval;
}
static int sn95031_set_pcm2_tristate(struct snd_soc_dai *codec_dai,
int tristate)
{
- u8 val;
-
- pr_debug("enter:%s\n", __func__);
- if (tristate)
- val = 0;
- else
- val = 1;
-
return snd_soc_update_bits(codec_dai->codec, SN95031_PCM2C2,
- BIT(0), val);
+ BIT(0), !tristate);
}
+static int sn95031_set_pcm1_tristate(struct snd_soc_dai *codec_dai,
+ int tristate)
+{
+ return snd_soc_update_bits(codec_dai->codec, SN95031_PCM1C3,
+ BIT(0), !tristate);
+}
+
+
static int sn95031_codec_set_params(struct snd_soc_codec *codec,
unsigned int param)
{
unsigned int format;
- pr_debug("enter:%s\n", __func__);
switch (param) {
case SNDRV_PCM_FORMAT_S16_LE:
format = BIT(4)|BIT(5);
return 0;
}
+static int sn95031_set_voice_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int mode, format;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mode = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ mode = BIT(7);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = BIT(2);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ format = BIT(1)|BIT(0);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = BIT(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = 0;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = BIT(0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, SN95031_PCM1C3, BIT(7), mode);
+ snd_soc_update_bits(codec, SN95031_PCM1C2, BIT(0)|BIT(1)|BIT(2),
+ format);
+ return 0;
+}
+
static int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
unsigned int rate;
- pr_debug("pcm_hw param\n");
switch (params_rate(params)) {
case 48000:
pr_debug("RATE_48000\n");
rate = 0;
break;
-
case 44100:
pr_debug("RATE_44100\n");
rate = BIT(7);
break;
-
default:
pr_err("ERR rate %d\n", params_rate(params));
return -EINVAL;
}
+ pr_debug("%s: format=0x%x\n", __func__, params_format(params));
snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
return 0;
default:
return -EINVAL;
}
+ pr_debug("%s: format=0x%x\n", __func__, format);
snd_soc_update_bits(dai->codec, SN95031_PCM1C3,
BIT(4)|BIT(5), format);
snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(6)|BIT(5)|BIT(4),
pcm1fs);
- /* enable pcm 1 */
- snd_soc_update_bits(dai->codec, SN95031_PCM1C3, BIT(0), BIT(0));
+ /* enable PCM1 */
+ sn95031_set_pcm1_tristate(dai, 0);
return 0;
}
static int sn95031_voice_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- pr_debug("inside hw_free");
- snd_soc_update_bits(dai->codec, SN95031_PCM1C3, BIT(0), 0);
+ pr_debug("%s\n", __func__);
+ sn95031_set_pcm1_tristate(dai, 1);
/* PCM1 should be in slave, short or long sync mode for
Tx line to be in Hi-Z state */
- snd_soc_update_bits(dai->codec, SN95031_PCM1C3, BIT(7), 0);
- snd_soc_write(dai->codec, SN95031_PCM1C2, 0x00);
+ sn95031_set_voice_dai_fmt(dai, SND_SOC_DAIFMT_CBS_CFS);
+ sn95031_set_voice_dai_fmt(dai, SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_DSP_A);
return 0;
}
static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
.digital_mute = sn95031_pcm_hs_mute,
.hw_params = sn95031_pcm_hw_params,
- .set_pll = sn95031_set_dai_pll,
.set_tristate = sn95031_set_pcm2_tristate,
};
static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
.digital_mute = sn95031_pcm_spkr_mute,
.hw_params = sn95031_pcm_hw_params,
- .set_pll = sn95031_set_dai_pll,
- .set_tristate = sn95031_set_pcm2_tristate,
-};
-
-static struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
- .hw_params = sn95031_pcm_hw_params,
- .set_pll = sn95031_set_dai_pll,
- .set_tristate = sn95031_set_pcm2_tristate,
-};
-
-static struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
- .hw_params = sn95031_pcm_hw_params,
- .set_pll = sn95031_set_dai_pll,
.set_tristate = sn95031_set_pcm2_tristate,
};
static struct snd_soc_dai_ops sn95031_voice_dai_ops = {
.digital_mute = sn95031_pcm_hs_mute,
.hw_params = sn95031_voice_hw_params,
+ .set_fmt = sn95031_set_voice_dai_fmt,
.hw_free = sn95031_voice_hw_free,
- .set_pll = sn95031_set_dai_pll,
+ .set_tristate = sn95031_set_pcm1_tristate,
};
static struct snd_soc_dai_driver sn95031_dais[] = {
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
- .ops = &sn95031_vib1_dai_ops,
+ .ops = NULL,
},
{ .name = "SN95031 Vibra2",
.playback = {
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
- .ops = &sn95031_vib2_dai_ops,
+ .ops = NULL,
},
{
.name = "SN95031 Voice",
.num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets),
.dapm_routes = sn95031_audio_map,
.num_dapm_routes = ARRAY_SIZE(sn95031_audio_map),
- .stream_event = sn95031_codec_stream_event,
+ .set_pll = sn95031_codec_set_pll,
};
static int __devinit sn95031_device_probe(struct platform_device *pdev)
extern void sn95031_oc_handler(struct snd_soc_codec *codec,
int oc_interrupt_value);
+void sn95031_configure_pll(struct snd_soc_codec *codec, int operation);
#ifdef CONFIG_SWITCH_MID
extern void mid_headset_report(int state);
#endif
config SND_MFLD_MACHINE_GI
tristate "SOC Machine Audio driver for Intel Medfield GI board"
- depends on INTEL_SCU_IPC
- depends on SND_INTEL_SST
+ depends on INTEL_SCU_IPC && X86 && GPIO_LANGWELL
depends on MSIC_GPADC
select SND_SOC_SN95031
select SND_SST_PLATFORM
- default N
+ select SND_INTEL_SST
+ default n
help
This adds support for ASoC machine driver for Gilligan Island board, based on
Intel(R) MID Medfield platform. This will create an alsa sound card.
snd-soc-sst-platform-objs := sst_platform.o
-snd-soc-mfld-machine-objs := mfld_machine.o
-snd-soc-mfld-machine-gi-objs := mfld_machine_gi.o
-snd-soc-clv-machine-objs := clv_machine.o
-
obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
+
+# Medfield board
+snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
+
+# Gilligan Island/Lexington board
+snd-soc-mfld-machine-gi-objs := mfld_machine_gi.o
obj-$(CONFIG_SND_MFLD_MACHINE_GI) += snd-soc-mfld-machine-gi.o
+
+# Cloverview/Clovertrail+ board
+snd-soc-clv-machine-objs := clv_machine.o
obj-$(CONFIG_SND_CLV_MACHINE) += snd-soc-clv-machine.o
+
+# DSP driver
obj-$(CONFIG_SND_INTEL_SST) += sst/
#include <linux/delay.h>
#include <linux/ipc_device.h>
#include <asm/intel_scu_ipc.h>
-#include <sound/intel_sst.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
static unsigned int vsp_mode;
-static vsp_mode_get(struct snd_kcontrol *kcontrol,
+static int vsp_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = vsp_mode;
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
- intel_sst_set_pll(true, SST_PLL_MSIC);
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
break;
case SND_SOC_BIAS_OFF:
- intel_sst_set_pll(false, SST_PLL_MSIC);
+ intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
}
codec->dapm.bias_level = level;
pr_debug("codec->bias_level %u\n", card->dapm.bias_level);
return ret;
}
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int clv_startup(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s - applying rate constraint\n", __func__);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+ return 0;
+}
+
static struct snd_soc_ops clv_asp_ops = {
+ .startup = clv_startup,
.hw_params = clv_asp_hw_params,
};
+
static struct snd_soc_ops clv_vsp_ops = {
.hw_params = clv_vsp_hw_params,
};
+
struct snd_soc_dai_link clv_msic_dailink[] = {
{
.name = "Cloverview ASP",
static int __init snd_clv_driver_init(void)
{
- pr_debug("In %s\n", __func__);
+ pr_info("In %s\n", __func__);
return ipc_driver_register(&snd_clv_mc_driver);
}
-
-module_init(snd_clv_driver_init);
+late_initcall(snd_clv_driver_init);
static void __exit snd_clv_driver_exit(void)
{
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/async.h>
#include <linux/gpio.h>
#include <linux/ipc_device.h>
#include <asm/intel-mid.h>
+#include <asm/intel_scu_ipc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/tlv.h>
#include "../codecs/sn95031.h"
-#define MID_MONO 1
-#define MID_STEREO 2
-#define MID_MAX_CAP 5
#define MFLD_JACK_INSERT 0x04
#define HEADSET_DET_PIN 77
MFLD_MV_UNDEFINED,
};
-static unsigned int hs_switch;
-static unsigned int lo_dac;
-
struct mfld_mc_private {
- struct ipc_device *socdev;
void __iomem *int_base;
- struct snd_soc_codec *codec;
u8 jack_interrupt_status;
u8 oc_interrupt_status;
spinlock_t lock; /* lock for interrupt status and jack debounce */
+ int pcm1_master_mode;
+ unsigned int hs_switch;
+ unsigned int lo_dac;
#ifdef CONFIG_HAS_WAKELOCK
struct wake_lock wake_lock;
#endif
static int headset_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- ucontrol->value.integer.value[0] = hs_switch;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
+ ucontrol->value.integer.value[0] = mc_drv_ctx->hs_switch;
return 0;
}
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- if (ucontrol->value.integer.value[0] == hs_switch)
+ if (ucontrol->value.integer.value[0] == mc_drv_ctx->hs_switch)
return 0;
if (ucontrol->value.integer.value[0]) {
mutex_lock(&codec->mutex);
snd_soc_dapm_sync(&codec->dapm);
mutex_unlock(&codec->mutex);
- hs_switch = ucontrol->value.integer.value[0];
+ mc_drv_ctx->hs_switch = ucontrol->value.integer.value[0];
return 0;
}
static void lo_enable_out_pins(struct snd_soc_codec *codec)
{
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
- if (hs_switch) {
+ if (mc_drv_ctx->hs_switch) {
snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
} else {
static int lo_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- ucontrol->value.integer.value[0] = lo_dac;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
+ ucontrol->value.integer.value[0] = mc_drv_ctx->lo_dac;
return 0;
}
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- if (ucontrol->value.integer.value[0] == lo_dac)
+ if (ucontrol->value.integer.value[0] == mc_drv_ctx->lo_dac)
return 0;
/* we dont want to work with last state of lineout so just enable all
mutex_lock(&codec->mutex);
snd_soc_dapm_sync(&codec->dapm);
mutex_unlock(&codec->mutex);
- lo_dac = ucontrol->value.integer.value[0];
+ mc_drv_ctx->lo_dac = ucontrol->value.integer.value[0];
return 0;
}
+
static int sn95031_get_pcm1_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int mode;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- mode = snd_soc_read(codec, SN95031_PCM1C3) >> 7;
- pr_debug("mode: %d\n", mode);
- ucontrol->value.integer.value[0] = mode;
+ pr_debug("PCM1 master mode: %d\n", mc_drv_ctx->pcm1_master_mode);
+ ucontrol->value.integer.value[0] = mc_drv_ctx->pcm1_master_mode;
return 0;
}
+
static int sn95031_set_pcm1_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- u8 mode = ucontrol->value.integer.value[0];
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- if (mode) {
- pr_debug("can we set the master mode settings\n");
- snd_soc_update_bits(codec, SN95031_PCM1C3,
- BIT(1)|BIT(2)|BIT(3)|BIT(7), BIT(7)|BIT(1));
- snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1),
- BIT(0)|BIT(1));
- snd_soc_update_bits(codec, SN95031_PCM1C2,
- BIT(0)|BIT(1)|BIT(2), 0);
- } else {
- pr_debug("setting the slave mode settings\n");
- snd_soc_update_bits(codec, SN95031_PCM1C3, BIT(7), 0);
- snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1), 0);
- snd_soc_update_bits(codec, SN95031_PCM1C2, BIT(2), BIT(2));
+ mc_drv_ctx->pcm1_master_mode = ucontrol->value.integer.value[0];
+ return 0;
+}
- }
+static int mfld_vibra_enable_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int clk_id = 0;
+
+ if (!strcmp(w->name, "Vibra1Clock"))
+ clk_id = CLK0_VIBRA1;
+ else if (!strcmp(w->name, "Vibra2Clock"))
+ clk_id = CLK0_VIBRA2;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ intel_scu_ipc_set_osc_clk0(true, clk_id);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ intel_scu_ipc_set_osc_clk0(false, clk_id);
return 0;
}
+/* Callback to set volume for *VOLCTRL regs. Needs to be implemented separately
+ * since clock and VAUDA need to be on before value can be written to the regs
+ */
+static int sn95031_set_vol_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int err;
+ unsigned int val, val2, val_mask;
+ int sst_pll_mode_saved;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] & mask);
+ val2 = (ucontrol->value.integer.value[1] & mask);
+
+ if (invert) {
+ val = max - val;
+ val2 = max - val2;
+ }
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ pr_debug("enabling PLL and VAUDA to change volume\n");
+ mutex_lock(&codec->mutex);
+ sst_pll_mode_saved = intel_scu_ipc_set_osc_clk0(true, CLK0_QUERY);
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+ snd_soc_dapm_sync(&codec->dapm);
+
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
+ goto restore_state;
+
+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+restore_state:
+ snd_soc_dapm_disable_pin(&codec->dapm, "VirtBias");
+ if (!(sst_pll_mode_saved & CLK0_MSIC))
+ intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
+ mutex_unlock(&codec->mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -6200, 100, 0);
+
static const struct snd_kcontrol_new mfld_snd_controls[] = {
SOC_ENUM_EXT("Playback Switch", headset_enum,
headset_get_switch, headset_set_switch),
lo_get_switch, lo_set_switch),
SOC_ENUM_EXT("PCM1 Mode", SN95031_pcm1_mode_config_enum,
sn95031_get_pcm1_mode, sn95031_set_pcm1_mode),
+ /* Add digital volume and mute controls for Headphone/Headset*/
+ SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", SN95031_HSLVOLCTRL,
+ SN95031_HSRVOLCTRL, 0, 71, 1,
+ snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+ out_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Speaker Playback Volume", SN95031_IHFLVOLCTRL,
+ SN95031_IHFRVOLCTRL, 0, 71, 1,
+ snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+ out_tlv),
};
static const struct snd_soc_dapm_widget mfld_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_MIC("Mic", NULL),
+ /* Dummy widget to trigger VAUDA on/off */
+ SND_SOC_DAPM_MICBIAS("VirtBias", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("Vibra1Clock", SND_SOC_NOPM, 0, 0,
+ mfld_vibra_enable_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Vibra2Clock", SND_SOC_NOPM, 0, 0,
+ mfld_vibra_enable_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route mfld_map[] = {
+ { "HPOUTL", NULL, "Headset Rail"},
+ { "HPOUTR", NULL, "Headset Rail"},
{"Headphones", NULL, "HPOUTR"},
{"Headphones", NULL, "HPOUTL"},
{"AMIC1", NULL, "Mic"},
+ {"VIB1SPI", NULL, "Vibra1Clock"},
+ {"VIB2SPI", NULL, "Vibra2Clock"},
};
static void mfld_jack_check(unsigned int intr_status)
{
struct snd_soc_codec *codec = runtime->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
int ret_val;
/* Add jack sense widgets */
pr_err("soc_add_controls failed %d", ret_val);
return ret_val;
}
- /* always connected */
- snd_soc_dapm_enable_pin(dapm, "Headphones");
- snd_soc_dapm_enable_pin(dapm, "Mic");
/* default is earpiece pin, userspace sets it explcitly */
snd_soc_dapm_disable_pin(dapm, "Headphones");
/* default is lineout NC, userspace sets it explcitly */
snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
- lo_dac = 3;
- hs_switch = 0;
+ mc_drv_ctx->lo_dac = 3;
+ mc_drv_ctx->hs_switch = 0;
/* we dont use linein in this so set to NC */
snd_soc_dapm_disable_pin(dapm, "LINEINL");
snd_soc_dapm_disable_pin(dapm, "LINEINR");
}
#endif
+static int mfld_media_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ pr_debug("%s\n", __func__);
+ /* Force the data width to 24 bit in MSIC since post processing
+ algorithms in DSP enabled with 24 bit precision */
+ ret = snd_soc_codec_set_params(codec, SNDRV_PCM_FORMAT_S24_LE);
+ if (ret < 0) {
+ pr_debug("codec_set_params returned error %d\n", ret);
+ return ret;
+ }
+ snd_soc_codec_set_pll(codec, 0, SN95031_PLLIN, 1, 1);
+
+ /* VAUD needs to be on before configuring PLL */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+ mutex_lock(&codec->mutex);
+ snd_soc_dapm_sync(&codec->dapm);
+ mutex_unlock(&codec->mutex);
+ usleep_range(5000, 6000);
+ sn95031_configure_pll(codec, ENABLE_PLL);
+
+ /* enable PCM2 */
+ snd_soc_dai_set_tristate(rtd->codec_dai, 0);
+ return 0;
+}
+
+static int mfld_voice_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_codec *codec = rtd->codec;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
+ pr_debug("%s\n", __func__);
+
+ if (mc_drv_ctx->pcm1_master_mode) { /* VOIP call */
+ snd_soc_codec_set_pll(codec, 0, SN95031_PLLIN, 1, 1);
+ snd_soc_dai_set_fmt(rtd->codec_dai, SND_SOC_DAIFMT_CBM_CFM
+ | SND_SOC_DAIFMT_DSP_A);
+ /* Sets the PCM1 clock rate */
+ snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1),
+ BIT(0)|BIT(1));
+ } else { /* CSV call */
+ snd_soc_codec_set_pll(codec, 0, SN95031_PCM1BCLK, 1, 1);
+ snd_soc_dai_set_fmt(rtd->codec_dai, SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_I2S);
+ snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1), 0);
+ }
+
+ /* VAUD needs to be on before configuring PLL */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+ mutex_lock(&codec->mutex);
+ snd_soc_dapm_sync(&codec->dapm);
+ mutex_unlock(&codec->mutex);
+ usleep_range(5000, 6000);
+ sn95031_configure_pll(codec, ENABLE_PLL);
+ return 0;
+}
+
+static unsigned int rates_44100[] = {
+ 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_44100 = {
+ .count = ARRAY_SIZE(rates_44100),
+ .list = rates_44100,
+};
+
+static int mfld_media_startup(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s - applying rate constraint\n", __func__);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_44100);
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+ return 0;
+}
+
+static void mfld_media_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ pr_debug("%s\n", __func__);
+
+ snd_soc_dapm_disable_pin(&rtd->codec->dapm, "VirtBias");
+ /* switch off PCM2 port */
+ if (!rtd->codec->active)
+ snd_soc_dai_set_tristate(codec_dai, 1);
+}
+
+static int mfld_voice_startup(struct snd_pcm_substream *substream)
+{
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+ return 0;
+}
+
+static void mfld_voice_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ pr_debug("%s\n", __func__);
+
+ snd_soc_dapm_disable_pin(&rtd->codec->dapm, "VirtBias");
+}
+
+static struct snd_soc_ops mfld_media_ops = {
+ .startup = mfld_media_startup,
+ .shutdown = mfld_media_shutdown,
+ .hw_params = mfld_media_hw_params,
+};
+
+static struct snd_soc_ops mfld_voice_ops = {
+ .startup = mfld_voice_startup,
+ .shutdown = mfld_voice_shutdown,
+ .hw_params = mfld_voice_hw_params,
+};
+
static struct snd_soc_dai_link mfld_msic_dailink[] = {
{
.name = "Medfield Headset",
.platform_name = "sst-platform",
.init = mfld_init,
.ignore_suspend = 1,
+ .ops = &mfld_media_ops,
},
{
.name = "Medfield Speaker",
.init = NULL,
#endif
.ignore_suspend = 1,
+ .ops = &mfld_media_ops,
},
/*
* This configurtaion doesnt need Vibra as PCM device
.init = NULL,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
+ .ops = &mfld_voice_ops,
},
};
#define snd_mfld_mc_poweroff NULL
#endif
+static int mfld_card_stream_event(struct snd_soc_dapm_context *dapm, int event)
+{
+ struct snd_soc_codec *codec = dapm->codec;
+ pr_debug("machine stream event: %d\n", event);
+ if (event == SND_SOC_DAPM_STREAM_STOP) {
+ if (!codec->active) {
+ sn95031_configure_pll(codec, DISABLE_PLL);
+ return intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
+ }
+ }
+ return 0;
+}
+
/* SoC card */
static struct snd_soc_card snd_soc_card_mfld = {
.name = "medfield_audio",
}
/* register the soc card */
snd_soc_card_mfld.dev = &ipcdev->dev;
- snd_soc_initialize_card_lists(&snd_soc_card_mfld);
+ snd_soc_card_mfld.dapm.stream_event = mfld_card_stream_event;
+ snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
ret_val = snd_soc_register_card(&snd_soc_card_mfld);
if (ret_val) {
pr_debug("snd_soc_register_card failed %d\n", ret_val);
goto freeirq;
}
ipc_set_drvdata(ipcdev, &snd_soc_card_mfld);
- snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
pr_debug("successfully exited probe\n");
return ret_val;
static int __init snd_mfld_driver_init(void)
{
- pr_debug("snd_mfld_driver_init called\n");
+ pr_info("snd_mfld_driver_init called\n");
return ipc_driver_register(&snd_mfld_mc_driver);
}
-module_init_async(snd_mfld_driver_init);
+late_initcall(snd_mfld_driver_init);
static void __exit snd_mfld_driver_exit(void)
{
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/async.h>
#include <linux/gpio.h>
#include <linux/ipc_device.h>
#include <asm/intel-mid.h>
+#include <asm/intel_scu_ipc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/tlv.h>
#include "../codecs/sn95031.h"
-#define MID_MONO 1
-#define MID_STEREO 2
-#define MID_MAX_CAP 5
#define MFLD_JACK_INSERT 0x04
#define HEADSET_DET_PIN 77
MFLD_MV_UNDEFINED,
};
-static unsigned int hs_switch;
-
struct mfld_mc_private {
- struct ipc_device *socdev;
void __iomem *int_base;
- struct snd_soc_codec *codec;
u8 jack_interrupt_status;
u8 oc_interrupt_status;
spinlock_t lock; /* lock for interrupt status and jack debounce */
+ int pcm1_master_mode;
+ unsigned int hs_switch;
#ifdef CONFIG_HAS_WAKELOCK
struct wake_lock wake_lock;
#endif
static int headset_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- ucontrol->value.integer.value[0] = hs_switch;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
+ ucontrol->value.integer.value[0] = mc_drv_ctx->hs_switch;
return 0;
}
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- if (ucontrol->value.integer.value[0] == hs_switch)
+ if (ucontrol->value.integer.value[0] == mc_drv_ctx->hs_switch)
return 0;
if (ucontrol->value.integer.value[0]) {
snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
}
+ mutex_lock(&codec->mutex);
snd_soc_dapm_sync(&codec->dapm);
- hs_switch = ucontrol->value.integer.value[0];
+ mutex_unlock(&codec->mutex);
+ mc_drv_ctx->hs_switch = ucontrol->value.integer.value[0];
return 0;
}
static int sn95031_get_pcm1_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int mode;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- mode = snd_soc_read(codec, SN95031_PCM1C3) >> 7;
- pr_debug("mode: %d\n", mode);
- ucontrol->value.integer.value[0] = mode;
+ pr_debug("PCM1 master mode: %d\n", mc_drv_ctx->pcm1_master_mode);
+ ucontrol->value.integer.value[0] = mc_drv_ctx->pcm1_master_mode;
return 0;
}
+
static int sn95031_set_pcm1_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- u8 mode = ucontrol->value.integer.value[0];
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
- if (mode) {
- pr_debug("can we set the master mode settings\n");
- snd_soc_update_bits(codec, SN95031_PCM1C3,
- BIT(1)|BIT(2)|BIT(3)|BIT(7), BIT(7)|BIT(1));
- snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1),
- BIT(0)|BIT(1));
- snd_soc_update_bits(codec, SN95031_PCM1C2,
- BIT(0)|BIT(1)|BIT(2), 0);
- } else {
- pr_debug("setting the slave mode settings\n");
- snd_soc_update_bits(codec, SN95031_PCM1C3, BIT(7), 0);
- snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1), 0);
- snd_soc_update_bits(codec, SN95031_PCM1C2, BIT(2), BIT(2));
+ mc_drv_ctx->pcm1_master_mode = ucontrol->value.integer.value[0];
+ return 0;
+}
- }
+static int mfld_vibra_enable_clk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int clk_id = 0;
+
+ if (!strcmp(w->name, "Vibra1Clock"))
+ clk_id = CLK0_VIBRA1;
+ else if (!strcmp(w->name, "Vibra2Clock"))
+ clk_id = CLK0_VIBRA2;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ intel_scu_ipc_set_osc_clk0(true, clk_id);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ intel_scu_ipc_set_osc_clk0(false, clk_id);
return 0;
}
+/* Callback to set volume for *VOLCTRL regs. Needs to be implemented separately
+ * since clock and VAUDA need to be on before value can be written to the regs
+ */
+static int sn95031_set_vol_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int err;
+ unsigned int val, val2, val_mask;
+ int sst_pll_mode_saved;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] & mask);
+ val2 = (ucontrol->value.integer.value[1] & mask);
+
+ if (invert) {
+ val = max - val;
+ val2 = max - val2;
+ }
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ pr_debug("enabling PLL and VAUDA to change volume\n");
+ mutex_lock(&codec->mutex);
+ sst_pll_mode_saved = intel_scu_ipc_set_osc_clk0(true, CLK0_QUERY);
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+ snd_soc_dapm_sync(&codec->dapm);
+
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
+ goto restore_state;
+
+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+restore_state:
+ snd_soc_dapm_disable_pin(&codec->dapm, "VirtBias");
+ if (!(sst_pll_mode_saved & CLK0_MSIC))
+ intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
+ mutex_unlock(&codec->mutex);
+ return err;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -6200, 100, 0);
+
static const struct snd_kcontrol_new mfld_snd_controls[] = {
SOC_ENUM_EXT("Playback Switch", headset_enum,
headset_get_switch, headset_set_switch),
SOC_ENUM_EXT("PCM1 Mode", SN95031_pcm1_mode_config_enum,
sn95031_get_pcm1_mode, sn95031_set_pcm1_mode),
+ /* Add digital volume and mute controls for Headphone/Headset*/
+ SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", SN95031_HSLVOLCTRL,
+ SN95031_HSRVOLCTRL, 0, 71, 1,
+ snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+ out_tlv),
+ SOC_DOUBLE_R_EXT_TLV("Speaker Playback Volume", SN95031_IHFLVOLCTRL,
+ SN95031_IHFRVOLCTRL, 0, 71, 1,
+ snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+ out_tlv),
};
static const struct snd_soc_dapm_widget mfld_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_MIC("BuiltinMic", NULL),
SND_SOC_DAPM_MIC("HeadsetMic", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ /* Dummy widget to trigger VAUDA on/off */
+ SND_SOC_DAPM_MICBIAS("VirtBias", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("Vibra1Clock", SND_SOC_NOPM, 0, 0,
+ mfld_vibra_enable_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("Vibra2Clock", SND_SOC_NOPM, 0, 0,
+ mfld_vibra_enable_clk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route mfld_map[] = {
+ { "HPOUTL", NULL, "Headset Rail"},
+ { "HPOUTR", NULL, "Headset Rail"},
{"Headphones", NULL, "HPOUTR"},
{"Headphones", NULL, "HPOUTL"},
{"AMIC2", NULL, "BuiltinMic"},
{"AMIC1", NULL, "HeadsetMic"},
+ {"VIB1SPI", NULL, "Vibra1Clock"},
+ {"VIB2SPI", NULL, "Vibra2Clock"},
};
static void mfld_jack_check(unsigned int intr_status)
{
struct snd_soc_codec *codec = runtime->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct mfld_mc_private *mc_drv_ctx =
+ snd_soc_card_get_drvdata(codec->card);
int ret_val;
/* Add jack sense widgets */
/* Set up the map */
snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));
-
ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
ARRAY_SIZE(mfld_snd_controls));
if (ret_val) {
/* default is lineout NC, userspace sets it explcitly */
snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
- hs_switch = 0;
+ mc_drv_ctx->hs_switch = 0;
/* we dont use linein in this so set to NC */
snd_soc_dapm_disable_pin(dapm, "LINEINL");
snd_soc_dapm_disable_pin(dapm, "LINEINR");
snd_soc_dapm_ignore_suspend(dapm, "Headphones");
snd_soc_dapm_ignore_suspend(dapm, "BuiltinMic");
snd_soc_dapm_ignore_suspend(dapm, "HeadsetMic");
+ mutex_lock(&codec->mutex);
snd_soc_dapm_sync(dapm);
+ mutex_unlock(&codec->mutex);
/* Headset and button jack detection */
ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
return cpu_dai->driver->ops->set_tdm_slot(cpu_dai, 0, 0, 1, 0);
}
+static int mfld_media_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ pr_debug("%s\n", __func__);
+ /* Force the data width to 24 bit in MSIC since post processing
+ algorithms in DSP enabled with 24 bit precision */
+ ret = snd_soc_codec_set_params(codec, SNDRV_PCM_FORMAT_S24_LE);
+ if (ret < 0) {
+ pr_debug("codec_set_params returned error %d\n", ret);
+ return ret;
+ }
+ snd_soc_codec_set_pll(codec, 0, SN95031_PLLIN, 1, 1);
+
+ /* VAUD needs to be on before configuring PLL */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+ mutex_lock(&codec->mutex);
+ snd_soc_dapm_sync(&codec->dapm);
+ mutex_unlock(&codec->mutex);
+ usleep_range(5000, 6000);
+ sn95031_configure_pll(codec, ENABLE_PLL);
+
+ /* enable PCM2 */
+ snd_soc_dai_set_tristate(rtd->codec_dai, 0);
+ return 0;
+}
+
+static int mfld_voice_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_codec *codec = rtd->codec;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
+ pr_debug("%s\n", __func__);
+
+ if (mc_drv_ctx->pcm1_master_mode) { /* VOIP call */
+ snd_soc_codec_set_pll(codec, 0, SN95031_PLLIN, 1, 1);
+ snd_soc_dai_set_fmt(rtd->codec_dai, SND_SOC_DAIFMT_CBM_CFM
+ | SND_SOC_DAIFMT_DSP_A);
+ /* Sets the PCM1 clock rate */
+ snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1),
+ BIT(0)|BIT(1));
+ } else { /* CSV call */
+ snd_soc_codec_set_pll(codec, 0, SN95031_PCM1BCLK, 1, 1);
+ snd_soc_dai_set_fmt(rtd->codec_dai, SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_I2S);
+ snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1), 0);
+ }
+
+ /* VAUD needs to be on before configuring PLL */
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+ mutex_lock(&codec->mutex);
+ snd_soc_dapm_sync(&codec->dapm);
+ mutex_unlock(&codec->mutex);
+ usleep_range(5000, 6000);
+ sn95031_configure_pll(codec, ENABLE_PLL);
+ return 0;
+}
+
+static unsigned int rates_44100[] = {
+ 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_44100 = {
+ .count = ARRAY_SIZE(rates_44100),
+ .list = rates_44100,
+};
+
+static int mfld_media_startup(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s - applying rate constraint\n", __func__);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_44100);
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+ return 0;
+}
+
+static void mfld_media_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ pr_debug("%s\n", __func__);
+
+ snd_soc_dapm_disable_pin(&rtd->codec->dapm, "VirtBias");
+ /* switch off PCM2 port */
+ if (!rtd->codec->active)
+ snd_soc_dai_set_tristate(codec_dai, 1);
+}
+
+static int mfld_voice_startup(struct snd_pcm_substream *substream)
+{
+ intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+ return 0;
+}
+
+static void mfld_voice_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ pr_debug("%s\n", __func__);
+
+ snd_soc_dapm_disable_pin(&rtd->codec->dapm, "VirtBias");
+}
+
+static struct snd_soc_ops mfld_media_ops = {
+ .startup = mfld_media_startup,
+ .shutdown = mfld_media_shutdown,
+ .hw_params = mfld_media_hw_params,
+};
+
+static struct snd_soc_ops mfld_voice_ops = {
+ .startup = mfld_voice_startup,
+ .shutdown = mfld_voice_shutdown,
+ .hw_params = mfld_voice_hw_params,
+};
+
static struct snd_soc_dai_link mfld_msic_dailink[] = {
{
.name = "Medfield Headset",
.platform_name = "sst-platform",
.init = mfld_init,
.ignore_suspend = 1,
+ .ops = &mfld_media_ops,
},
{
.name = "Medfield Speaker",
.platform_name = "sst-platform",
.init = mfld_speaker_init,
.ignore_suspend = 1,
+ .ops = &mfld_media_ops,
},
{
.name = "Medfield Voice",
.init = NULL,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
+ .ops = &mfld_voice_ops,
},
};
#define snd_mfld_mc_poweroff NULL
#endif
+static int mfld_card_stream_event(struct snd_soc_dapm_context *dapm, int event)
+{
+ struct snd_soc_codec *codec = dapm->codec;
+ pr_debug("machine stream event: %d\n", event);
+ if (event == SND_SOC_DAPM_STREAM_STOP) {
+ if (!codec->active) {
+ sn95031_configure_pll(codec, DISABLE_PLL);
+ return intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
+ }
+ }
+ return 0;
+}
+
/* SoC card */
static struct snd_soc_card snd_soc_card_mfld = {
.name = "medfield_audio",
}
/* register the soc card */
snd_soc_card_mfld.dev = &ipcdev->dev;
- snd_soc_initialize_card_lists(&snd_soc_card_mfld);
+ snd_soc_card_mfld.dapm.stream_event = mfld_card_stream_event;
+ snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
ret_val = snd_soc_register_card(&snd_soc_card_mfld);
if (ret_val) {
pr_debug("snd_soc_register_card failed %d\n", ret_val);
goto freeirq;
}
ipc_set_drvdata(ipcdev, &snd_soc_card_mfld);
- snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
pr_debug("successfully exited probe\n");
return ret_val;
static int __init snd_mfld_driver_init(void)
{
- pr_debug("snd_mfld_driver_init called\n");
+ pr_info("snd_mfld_driver_init called\n");
return ipc_driver_register(&snd_mfld_mc_driver);
}
-module_init_async(snd_mfld_driver_init);
+late_initcall(snd_mfld_driver_init);
static void __exit snd_mfld_driver_exit(void)
{
#include <linux/pm_runtime.h>
#include <linux/async.h>
#include <linux/lnw_gpio.h>
+#include <linux/delay.h>
#include <asm/intel-mid.h>
-#include <asm/intel_scu_ipc.h>
-#include <sound/intel_sst.h>
#include <sound/intel_sst_ioctl.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
-#include <linux/delay.h>
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
stream->period_elapsed(stream->pcm_substream);
return IRQ_HANDLED;
}
- pr_err("recieved IPC %x\n", header.full);
+ pr_debug("received IPC %x\n", header.full);
if (header.part.large)
size = header.part.data;
if (header.part.msg_id & REPLY_MSG) {
INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, sst_post_message);
INIT_WORK(&sst_drv_ctx->ipc_process_msg.wq, sst_process_message);
INIT_WORK(&sst_drv_ctx->ipc_process_reply.wq, sst_process_reply);
- INIT_WORK(&sst_drv_ctx->mad_ops.wq, sst_process_mad_ops);
init_waitqueue_head(&sst_drv_ctx->wait_queue);
sst_drv_ctx->mad_wq = create_singlethread_workqueue("sst_mad_wq");
pci_set_drvdata(pci, sst_drv_ctx);
pm_runtime_allow(&pci->dev);
pm_runtime_put_noidle(&pci->dev);
- pr_debug("...successfully done!!!\n");
+ register_sst(&pci->dev);
+ pr_info("%s successfully done!\n", __func__);
return ret;
do_free_misc:
{
pm_runtime_get_noresume(&pci->dev);
pm_runtime_forbid(&pci->dev);
+ unregister_sst(&pci->dev);
pci_dev_put(sst_drv_ctx->pci);
sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
misc_deregister(&lpe_ctrl);
return;
}
-int intel_sst_set_pll(unsigned int enable, enum intel_sst_pll_mode mode)
-{
- int ret = 0;
- int clock_enable = 0;
- static const unsigned int clock_khz = 19200;
-
- pr_debug("set_pll, Enable %x, Mode %x\n", enable, mode);
- mutex_lock(&sst_drv_ctx->sst_lock);
- if (enable == true) {
- /*enable clock */
- clock_enable = 1;
- if (sst_drv_ctx->pll_mode) {
- /* clock is on, so just update and return */
- sst_drv_ctx->pll_mode |= mode;
- goto out;
- }
- sst_drv_ctx->pll_mode |= mode;
- pr_debug("set_pll, enabling pll %x\n", sst_drv_ctx->pll_mode);
- } else {
- /* for turning off only, we check device state and turn off only
- * when device is supspended
- */
- sst_drv_ctx->pll_mode &= ~mode;
- pr_debug("set_pll, disabling pll %x\n", sst_drv_ctx->pll_mode);
- if (sst_drv_ctx->pll_mode)
- goto out;
- clock_enable = 0; /*disbale clock */
- }
- /* send ipc command to configure the PNW clock to MSIC PLLIN */
- pr_debug("configuring clock now\n");
- ret = intel_scu_ipc_osc_clk(OSC_CLK_AUDIO, clock_enable ?
- clock_khz : 0);
- if (ret)
- pr_err("ipc clk disable command failed: %d\n", ret);
-out:
- mutex_unlock(&sst_drv_ctx->sst_lock);
- return ret;
-}
-EXPORT_SYMBOL(intel_sst_set_pll);
-
void intel_sst_pwm_suspend(unsigned int pwm_suspend)
{
}
}
EXPORT_SYMBOL(intel_sst_pwm_suspend);
-/*
- * The runtime_suspend/resume is pretty much similar to the legacy
- * suspend/resume with the noted exception below: The PCI core takes care of
- * taking the system through D3hot and restoring it back to D0 and so there is
- * no need to duplicate that here.
- */
-int intel_sst_get_pll(void)
-{
- return sst_drv_ctx->pll_mode;
-}
-EXPORT_SYMBOL(intel_sst_get_pll);
int vibra_pwm_configure(unsigned int enable)
{
return 0;
}
+/*
+ * The runtime_suspend/resume is pretty much similar to the legacy
+ * suspend/resume with the noted exception below: The PCI core takes care of
+ * taking the system through D3hot and restoring it back to D0 and so there is
+ * no need to duplicate that here.
+ */
static int intel_sst_runtime_suspend(struct device *dev)
{
union config_status_reg csr;
sst_drv_ctx->sst_state = SST_SUSPENDED;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
mutex_unlock(&sst_drv_ctx->sst_lock);
- intel_sst_set_pll(false, SST_PLL_AUDIO);
if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID)
vibra_pwm_configure(false);
return 0;
vibra_pwm_configure(true);
}
- intel_sst_set_pll(true, SST_PLL_AUDIO);
sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
return 0;
}
{
/* Init all variables, data structure etc....*/
int ret = 0;
- pr_debug("INFO: ******** SST DRIVER loading.. Ver: %s\n",
+ pr_info("INFO: ******** SST DRIVER loading.. Ver: %s\n",
SST_DRIVER_VERSION);
mutex_init(&drv_ctx_lock);
#include <linux/rar_register.h>
#include "../../../drivers/staging/memrar/memrar.h"
#endif
-#include <sound/intel_sst.h>
#include <sound/intel_sst_ioctl.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
break;
case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): {
- struct snd_sst_params str_param;
+ struct sst_stream_params str_param;
pr_debug("IOCTL_SET_PARAMS received!\n");
if (minor != STREAM_MODULE) {
/* Block the call for reply */
if (!retval) {
int sfreq = 0, word_size = 0, num_channel = 0;
- sfreq = str_param.sparams.uc.pcm_params.sfreq;
- word_size =
- str_param.sparams.uc.pcm_params.pcm_wd_sz;
- num_channel =
- str_param.sparams.uc.pcm_params.num_chan;
+ sfreq = str_param.sparams.sfreq;
+ word_size = str_param.sparams.pcm_wd_sz;
+ num_channel = str_param.sparams.num_chan;
}
}
break;
u32 curr_bytes;
u32 cumm_bytes;
u32 src;
- enum snd_sst_audio_device_type device;
+ enum sst_audio_device_type device;
u8 pcm_slot;
};
struct work_struct wq;
};
-struct mad_ops_wq {
- int stream_id;
- enum sst_controls control_op;
- struct work_struct wq;
-
-};
-
#define SST_MMAP_PAGES (640*1024 / PAGE_SIZE)
#define SST_MMAP_STEP (40*1024 / PAGE_SIZE)
struct sst_ipc_msg_wq ipc_process_msg;
struct sst_ipc_msg_wq ipc_process_reply;
struct sst_ipc_msg_wq ipc_post_msg;
- struct mad_ops_wq mad_ops;
wait_queue_head_t wait_queue;
struct workqueue_struct *mad_wq;
struct workqueue_struct *post_msg_wq;
vol_info_blk, mute_info_blk, hs_info_blk,
dma_info_blk;
struct mutex list_lock;/* mutex for IPC list locking */
- spinlock_t list_spin_lock; /* mutex for IPC list locking */
+ spinlock_t list_spin_lock; /* mutex for IPC list locking */
struct snd_pmic_ops *scard_ops;
struct pci_dev *pci;
void *mmap_mem;
unsigned int lpe_stalled; /* LPE is stalled or not */
unsigned int pmic_port_instance; /*pmic port instance*/
unsigned int lpaudio_start;
- /* 1 - LPA stream(MP3 pb) in progress*/
+ /* 1 - LPA stream(MP3 pb) in progress*/
unsigned int audio_start;
dev_t devt_d, devt_c;
unsigned int max_streams;
unsigned int *fw_cntx;
unsigned int fw_cntx_size;
unsigned int csr_value;
- unsigned int pll_mode;
- const struct firmware *fw;
+ const struct firmware *fw;
struct sst_dma dma;
void *fw_in_mem;
struct sst_runtime_param runtime_param;
- unsigned int device_input_mixer;
- struct mutex mixer_ctrl_lock;
+ unsigned int device_input_mixer;
+ struct mutex mixer_ctrl_lock;
struct dma_async_tx_descriptor *desc;
struct sst_sg_list fw_sg_list, library_list;
};
int sst_play_frame(int streamID);
int sst_pcm_play_frame(int str_id, struct sst_stream_bufs *sst_buf);
int sst_capture_frame(int streamID);
-int sst_set_stream_param(int streamID, struct snd_sst_params *str_param);
+int sst_set_stream_param(int streamID, struct sst_stream_params *str_param);
int sst_target_device_select(struct snd_sst_target_device *target_device);
int sst_decode(int str_id, struct snd_sst_dbufs *dbufs);
int sst_get_decoded_bytes(int str_id, unsigned long long *bytes);
int sst_get_fw_info(struct snd_sst_fw_info *info);
int sst_get_stream_params(int str_id,
struct snd_sst_get_stream_params *get_params);
-int sst_get_stream(struct snd_sst_params *str_param);
-int sst_get_stream_allocated(struct snd_sst_params *str_param,
+int sst_get_stream(struct sst_stream_params *str_param);
+int sst_get_stream_allocated(struct sst_stream_params *str_param,
struct snd_sst_lib_download **lib_dnld);
int sst_drain_stream(int str_id);
int sst_get_vol(struct snd_sst_vol *set_vol);
*/
static inline void sst_init_stream(struct stream_info *stream,
int codec, int sst_id, int ops, u8 slot,
- enum snd_sst_audio_device_type device)
+ enum sst_audio_device_type device)
{
stream->status = STREAM_INIT;
stream->prev = STREAM_UN_INIT;
sst_drv_ctx->sst_state = sst_state;
mutex_unlock(&sst_drv_ctx->sst_lock);
}
+
+int register_sst(struct device *);
+int unregister_sst(struct device *);
#endif /* __INTEL_SST_COMMON_H__ */
#include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
-#include <sound/intel_sst.h>
#include <sound/intel_sst_ioctl.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
}
}
-void sst_send_lpe_mixer_algo_params()
+void sst_send_lpe_mixer_algo_params(void)
{
struct snd_ppp_params algo_param;
struct snd_ppp_mixer_params mixer_param;
* This creates new stream id for a stream, in case lib is to be downloaded to
* DSP, it downloads that
*/
-int sst_get_stream_allocated(struct snd_sst_params *str_param,
+int sst_get_stream_allocated(struct sst_stream_params *str_param,
struct snd_sst_lib_download **lib_dnld)
{
int retval, str_id;
*
* @str_param : stream params
*/
-static int sst_get_sfreq(struct snd_sst_params *str_param)
+static int sst_get_sfreq(struct sst_stream_params *str_param)
{
switch (str_param->codec) {
case SST_CODEC_TYPE_PCM:
- return str_param->sparams.uc.pcm_params.sfreq;
+ return str_param->sparams.sfreq;
case SST_CODEC_TYPE_MP3:
- return str_param->sparams.uc.mp3_params.sfreq;
+ return str_param->sparams.sfreq;
case SST_CODEC_TYPE_AAC:
- return str_param->sparams.uc.aac_params.sfreq;
+ return str_param->sparams.sfreq;
case SST_CODEC_TYPE_WMA9:
- return str_param->sparams.uc.wma_params.sfreq;
+ return str_param->sparams.sfreq;
default:
return 0;
}
*
* @str_param : stream param
*/
-int sst_get_stream(struct snd_sst_params *str_param)
+int sst_get_stream(struct sst_stream_params *str_param)
{
int i, retval;
struct stream_info *str_info;
retval = sst_download_fw();
if (retval) {
pr_err("FW download fail %x\n", retval);
- pr_debug("doing rtpm_put\n");
sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
- pm_runtime_put(&sst_drv_ctx->pci->dev);
return retval;
}
} else {
case SST_SND_STREAM_PROCESS:
pr_debug("play/capt frames...\n");
break;
- case SST_SND_DEVICE_RESUME:
- pr_debug("SST_SND_DEVICE_RESUME\n");
- pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
- sst_prepare_fw();
- break;
default:
pr_err(" wrong control_ops reported\n");
}
* This function is called by MID sound card driver to open
* a new pcm interface
*/
-static int sst_open_pcm_stream(struct snd_sst_params *str_param)
+static int sst_open_pcm_stream(struct sst_stream_params *str_param)
{
struct stream_info *str_info;
int retval;
+ if (!str_param)
+ return -EINVAL;
+
pr_debug("open_pcm, doing rtpm_get\n");
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
retval = sst_prepare_fw();
- if (retval)
+ if (retval) {
+ pr_err("Unable to download FW\n");
+ pm_runtime_put(&sst_drv_ctx->pci->dev);
return retval;
-
- if (!str_param) {
- pr_debug("open_pcm, doing rtpm_put\n");
- return -EINVAL;
}
mutex_lock(&sst_drv_ctx->sst_lock);
case SST_SND_PAUSE:
case SST_SND_RESUME:
case SST_SND_DROP:
- case SST_SND_START:
- case SST_SND_DEVICE_RESUME: {
+ case SST_SND_START: {
struct mad_ops_wq *work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work)
return -ENOMEM;
queue_work(sst_drv_ctx->mad_wq, &work->wq);
break;
}
- case SST_SND_DEVICE_RESUME_SYNC:
- pr_debug("SST_SND_DEVICE_RESUME_SYNC\n");
- pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
- sst_prepare_fw();
- break;
- case SST_SND_DEVICE_SUSPEND:
- pr_debug("SST_SND_DEVICE_SUSPEND doing rtpm_put\n");
- pm_runtime_put(&sst_drv_ctx->pci->dev);
- break;
case SST_SND_STREAM_INIT: {
struct pcm_stream_info *str_info;
struct stream_info *stream;
return ret_val;
}
-static struct intel_sst_pcm_control pcm_ops = {
+static struct sst_ops pcm_ops = {
.open = sst_open_pcm_stream,
.device_control = sst_device_control,
.set_generic_params = sst_set_generic_params,
.close = sst_close_pcm_stream,
};
-static struct intel_sst_card_ops sst_pmic_ops = {
- .pcm_control = &pcm_ops,
+static struct sst_device sst_dsp_device = {
+ .name = "Intel(R) SST LPE",
+ .dev = NULL,
+ .ops = &pcm_ops,
};
/*
- * register_sst_card - function for sound card to register
+ * register_sst - function to register DSP
*
- * @card: pointer to structure of operations
- *
- * This function is called card driver loads and is ready for registration
+ * This functions registers DSP with the platform driver
*/
-int register_sst_card(struct intel_sst_card_ops *card)
+int register_sst(struct device *dev)
{
- pr_debug("driver register card %p\n", sst_drv_ctx);
- if (!sst_drv_ctx) {
- pr_err("No SST driver register card reject\n");
- return -ENODEV;
- }
+ int ret_val;
+ sst_dsp_device.dev = dev;
+ ret_val = sst_register_dsp(&sst_dsp_device);
+ if (ret_val)
+ pr_err("Unable to register DSP with platform driver\n");
- if (!card) {
- pr_err("Null Pointer Passed\n");
- return -EINVAL;
- }
- card->pcm_control = sst_pmic_ops.pcm_control;
- return 0;
+ return ret_val;
}
-EXPORT_SYMBOL_GPL(register_sst_card);
-/*
- * unregister_sst_card- function for sound card to un-register
- *
- * @card: pointer to structure of operations
- *
- * This function is called when card driver unloads
- */
-void unregister_sst_card(struct intel_sst_card_ops *card)
+int unregister_sst(struct device *dev)
{
- return;
+ return sst_unregister_dsp(&sst_dsp_device);
}
-EXPORT_SYMBOL_GPL(unregister_sst_card);
#include <linux/firmware.h>
#include <linux/dmaengine.h>
#include <linux/intel_mid_dma.h>
-#include <sound/intel_sst.h>
#include <sound/intel_sst_ioctl.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
#include <linux/sched.h>
}
/*converting from physical to virtual because
scattergather list works on virtual pointers*/
- ram = phys_to_virt(ram);
+ ram = (int) phys_to_virt(ram);
sst_fill_sglist(ram, block, &sg_src, &sg_dst);
block = (void *)block + sizeof(*block) + block->size;
}
__u32 type; /*Type of the parameter */
__u32 size;
__u32 input_stream_bitmap; /*Input stream Bit Map*/
-} __attribute__ ((packed));
+} __packed;
struct snd_sst_lib_download {
struct module_info lib_info; /* library info type, capabilities etc */
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/sched.h>
-#include <sound/intel_sst.h>
#include <sound/intel_sst_ioctl.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
#include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/sched.h>
-#include <sound/intel_sst.h>
#include <sound/intel_sst_ioctl.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <sound/intel_sst_ioctl.h>
-#include <sound/intel_sst.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
pr_debug("SST DBG:%d %d %d\n", stream_ops, codec, device);
BUG_ON(!params);
- sparams = (struct snd_sst_stream_params *)params;
+ sparams = kzalloc(sizeof(*sparams), GFP_KERNEL);
+ if (!sparams) {
+ pr_err("Unable to allocate snd_sst_stream_params\n");
+ return -ENOMEM;
+ }
+ /* TODO: figure out type of codec - assuming PCM size here */
+ memcpy(&sparams->uc, params, sizeof(struct sst_pcm_params));
+
num_ch = sparams->uc.pcm_params.num_chan;
/*check the device type*/
if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) {
alloc_param.str_type.protected_str = 0; /* non drm */
alloc_param.str_type.time_slots = pcm_slot;
alloc_param.str_type.result = alloc_param.str_type.reserved = 0;
- memcpy(&alloc_param.stream_params, params,
+ memcpy(&alloc_param.stream_params, sparams,
sizeof(struct snd_sst_stream_params));
+ kfree(sparams);
memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
memcpy(msg->mailbox_data + sizeof(u32), &alloc_param,
#include "../memrar/memrar.h"
#endif
#include <sound/intel_sst_ioctl.h>
-#include <sound/intel_sst.h>
+#include "../sst_platform.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
+
/**
* sst_get_stream_params - Send msg to query for stream parameters
* @str_id: stream id for which the parameters are queried for
*
* This function sets stream params during runtime
*/
-int sst_set_stream_param(int str_id, struct snd_sst_params *str_param)
+int sst_set_stream_param(int str_id, struct sst_stream_params *str_param)
{
int retval = 0;
struct ipc_post *msg = NULL;
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/intel_sst_ioctl.h>
-#include <sound/intel_sst.h>
-#include "../codecs/sn95031.h"
#include "sst_platform.h"
+#include "sst_platform_pvt.h"
-#define SST_VOICE_DAI "Voice-cpu-dai"
-
-static struct sst_platform_ctx *sst_cpu_ctx;
+struct sst_device *sst_dsp;
+static DEFINE_MUTEX(sst_dsp_lock);
static struct snd_pcm_hardware sst_platform_pcm_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
.periods_max = SST_MAX_PERIODS,
.fifo_size = SST_FIFO_SIZE,
};
+
#ifdef CONFIG_SND_CLV_MACHINE
static unsigned int lpe_mixer_input_ihf;
static unsigned int lpe_mixer_input_hs;
-int sst_set_mixer_param(unsigned int device_input_mixer)
+static int sst_set_mixer_param(unsigned int device_input_mixer)
{
- struct intel_sst_card_ops *sstdrv_ops;
- int ret_val;
-
- /* allocate memory for SST API set */
- sstdrv_ops = kzalloc(sizeof(*sstdrv_ops), GFP_KERNEL);
- if (!sstdrv_ops)
- return -ENOMEM;
- /* registering with SST driver to get access to SST APIs to use */
- ret_val = register_sst_card(sstdrv_ops);
- if (ret_val) {
- pr_err("sst: sst card registration failed\n");
- return -EIO;
+ if (!sst_dsp) {
+ pr_err("sst: DSP not registered\n");
+ return -ENODEV;
}
/*allocate memory for params*/
- ret_val = sstdrv_ops->pcm_control->set_generic_params(
- SST_SET_ALGO_PARAMS, (void *)&device_input_mixer);
- kfree(sstdrv_ops);
- return ret_val;
-}
-#endif
-
-static int sst_platform_ihf_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask,
- int slots, int slot_width) {
- struct intel_sst_card_ops *sstdrv_ops;
- struct snd_sst_runtime_params params_data;
- int channels = slots;
- int ret_val;
-
- /* allocate memory for SST API set */
- sstdrv_ops = kzalloc(sizeof(*sstdrv_ops), GFP_KERNEL);
- if (!sstdrv_ops)
- return -ENOMEM;
- /* registering with SST driver to get access to SST APIs to use */
- ret_val = register_sst_card(sstdrv_ops);
- if (ret_val) {
- pr_err("sst: sst card registration failed\n");
- return -EIO;
- }
- params_data.type = SST_SET_CHANNEL_INFO;
- params_data.str_id = SND_SST_DEVICE_IHF;
- params_data.size = sizeof(channels);
- params_data.addr = &channels;
- ret_val = sstdrv_ops->pcm_control->set_generic_params(SST_SET_RUNTIME_PARAMS,
- (void *)¶ms_data);
- kfree(sstdrv_ops);
- return ret_val;
+ return sst_dsp->ops->set_generic_params(SST_SET_ALGO_PARAMS,
+ (void *)&device_input_mixer);
}
-static const struct snd_soc_dai_ops sst_ihf_ops = {
- .set_tdm_slot = sst_platform_ihf_set_tdm_slot,
-};
-#ifdef CONFIG_SND_CLV_MACHINE
static int lpe_mixer_ihf_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
| SST_INPUT_STREAM_PCM;
break;
case 1:
- pr_debug("input is Comptress stream\n");
+ pr_debug("input is Compress stream\n");
device_input_mixer = SST_STREAM_DEVICE_IHF
| SST_INPUT_STREAM_COMPRESS;
break;
case 2:
- pr_debug("input is mixed stream\n");
+ pr_debug("input is Mixed stream\n");
device_input_mixer = SST_STREAM_DEVICE_IHF
| SST_INPUT_STREAM_MIXED;
break;
default:
- pr_err("Invalid Input%d\n", ucontrol->value.integer.value[0]);
+ pr_err("Invalid Input:%ld\n", ucontrol->value.integer.value[0]);
return -EINVAL;
}
lpe_mixer_input_ihf = ucontrol->value.integer.value[0];
| SST_INPUT_STREAM_PCM;
break;
case 1:
- pr_debug("input is Comptress stream\n");
+ pr_debug("input is Compress stream\n");
mixer_input_stream = SST_STREAM_DEVICE_HS
| SST_INPUT_STREAM_COMPRESS;
break;
case 2:
- pr_debug("input is PCM stream\n");
+ pr_debug("input is Mixed stream\n");
mixer_input_stream = SST_STREAM_DEVICE_HS
| SST_INPUT_STREAM_MIXED;
break;
default:
- pr_err("Invalid Input%d\n", ucontrol->value.integer.value[0]);
+ pr_err("Invalid Input:%ld\n", ucontrol->value.integer.value[0]);
return -EINVAL;
}
lpe_mixer_input_hs = ucontrol->value.integer.value[0];
sst_set_mixer_param(mixer_input_stream);
return 0;
}
+
static const char *lpe_mixer_text[] = {
- "PCM", "Compressed", "PCM and Compressed"};
+ "PCM", "Compressed", "PCM and Compressed",
+};
static const struct soc_enum lpe_mixer_enum =
SOC_ENUM_SINGLE_EXT(3, lpe_mixer_text);
-
static const struct snd_kcontrol_new sst_controls[] = {
SOC_ENUM_EXT("LPE IHF mixer", lpe_mixer_enum,
lpe_mixer_ihf_get, lpe_mixer_ihf_set),
};
#endif
-/* MFLD - MSIC */
-static struct snd_soc_dai_driver sst_platform_dai[] = {
-{
- .name = "Headset-cpu-dai",
- .playback = {
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
-/**FIXME ***/
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
- .rates = SNDRV_PCM_RATE_44100,
-#else
- .rates = SNDRV_PCM_RATE_48000,
-#endif
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
- .rates = SNDRV_PCM_RATE_44100,
-#else
- .rates = SNDRV_PCM_RATE_48000,
-#endif
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "Speaker-cpu-dai",
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_STEREO,
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
- .rates = SNDRV_PCM_RATE_44100,
-#else
- .rates = SNDRV_PCM_RATE_48000,
-#endif
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &sst_ihf_ops,
-},
-{
- .name = "Vibra1-cpu-dai",
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_MONO,
- .rates = SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "Vibra2-cpu-dai",
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_MONO,
- .rates = SNDRV_PCM_RATE_44100,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "Voice-cpu-dai",
- .playback = {
- .channels_min = SST_MONO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = SST_MONO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-};
+static int sst_platform_ihf_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width) {
+ struct snd_sst_runtime_params params_data;
+ int channels = slots;
+
+ /* registering with SST driver to get access to SST APIs to use */
+ if (!sst_dsp) {
+ pr_err("sst: DSP not registered\n");
+ return -EIO;
+ }
+ params_data.type = SST_SET_CHANNEL_INFO;
+ params_data.str_id = SND_SST_DEVICE_IHF;
+ params_data.size = sizeof(channels);
+ params_data.addr = &channels;
+ return sst_dsp->ops->set_generic_params(SST_SET_RUNTIME_PARAMS,
+ (void *)¶ms_data);
+}
/* helper functions */
static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
}
static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
- struct snd_sst_stream_params *param)
-{
-
- param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
- 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.reserved = 0;
- param->uc.pcm_params.sfreq = substream->runtime->rate;
- param->uc.pcm_params.ring_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- param->uc.pcm_params.period_count = substream->runtime->period_size;
- param->uc.pcm_params.ring_buffer_addr =
- virt_to_phys(substream->dma_buffer.area);
- pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count);
- pr_debug("sfreq= %d, wd_sz = %d\n",
- param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz);
+ struct sst_pcm_params *param)
+{
+ param->codec = SST_CODEC_TYPE_PCM;
+ 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)
{
struct sst_runtime_stream *stream =
substream->runtime->private_data;
- struct snd_sst_stream_params param = {{{0,},},};
- struct snd_sst_params str_params = {0};
+ struct sst_pcm_params param = {0};
+ struct sst_stream_params str_params = {0};
int ret_val;
/* set codec params and inform SST driver the same */
sst_fill_pcm_params(substream, ¶m);
substream->runtime->dma_area = substream->dma_buffer.area;
str_params.sparams = param;
- str_params.codec = param.uc.pcm_params.codec;
+ 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",
+ pr_debug("Playback 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",
+ pr_debug("Capture stream, Device %d\n",
substream->pcm->device);
}
- ret_val = stream->sstdrv_ops->pcm_control->open(&str_params);
- pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+ ret_val = stream->ops->open(&str_params);
+ pr_debug("platform prepare: stream open ret_val = 0x%x\n", ret_val);
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);
+ pr_debug("platform allocated strid: %d\n", stream->stream_info.str_id);
return ret_val;
}
stream->stream_info.mad_substream = substream;
stream->stream_info.buffer_ptr = 0;
stream->stream_info.sfreq = substream->runtime->rate;
- ret_val = stream->sstdrv_ops->pcm_control->device_control(
+ ret_val = stream->ops->device_control(
SST_SND_STREAM_INIT, &stream->stream_info);
if (ret_val)
pr_err("control_set ret error %d\n", ret_val);
}
/* 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)
{
- struct snd_pcm_runtime *runtime;
- struct sst_runtime_stream *stream;
int ret_val = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
-
- pr_debug("sst_platform_open called:%s\n", dai_link->cpu_dai_name);
-
- runtime = substream->runtime;
- runtime->hw = sst_platform_pcm_hw;
- if (!strcmp(dai_link->cpu_dai_name, SST_VOICE_DAI)) {
- sst_cpu_ctx->active_voice_cnt++;
-/**FIXME in clean up patch***/
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
-
- ret_val = intel_sst_set_pll(true, SST_PLL_VOICE);
-#endif
- if (!ret_val)
- return snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- else
- return ret_val;
- }
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sst_runtime_stream *stream;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
return -ENOMEM;
spin_lock_init(&stream->status_lock);
- stream->stream_info.str_id = 0;
- sst_set_stream_status(stream, SST_PLATFORM_INIT);
- stream->stream_info.mad_substream = substream;
- /* allocate memory for SST API set */
- stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops),
- GFP_KERNEL);
- if (!stream->sstdrv_ops) {
- pr_err("sst: mem allocation for ops fail\n");
- ret_val = -ENOMEM;
+
+ /* get the sst ops */
+ mutex_lock(&sst_dsp_lock);
+ if (!sst_dsp ||
+ !try_module_get(sst_dsp->dev->driver->owner)) {
+ pr_err("no device available to run\n");
+ ret_val = -ENODEV;
goto out_ops;
}
- stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
- /* registering with SST driver to get access to SST APIs to use */
- ret_val = register_sst_card(stream->sstdrv_ops);
- if (ret_val) {
- pr_err("sst: sst card registration failed\n");
- goto out_reg_sst;
- }
+ stream->ops = sst_dsp->ops;
+ mutex_unlock(&sst_dsp_lock);
+
+ stream->stream_info.str_id = 0;
+ sst_set_stream_status(stream, SST_PLATFORM_UNINIT);
+ stream->stream_info.mad_substream = substream;
runtime->private_data = stream;
- sst_cpu_ctx->active_nonvoice_cnt++;
/* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step(substream->runtime, 0,
return snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
-
-out_reg_sst:
- kfree(stream->sstdrv_ops);
out_ops:
kfree(stream);
+ mutex_unlock(&sst_dsp_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;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- pr_debug("sst_platform_close called:%s\n", dai_link->cpu_dai_name);
-
- if (!strcmp(dai_link->cpu_dai_name, SST_VOICE_DAI)) {
- sst_cpu_ctx->active_voice_cnt--;
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
- intel_sst_set_pll(false, SST_PLL_VOICE);
-#endif
- goto func_exit;
- }
- sst_cpu_ctx->active_nonvoice_cnt--;
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
- if (str_id) {
- if (stream->stream_status == SST_PLATFORM_SUSPENDED) {
- pr_debug("device suspended resume for closure\n");
- ret_val = stream->sstdrv_ops->pcm_control->
- device_control(SST_SND_DEVICE_RESUME_SYNC,
- &str_id);
- if (!ret_val)
- sst_set_stream_status(stream,
- SST_PLATFORM_DROPPED);
- }
- ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
- }
- unregister_sst_card(stream->sstdrv_ops);
- kfree(stream->sstdrv_ops);
+ if (str_id)
+ ret_val = stream->ops->close(str_id);
+ module_put(sst_dsp->dev->driver->owner);
kfree(stream);
-func_exit:
-/**FIXME ***/
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
- if (!sst_cpu_ctx->active_nonvoice_cnt)
- snd_soc_dai_set_tristate(codec_dai, 1);
-#endif
- return ret_val;
+ pr_debug("%s: %d\n", __func__, ret_val);
}
-static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+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;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
- pr_debug("sst_platform_pcm_prepare called\n");
+ pr_debug("%s\n", __func__);
- if (!strcmp(dai_link->cpu_dai_name, SST_VOICE_DAI)) {
- pr_debug("pcm_preare for Voice, returning.\n");
- return ret_val;
- }
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (stream->stream_info.str_id)
ret_val = sst_platform_alloc_stream(substream);
if (ret_val <= 0)
- goto out;
+ return ret_val;
snprintf(substream->pcm->id, sizeof(substream->pcm->id),
"%d", stream->stream_info.str_id);
if (ret_val)
return ret_val;
substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+ 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)
+{
+ pr_debug("%s\n", __func__);
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+ return 0;
+}
-out:
- /* we are suspending the sink/source here as we want to have
- * device suspended in idle, before handling next request we will send
- * an explict RESUME call */
- pr_debug("Suspend NOW\n");
- stream->sstdrv_ops->pcm_control->device_control(
- SST_SND_DEVICE_SUSPEND, &stream->stream_info.str_id);
- sst_set_stream_status(stream, SST_PLATFORM_SUSPENDED);
- return ret_val;
+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,
+ .set_tdm_slot = sst_platform_ihf_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+ .name = SST_HEADSET_DAI,
+ .ops = &sst_media_dai_ops,
+ .playback = {
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = SST_SPEAKER_DAI,
+ .ops = &sst_media_dai_ops,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = SST_VIBRA1_DAI,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_MONO,
+ .rates = SNDRV_PCM_RATE_44100,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = SST_VIBRA2_DAI,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_MONO,
+ .rates = SNDRV_PCM_RATE_44100,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = SST_VOICE_DAI,
+ .playback = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = SST_MONO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+ pr_debug("sst_platform_open called:%s\n", dai_link->cpu_dai_name);
+ runtime = substream->runtime;
+ runtime->hw = sst_platform_pcm_hw;
+ return 0;
+}
+
+static int sst_platform_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ pr_debug("sst_platform_close called:%s\n", dai_link->cpu_dai_name);
+ return 0;
}
static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
str_id = stream->stream_info.str_id;
alsa_state = substream->runtime->status->state;
- if (stream->stream_status == SST_PLATFORM_SUSPENDED) {
- pr_debug("in trigger, device is suspended, so resume it\n");
- ret_val = stream->sstdrv_ops->pcm_control->device_control(
- SST_SND_DEVICE_RESUME, &str_id);
- if (!ret_val)
- sst_set_stream_status(stream, SST_PLATFORM_DROPPED);
- }
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- pr_debug("sst: Trigger Start\n");
+ pr_debug("Trigger Start\n");
str_cmd = SST_SND_START;
status = SST_PLATFORM_RUNNING;
stream->stream_info.mad_substream = substream;
break;
case SNDRV_PCM_TRIGGER_STOP:
- pr_debug("sst: in stop\n");
+ pr_debug("Trigger stop\n");
str_cmd = SST_SND_DROP;
status = SST_PLATFORM_DROPPED;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- pr_debug("sst: in pause\n");
+ pr_debug("Trigger pause\n");
str_cmd = SST_SND_PAUSE;
status = SST_PLATFORM_PAUSED;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- pr_debug("sst: in pause release\n");
+ pr_debug("Trigger pause release\n");
str_cmd = SST_SND_RESUME;
status = SST_PLATFORM_RUNNING;
break;
default:
return -EINVAL;
}
- ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd,
- &str_id);
+ ret_val = stream->ops->device_control(str_cmd, &str_id);
if (!ret_val)
sst_set_stream_status(stream, status);
- if (cmd == SNDRV_PCM_TRIGGER_STOP &&
- alsa_state == SNDRV_PCM_STATE_DRAINING) {
- ret_val = stream->sstdrv_ops->pcm_control->device_control(
- SST_SND_DEVICE_SUSPEND, &str_id);
- if (!ret_val)
- sst_set_stream_status(stream, SST_PLATFORM_SUSPENDED);
- }
return ret_val;
}
if (status == SST_PLATFORM_INIT)
return 0;
str_info = &stream->stream_info;
- ret_val = stream->sstdrv_ops->pcm_control->device_control(
+ ret_val = stream->ops->device_control(
SST_SND_BUFFER_POINTER, str_info);
if (ret_val) {
pr_err("sst: error code = %d\n", 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)
-{
- int ret;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
-/**FIXME -Need to move all codec related stuff to Machine driver ***/
-#if (defined(CONFIG_SND_MFLD_MACHINE) || (CONFIG_SND_MFLD_MACHINE_GI))
- if (strcmp(rtd->dai_link->cpu_dai_name, SST_VOICE_DAI)) {
- /* Force the data width to 24 bit in MSIC. Post Processing
- algorithms in DSP enabled with 24 bit precision */
- ret = snd_soc_codec_set_params(codec, SNDRV_PCM_FORMAT_S24_LE);
- if (ret < 0) {
- pr_debug("codec set_params returned error %d\n", ret);
- return ret;
- }
- }
- /*last two parameters have to non-zero, otherwise pll gets disabled*/
- snd_soc_dai_set_pll(codec_dai, 0, SST_CLK_UNINIT, 1,
- params_rate(params));
-#endif
- 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);
-}
-
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)
}
return retval;
}
+
#ifdef CONFIG_SND_CLV_MACHINE
static int sst_soc_probe(struct snd_soc_platform *platform)
{
return snd_soc_add_platform_controls(platform, sst_controls,
ARRAY_SIZE(sst_controls));
}
-
-static int sst_soc_remove(struct snd_soc_platform *platform)
-{
- pr_debug("%s called\n", __func__);
- return 0;
-}
#endif
+
static struct snd_soc_platform_driver sst_soc_platform_drv = {
#ifdef CONFIG_SND_CLV_MACHINE
- .probe = &sst_soc_probe,
- .remove = &sst_soc_remove,
+ .probe = sst_soc_probe,
#endif
.ops = &sst_platform_ops,
.pcm_new = sst_pcm_new,
.pcm_free = sst_pcm_free,
};
+int sst_register_dsp(struct sst_device *dev)
+{
+ if (!dev)
+ return -ENODEV;
+ mutex_lock(&sst_dsp_lock);
+ if (sst_dsp) {
+ pr_err("we already have a device %s\n", sst_dsp->name);
+ mutex_unlock(&sst_dsp_lock);
+ return -EEXIST;
+ }
+ pr_debug("registering device %s\n", dev->name);
+ sst_dsp = dev;
+ mutex_unlock(&sst_dsp_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_register_dsp);
+
+int sst_unregister_dsp(struct sst_device *dev)
+{
+ if (dev != sst_dsp)
+ return -EINVAL;
+
+ mutex_lock(&sst_dsp_lock);
+ if (sst_dsp) {
+ mutex_unlock(&sst_dsp_lock);
+ return -EIO;
+ }
+
+ pr_debug("unregister %s\n", sst_dsp->name);
+ sst_dsp = NULL;
+ mutex_unlock(&sst_dsp_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_unregister_dsp);
+
static int sst_platform_probe(struct platform_device *pdev)
{
int ret;
return ret;
}
- sst_cpu_ctx = kzalloc(sizeof(*sst_cpu_ctx), GFP_KERNEL);
- if (!sst_cpu_ctx) {
- pr_err("memory alloc failed for cpu_dais context\n");
- snd_soc_unregister_platform(&pdev->dev);
- return -ENOMEM;
- }
-
ret = snd_soc_register_dais(&pdev->dev,
sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
if (ret) {
pr_err("registering cpu dais failed\n");
snd_soc_unregister_platform(&pdev->dev);
- kfree(sst_cpu_ctx);
}
return ret;
}
snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
snd_soc_unregister_platform(&pdev->dev);
- kfree(sst_cpu_ctx);
pr_debug("sst_platform_remove success\n");
return 0;
}
#ifndef __SST_PLATFORMDRV_H__
#define __SST_PLATFORMDRV_H__
-#define SST_MONO 1
-#define SST_STEREO 2
+enum sst_audio_device_type {
+ SND_SST_DEVICE_HEADSET = 1,
+ SND_SST_DEVICE_IHF,
+ SND_SST_DEVICE_VIBRA,
+ SND_SST_DEVICE_HAPTIC,
+ SND_SST_DEVICE_CAPTURE,
+};
+enum snd_sst_input_stream {
+ SST_INPUT_STREAM_PCM = 0x2,
+ SST_INPUT_STREAM_COMPRESS = 0x8,
+ SST_INPUT_STREAM_MIXED = 0xA,
+};
-#define SST_MIN_RATE 8000
-#define SST_MAX_RATE 48000
-#define SST_MIN_CHANNEL 1
-#define SST_MAX_CHANNEL 2
+enum snd_sst_stream_type {
+ SST_STREAM_DEVICE_HS = 32,
+ SST_STREAM_DEVICE_IHF = 33,
+ SST_STREAM_DEVICE_MIC0 = 34,
+ SST_STREAM_DEVICE_MIC1 = 35,
+};
-/**FIXME ***/
-#ifdef CONFIG_SND_MFLD_MACHINE
-#define SST_MAX_BUFFER 88200 /*500ms*/
-#define SST_MIN_PERIOD_BYTES 1764 /*10ms@44.1,16bit,2ch*/
-#define SST_MAX_PERIOD_BYTES 44100 /*250ms*/
-#else
-#define SST_MAX_BUFFER 96000 /*500ms*/
-#define SST_MIN_PERIOD_BYTES 1920 /*10ms@44.1,16bit,2ch*/
-#define SST_MAX_PERIOD_BYTES 48000 /*250ms*/
-#endif
+enum sst_controls {
+ SST_SND_ALLOC = 0x1000,
+ SST_SND_PAUSE = 0x1001,
+ SST_SND_RESUME = 0x1002,
+ SST_SND_DROP = 0x1003,
+ SST_SND_FREE = 0x1004,
+ SST_SND_BUFFER_POINTER = 0x1005,
+ SST_SND_STREAM_INIT = 0x1006,
+ SST_SND_START = 0x1007,
+ SST_SND_STREAM_PROCESS = 0x1008,
+ SST_CONTROL_BASE = 0x1009,
+ SST_VMIC_CHANNEL_SELECT = 0x1010,
+ SST_SET_RUNTIME_PARAMS = 0x1011,
+ SST_SET_ALGO_PARAMS = 0x1012,
+ SST_MAX_CONTROLS = 0x1012,
+};
-#define SST_MIN_PERIODS 2
-#define SST_MAX_PERIODS 50
-#define SST_FIFO_SIZE 0
-#define SST_CARD_NAMES "intel_mid_card"
-#define MSIC_VENDOR_ID 3
-#define SST_CLK_UNINIT 0x03
+struct pcm_stream_info {
+ int str_id;
+ void *mad_substream;
+ void (*period_elapsed) (void *mad_substream);
+ unsigned long long buffer_ptr;
+ unsigned long long pcm_delay;
+ int sfreq;
+};
+
+enum sst_stream_ops {
+ STREAM_OPS_PLAYBACK = 0, /* Decode */
+ STREAM_OPS_CAPTURE, /* Encode */
+ STREAM_OPS_PLAYBACK_DRM, /* Play Audio/Voice */
+ STREAM_OPS_PLAYBACK_ALERT, /* Play Audio/Voice */
+ STREAM_OPS_CAPTURE_VOICE_CALL, /* CSV Voice recording */
+};
+
+/* PCM Parameters */
+struct sst_pcm_params {
+ u16 codec; /* codec type */
+ 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 */
+ u32 ring_buffer_size;
+ u32 period_count; /* period elapsed in samples*/
+ u32 ring_buffer_addr;
+};
+
+struct sst_stream_params {
+ u32 result;
+ u32 stream_id;
+ u8 codec;
+ u8 ops;
+ u8 stream_type;
+ u8 device_type;
+ struct sst_pcm_params sparams;
+};
+
+enum lpe_param_types_mixer {
+ SST_ALGO_PARAM_MIXER_STREAM_CFG = 0x801,
+};
+
+struct mad_ops_wq {
+ int stream_id;
+ enum sst_controls control_op;
+ struct work_struct wq;
+};
+
+struct sst_ops {
+ int (*open) (struct sst_stream_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);
+};
struct sst_runtime_stream {
int stream_status;
struct pcm_stream_info stream_info;
- struct intel_sst_card_ops *sstdrv_ops;
+ struct sst_ops *ops;
spinlock_t status_lock;
};
-enum sst_drv_status {
- SST_PLATFORM_INIT = 1,
- SST_PLATFORM_STARTED,
- SST_PLATFORM_RUNNING,
- SST_PLATFORM_PAUSED,
- SST_PLATFORM_DROPPED,
- SST_PLATFORM_SUSPENDED,
+struct sst_device {
+ char *name;
+ struct device *dev;
+ struct sst_ops *ops;
};
-struct sst_platform_ctx {
- int active_nonvoice_cnt;
- int active_voice_cnt;
-};
+int sst_register_dsp(struct sst_device *sst);
+int sst_unregister_dsp(struct sst_device *sst);
+/* FIXME: remove once vibra becomes PCI driver */
+void intel_sst_pwm_suspend(unsigned int suspend);
#endif
--- /dev/null
+/*
+ * sst_platform_pvt.h - Intel MID Platform driver header file
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@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 __SST_PLATFORMDRVPVT_H__
+#define __SST_PLATFORMDRVPVT_H__
+
+#define SST_MONO 1
+#define SST_STEREO 2
+
+#define SST_MIN_RATE 8000
+#define SST_MAX_RATE 48000
+#define SST_MIN_CHANNEL 1
+#define SST_MAX_CHANNEL 2
+
+#define SST_MAX_BUFFER 96000 /*500ms@48,16bit,2ch - CLV*/
+#define SST_MIN_PERIOD_BYTES 1764 /*10ms@44.1,16bit,2ch - MFLD*/
+#define SST_MAX_PERIOD_BYTES 48000 /*250ms@48,16bit,2ch - CLV*/
+
+#define SST_MIN_PERIODS 2
+#define SST_MAX_PERIODS 50
+#define SST_FIFO_SIZE 0
+#define SST_CLK_UNINIT 0x03
+#define SST_CODEC_TYPE_PCM 1
+
+#define SST_HEADSET_DAI "Headset-cpu-dai"
+#define SST_SPEAKER_DAI "Speaker-cpu-dai"
+#define SST_VIBRA1_DAI "Vibra1-cpu-dai"
+#define SST_VIBRA2_DAI "Vibra2-cpu-dai"
+#define SST_VOICE_DAI "Voice-cpu-dai"
+
+struct sst_device;
+
+enum sst_drv_status {
+ SST_PLATFORM_UNINIT,
+ SST_PLATFORM_INIT,
+ SST_PLATFORM_RUNNING,
+ SST_PLATFORM_PAUSED,
+ SST_PLATFORM_DROPPED,
+};
+
+#endif
codec->dapm.dev = dev;
codec->dapm.codec = codec;
codec->dapm.seq_notifier = codec_drv->seq_notifier;
+ codec->dapm.stream_event = codec_drv->stream_event;
codec->dev = dev;
codec->driver = codec_drv;
codec->num_dai = num_dai;
&async_domain);
async_synchronize_full_domain(&async_domain);
+ list_for_each_entry(d, &dapm->card->dapm_list, list)
+ if (d->stream_event)
+ d->stream_event(dapm, event);
+
pop_dbg(dapm->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time);
pop_wait(card->pop_time);
}
dapm_power_widgets(dapm, event);
-
- if (dapm->codec->driver->stream_event)
- dapm->codec->driver->stream_event(dapm, event);
}
/**