generic-poky/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-aud...

2392 lines
69 KiB
Diff

From d88bb1ae711414e9bca4a23a7d6375cb4bad18f4 Mon Sep 17 00:00:00 2001
From: R, Dharageswari <dharageswari.r@intel.com>
Date: Thu, 29 Apr 2010 20:28:59 +0530
Subject: [PATCH] ADR-Post-Beta-0.05.002.03-7/8-Moorestown Audio Drivers: sound card ALSA driver
This adds support for Moorestown ALSA Sound card driver.
This is an ALSA driver for supporting PCM playback/capture in
traditional ALSA way. Anyone who chooses not to use DSP for
decoding/encoding can use ALSA path to play/capture, but obvious loss will
be power. This driver registers the control interface and PCM interface with
the SST driver which finally sends it to the hardware. This driver allows any
subsystem in OS which wants to use the audio-subsystems to be routed
through the ALSA.The patch includes ALSA driver header file for handling
mixer controls for Intel MAD chipset.This patch also includes enum additions to
jack.h of ALSA Framework
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
modified: include/sound/jack.h
new file: sound/pci/sst/intelmid.c
new file: sound/pci/sst/intelmid.h
new file: sound/pci/sst/intelmid_ctrl.c
new file: sound/pci/sst/intelmid_ctrl.h
new file: sound/pci/sst/intelmid_pvt.c
Patch-mainline: 2.6.35?
---
include/sound/jack.h | 2 +
sound/pci/sst/intelmid.c | 1205 +++++++++++++++++++++++++++++++++++++++++
sound/pci/sst/intelmid.h | 170 ++++++
sound/pci/sst/intelmid_ctrl.c | 555 +++++++++++++++++++
sound/pci/sst/intelmid_ctrl.h | 33 ++
sound/pci/sst/intelmid_pvt.c | 343 ++++++++++++
6 files changed, 2308 insertions(+), 0 deletions(-)
create mode 100644 sound/pci/sst/intelmid.c
create mode 100644 sound/pci/sst/intelmid.h
create mode 100644 sound/pci/sst/intelmid_ctrl.c
create mode 100644 sound/pci/sst/intelmid_ctrl.h
create mode 100644 sound/pci/sst/intelmid_pvt.c
diff --git a/include/sound/jack.h b/include/sound/jack.h
index f236e42..791c550 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -42,6 +42,8 @@ enum snd_jack_types {
SND_JACK_MECHANICAL = 0x0008, /* If detected separately */
SND_JACK_VIDEOOUT = 0x0010,
SND_JACK_AVOUT = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT,
+ SND_JACK_HS_SHORT_PRESS = SND_JACK_HEADSET | 0x0020,
+ SND_JACK_HS_LONG_PRESS = SND_JACK_HEADSET | 0x0040,
};
struct snd_jack {
diff --git a/sound/pci/sst/intelmid.c b/sound/pci/sst/intelmid.c
new file mode 100644
index 0000000..c5a3b36
--- /dev/null
+++ b/sound/pci/sst/intelmid.c
@@ -0,0 +1,1205 @@
+/*
+ * intelmid.c - Intel Sound card driver for MID
+ *
+ * Copyright (C) 2008-10 Intel Corp
+ * Authors: Harsha Priya <priya.harsha@intel.com>
+ * Vinod Koul <vinod.koul@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * ALSA driver for Intel MID sound card chipset
+ */
+#include <linux/spi/spi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+
+#include <sound/pcm-indirect.h>
+#include <sound/intel_lpe.h>
+#include <sound/intel_sst_ioctl.h>
+/* #include <net/netlink.h>
+#include <net/genetlink.h> */
+
+#include "intelmid_snd_control.h"
+#include "intelmid.h"
+#include "intelmid_ctrl.h"
+
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_DESCRIPTION("Intel MAD Sound card driver");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{Intel,Intel_MAD}");
+
+
+static int card_index = SNDRV_DEFAULT_IDX1;/* Index 0-MAX */
+static char *card_id = SNDRV_DEFAULT_STR1; /* ID for this card */
+
+module_param(card_index, int, 0444);
+MODULE_PARM_DESC(card_index, "Index value for INTELMAD soundcard.");
+module_param(card_id, charp, 0444);
+MODULE_PARM_DESC(card_id, "ID string for INTELMAD soundcard.");
+
+int sst_card_vendor_id;
+int audio_interrupt_enable = 0;
+
+/* Data path functionalities */
+static struct snd_pcm_hardware snd_intelmad_stream = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP|
+ SNDRV_PCM_INFO_MMAP_VALID |
+ /* SNDRV_PCM_INFO_BATCH | */
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
+ /* SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | */
+ SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24),
+ .rates = (SNDRV_PCM_RATE_8000|
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000),
+ .rate_min = MIN_RATE,
+
+ .rate_max = MAX_RATE,
+ .channels_min = MIN_CHANNEL,
+ .channels_max = MAX_CHANNEL,
+ .buffer_bytes_max = MAX_BUFFER,
+ .period_bytes_min = MIN_PERIOD_BYTES,
+ .period_bytes_max = MAX_PERIOD_BYTES,
+ .periods_min = MIN_PERIODS,
+ .periods_max = MAX_PERIODS,
+ .fifo_size = FIFO_SIZE,
+};
+
+static int snd_intelmad_pcm_ack(struct snd_pcm_substream *substream)
+{
+ struct mad_stream_pvt *stream;
+ struct snd_pcm_indirect *rec;
+
+ WARN_ON(!substream);
+ WARN_ON(!substream->runtime);
+
+ stream = substream->runtime->private_data;
+ WARN_ON(!stream);
+
+// printk(KERN_DEBUG "SST DBG:called %d\n", stream->stream_status);
+ if (stream->stream_status != INIT) {
+
+ rec = &stream->pcm_indirect;
+ if (substream->stream == STREAM_OPS_PLAYBACK) {
+// printk(KERN_DEBUG "SST DBG:calling indirect playback transfer\n");
+ snd_pcm_indirect_playback_transfer(substream, rec,
+ send_buffer_to_sst);
+ } else if (substream->stream == STREAM_OPS_CAPTURE) {
+// printk(KERN_DEBUG "SST DBG:calling indirect capture transfer\n");
+ snd_pcm_indirect_capture_transfer(substream, rec,
+ send_buffer_to_sst);
+ }
+
+ stream->stream_status = RUNNING;
+ }
+ return 0;
+}
+
+/**
+* snd_intelmad_pcm_trigger - stream activities are handled here
+* @substream:substream for which the stream function is called
+*@cmd:the stream commamd thats requested from upper layer
+* This function is called whenever an a stream activity is invoked
+*/
+static int snd_intelmad_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int ret_val = 0;
+ struct snd_intelmad *intelmaddata;
+ struct mad_stream_pvt *stream;
+ struct stream_buffer buffer_to_sst;
+
+// printk(KERN_DEBUG "SST DBG:called\n");
+
+ WARN_ON(!substream);
+
+ intelmaddata = snd_pcm_substream_chip(substream);
+ stream = substream->runtime->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+ WARN_ON(!intelmaddata->sstdrv_ops->scard_ops);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ stream->substream = substream;
+/*
+ printk(KERN_DEBUG "SST DBG:pcm_size+\
+ =%d\n", snd_pcm_lib_buffer_bytes(substream));
+*/
+ stream->stream_status = STARTED;
+
+ if (substream->stream == STREAM_OPS_PLAYBACK)
+ snd_intelmad_pcm_ack(substream);
+ else if (substream->stream == STREAM_OPS_CAPTURE) {
+ buffer_to_sst.length =
+ frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ buffer_to_sst.addr = (unsigned long)
+ substream->runtime->dma_area;
+ ret_val = intelmaddata->sstdrv_ops->send_buffer(
+ stream->stream_info.str_id,
+ &buffer_to_sst);
+ stream->dbg_cum_bytes +=
+ frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ stream->stream_status = RUNNING;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+// printk(KERN_DEBUG "SST DBG:in stop\n");
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_DROP,
+ &stream->stream_info.str_id);
+ if (ret_val)
+ return ret_val;
+ stream->stream_status = DROPPED;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+// printk(KERN_DEBUG "SST DBG:in pause\n");
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_PAUSE,
+ &stream->stream_info.str_id);
+ if (ret_val)
+ return ret_val;
+ stream->stream_status = PAUSED;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+// printk(KERN_DEBUG "SST DBG:in pause release \n");
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_RESUME,
+ &stream->stream_info.str_id);
+ if (ret_val)
+ return ret_val;
+ stream->stream_status = RUNNING;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret_val;
+}
+
+/**
+* snd_intelmad_pcm_prepare- internal preparation before starting a stream
+* @substream: substream for which the function is called
+* This function is called when a stream is started for internal preparation.
+*/
+static int snd_intelmad_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct mad_stream_pvt *stream;
+ int ret_val = 0;
+ struct snd_intelmad *intelmaddata;
+
+ printk(KERN_DEBUG "SST DBG:called \n");
+
+ WARN_ON(!substream);
+ stream = substream->runtime->private_data;
+ intelmaddata = snd_pcm_substream_chip(substream);
+ printk(KERN_DEBUG "SST DBG:pb cnt = %d cap cnt = %d\n",\
+ intelmaddata->playback_cnt,
+ intelmaddata->capture_cnt);
+
+ if(stream->stream_info.str_id) {
+ printk(KERN_DEBUG "SST DBG:Prepare called for already set stream\n");
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_DROP,
+ &stream->stream_info.str_id);
+
+ } else {
+ ret_val = snd_intelmad_alloc_stream(substream);
+ if (ret_val < 0)
+ return ret_val;
+ stream->dbg_cum_bytes = 0;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ intelmaddata->playback_cnt++;
+ else
+ intelmaddata->capture_cnt++;
+ printk(KERN_DEBUG "SST DBG:period size = %d \n",
+ (int)substream->runtime->period_size);
+ printk(KERN_DEBUG "SST DBG:buf size = %d \n",
+ (int)substream->runtime->buffer_size);
+ memset(&stream->pcm_indirect, 0, sizeof(stream->pcm_indirect));
+ stream->pcm_indirect.hw_buffer_size =
+ snd_pcm_lib_buffer_bytes(substream);
+ stream->pcm_indirect.sw_buffer_size =
+ stream->pcm_indirect.hw_buffer_size;
+ /* return back the stream id */
+ snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+ "%d", stream->stream_info.str_id);
+ printk(KERN_DEBUG "SST DBG:stream id to user = %s\n", substream->pcm->id);
+ }
+ ret_val = snd_intelmad_init_stream(substream);
+ if (ret_val)
+ return ret_val;
+
+ return ret_val;
+}
+
+static int snd_intelmad_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int ret_val;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+ ret_val = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ memset(substream->runtime->dma_area, 0,
+ params_buffer_bytes(hw_params));
+ return ret_val;
+}
+
+static int snd_intelmad_hw_free(struct snd_pcm_substream *substream)
+{
+ printk(KERN_DEBUG "SST DBG:called\n");
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/**
+* snd_intelmad_pcm_pointer- to send the current buffer pointer processed by hw
+* @substream: substream for which the function is called
+* This function is called by ALSA framework to get the current hw buffer ptr
+* when a period is elapsed
+*/
+static snd_pcm_uframes_t snd_intelmad_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ /* struct snd_pcm_runtime *runtime = substream->runtime; */
+ struct mad_stream_pvt *stream;
+ struct snd_intelmad *intelmaddata;
+ int ret_val;
+ unsigned long buf_size;
+
+ WARN_ON(!substream);
+
+ intelmaddata = snd_pcm_substream_chip(substream);
+ stream = substream->runtime->private_data;
+ if (stream->stream_status == INIT)
+ return 0;
+
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_BUFFER_POINTER,
+ &stream->stream_info);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: error code = 0x%x \n", ret_val);
+ return ret_val;
+ }
+/* printk(KERN_DEBUG "SST DBG:samples reported out 0x%llx \n",
+ stream->stream_info.buffer_ptr);
+ printk(KERN_DEBUG "SST DBG:Frame bits:: %d period_count :: %d \n",
+ (int)substream->runtime->frame_bits,
+ (int)substream->runtime->period_size);
+*/
+ if (substream->stream == STREAM_OPS_PLAYBACK) {
+ if(SNDRV_PCM_POS_XRUN == stream->stream_info.buffer_ptr)
+ return SNDRV_PCM_POS_XRUN;
+ }
+
+ buf_size = frames_to_bytes(substream->runtime,
+ stream->stream_info.buffer_ptr);
+
+// printk(KERN_DEBUG "SST DBG: bytes reported out = 0x%lx\n", buf_size);
+ if (buf_size > stream->dbg_cum_bytes)
+ dev_err(&intelmaddata->spi->dev, "SST ERR: excess reported \n");
+
+ if (substream->stream == STREAM_OPS_PLAYBACK)
+ return snd_pcm_indirect_playback_pointer(
+ substream, &stream->pcm_indirect, buf_size);
+ else
+ return snd_pcm_indirect_capture_pointer(
+ substream, &stream->pcm_indirect, buf_size);
+}
+
+/**
+* snd_intelmad_close- to free parameteres when stream is stopped
+* @substream: substream for which the function is called
+* This function is called by ALSA framework when stream is stopped
+*/
+static int snd_intelmad_close(struct snd_pcm_substream *substream)
+{
+ struct snd_intelmad *intelmaddata;
+ struct mad_stream_pvt *stream;
+ int ret_val = 0;
+
+ WARN_ON(!substream);
+
+ stream = substream->runtime->private_data;
+
+ printk(KERN_DEBUG "SST DBG:called \n");
+ intelmaddata = snd_pcm_substream_chip(substream);
+
+ printk(KERN_DEBUG "SST DBG:str id = %d\n", stream->stream_info.str_id);
+ if (stream->stream_info.str_id) {
+ /* SST API to actually stop/free the stream */
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_FREE,
+ &stream->stream_info.str_id);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ intelmaddata->playback_cnt--;
+ else
+ intelmaddata->capture_cnt--;
+ }
+ printk(KERN_DEBUG "SST DBG:pb cnt = %d cap cnt = %d\n", intelmaddata->playback_cnt,
+ intelmaddata->capture_cnt);
+ kfree(substream->runtime->private_data);
+ return ret_val;
+}
+
+/**
+* snd_intelmad_open- to set runtime parameters during stream start
+* @substream: substream for which the function is called
+* This function is called by ALSA framework when stream is started
+*/
+static int snd_intelmad_open(struct snd_pcm_substream *substream)
+{
+ struct snd_intelmad *intelmaddata;
+ struct snd_pcm_runtime *runtime;
+ struct mad_stream_pvt *stream;
+
+ WARN_ON(!substream);
+
+ printk(KERN_DEBUG "SST DBG:called \n");
+
+ intelmaddata = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+ /* set the runtime hw parameter with local snd_pcm_hardware struct */
+ runtime->hw = snd_intelmad_stream;
+ /* setup the internal datastruture stream pointers based on it being
+ playback or capture stream */
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+ stream->stream_info.str_id = 0;
+ stream->stream_status = INIT;
+ runtime->private_data = stream;
+ return snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static struct snd_pcm_ops snd_intelmad_playback_ops = {
+ .open = snd_intelmad_open,
+ .close = snd_intelmad_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intelmad_hw_params,
+ .hw_free = snd_intelmad_hw_free,
+ .prepare = snd_intelmad_pcm_prepare,
+ .trigger = snd_intelmad_pcm_trigger,
+ .pointer = snd_intelmad_pcm_pointer,
+ .ack = snd_intelmad_pcm_ack,
+};
+
+static struct snd_pcm_ops snd_intelmad_capture_ops = {
+ .open = snd_intelmad_open,
+ .close = snd_intelmad_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_intelmad_hw_params,
+ .hw_free = snd_intelmad_hw_free,
+ .prepare = snd_intelmad_pcm_prepare,
+ .trigger = snd_intelmad_pcm_trigger,
+ .pointer = snd_intelmad_pcm_pointer,
+ .ack = snd_intelmad_pcm_ack,
+};
+
+
+#ifdef REG_IRQ
+/**
+* snd_intelmad_intr_handler- interrupt handler
+*@irq : irq number of the interrupt received
+*@dev: device context
+* This function is called when an interrupt is raised at the sound card
+*/
+static irqreturn_t snd_intelmad_intr_handler(int irq, void *dev)
+{
+ struct snd_intelmad *intelmaddata =
+ (struct snd_intelmad *)dev;
+ u8 intsts;
+
+ memcpy_fromio(&intsts,
+ ((void *)(intelmaddata->int_base)),
+ sizeof(u8));
+ intelmaddata->mad_jack_msg.intsts = intsts;
+ intelmaddata->mad_jack_msg.intelmaddata = intelmaddata;
+
+ queue_work(intelmaddata->mad_jack_wq, &intelmaddata->mad_jack_msg.wq);
+
+ return IRQ_HANDLED;
+}
+
+void sst_mad_send_jack_report(struct snd_jack *jack, int buttonpressevent , int status)
+{
+
+ if (!jack) {
+ printk(KERN_DEBUG "SST DBG:MAD error jack empty \n");
+
+ } else {
+ printk(KERN_DEBUG "SST DBG:MAD sending jack +\
+ report for = %d!!!\n", status);
+ if (jack)
+ printk(KERN_DEBUG "SST DBG:MAD sending +\
+ jack report for = %d !!!\n", jack->type);
+
+ snd_jack_report(jack, status);
+
+ /*button pressed and released */
+ if (buttonpressevent)
+ snd_jack_report(jack, 0);
+ printk(KERN_DEBUG "SST DBG:MAD sending jack report Done !!!\n");
+ }
+
+
+
+}
+
+void sst_mad_jackdetection_fs(u8 intsts , struct snd_intelmad *intelmaddata)
+{
+ struct snd_jack *jack = NULL;
+ unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
+ struct sc_reg_access sc_access[] = {
+ {0x187, 0x00, MASK7},
+ {0x188, 0x10, MASK4},
+ {0x18b, 0x10, MASK4},
+ };
+
+ struct sc_reg_access sc_access_write[] = {
+ {0x198, 0x00, 0x0},
+ };
+
+ if (intsts & 0x4) {
+
+ if (!(audio_interrupt_enable)) {
+ printk(KERN_DEBUG "SST DBG:Audio interrupt enable\n");
+ sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3);
+
+ sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1);
+ audio_interrupt_enable = 1;
+ intelmaddata->jack[0].jack_status = 0;
+ intelmaddata->jack[1].jack_status = 0;
+
+ }
+ /* send headphone detect */
+ printk(KERN_DEBUG "SST DBG:MAD headphone +\
+ = %d!!!\n", intsts & 0x4);
+ jack = &intelmaddata->jack[0].jack;
+ present = !(intelmaddata->jack[0].jack_status);
+ intelmaddata->jack[0].jack_status = present;
+ jack_event_flag = 1;
+
+ }
+
+ if (intsts & 0x2) {
+ /* send short push */
+ printk(KERN_DEBUG "SST DBG:MAD short push +\
+ = %d!!!\n", intsts & 0x2);
+ jack = &intelmaddata->jack[2].jack;
+ present = 1;
+ jack_event_flag = 1;
+ buttonpressflag = 1;
+ }
+ if (intsts & 0x1) {
+ /* send long push */
+ printk(KERN_DEBUG "SST DBG:MAD long push+\
+ = %d!!!\n", intsts & 0x1);
+ jack = &intelmaddata->jack[3].jack;
+ present = 1;
+ jack_event_flag = 1;
+ buttonpressflag = 1;
+ }
+ if (intsts & 0x8) {
+ if (!(audio_interrupt_enable)) {
+ printk(KERN_DEBUG "SST DBG:Audio interrupt enable\n");
+ sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3);
+
+ sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1);
+ audio_interrupt_enable = 1;
+ intelmaddata->jack[0].jack_status = 0;
+ intelmaddata->jack[1].jack_status = 0;
+ }
+ /* send headset detect */
+ printk(KERN_DEBUG "SST DBG:MAD headset +\
+ = %d!!!\n", intsts & 0x8);
+ jack = &intelmaddata->jack[1].jack;
+ present = !(intelmaddata->jack[1].jack_status);
+ intelmaddata->jack[1].jack_status = present;
+ jack_event_flag = 1;
+ }
+
+
+ if (jack_event_flag)
+ sst_mad_send_jack_report( jack, buttonpressflag, present);
+}
+
+
+void sst_mad_jackdetection_mx(u8 intsts, struct snd_intelmad *intelmaddata)
+{
+ u8 value = 0,jack_prev_state = 0;
+ struct snd_jack *jack = NULL;
+ unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
+ time_t timediff;
+ struct sc_reg_access sc_access_read = {0,};
+
+
+
+ printk(KERN_DEBUG "SST DBG:previos value: = 0x%x \n" ,intelmaddata->jack_prev_state);
+
+ if (!(audio_interrupt_enable)) {
+ printk(KERN_DEBUG "SST DBG:Audio interrupt enable\n");
+ intelmaddata->jack_prev_state = 0xC0;
+ audio_interrupt_enable = 1;
+ }
+
+ if (intsts & 0x2) {
+ jack_prev_state = intelmaddata->jack_prev_state;
+ if(intelmaddata->pmic_status == PMIC_INIT) {
+ sc_access_read.reg_addr = 0x201;
+ sst_sc_reg_access(&sc_access_read, PMIC_READ, 1);
+ value = (sc_access_read.value);
+ printk(KERN_DEBUG "value returned = 0x%x\n", value);
+ }
+
+ if ((jack_prev_state == 0xc0) && (value == 0x40) ) {
+ //headset detected.
+ printk(KERN_DEBUG "MAD headset inserted\n");
+ jack = &intelmaddata->jack[1].jack;
+ present= 1;
+ jack_event_flag = 1;
+ intelmaddata->jack[1].jack_status = 1;
+
+ }
+
+ if ((jack_prev_state == 0xc0 ) && ( value == 0x00) ) {
+ //headphone detected.
+ printk(KERN_DEBUG "MAD headphone inserted\n");
+ jack = &intelmaddata->jack[0].jack;
+ present= 1;
+ jack_event_flag = 1;
+
+ }
+
+ if ( (jack_prev_state == 0x40 ) && ( value == 0xc0) ) {
+ //headset removed
+ printk(KERN_DEBUG "Jack headset status %d\n",\
+ intelmaddata->jack[1].jack_status);
+ printk(KERN_DEBUG "MAD headset removed \n");
+ jack = &intelmaddata->jack[1].jack;
+ present= 0;
+ jack_event_flag = 1;
+ intelmaddata->jack[1].jack_status = 0;
+ }
+
+ if ( (jack_prev_state == 0x00 ) && ( value == 0xc0) ) {
+ //headphone detected.
+ printk(KERN_DEBUG "Jack headphone status %d\n",\
+ intelmaddata->jack[0].jack_status);
+ printk(KERN_DEBUG "MAD headphone removed\n");
+ jack = &intelmaddata->jack[0].jack;
+ present= 0;
+ jack_event_flag = 1;
+ }
+
+ if ( (jack_prev_state == 0x40 ) && (value == 0x00) ) {
+ //button pressed
+ do_gettimeofday(&intelmaddata->jack[1].buttonpressed);
+ printk(KERN_DEBUG "MAD button press detected n");
+ }
+
+
+ if( (jack_prev_state == 0x00 ) && ( value == 0x40) ) {
+ if ( intelmaddata->jack[1].jack_status ) {
+ //button pressed
+ do_gettimeofday(&intelmaddata->jack[1].buttonreleased);
+ /*button pressed */
+ printk(KERN_DEBUG "MAD Button Released detected time \n" );
+ timediff = intelmaddata->jack[1].buttonreleased.tv_sec -
+ intelmaddata->jack[1].buttonpressed.tv_sec;
+ buttonpressflag = 1;
+ if(timediff > 1) {
+ printk(KERN_DEBUG "MAD long press detected time \n" );
+ /* send headphone detect/undetect */
+ jack = &intelmaddata->jack[3].jack;
+ present= 1;
+ jack_event_flag = 1;
+ }
+ else {
+ printk(KERN_DEBUG "MAD short press detected time \n" );
+ /* send headphone detect/undetect */
+ jack = &intelmaddata->jack[2].jack;
+ present= 1;
+ jack_event_flag = 1;
+ }
+ }
+
+ }
+ intelmaddata->jack_prev_state = value ;
+
+ }
+
+ if (jack_event_flag)
+ sst_mad_send_jack_report( jack, buttonpressflag, present);
+}
+
+
+void sst_mad_jackdetection_nec(u8 intsts, struct snd_intelmad *intelmaddata)
+{
+ u8 value = 0;
+ struct snd_jack *jack = NULL;
+ unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0;
+
+ struct sc_reg_access sc_access_read = {0,};
+
+ if (intelmaddata->pmic_status == PMIC_INIT) {
+ sc_access_read.reg_addr = 0x132;
+ sst_sc_reg_access(&sc_access_read, PMIC_READ, 1);
+ value = (sc_access_read.value);
+ printk(KERN_DEBUG "SST DBG:value returned = 0x%x\n", value);
+ }
+ if (intsts & 0x1) {
+ printk(KERN_DEBUG "SST DBG:MAD headset detected\n");
+ /* send headset detect/undetect */
+ jack = &intelmaddata->jack[1].jack;
+ present = (value == 0x1) ? 1 : 0;
+ jack_event_flag = 1;
+ }
+ if (intsts & 0x2) {
+ printk(KERN_DEBUG "SST DBG:MAD headphone detected\n");
+ /* send headphone detect/undetect */
+ jack = &intelmaddata->jack[0].jack;
+ present = (value == 0x2) ? 1 : 0;
+ jack_event_flag = 1;
+ }
+ if (intsts & 0x4) {
+ printk(KERN_DEBUG "SST DBG:MAD short push detected\n");
+ /* send short push */
+ jack = &intelmaddata->jack[2].jack;
+ present = 1;
+ jack_event_flag = 1;
+ buttonpressflag = 1;
+ }
+ if (intsts & 0x8) {
+ printk(KERN_DEBUG "SST DBG:MAD long push detected\n");
+ /* send long push */
+ jack = &intelmaddata->jack[3].jack;
+ present = 1;
+ jack_event_flag = 1;
+ buttonpressflag = 1;
+ }
+
+ if (jack_event_flag)
+ sst_mad_send_jack_report( jack, buttonpressflag, present);
+
+
+}
+
+void sst_process_mad_jack_detection(struct work_struct *work)
+{
+ u8 intsts;
+ struct mad_jack_msg_wq *mad_jack_detect =
+ container_of(work, struct mad_jack_msg_wq, wq);
+
+ struct snd_intelmad *intelmaddata =
+ mad_jack_detect->intelmaddata;
+
+ intsts = mad_jack_detect->intsts;
+
+ switch (intelmaddata->sstdrv_ops->vendor_id) {
+ case SND_FS:
+ sst_mad_jackdetection_fs(intsts,intelmaddata);
+ break;
+ case SND_MX:
+ sst_mad_jackdetection_mx(intsts,intelmaddata);
+ break;
+ case SND_NC:
+ sst_mad_jackdetection_nec(intsts,intelmaddata);
+ break;
+ }
+}
+
+
+static int __devinit snd_intelmad_register_irq(
+ struct snd_intelmad *intelmaddata)
+{
+ int ret_val;
+ u32 regbase = AUDINT_BASE, regsize = 8;
+
+ printk(KERN_DEBUG "SST DBG:irq reg done, now mapping... regbase 0x%x, regsize 0x%x\n",
+ regbase, regsize);
+ intelmaddata->int_base = ioremap_nocache(regbase, regsize);
+ if (!intelmaddata->int_base)
+ dev_err(&intelmaddata->spi->dev, "SST ERR: +\
+ Mapping of cache failed \n");
+
+ /* interpret irq field */
+ printk(KERN_DEBUG "SST DBG:irq = 0x%x\n", intelmaddata->irq);
+ ret_val = request_irq(intelmaddata->irq,
+ snd_intelmad_intr_handler,
+ IRQF_SHARED, DRIVER_NAME,
+ intelmaddata);
+ if (ret_val)
+ dev_err(&intelmaddata->spi->dev, "SST ERR: cannot +\
+ register IRQ \n");
+ return ret_val;
+}
+
+/*static int __devinit snd_intelmad_register_netlink(void)
+{
+ int ret_val;
+
+ ret_val = genl_register_family(&audio_event_genl_family);
+ if (ret_val) {
+ printk(KERN_DEBUG "SST DBG:netlink registration failed\n");
+ return ret_val;
+ }
+ ret_val = genl_register_mc_group(&audio_event_genl_family,
+ &audio_event_mcgrp);
+ if (ret_val) {
+ printk(KERN_DEBUG "SST DBG:netlink +\
+ group registration failed\n");
+ genl_unregister_family(&audio_event_genl_family);
+ return ret_val;
+ }
+ return ret_val;
+}*/
+#endif
+
+static int __devinit snd_intelmad_sst_register(
+ struct snd_intelmad *intelmaddata)
+{
+ int ret_val;
+ struct sc_reg_access pmic_reg = {0,};
+
+ pmic_reg.reg_addr = 0;
+ ret_val = sst_sc_reg_access(&pmic_reg, PMIC_READ, 1);
+
+ if (ret_val)
+ return ret_val;
+
+ sst_card_vendor_id = pmic_reg.value & (MASK2|MASK1|MASK0);
+ printk(KERN_DEBUG "SST DBG:orginal reg n extrated vendor id = 0x%x %d\n",
+ pmic_reg.value, sst_card_vendor_id);
+ if (sst_card_vendor_id < 0 || sst_card_vendor_id > 2) {
+ dev_err(&intelmaddata->spi->dev, \
+ "SST ERR: vendor card not supported!! \n");
+ return -EIO;
+ }
+ intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES;
+ intelmaddata->sstdrv_ops->vendor_id = sst_card_vendor_id;
+ intelmaddata->sstdrv_ops->scard_ops =
+ intelmad_vendor_ops[sst_card_vendor_id];
+
+ /* registering with SST driver to get access to SST APIs to use */
+ ret_val = register_sst_card(intelmaddata->sstdrv_ops);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev, \
+ "SST ERR: sst card registration failed \n");
+ return ret_val;
+ }
+
+ sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id;
+ intelmaddata->pmic_status = PMIC_UNINIT;
+ return ret_val;
+}
+
+/* Driver Init/exit functionalities */
+/**
+* snd_intelmad_pcm- to setup pcm for the card
+* @card: pointer to the sound card structure
+*@intelmaddata: pointer to internal context
+* This function is called from probe function to set up pcm params and functions
+*/
+static int __devinit snd_intelmad_pcm(struct snd_card *card,
+ struct snd_intelmad *intelmaddata)
+{
+ struct snd_pcm *pcm;
+ int i, ret_val = 0;
+ char name[32] = INTEL_MAD;
+
+ WARN_ON(!card);
+ WARN_ON(!intelmaddata);
+
+ for (i = 0; i < MAX_DEVICES; i++) {
+ ret_val = snd_pcm_new(card, name, i, PLAYBACK_COUNT,
+ CAPTURE_COUNT, &pcm);
+ if (ret_val)
+ break;
+ /* setup the ops for playback and capture streams */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_intelmad_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_intelmad_capture_ops);
+ /* setup private data which can be retrieved when required */
+ pcm->private_data = intelmaddata;
+ pcm->info_flags = 0;
+ strncpy(pcm->name, card->shortname, strlen(card->shortname));
+ /* allocate dma pages for ALSA stream operations */
+ snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ MIN_BUFFER, MAX_BUFFER);
+ }
+ return ret_val;
+}
+
+/**
+* snd_intelmad_jack- to setup jack settings of the card
+*@intelmaddata: pointer to internal context
+* This function is called from probe function to set up mixer controls
+*/
+static int __devinit snd_intelmad_jack(struct snd_intelmad *intelmaddata)
+{
+ struct snd_jack *jack;
+ int retval;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+ jack = &intelmaddata->jack[0].jack;
+ retval = snd_jack_new(intelmaddata->card, "Headphone",
+ SND_JACK_HEADPHONE, &jack);
+ if (retval < 0)
+ return retval;
+ snd_jack_report(jack, 0);
+
+ jack->private_data = jack;
+ intelmaddata->jack[0].jack = *jack;
+
+
+ jack = &intelmaddata->jack[1].jack;
+ retval = snd_jack_new(intelmaddata->card, "Headset",
+ SND_JACK_HEADSET, &jack);
+ if (retval < 0)
+ return retval;
+
+
+
+ jack->private_data = jack;
+ intelmaddata->jack[1].jack = *jack;
+
+
+ jack = &intelmaddata->jack[2].jack;
+ retval = snd_jack_new(intelmaddata->card, "Short Press",
+ SND_JACK_HS_SHORT_PRESS, &jack);
+ if (retval < 0)
+ return retval;
+
+
+ jack->private_data = jack;
+ intelmaddata->jack[2].jack = *jack;
+
+
+ jack = &intelmaddata->jack[3].jack;
+ retval = snd_jack_new(intelmaddata->card, "Long Press",
+ SND_JACK_HS_LONG_PRESS, &jack);
+ if (retval < 0)
+ return retval;
+
+
+ jack->private_data = jack;
+ intelmaddata->jack[3].jack = *jack;
+
+ return retval;
+}
+
+/**
+* snd_intelmad_mixer- to setup mixer settings of the card
+*@intelmaddata: pointer to internal context
+* This function is called from probe function to set up mixer controls
+*/
+static int __devinit snd_intelmad_mixer(struct snd_intelmad *intelmaddata)
+{
+ struct snd_card *card;
+ unsigned int idx;
+ int ret_val = 0;
+ char *mixername = "IntelMAD Controls";
+
+ WARN_ON(!intelmaddata);
+
+ card = intelmaddata->card;
+
+ strncpy(card->mixername, mixername, strlen(mixername));
+ /* add all widget controls and expose the same */
+ for (idx = 0; idx < MAX_CTRL; idx++) {
+ ret_val = snd_ctl_add(card,
+ snd_ctl_new1(&snd_intelmad_controls[idx],
+ intelmaddata));
+ printk(KERN_DEBUG "SST DBG:mixer[idx]=%d added \n", idx);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev, \
+ "SST ERR: adding of control +\
+ failed index = %d \n", idx);
+ break;
+ }
+ }
+ return ret_val;
+}
+
+/**
+* snd_intelmad_dev_free- to free device
+*@device: pointer to the device
+* This function is called when driver module is removed
+*/
+static int snd_intelmad_dev_free(struct snd_device *device)
+{
+ struct snd_intelmad *intelmaddata;
+
+ WARN_ON(!device);
+
+ intelmaddata = device->device_data;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+ snd_card_free(intelmaddata->card);
+ /*genl_unregister_family(&audio_event_genl_family);*/
+ unregister_sst_card(intelmaddata->sstdrv_ops);
+
+ /* free allocated memory for internal context */
+ destroy_workqueue(intelmaddata->mad_jack_wq);
+ kfree(intelmaddata->sstdrv_ops);
+ kfree(intelmaddata);
+ return 0;
+}
+
+/**
+* snd_intelmad_create- called from probe to create a snd device
+*@intelmaddata : pointer to the internal context
+*@card : pointer to the sound card
+* This function is called when driver module is started
+*/
+static int __devinit snd_intelmad_create(
+ struct snd_intelmad *intelmaddata,
+ struct snd_card *card)
+{
+ int ret_val;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_intelmad_dev_free,
+ };
+
+ WARN_ON(!intelmaddata);
+ WARN_ON(!card);
+ /* ALSA api to register for the device */
+ ret_val = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelmaddata, &ops);
+ return ret_val;
+}
+
+/*********************************************************************
+ * SPI Functions
+ *********************************************************************/
+
+
+/**
+* snd_intelmad_probe- function registred for init
+*@spi : pointer to the spi device context
+* This function is called when the device is initialized
+*/
+int __devinit snd_intelmad_probe(struct spi_device *spi)
+{
+ struct snd_card *card;
+ int ret_val;
+ struct snd_intelmad *intelmaddata;
+
+ printk(KERN_DEBUG "SST DBG:called \n");
+
+ /* allocate memory for saving internal context and working */
+ intelmaddata = kzalloc(sizeof(*intelmaddata), GFP_KERNEL);
+ if (!intelmaddata)
+ return -ENOMEM;
+
+ /* allocate memory for LPE API set */
+ intelmaddata->sstdrv_ops = kzalloc(sizeof(struct intel_sst_card_ops),
+ GFP_KERNEL);
+ if (!intelmaddata->sstdrv_ops) {
+ dev_err(&intelmaddata->spi->dev, "SST ERR: +\
+ mem alloctn fail \n");
+ kfree(intelmaddata);
+ return -ENOMEM;
+ }
+
+ /* create a card instance with ALSA framework */
+ ret_val = snd_card_create(card_index, card_id, THIS_MODULE, 0, &card);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev, "SST +\
+ ERR: snd_card_create fail \n");
+ goto free_allocs;
+ }
+
+ intelmaddata->spi = spi;
+ intelmaddata->irq = spi->irq;
+ dev_set_drvdata(&spi->dev, intelmaddata);
+ intelmaddata->card = card;
+ intelmaddata->card_id = card_id;
+ intelmaddata->card_index = card_index;
+ intelmaddata->playback_cnt = intelmaddata->capture_cnt = 0;
+ strncpy(card->driver, INTEL_MAD, strlen(INTEL_MAD));
+ strncpy(card->shortname, INTEL_MAD, strlen(INTEL_MAD));
+
+
+ intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES;
+ /* registering with LPE driver to get access to SST APIs to use */
+ ret_val = snd_intelmad_sst_register(intelmaddata);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR:+ snd_intelmad_sst_register failed \n");
+ goto free_allocs;
+ }
+
+ intelmaddata->pmic_status = PMIC_INIT;
+
+ ret_val = snd_intelmad_pcm(card, intelmaddata);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: snd_intelmad_pcm failed \n");
+ goto free_allocs;
+ }
+
+ ret_val = snd_intelmad_mixer(intelmaddata);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: snd_intelmad_mixer failed \n");
+ goto free_allocs;
+ }
+
+ ret_val = snd_intelmad_jack(intelmaddata);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: snd_intelmad_jack failed \n");
+ goto free_allocs;
+ }
+
+ /*create work queue for jack interrupt*/
+
+ INIT_WORK(&intelmaddata->mad_jack_msg.wq, \
+ sst_process_mad_jack_detection);
+
+ intelmaddata->mad_jack_wq = create_workqueue("sst_mad_jack_wq");
+ if (!intelmaddata->mad_jack_wq)
+ goto free_mad_jack_wq;
+
+#ifdef REG_IRQ
+ ret_val = snd_intelmad_register_irq(intelmaddata);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: snd_intelmad_register_irq fail \n");
+ goto free_allocs;
+ }
+ /*ret_val = snd_intelmad_register_netlink();
+ if (ret_val) {
+ printk(KERN_DEBUG "SST DBG:...complete\n");
+ return ret_val;
+ }*/
+#endif
+
+ /* internal function call to register device with ALSA */
+ ret_val = snd_intelmad_create(intelmaddata, card);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: snd_intelmad_create failed \n");
+ goto free_allocs;
+ }
+ card->private_data = &intelmaddata;
+ snd_card_set_dev(card, &spi->dev);
+ ret_val = snd_card_register(card);
+ if (ret_val) {
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: snd_card_register failed \n");
+ goto free_allocs;
+ }
+
+ printk(KERN_DEBUG "SST DBG:...complete\n");
+ return ret_val;
+
+free_mad_jack_wq:
+ destroy_workqueue(intelmaddata->mad_jack_wq);
+
+free_allocs:
+ /* TODO: unregister IRQ */
+ dev_err(&intelmaddata->spi->dev, "SST ERR: probe failed \n");
+ /* snd_card_free(card); */
+ kfree(intelmaddata->sstdrv_ops);
+ kfree(intelmaddata);
+ return ret_val;
+}
+
+
+/**
+* snd_intelmad_remove- function registred for exit
+*@spi : pointer to the spi device context
+* This function is called when the device is uninitialized
+*/
+static int snd_intelmad_remove(struct spi_device *spi)
+{
+ struct snd_intelmad *intelmaddata =
+ dev_get_drvdata(&spi->dev);
+ /*
+ * TODO:: de-register interrupt handler
+ */
+
+ if (intelmaddata) {
+ snd_card_free(intelmaddata->card);
+ /*genl_unregister_family(&audio_event_genl_family);*/
+ unregister_sst_card(intelmaddata->sstdrv_ops);
+ /* free allocated memory for internal context */
+ destroy_workqueue(intelmaddata->mad_jack_wq);
+ kfree(intelmaddata->sstdrv_ops);
+ kfree(intelmaddata);
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * Driver initialization and exit
+ *********************************************************************/
+
+static struct spi_driver snd_intelmad_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = snd_intelmad_probe,
+ .remove = __devexit_p(snd_intelmad_remove),
+};
+
+/*
+* alsa_card_intelmad_init- driver init function
+* This function is called when driver module is inserted
+*/
+static int __init alsa_card_intelmad_init(void)
+{
+ printk(KERN_DEBUG "SST DBG:called\n");
+ return spi_register_driver(&snd_intelmad_driver);
+}
+
+/**
+* alsa_card_intelmad_exit- driver exit function
+* This function is called when driver module is removed
+*/
+static void __exit alsa_card_intelmad_exit(void)
+{
+ printk(KERN_DEBUG "SST DBG:called\n");
+ spi_unregister_driver(&snd_intelmad_driver);
+}
+
+module_init(alsa_card_intelmad_init)
+module_exit(alsa_card_intelmad_exit)
+
diff --git a/sound/pci/sst/intelmid.h b/sound/pci/sst/intelmid.h
new file mode 100644
index 0000000..235115e
--- /dev/null
+++ b/sound/pci/sst/intelmid.h
@@ -0,0 +1,170 @@
+/*
+ * intelmid.h - Intel Sound card driver for MID
+ *
+ * Copyright (C) 2008-10 Intel Corp
+ * Authors: Harsha Priya <priya.harsha@intel.com>
+ * Vinod Koul <vinod.koul@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * ALSA driver header for Intel MAD chipset
+ */
+#ifndef __INTELMID_H
+#define __INTELMID_H
+
+#include <linux/time.h>
+
+#define DRIVER_NAME "pmic_audio"
+#define PMIC_SOUND_IRQ_TYPE_MASK (1 << 15)
+#define AUDINT_BASE (0xFFFFEFF8 + (6 * sizeof(u8)))
+#define REG_IRQ
+/* values #defined */
+/* will differ for different hw - to be taken from config */
+#define MAX_DEVICES 1
+#define MIN_RATE 8000
+#define MAX_RATE 48000
+#define MAX_BUFFER (128*1024) /* TBD for PCM */
+#define MIN_BUFFER (128*1024)
+#define MAX_PERIODS (1024)
+#define MIN_PERIODS 2
+#define MAX_PERIOD_BYTES MAX_BUFFER
+//#define MIN_PERIOD_BYTES 32
+#define MIN_PERIOD_BYTES 160
+#define MAX_MUTE 1
+#define MIN_MUTE 0
+#define MONO_CNTL 1
+#define STEREO_CNTL 2
+#define MIN_CHANNEL 1
+#define MAX_CHANNEL 2
+#define FIFO_SIZE 0 /* fifo not being used */
+#define INTEL_MAD "Intel MAD"
+#define MAX_CTRL 7
+#define MAX_VENDORS 3
+/* TODO +6 db */
+#define MAX_VOL 64
+/* TODO -57 db */
+#define MIN_VOL 0
+#define PLAYBACK_COUNT 1
+#define CAPTURE_COUNT 1
+
+extern int sst_card_vendor_id;
+
+struct mad_jack {
+ struct snd_jack jack;
+ int jack_status;
+ struct timeval buttonpressed;
+ struct timeval buttonreleased;
+};
+struct mad_jack_msg_wq {
+ u8 intsts;
+ struct snd_intelmad *intelmaddata;
+ struct work_struct wq;
+
+};
+struct snd_intelmad {
+ struct snd_card *card; /* ptr to the card details */
+ int card_index;/* card index */
+ char *card_id; /* card id */
+ struct intel_sst_card_ops *sstdrv_ops;/* ptr to sst driver ops */
+ struct spi_device *spi;
+ int irq;
+ int pmic_status;
+ void __iomem *int_base;
+ int output_sel;
+ int input_sel;
+ int master_mute;
+ struct mad_jack jack[4];
+ int playback_cnt;
+ int capture_cnt;
+ struct mad_jack_msg_wq mad_jack_msg;
+ struct workqueue_struct *mad_jack_wq;
+ u8 jack_prev_state;
+};
+
+struct snd_control_val {
+ int playback_vol_max;
+ int playback_vol_min;
+ int capture_vol_max;
+ int capture_vol_min;
+};
+
+struct mad_stream_pvt {
+ int stream_status;
+ int stream_ops;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_indirect pcm_indirect;
+ struct pcm_stream_info stream_info;
+ ssize_t dbg_cum_bytes;
+};
+
+enum mad_drv_status {
+ INIT = 1,
+ STARTED,
+ RUNNING,
+ PAUSED,
+ DROPPED,
+};
+
+enum mad_pmic_status {
+ PMIC_UNINIT = 1,
+ PMIC_INIT,
+};
+enum _widget_ctrl {
+ PLAYBACK_VOL = 1 ,
+ PLAYBACK_MUTE,
+ CAPTURE_VOL,
+ CAPTURE_MUTE,
+ OUTPUT_SEL,
+ INPUT_SEL,
+ MASTER_MUTE
+};
+
+/*enum {
+ AUDIO_GENL_ATTR_UNSPEC = 0,
+ AUDIO_GENL_ATTR_EVENT,
+ AUDIO_GENL_ATTR_MAX,
+};
+enum {
+ AUDIO_GENL_CMD_UNSPEC,
+ AUDIO_GENL_CMD_EVENT,
+ AUDIO_GENL_CMD_MAX,
+};
+
+enum eaudio_events {
+ AUDIO_EVENT_HP_DETECT,
+ AUDIO_EVENT_HS_DETECT,
+ AUDIO_EVENT_SHORT_PRESS,
+ AUDIO_EVENT_LONG_PRESS,
+ AUDIO_EVENT_COUNT,
+};
+
+struct audio_genl_event {
+ u32 orig;
+ enum eaudio_events event;
+};*/
+
+
+void period_elapsed(void *mad_substream);
+int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream);
+int snd_intelmad_init_stream(struct snd_pcm_substream *substream);
+void send_buffer_to_sst(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect *rec, size_t bytes);
+int sst_sc_reg_access(struct sc_reg_access *sc_access,
+ int type, int num_val);
+
+
+#endif /* __INTELMID_H */
diff --git a/sound/pci/sst/intelmid_ctrl.c b/sound/pci/sst/intelmid_ctrl.c
new file mode 100644
index 0000000..f778628
--- /dev/null
+++ b/sound/pci/sst/intelmid_ctrl.c
@@ -0,0 +1,555 @@
+/*
+ * intelmid_ctrl.c - Intel Sound card driver for MID
+ *
+ * Copyright (C) 2008-10 Intel Corp
+ * Authors: Harsha Priya <priya.harsha@intel.com>
+ * Vinod Koul <vinod.koul@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * ALSA driver handling mixer controls for Intel MAD chipset
+ */
+#include <linux/spi/spi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm-indirect.h>
+#include <sound/intel_lpe.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intelmid_snd_control.h"
+#include "intelmid.h"
+
+static char *out_names[] = {"Headphones",
+ "Internal speakers"};
+static char *in_names[] = {"HS_MIC",
+ "AMIC",
+ "DMIC"};
+
+struct snd_pmic_ops *intelmad_vendor_ops[MAX_VENDORS] = {
+ &snd_pmic_ops_fs,
+ &snd_pmic_ops_mx,
+ &snd_pmic_ops_nc
+};
+
+struct snd_control_val intelmad_ctrl_val[MAX_VENDORS] = {
+ {
+ .playback_vol_max = 63,
+ .playback_vol_min = 0,
+ .capture_vol_max = 63,
+ .capture_vol_min = 0,
+ },
+ {
+ .playback_vol_max = 0,
+ .playback_vol_min = -31,
+ .capture_vol_max = 0,
+ .capture_vol_min = -20,
+ },
+ {
+ .playback_vol_max = 0,
+ .playback_vol_min = -126,
+ .capture_vol_max = 0,
+ .capture_vol_min = -31,
+ },
+};
+
+/* control path functionalities */
+
+static inline int snd_intelmad_volume_info(struct snd_ctl_elem_info *uinfo,
+ int control_type, int max, int min)
+{
+ WARN_ON(!uinfo);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = control_type;
+ uinfo->value.integer.min = min;
+ uinfo->value.integer.max = max;
+ return 0;
+}
+
+/**
+* snd_intelmad_mute_info - provides information about the mute controls
+* @kcontrol: pointer to the control
+* @uinfo: pointer to the structure where the control's info need
+* to be filled
+* This function is called when a mixer application requests for control's info
+*/
+static int snd_intelmad_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ WARN_ON(!uinfo);
+ WARN_ON(!kcontrol);
+
+ /* set up the mute as a boolean mono control with min-max values */
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = MONO_CNTL;
+ uinfo->value.integer.min = MIN_MUTE;
+ uinfo->value.integer.max = MAX_MUTE;
+ return 0;
+}
+
+/**
+* snd_intelmad_capture_volume_info - provides info about the volume control
+* @kcontrol: pointer to the control
+* @uinfo: pointer to the structure where the control's info need
+* to be filled
+* This function is called when a mixer application requests for control's info
+*/
+static int snd_intelmad_capture_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, MONO_CNTL,
+ intelmad_ctrl_val[sst_card_vendor_id].capture_vol_max,
+ intelmad_ctrl_val[sst_card_vendor_id].capture_vol_min);
+ return 0;
+}
+
+/**
+* snd_intelmad_playback_volume_info - provides info about the volume control
+* @kcontrol: pointer to the control
+* @uinfo: pointer to the structure where the control's info need
+* to be filled
+* This function is called when a mixer application requests for control's info
+*/
+static int snd_intelmad_playback_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ intelmad_ctrl_val[sst_card_vendor_id].playback_vol_max,
+ intelmad_ctrl_val[sst_card_vendor_id].playback_vol_min);
+ return 0;
+}
+
+/**
+* snd_intelmad_device_info - provides information about the devices available
+* @kcontrol: pointer to the control
+* @uinfo: pointer to the structure where the devices's info need
+* to be filled
+* This function is called when a mixer application requests for device's info
+*/
+static int snd_intelmad_device_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ WARN_ON(!kcontrol);
+ WARN_ON(!uinfo);
+ /* setup device select as drop down controls with different values */
+ if (kcontrol->id.numid == OUTPUT_SEL)
+ uinfo->value.enumerated.items = ARRAY_SIZE(out_names);
+ else
+ uinfo->value.enumerated.items = ARRAY_SIZE(in_names);
+ uinfo->count = MONO_CNTL;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = 1;
+ if (kcontrol->id.numid == OUTPUT_SEL)
+ strncpy(uinfo->value.enumerated.name,
+ out_names[uinfo->value.enumerated.item],
+ strlen(out_names[uinfo->value.enumerated.item]));
+ else
+ strncpy(uinfo->value.enumerated.name,
+ in_names[uinfo->value.enumerated.item],
+ strlen(in_names[uinfo->value.enumerated.item]));
+ return 0;
+}
+
+/**
+* snd_intelmad_volume_get - gets the current volume for the control
+* @kcontrol: pointer to the control
+* @uval: pointer to the structure where the control's info need
+* to be filled
+* This function is called when .get function of a control is invoked from app
+*/
+static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uval)
+{
+ int ret_val = 0, cntl_list[2] = {0,};
+ u8 value = 0;
+ struct snd_intelmad *intelmaddata;
+ struct snd_pmic_ops *scard_ops;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ switch (kcontrol->id.numid) {
+ case PLAYBACK_VOL:
+ cntl_list[0] = PMIC_SND_RIGHT_PB_VOL;
+ cntl_list[1] = PMIC_SND_LEFT_PB_VOL;
+ break;
+
+ case CAPTURE_VOL:
+ cntl_list[0] = PMIC_SND_CAPTURE_VOL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->get_vol(cntl_list[0], &value);
+ uval->value.integer.value[0] = value;
+
+ if (ret_val)
+ return ret_val;
+
+ if (kcontrol->id.numid == PLAYBACK_VOL) {
+ ret_val = scard_ops->get_vol(cntl_list[1], &value);
+ uval->value.integer.value[1] = value;
+ }
+ return ret_val;
+}
+
+/**
+* snd_intelmad_mute_get - gets the current mute status for the control
+* @kcontrol: pointer to the control
+* @uval: pointer to the structure where the control's info need
+* to be filled
+* This function is called when .get function of a control is invoked from app
+*/
+static int snd_intelmad_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uval)
+{
+
+ int cntl_list = 0, ret_val = 0;
+ u8 value = 0;
+ struct snd_intelmad *intelmaddata;
+ struct snd_pmic_ops *scard_ops;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ switch (kcontrol->id.numid) {
+ case PLAYBACK_MUTE:
+ if (intelmaddata->output_sel == STEREO_HEADPHONE)
+ cntl_list = PMIC_SND_LEFT_HP_MUTE;
+ else if (intelmaddata->output_sel == INTERNAL_SPKR)
+ cntl_list = PMIC_SND_LEFT_SPEAKER_MUTE;
+ break;
+
+ case CAPTURE_MUTE:
+ if (intelmaddata->input_sel == DMIC)
+ cntl_list = PMIC_SND_DMIC_MUTE;
+ else if (intelmaddata->input_sel == AMIC)
+ cntl_list = PMIC_SND_AMIC_MUTE;
+ else if (intelmaddata->input_sel == HS_MIC)
+ cntl_list = PMIC_SND_HP_MIC_MUTE;
+ break;
+ case MASTER_MUTE:
+ uval->value.integer.value[0] = intelmaddata->master_mute;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->get_mute(cntl_list, &value);
+ uval->value.integer.value[0] = value;
+ return ret_val;
+}
+
+/**
+* snd_intelmad_volume_set - sets the volume control's info
+* @kcontrol: pointer to the control
+* @uval: pointer to the structure where the control's info is
+* available to be set
+* This function is called when .set function of a control is invoked from app
+*/
+static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uval)
+{
+
+ int ret_val, cntl_list[2] = {0,};
+ struct snd_intelmad *intelmaddata;
+ struct snd_pmic_ops *scard_ops;
+
+ printk(KERN_DEBUG "SST DBG:volume set called:%ld %ld \n",
+ uval->value.integer.value[0],
+ uval->value.integer.value[1]);
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ switch (kcontrol->id.numid) {
+ case PLAYBACK_VOL:
+ cntl_list[0] = PMIC_SND_LEFT_PB_VOL;
+ cntl_list[1] = PMIC_SND_RIGHT_PB_VOL;
+ break;
+
+ case CAPTURE_VOL:
+ cntl_list[0] = PMIC_SND_CAPTURE_VOL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->set_vol(cntl_list[0],
+ uval->value.integer.value[0]);
+ if (ret_val)
+ return ret_val;
+
+ if (kcontrol->id.numid == PLAYBACK_VOL)
+ ret_val = scard_ops->set_vol(cntl_list[1],
+ uval->value.integer.value[1]);
+ return ret_val;
+}
+
+/**
+* snd_intelmad_mute_set - sets the mute control's info
+* @kcontrol: pointer to the control
+* @uval: pointer to the structure where the control's info is
+* available to be set
+* This function is called when .set function of a control is invoked from app
+*/
+static int snd_intelmad_mute_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uval)
+{
+ int cntl_list[2] = {0,}, ret_val;
+ struct snd_intelmad *intelmaddata;
+ struct snd_pmic_ops *scard_ops;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ kcontrol->private_value = uval->value.integer.value[0];
+
+ switch (kcontrol->id.numid) {
+ case PLAYBACK_MUTE:
+ if (intelmaddata->output_sel == STEREO_HEADPHONE) {
+ cntl_list[0] = PMIC_SND_LEFT_HP_MUTE;
+ cntl_list[1] = PMIC_SND_RIGHT_HP_MUTE;
+ } else if (intelmaddata->output_sel == INTERNAL_SPKR) {
+ cntl_list[0] = PMIC_SND_LEFT_SPEAKER_MUTE;
+ cntl_list[1] = PMIC_SND_RIGHT_SPEAKER_MUTE;
+ }
+ break;
+
+ case CAPTURE_MUTE:/*based on sel device mute the i/p dev*/
+ if (intelmaddata->input_sel == DMIC)
+ cntl_list[0] = PMIC_SND_DMIC_MUTE;
+ else if (intelmaddata->input_sel == AMIC)
+ cntl_list[0] = PMIC_SND_AMIC_MUTE;
+ else if (intelmaddata->input_sel == HS_MIC)
+ cntl_list[0] = PMIC_SND_HP_MIC_MUTE;
+ break;
+ case MASTER_MUTE:
+ cntl_list[0] = PMIC_SND_MUTE_ALL;
+ intelmaddata->master_mute = uval->value.integer.value[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->set_mute(cntl_list[0],
+ uval->value.integer.value[0]);
+ if (ret_val)
+ return ret_val;
+
+ if (kcontrol->id.numid == PLAYBACK_MUTE)
+ ret_val = scard_ops->set_mute(cntl_list[1],
+ uval->value.integer.value[0]);
+ return ret_val;
+}
+
+/**
+* snd_intelmad_device_get - get the device select control's info
+* @kcontrol: pointer to the control
+* @uval: pointer to the structure where the control's info is
+* to be filled
+* This function is called when .get function of a control is invoked from app
+*/
+static int snd_intelmad_device_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uval)
+{
+ printk(KERN_DEBUG "SST DBG:called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ uval->value.enumerated.item[0] = kcontrol->private_value;
+ return 0;
+}
+
+/**
+* snd_intelmad_device_set - set the device select control's info
+* @kcontrol: pointer to the control
+* @uval: pointer to the structure where the control's info is
+* available to be set
+* This function is called when .set function of a control is invoked from app
+*/
+static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_intelmad *intelmaddata;
+ struct snd_pmic_ops *scard_ops;
+ int ret_val = 0, vendor, status;
+
+ printk(KERN_DEBUG "SST DBG:called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+ status = -1;
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ /* store value with driver */
+ kcontrol->private_value = uval->value.enumerated.item[0];
+
+ switch (kcontrol->id.numid) {
+ case OUTPUT_SEL:
+ ret_val = scard_ops->set_output_dev(
+ uval->value.enumerated.item[0]);
+ intelmaddata->output_sel = uval->value.enumerated.item[0];
+ break;
+ case INPUT_SEL:
+ vendor = intelmaddata->sstdrv_ops->vendor_id;
+ if ((vendor == SND_MX) || (vendor == SND_FS )) {
+ if(uval->value.enumerated.item[0] == HS_MIC) {
+ status = 1;
+ intelmaddata->sstdrv_ops->control_set(SST_ENABLE_RX_TIME_SLOT, &status);
+ }
+ else {
+ status = 0;
+ intelmaddata->sstdrv_ops->control_set(SST_ENABLE_RX_TIME_SLOT, &status);
+ }
+ }
+ ret_val = scard_ops->set_input_dev(
+ uval->value.enumerated.item[0]);
+ intelmaddata->input_sel = uval->value.enumerated.item[0];
+ break;
+ default:
+ return -EINVAL;
+ }
+ kcontrol->private_value = uval->value.enumerated.item[0];
+ return ret_val;
+}
+
+struct snd_kcontrol_new snd_intelmad_controls[MAX_CTRL] __devinitdata = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_playback_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_mute_info,
+ .get = snd_intelmad_mute_get,
+ .put = snd_intelmad_mute_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_capture_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Capture Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_mute_info,
+ .get = snd_intelmad_mute_get,
+ .put = snd_intelmad_mute_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_device_info,
+ .get = snd_intelmad_device_get,
+ .put = snd_intelmad_device_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_device_info,
+ .get = snd_intelmad_device_get,
+ .put = snd_intelmad_device_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_mute_info,
+ .get = snd_intelmad_mute_get,
+ .put = snd_intelmad_mute_set,
+ .private_value = 0,
+},
+};
diff --git a/sound/pci/sst/intelmid_ctrl.h b/sound/pci/sst/intelmid_ctrl.h
new file mode 100644
index 0000000..fa5feaf
--- /dev/null
+++ b/sound/pci/sst/intelmid_ctrl.h
@@ -0,0 +1,33 @@
+/*
+ * intelmid_ctrl.h - Intel Sound card driver for MID
+ *
+ * Copyright (C) 2008-10 Intel Corp
+ * Authors: Harsha Priya <priya.harsha@intel.com>
+ * Vinod Koul <vinod.koul@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * ALSA driver header file for handling mixer controls for Intel MAD chipset
+ */
+#ifndef __INTELMID_CTRL_H
+#define __INTELMID_CTRL_H
+
+extern struct snd_control_val intelmad_ctrl_val[];
+extern struct snd_kcontrol_new snd_intelmad_controls[];
+extern struct snd_pmic_ops *intelmad_vendor_ops[];
+
+#endif /*__INTELMID_CTRL_H*/
diff --git a/sound/pci/sst/intelmid_pvt.c b/sound/pci/sst/intelmid_pvt.c
new file mode 100644
index 0000000..1dd00c3
--- /dev/null
+++ b/sound/pci/sst/intelmid_pvt.c
@@ -0,0 +1,343 @@
+/*
+ * intelmid_pvt.h - Intel Sound card driver for MID
+ *
+ * Copyright (C) 2008-10 Intel Corp
+ * Authors: Harsha Priya <priya.harsha@intel.com>
+ * Vinod Koul <vinod.koul@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * ALSA driver for Intel MID sound card chipset - holding private functions
+ */
+#include <linux/spi/spi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <asm/ipc_defs.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm-indirect.h>
+#include <sound/intel_lpe.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intelmid_snd_control.h"
+#include "intelmid.h"
+
+
+/*static unsigned int audio_event_seqnum;
+static struct genl_family audio_event_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .name = "audio events",
+ .version = 0x01,
+ .maxattr = 0,
+};
+
+static struct genl_multicast_group audio_event_mcgrp = {
+ .name = "audio_group",
+};
+*/
+
+void period_elapsed(void *mad_substream)
+{
+ struct snd_pcm_substream *substream = mad_substream;
+ struct mad_stream_pvt *stream;
+
+ if (!substream || !substream->runtime)
+ return;
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return;
+
+// printk(KERN_DEBUG "SST DBG:called\n");
+ if (stream->stream_status != RUNNING)
+ return;
+// printk(KERN_DEBUG "SST DBG:calling period elapsed\n");
+ snd_pcm_period_elapsed(substream);
+ return;
+}
+
+
+int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream)
+{
+ struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream);
+ struct mad_stream_pvt *stream = substream->runtime->private_data;
+ unsigned int bits_per_sec = (substream->runtime->sample_bits/8)
+ * (substream->runtime->channels)
+ * (substream->runtime->rate);
+ struct snd_sst_stream_params param = {{{0,},},};
+ struct snd_sst_params str_params = {0};
+ int ret_val;
+
+ /* set codec params and inform SST driver the same */
+
+ param.uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
+ param.uc.pcm_params.brate = bits_per_sec;
+ param.uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+ param.uc.pcm_params.sfreq = substream->runtime->rate;
+ param.uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+// param.uc.pcm_params.frame_size = 0;
+// param.uc.pcm_params.samples_per_frame = 250; /* FIXME */
+ param.uc.pcm_params.buffer_size = substream->runtime->buffer_size;
+ param.uc.pcm_params.period_count = substream->runtime->period_size;
+ printk(KERN_DEBUG "SST DBG:period_count +\
+ = %d\n", param.uc.pcm_params.period_count);
+ printk(KERN_DEBUG "SST DBG:sfreq= %d, wd_sz = %d\n", +\
+ param.uc.pcm_params.sfreq, param.uc.pcm_params.pcm_wd_sz);
+
+ str_params.sparams = param;
+ str_params.codec = SST_CODEC_TYPE_PCM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ str_params.ops = STREAM_OPS_PLAYBACK;
+ else
+ str_params.ops = STREAM_OPS_CAPTURE;
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_ALLOC,
+ &str_params);
+ printk(KERN_DEBUG "SST DBG:SST_SND_PLAY/CAPTURE ret_val = %x\n",
+ ret_val);
+ if (ret_val < 0)
+ return ret_val;
+
+ stream->stream_info.str_id = ret_val;
+ stream->stream_status = INIT;
+ stream->stream_info.buffer_ptr = 0;
+ printk(KERN_DEBUG "SST DBG:str id : %d\n", stream->stream_info.str_id);
+
+ return ret_val;
+}
+
+int snd_intelmad_init_stream(struct snd_pcm_substream *substream)
+{
+ struct mad_stream_pvt *stream = substream->runtime->private_data;
+ struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream);
+ int ret_val;
+
+ printk(KERN_DEBUG "SST DBG:setting buffer ptr param\n");
+ stream->stream_info.period_elapsed = period_elapsed;
+ stream->stream_info.mad_substream = substream;
+ stream->stream_info.buffer_ptr = 0;
+ stream->stream_info.sfreq = substream->runtime->rate;
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_STREAM_INIT,
+ &stream->stream_info);
+ if (ret_val)
+ dev_err(&intelmaddata->spi->dev,\
+ "SST ERR: error code = %d \n", ret_val);
+ return ret_val;
+
+}
+
+void send_buffer_to_sst(struct snd_pcm_substream *substream,
+ struct snd_pcm_indirect *rec, size_t bytes)
+{
+ struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream);
+ struct mad_stream_pvt *stream = substream->runtime->private_data;
+ struct stream_buffer buffer_to_sst = {0,};
+ int ret_val;
+
+ /* sends data to SST to be processed */
+ stream->dbg_cum_bytes += bytes;
+ printk(KERN_DEBUG "SST DBG:bytes = %d \n", bytes);
+ printk(KERN_DEBUG "SST DBG:cum_bytes +\
+ = 0x%x, \n", stream->dbg_cum_bytes);
+ buffer_to_sst.length = bytes;
+ buffer_to_sst.addr = (unsigned long) substream->runtime->dma_area +
+ rec->sw_data;
+ /* SST API to actually send the buffer to be played */
+ ret_val = intelmaddata->sstdrv_ops->send_buffer(
+ stream->stream_info.str_id,
+ &buffer_to_sst);
+ printk(KERN_DEBUG "SST DBG:send_buffer +\
+ ret_val = 0x%x \n", ret_val);
+ return;
+}
+
+/*int snd_intelmad_generate_netlink(u32 orig, enum eaudio_events event)
+{
+ struct sk_buff *skb = NULL;
+ struct nlattr *attr = NULL;
+ struct audio_genl_event *aud_event = NULL;
+ void *msg_header = NULL;
+ int size = 0, ret_val = 0;
+
+
+ size = nla_total_size(sizeof(struct audio_genl_event)) + \
+ nla_total_size(0);
+
+ skb = genlmsg_new(size, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+
+ msg_header = genlmsg_put(skb, 0, audio_event_seqnum++,
+ &audio_event_genl_family, 0,
+ AUDIO_GENL_CMD_EVENT);
+ if (!msg_header) {
+ nlmsg_free(skb);
+ return -ENOMEM;
+ }
+
+ attr = nla_reserve(skb, AUDIO_GENL_ATTR_EVENT, \
+ sizeof(struct audio_genl_event));
+
+ if (!attr) {
+ nlmsg_free(skb);
+ return -EINVAL;
+ }
+
+ aud_event = nla_data(attr);
+ if (!aud_event) {
+ nlmsg_free(skb);
+ return -EINVAL;
+ }
+
+ memset(aud_event, 0, sizeof(struct audio_genl_event));
+
+ aud_event->orig = orig;
+ aud_event->event = event;
+
+
+ ret_val = genlmsg_end(skb, msg_header);
+ if (ret_val < 0) {
+ nlmsg_free(skb);
+ return ret_val;
+ }
+
+ ret_val = genlmsg_multicast(skb, 0, audio_event_mcgrp.id, GFP_ATOMIC);
+
+ if (ret_val)
+ printk(KERN_INFO "Failed to send a Genetlink message!\n");
+ return 0;
+}*/
+
+
+/**
+* Reads/writes/read-modify operations on registers accessed through SCU (sound
+* card and few SST DSP regsiters that are not accissible to IA)
+*/
+int sst_sc_reg_access(struct sc_reg_access *sc_access,
+ int type, int num_val)
+{
+ int i, retval = 0, j = 0, k = 0, count = 0;
+ struct ipc_pmic_reg_data reg_data;
+ struct ipc_pmic_mod_reg_data pmic_mod_reg = {0};
+
+ reg_data.ioc = TRUE;
+ if (type == PMIC_WRITE) {
+ do {
+ int max_retries = 0;
+
+ if (num_val <= 4)
+ count = num_val;
+ else
+ count = 4;
+retry_write:
+ for (i = 0; i < count; i++, j++) {
+ reg_data.pmic_reg_data[i].
+ register_address = sc_access[j].reg_addr;
+
+ reg_data.pmic_reg_data[i].value =
+ sc_access[j].value;
+ }
+ reg_data.num_entries = (u8) count;
+ retval = ipc_pmic_register_write(&reg_data, 0);
+ if (retval == E_NO_INTERRUPT_ON_IOC &&
+ max_retries < 10) {
+ printk(KERN_ERR "SST ERR: write communcation needs retry \n");
+ max_retries++;
+ goto retry_write;
+ }
+ if (0 != retval) {
+ printk(KERN_ERR "SST ERR: pmic write failed \n");
+ return retval;
+ }
+ num_val -= count;
+ } while (num_val > 0);
+ } else if (type == PMIC_READ) {
+ do {
+ int max_retries = 0;
+ if (num_val <= 4)
+ count = num_val;
+ else
+ count = 4;
+retry_read:
+ for (i = 0; i < count; i++, j++)
+ reg_data.pmic_reg_data[i].register_address
+ = sc_access[j].reg_addr;
+ reg_data.num_entries = count;
+ retval = ipc_pmic_register_read(&reg_data);
+ if (retval == E_NO_INTERRUPT_ON_IOC &&
+ max_retries < 10) {
+ printk(KERN_ERR "ERR: read communcation needs retry \n");
+ max_retries++;
+ goto retry_read;
+ }
+ if (0 != retval) {
+ printk(KERN_ERR "ERR: pmic read failed \n");
+ return retval;
+ }
+
+ for (i = 0; i < count; i++, k++)
+ sc_access[k].value =
+ reg_data.pmic_reg_data[i].value;
+ num_val -= count;
+ } while (num_val > 0);
+ } else {
+ pmic_mod_reg.ioc = TRUE;
+ do {
+ int max_retries = 0;
+ if (num_val <= 4)
+ count = num_val;
+ else
+ count = 4;
+retry_readmod:
+ for (i = 0; i < count; i++, j++) {
+ pmic_mod_reg.pmic_mod_reg_data[i].
+ register_address = sc_access[j].reg_addr;
+ pmic_mod_reg.pmic_mod_reg_data[i].value =
+ sc_access[j].value;
+ pmic_mod_reg.pmic_mod_reg_data[i].bit_map =
+ sc_access[j].mask;
+ }
+ pmic_mod_reg.num_entries = count;
+ printk(KERN_DEBUG "SST DBG:read_modify +\
+ called for cnt = %d\n", count);
+ retval = ipc_pmic_register_read_modify(&pmic_mod_reg);
+ if (retval == E_NO_INTERRUPT_ON_IOC &&
+ max_retries < 10) {
+ printk(KERN_ERR "SST ERR: read/modify retry \n");
+ max_retries++;
+ goto retry_readmod;
+ }
+ if (0 != retval) {
+ /* pmic communication fails */
+ printk(KERN_ERR "SST ERR: pmic read_modify failed \n");
+ return retval;
+ }
+ num_val -= count;
+ } while (num_val > 0);
+ }
+ return retval;
+}
+
--
1.6.2.2