From 5e8c7c54a9b297dae0081dd19a7bb94e23040a3d Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Tue, 18 May 2010 14:51:13 +0100 Subject: linux-moblin: add 2.6.33.2 kernel from MeeGo 1.0 Signed-off-by: Joshua Lock --- ...ux-2.6.34-moorestown-audio-driver-6.0-7-8.patch | 2391 ++++++++++++++++++++ 1 file changed, 2391 insertions(+) create mode 100644 meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-audio-driver-6.0-7-8.patch') 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 +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 + + 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 ++ * Vinod Koul ++ * Dharageswari R ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++/* #include ++#include */ ++ ++#include "intelmid_snd_control.h" ++#include "intelmid.h" ++#include "intelmid_ctrl.h" ++ ++MODULE_AUTHOR("Harsha Priya "); ++MODULE_AUTHOR("Vinod Koul "); ++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 ++ * Vinod Koul ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++ ++#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 ++ * Vinod Koul ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++ * Vinod Koul ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++ * Vinod Koul ++ * KP Jeeja ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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(®_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(®_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 + -- cgit v1.2.3