summaryrefslogtreecommitdiff
path: root/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch')
-rw-r--r--meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch2391
1 files changed, 2391 insertions, 0 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch
new file mode 100644
index 000000000..557b67d93
--- /dev/null
+++ b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch
@@ -0,0 +1,2391 @@
+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
+