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