Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
August 2009
- 107 participants
- 225 discussions
HelloI have an MSI Media Live DIVA motherboard. It has an AMD SB600 HD Audio
controller, and two CODECs on the HDA bus.
First is an ALC888
Second is an Intersil DAE-3.
Two alsa-info outputs in different circumstances
http://www.alsa-project.org/db/?f=20c86e755fdbbdbc8761c8c2f4de76bce2ea0c11
http://www.alsa-project.org/db/?f=15c5e5eab0104622a84b2dd0df03f26c17c7242d
Output of codecgraph with "modprobe hda-intel probe_mask=2"
http://pastebin.com/f54b6a9b8
What's the next step to get this supported?
--
- Alex Austin
(651) 238-9273
"...and then I visited Wikipedia ...and the next 8 hours are a blur."
3
13
Hi.
Following the patch to add the amplifier switch (changing input levels
phono/line) for the Serato Scratch Live device.
Please apply or complain ;)
2
3
[alsa-devel] [PATCH 1/3] ASoC: DaVinci: i2s, reduce underruns by combining into 1 element
by Troy Kisky 01 Oct '09
by Troy Kisky 01 Oct '09
01 Oct '09
Allow the left and right 16 bit samples to be shifted out as 1
32 bit sample.
Signed-off-by: Troy Kisky <troy.kisky(a)boundarydevices.com>
--
This applies to Kevin's temp/asoc branch
---
arch/arm/mach-davinci/include/mach/asp.h | 6 ++
sound/soc/davinci/davinci-i2s.c | 74 ++++++++++++++++++++++--------
2 files changed, 61 insertions(+), 19 deletions(-)
diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h
index 18e4ce3..a3d2aa1 100644
--- a/arch/arm/mach-davinci/include/mach/asp.h
+++ b/arch/arm/mach-davinci/include/mach/asp.h
@@ -51,6 +51,12 @@ struct snd_platform_data {
u32 rx_dma_offset;
enum dma_event_q eventq_no; /* event queue number */
unsigned int codec_fmt;
+ /*
+ * Allowing this is more efficient and eliminates left and right swaps
+ * caused by underruns, but will swap the left and right channels
+ * when compared to previous behavior.
+ */
+ unsigned disable_channel_combine:1;
/* McASP specific fields */
int tdm_slots;
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 12a6c54..081b2d4 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -97,6 +97,23 @@ enum {
DAVINCI_MCBSP_WORD_32,
};
+static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = 1,
+ [SNDRV_PCM_FORMAT_S16_LE] = 2,
+ [SNDRV_PCM_FORMAT_S32_LE] = 4,
+};
+
+static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
+ [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
+ [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
+};
+
+static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE,
+ [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE,
+};
+
static struct davinci_pcm_dma_params davinci_i2s_pcm_out = {
.name = "I2S PCM Stereo out",
};
@@ -113,6 +130,27 @@ struct davinci_mcbsp_dev {
u32 pcr;
struct clk *clk;
struct davinci_pcm_dma_params *dma_params[2];
+ /*
+ * Combining both channels into 1 element will at least double the
+ * amount of time between servicing the dma channel, increase
+ * effiency, and reduce the chance of overrun/underrun. But,
+ * it will result in the left & right channels being swapped.
+ *
+ * If relabeling the left and right channels is not possible,
+ * you may want to let the codec know to swap them back.
+ *
+ * It may allow x10 the amount of time to service dma requests,
+ * if the codec is master and is using an unnecessarily fast bit clock
+ * (ie. tlvaic23b), independent of the sample rate. So, having an
+ * entire frame at once means it can be serviced at the sample rate
+ * instead of the bit clock rate.
+ *
+ * In the now unlikely case that an underrun still
+ * occurs, both the left and right samples will be repeated
+ * so that no pops are heard, and the left and right channels
+ * won't end up being swapped because of the underrun.
+ */
+ unsigned disable_channel_combine:1;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -359,6 +397,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
int mcbsp_word_length;
unsigned int rcr, xcr, srgr;
u32 spcr;
+ snd_pcm_format_t fmt;
+ unsigned element_cnt = 1;
/* general line settings */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -388,27 +428,22 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
}
/* Determine xfer data type */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- dma_params->data_type = 1;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- dma_params->data_type = 2;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- dma_params->data_type = 4;
- mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
- break;
- default:
+ fmt = params_format(params);
+ if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
return -EINVAL;
}
-
- dma_params->acnt = dma_params->data_type;
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
+ if (params_channels(params) == 2) {
+ element_cnt = 2;
+ if (double_fmt[fmt] && !dev->disable_channel_combine) {
+ element_cnt = 1;
+ fmt = double_fmt[fmt];
+ }
+ }
+ dma_params->acnt = dma_params->data_type = data_type[fmt];
+ mcbsp_word_length = asp_word_length[fmt];
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@@ -524,7 +559,8 @@ static int davinci_i2s_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_release_region;
}
-
+ if (pdata)
+ dev->disable_channel_combine = pdata->disable_channel_combine;
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
--
1.5.6.3
6
28
Takashi I came across a workaround for VMware in ALSA kernel code
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h…
Thanks for the fix.
In the comments you mention about inaccurate timer source.
Could you elaborate on the problem?
As of now we see that sound apps running in Fedora 11 guest OS with
kernel 2.6.29.4-167 don't make any progress and can hang window
manager like Metacity.
VMware virtualizes Ensoniq ES1371 sound device.
Is there something specific you would like us to fix in our virtual
sound device or timer source?
Thanks,
Bankim.
4
6
Hello all,
all ALSA packages except alsa-oss and alsa-firmware (no changes
from 1.0.20) are available for download at http://www.alsa-project.org .
Changelog is available here:
http://www.alsa-project.org/main/index.php/Changes_v1.0.20_v1.0.21
Jaroslav Kysela
-----
Jaroslav Kysela <perex(a)perex.cz>
Linux Kernel Sound Maintainer
ALSA Project, Red Hat, Inc.
3
5
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 (in non low power mode).
This driver registers the control interface and PCM
interface with the LPE 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
Signed-off-by: Vinod Koul <vinod.koul(a)intel.com>
Signed-off-by: Harsha Priya <priya.harsha(a)intel.com>
new file: sound/pci/sst/intelmid.c
---
sound/pci/sst/intelmid.c | 1761 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1761 insertions(+), 0 deletions(-)
create mode 100644 sound/pci/sst/intelmid.c
diff --git a/sound/pci/sst/intelmid.c b/sound/pci/sst/intelmid.c
new file mode 100644
index 0000000..28a6dcc
--- /dev/null
+++ b/sound/pci/sst/intelmid.c
@@ -0,0 +1,1761 @@
+/*
+ * intelmid.c - Intel Sound card driver for MID
+ *
+ * Copyright (C) 2008-09 Intel Corp
+ * Authors: Harsha Priya <priya.harsha(a)intel.com>
+ * Vinod Koul <vinod.koul(a)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 <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm-indirect.h>
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
+#include "intelmid_snd_control.h"
+#include "intelmid.h"
+#include "intelmid_pvt.h"
+#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 <linux/gpe.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+MODULE_AUTHOR("Harsha Priya <priya.harsha(a)intel.com>");
+MODULE_AUTHOR("Vinod Koul <vinod.koul(a)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 */
+static int vendor_id;
+static unsigned int audio_event_seqnum;
+static int cum_bytes;
+
+
+#ifdef FULL_CTRL
+static char *out_names[] = {"Headphones",
+ "Internal speakers",
+ "EarPiece"};
+static char *in_names[] = { "DMIC",
+ "MIC1",
+ "MIC2",
+ "Line_in"};
+#endif
+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",
+};
+
+/*control path functionalities*/
+/**
+* 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;
+}
+
+#ifdef FULL_CTRL
+/**
+* snd_intelmad_volume_line_info - provides information 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_line_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ ctrl_val[vendor_id].linein_vol_max,
+ ctrl_val[vendor_id].linein_vol_min);
+ return 0;
+}
+
+/**
+* snd_intelmad_mic1_volume_info - provides information 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_mic1_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ ctrl_val[vendor_id].mic1_vol_max,
+ ctrl_val[vendor_id].mic1_vol_min);
+ return 0;
+}
+
+/**
+* snd_intelmad_mic2_volume_info - provides information 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_mic2_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ ctrl_val[vendor_id].mic2_vol_max,
+ ctrl_val[vendor_id].mic2_vol_min);
+ return 0;
+}
+
+/**
+* snd_intelmad_dmic_volume_info - provides information 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_dmic_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ ctrl_val[vendor_id].dmic_vol_max,
+ ctrl_val[vendor_id].dmic_vol_min);
+ return 0;
+}
+#endif
+/**
+* snd_intelmad_hp_volume_info - provides information 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_hp_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ ctrl_val[vendor_id].hp_vol_max,
+ ctrl_val[vendor_id].hp_vol_min);
+ return 0;
+}
+
+#ifdef FULL_CTRL
+/**
+* snd_intelmad_speaker_volume_info - provides information 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_speaker_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, STEREO_CNTL,
+ ctrl_val[vendor_id].speaker_vol_max,
+ ctrl_val[vendor_id].speaker_vol_min);
+ return 0;
+}
+
+/**
+* snd_intelmad_earpiece_volume_info - provides information 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_earpiece_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ snd_intelmad_volume_info(uinfo, MONO_CNTL,
+ ctrl_val[vendor_id].earpiece_vol_max,
+ ctrl_val[vendor_id].earpiece_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 (OUTPUT_SEL == kcontrol->id.numid)
+ 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 (OUTPUT_SEL == kcontrol->id.numid)
+ 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;
+}
+#endif
+
+/**
+* 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,}, value = 0;
+ struct snd_intelmad *intelmaddata = NULL;
+ struct snd_pmic_ops *scard_ops = NULL;
+
+ sst_dbg("called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ if (PMIC_UNINIT == intelmaddata->pmic_status) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ }
+
+ if (0 != ret_val)
+ return ret_val;
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ switch (kcontrol->id.numid) {
+ case HEADPHONES_VOL:
+ cntl_list[0] = PMIC_SND_RIGHT_HP_VOL;
+ cntl_list[1] = PMIC_SND_LEFT_HP_VOL;
+ break;
+#ifdef FULL_CTRL
+ case INTERNAL_SPEAKERS_VOL:
+ cntl_list[0] = PMIC_SND_RIGHT_SPEAKER_VOL;
+ cntl_list[1] = PMIC_SND_LEFT_SPEAKER_VOL;
+ break;
+ case LINE_IN_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_RIGHT_LINE_IN;
+ cntl_list[1] = PMIC_SND_INPUT_VOL_LEFT_LINE_IN;
+ break;
+ case EARPIECE_VOL:
+ cntl_list[0] = PMIC_SND_MONO_EARPIECE_VOL;
+ break;
+ case MIC1_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_MIC1;
+ break;
+ case MIC2_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_MIC2;
+ break;
+ case DMIC_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_DMIC;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->get_vol(cntl_list[0], &value);
+ uval->value.integer.value[0] = value;
+
+ if (0 != ret_val)
+ return ret_val;
+#ifdef FULL_CTRL
+ if (HEADPHONES_VOL == kcontrol->id.numid
+ || INTERNAL_SPEAKERS_VOL == kcontrol->id.numid ||
+ LINE_IN_VOL == kcontrol->id.numid) {
+#else
+ if (1) {
+#endif
+
+ ret_val = scard_ops->get_vol(cntl_list[1], &value);
+ uval->value.integer.value[1] = value;
+
+ if (0 != ret_val)
+ return ret_val;
+ }
+ 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, value = 0;
+ struct snd_intelmad *intelmaddata = NULL;
+ struct snd_pmic_ops *scard_ops = NULL;
+
+ sst_dbg("called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ if (PMIC_UNINIT == intelmaddata->pmic_status) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ }
+
+ if (0 != ret_val)
+ return ret_val;
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ switch (kcontrol->id.numid) {
+ case HEADPHONES_MUTE:
+ cntl_list = PMIC_SND_LEFT_HP_MUTE;
+ break;
+#ifdef FULL_CTRL
+ case INTERNAL_SPEAKERS_MUTE:
+ cntl_list = PMIC_SND_LEFT_SPEAKER_MUTE;
+ break;
+ case EARPIECE_MUTE:
+ cntl_list = PMIC_SND_MONO_EARPIECE_MUTE;
+ break;
+ case LINE_IN_MUTE:
+ cntl_list = PMIC_SND_INPUT_MUTE_LINE_IN;
+ break;
+ case MIC1_MUTE:
+ cntl_list = PMIC_SND_INPUT_MUTE_MIC1;
+ break;
+ case MIC2_MUTE:
+ cntl_list = PMIC_SND_INPUT_MUTE_MIC2;
+ break;
+ case DMIC_MUTE:
+ cntl_list = PMIC_SND_INPUT_MUTE_DMIC;
+ break;
+ case MASTER_MUTE:
+ uval->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+#endif
+
+ 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 = 0, cntl_list[2] = {0,};
+ struct snd_intelmad *intelmaddata = NULL;
+ struct snd_pmic_ops *scard_ops = NULL;
+
+ sst_dbg("volume set called\n:%ld %ld",
+ 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);
+
+ if (PMIC_UNINIT == intelmaddata->pmic_status) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ }
+
+ if (0 != ret_val)
+ return ret_val;
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ switch (kcontrol->id.numid) {
+ case HEADPHONES_VOL:
+ cntl_list[0] = PMIC_SND_RIGHT_HP_VOL;
+ cntl_list[1] = PMIC_SND_LEFT_HP_VOL;
+ break;
+#ifdef FULL_CTRL
+ case INTERNAL_SPEAKERS_VOL:
+ cntl_list[0] = PMIC_SND_RIGHT_SPEAKER_VOL;
+ cntl_list[1] = PMIC_SND_LEFT_SPEAKER_VOL;
+ break;
+ case LINE_IN_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_RIGHT_LINE_IN;
+ cntl_list[1] = PMIC_SND_INPUT_VOL_LEFT_LINE_IN;
+ break;
+ case EARPIECE_VOL:
+ cntl_list[0] = PMIC_SND_MONO_EARPIECE_VOL;
+ break;
+ case MIC1_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_MIC1;
+ break;
+ case MIC2_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_MIC2;
+ break;
+ case DMIC_VOL:
+ cntl_list[0] = PMIC_SND_INPUT_VOL_DMIC;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->set_vol(cntl_list[0],
+ uval->value.integer.value[0]);
+ if (0 != ret_val)
+ return ret_val;
+#ifdef FULL_CTRL
+ if (HEADPHONES_VOL == kcontrol->id.numid
+ || INTERNAL_SPEAKERS_VOL == kcontrol->id.numid ||
+ LINE_IN_VOL == kcontrol->id.numid) {
+#else
+ if (1) {
+#endif
+ ret_val = scard_ops->set_vol(cntl_list[1],
+ uval->value.integer.value[1]);
+ if (0 != ret_val)
+ return ret_val;
+ }
+ kcontrol->private_value = uval->value.integer.value[0];
+ 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 = 0;
+ struct snd_intelmad *intelmaddata = NULL;
+ struct snd_pmic_ops *scard_ops = NULL;
+
+ sst_dbg("called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ if (PMIC_UNINIT == intelmaddata->pmic_status) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ }
+
+ if (0 != ret_val)
+ return ret_val;
+
+ scard_ops = intelmaddata->sstdrv_ops->scard_ops;
+
+ WARN_ON(!scard_ops);
+
+ kcontrol->private_value = uval->value.integer.value[0];
+
+ switch (kcontrol->id.numid) {
+ case HEADPHONES_MUTE:
+ cntl_list[0] = PMIC_SND_LEFT_HP_MUTE;
+ cntl_list[1] = PMIC_SND_RIGHT_HP_MUTE;
+ break;
+#ifdef FULL_CTRL
+ case INTERNAL_SPEAKERS_MUTE:
+ cntl_list[0] = PMIC_SND_LEFT_SPEAKER_MUTE;
+ cntl_list[1] = PMIC_SND_RIGHT_SPEAKER_MUTE;
+ break;
+ case EARPIECE_MUTE:
+ cntl_list[0] = PMIC_SND_MONO_EARPIECE_MUTE;
+ break;
+ case LINE_IN_MUTE:
+ cntl_list[0] = PMIC_SND_INPUT_MUTE_LINE_IN;
+ break;
+ case MIC1_MUTE:
+ cntl_list[0] = PMIC_SND_INPUT_MUTE_MIC1;
+ break;
+ case MIC2_MUTE:
+ cntl_list[0] = PMIC_SND_INPUT_MUTE_MIC2;
+ break;
+ case DMIC_MUTE:
+ cntl_list[0] = PMIC_SND_INPUT_MUTE_DMIC;
+ break;
+ case MASTER_MUTE:
+ mute_all(kcontrol, uval);
+ kcontrol->private_value = uval->value.integer.value[0];
+ return 0;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ ret_val = scard_ops->set_mute(cntl_list[0],
+ uval->value.integer.value[0]);
+ if (0 != ret_val)
+ return ret_val;
+
+#ifdef FULL_CTRL
+ if (HEADPHONES_MUTE == kcontrol->id.numid
+ || INTERNAL_SPEAKERS_MUTE == kcontrol->id.numid)
+#endif
+ ret_val = scard_ops->set_mute(cntl_list[1],
+ uval->value.integer.value[0]);
+ kcontrol->private_value = uval->value.integer.value[0];
+ return ret_val;
+}
+
+#ifdef FULL_CTRL
+/**
+* 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)
+{
+ 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 = NULL;
+ struct snd_pmic_ops *scard_ops = NULL;
+ int ret_val = 0;
+
+ sst_dbg("called\n");
+
+ WARN_ON(!uval);
+ WARN_ON(!kcontrol);
+
+ intelmaddata = kcontrol->private_data;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+
+ if (PMIC_UNINIT == intelmaddata->pmic_status) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ }
+
+ if (0 != ret_val)
+ return ret_val;
+
+ 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]);
+ break;
+ case INPUT_SEL:
+ ret_val = scard_ops->set_input_dev(
+ uval->value.enumerated.item[0]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ kcontrol->private_value = uval->value.enumerated.item[0];
+ return ret_val;
+}
+#endif
+
+/*structure of all the controls of the sound card that is exposed*/
+static struct snd_kcontrol_new snd_intelmad_controls[] __devinitdata = {
+#ifdef FULL_CTRL
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_line_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line 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 = "DMIC Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_dmic_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DMIC 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 = "Mic1 Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_mic1_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic1 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 = "Mic2 Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_mic2_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic2 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 = "Master 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,
+},
+#endif
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_hp_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone 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,
+},
+#ifdef FULL_CTRL
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Internal Speaker Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_speaker_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Internal Speaker 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 = "Mono Earpiece Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_intelmad_earpiece_volume_info,
+ .get = snd_intelmad_volume_get,
+ .put = snd_intelmad_volume_set,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mono Earpiece 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 = "Master 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 = "Master Mute 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,
+},
+#endif
+};
+
+/*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_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 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 stream_buffer buffer_to_sst = {0,};
+ struct mad_stream_pvt *stream = substream->runtime->private_data;
+ int ret_val = 0;
+ /*sends data to SST to be processed*/
+
+ cum_bytes += bytes;
+ sst_dbg("bytes = %d \n", bytes);
+ sst_dbg("cum_bytes = 0x%x, \n", 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_id,
+ &buffer_to_sst);
+ sst_dbg("send_buffer ret_val = 0x%x \n", ret_val);
+ return;
+}
+
+void start_capture(struct work_struct *work)
+{
+ int ret_val = 0;
+ struct mad_capture_wq *stream_work =
+ container_of(work, struct mad_capture_wq, wq);
+ struct stream_buffer *buffer_to_sst = &stream_work->buffer_to_sst;
+ struct snd_pcm_substream *substream = stream_work->substream;
+ struct mad_stream_pvt *stream = substream->runtime->private_data;
+ struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream);
+
+ ret_val = intelmaddata->sstdrv_ops->send_buffer(stream->stream_id,
+ buffer_to_sst);
+ sst_dbg("send_buffer ret_val = 0x%x \n", ret_val);
+ return;
+}
+
+void snd_card_pcm_wq_function(struct work_struct *work)
+{
+ struct mad_wq *stream_work =
+ container_of(work, struct mad_wq, wq);
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_indirect *rec = NULL;
+ struct mad_stream_pvt *stream;
+
+ mutex_lock(&stream_work->wq_lock);
+ substream = stream_work->substream;
+ if (substream == NULL || substream->runtime == NULL) {
+ sst_err("subsream structure is null \n");
+ mutex_unlock(&stream_work->wq_lock);
+ return;
+ }
+ stream = substream->runtime->private_data;
+ sst_dbg("called %d\n", stream->stream_status);
+ if (stream == NULL) {
+ sst_err("stream is null in wq function \n");
+ mutex_unlock(&stream_work->wq_lock);
+ return;
+ }
+
+ if (stream->stream_status != INIT) {
+ rec = &stream->pcm_indirect;
+ if (substream->stream == STREAM_OPS_PLAYBACK)
+ snd_pcm_indirect_playback_transfer(substream, rec,
+ send_buffer_to_sst);
+ else if (substream->stream == STREAM_OPS_CAPTURE) {
+ sst_dbg("calling indirect capture transfer\n");
+ snd_pcm_indirect_capture_transfer(substream, rec,
+ send_buffer_to_sst);
+ }
+ stream->stream_status = RUNNING;
+ }
+ mutex_unlock(&stream_work->wq_lock);
+ return;
+}
+
+static int snd_intelmad_pcm_ack(struct snd_pcm_substream *substream)
+{
+ struct mad_stream_pvt *stream;
+
+ stream = substream->runtime->private_data;
+ sst_dbg("...called\n");
+ stream->mad_msg_wq.substream = substream;
+ schedule_work(&stream->mad_msg_wq.wq);
+ 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, stream_type = 0/*, sample_size = 0*/;
+ struct snd_intelmad *intelmaddata = NULL;
+ struct mad_stream_pvt *stream = NULL;
+
+ sst_dbg("called\n");
+
+ WARN_ON(!substream);
+
+ intelmaddata = snd_pcm_substream_chip(substream);
+ stream_type = substream->stream;
+ stream = substream->runtime->private_data;
+
+ if (PMIC_UNINIT == intelmaddata->pmic_status) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ }
+
+ if (0 != ret_val)
+ return ret_val;
+
+ WARN_ON(!intelmaddata->sstdrv_ops);
+ WARN_ON(!intelmaddata->sstdrv_ops->scard_ops);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /*invoked on a power managment -resume operation*/
+ /*stream parameters and context resuming to be done here*/
+ /*TBD*/
+ sst_dbg("in resume\n");
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ stream->substream = substream;
+ stream->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+ sst_dbg("pcm_size=%d\n", stream->pcm_size);
+#ifdef TIMER_TS
+ bps = (substream->runtime->sample_bits/8)
+ * (substream->runtime->channels)
+ * (substream->runtime->rate);
+ stream->pcm_jiffie = ((bps * ELAPSED_PERIOD) / HZ);
+ stream->pcm_buf_pos = 0;
+ sst_dbg("pcm_jiffie=%d \n", stream->pcm_jiffie);
+#endif
+ stream->stream_status = STARTED;
+ if (substream->stream == STREAM_OPS_PLAYBACK)
+ snd_intelmad_pcm_ack(substream);
+ else if (substream->stream == STREAM_OPS_CAPTURE) {
+ stream->mad_capture_wq.substream = substream;
+ stream->mad_capture_wq.buffer_to_sst.length =
+ frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ stream->mad_capture_wq.buffer_to_sst.addr =
+ (unsigned long) substream->runtime->dma_area;
+ schedule_work(&stream->mad_capture_wq.wq);
+ cum_bytes = stream->mad_capture_wq.buffer_to_sst.length;
+ stream->stream_status = RUNNING;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /*invoked on suspend - power management event*/
+ /*stream parameters and context to be saved*/
+ /*TBD*/
+ sst_dbg("in suspend\n");
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ sst_dbg("in stop\n");
+ /*ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_DROP,
+ &stream->stream_id);
+ if (0 != ret_val)
+ return ret_val;*/
+ stream->stream_status = DROPPED;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sst_dbg("in pause\n");
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_PAUSE,
+ &stream->stream_id);
+ if (0 != ret_val)
+ return ret_val;
+ stream->stream_status = PAUSED;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sst_dbg("in pause release \n");
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_RESUME,
+ &stream->stream_id);
+ if (0 != 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 = NULL;
+ int ret_val = 0;
+
+ sst_dbg("called \n");
+
+ WARN_ON(!substream);
+
+ stream = substream->runtime->private_data;
+
+ sst_dbg("format = %d \n", substream->runtime->format);
+ if (stream->stream_id == 0) {
+ ret_val = snd_intelmad_alloc_stream(substream);
+ cum_bytes = 0;
+ if (ret_val < 0)
+ return ret_val;
+ ret_val = snd_intelmad_init_stream(substream);
+ if (0 != ret_val)
+ return ret_val;
+ sst_dbg("period size = %d \n",
+ (int)substream->runtime->period_size);
+ 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_id);
+ sst_dbg("stream id to user = %s\n", substream->pcm->id);
+ }
+ return ret_val;
+}
+
+static int snd_intelmad_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int ret_val = 0;
+
+ 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)
+{
+ 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 = NULL;
+ struct snd_intelmad *intelmaddata = NULL;
+ int ret_val = 0;
+ unsigned long buf_size = 0;
+
+ WARN_ON(!substream);
+
+ intelmaddata = snd_pcm_substream_chip(substream);
+ stream = substream->runtime->private_data;
+ if (stream->stream_status == INIT)
+ return 0;
+
+ stream->buf_ptr.str_id = stream->stream_id;
+
+#ifndef TIMER_TS
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_BUFFER_POINTER,
+ &stream->buf_ptr);
+ if (0 != ret_val) {
+ sst_err("error code = 0x%x \n", ret_val);
+ return ret_val;
+ }
+ sst_dbg("samples reported out 0x%lx \n", stream->buf_ptr.buffer_ptr);
+ buf_size = frames_to_bytes(substream->runtime,
+ stream->buf_ptr.buffer_ptr);
+ sst_dbg(" bytes reported out = 0x%lx\n", buf_size);
+ if (buf_size > cum_bytes)
+ sst_err("excess reported\n");
+
+ if (substream->stream == STREAM_OPS_PLAYBACK)
+ stream->period_elapsed_ptr = snd_pcm_indirect_playback_pointer(
+ substream, &stream->pcm_indirect, buf_size);
+ else
+ stream->period_elapsed_ptr = snd_pcm_indirect_capture_pointer(
+ substream, &stream->pcm_indirect, buf_size);
+
+ sst_dbg("indirect value = 0x%lx\n", stream->period_elapsed_ptr);
+
+ return stream->period_elapsed_ptr;
+#else
+ stream->pcm_buf_pos += stream->pcm_jiffie;
+ stream->pcm_buf_pos %= stream->pcm_size;
+ return snd_pcm_indirect_playback_pointer(substream,
+ &stream->pcm_indirect, stream->pcm_buf_pos);
+#endif
+
+}
+
+/**
+* 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 = NULL;
+ struct mad_stream_pvt *stream = NULL;
+ int ret_val = 0;
+
+ WARN_ON(!substream);
+
+ stream = substream->runtime->private_data;
+
+ sst_dbg("called \n");
+ intelmaddata = snd_pcm_substream_chip(substream);
+ mutex_lock(&stream->mad_msg_wq.wq_lock);
+ sst_dbg("str id = %d\n", stream->stream_id);
+ /*SST API to actually stop/free the stream*/
+ ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_FREE,
+ &stream->stream_id);
+ kfree(substream->runtime->private_data);
+ mutex_unlock(&stream->mad_msg_wq.wq_lock);
+ 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);
+
+ sst_dbg("called \n");
+
+ intelmaddata = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+ /*set the runtime hw parameter with our 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 (NULL == stream)
+ return -ENOMEM;
+ stream->stream_id = 0;
+ stream->stream_status = INIT;
+ INIT_WORK(&stream->mad_msg_wq.wq, snd_card_pcm_wq_function);
+ INIT_WORK(&stream->mad_capture_wq.wq, start_capture);
+ mutex_init(&stream->mad_msg_wq.wq_lock);
+ runtime->private_data = stream;
+ return 0;
+}
+
+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,
+};
+
+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;
+
+ /* allocate memory */
+ size = nla_total_size(sizeof(struct audio_genl_event)) + \
+ nla_total_size(0);
+
+ skb = genlmsg_new(size, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ /* add the genetlink message header */
+ 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;
+ }
+
+ /* fill the data */
+ 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;
+
+ /* send multicast genetlink message */
+ 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;
+}
+
+#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;
+ struct sc_reg_access pmic_reg = {0,};
+ int ret_val = 0;
+ u8 intsts = 0;
+ memcpy_fromio(&intsts,
+ ((void *)(intelmaddata->int_base)),
+ sizeof(u8));
+ if (intsts != 0)
+ sst_dbg("intstatus = 0x%x\n", intsts);
+ switch (intelmaddata->sstdrv_ops->vendor_id) {
+ case SND_FS:
+ if (intsts & 0x1) {
+ /*send long push*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_LONG_PRESS);
+ }
+ if (intsts & 0x2) {
+ /*send short push*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_SHORT_PRESS);
+ }
+ if (intsts & 0x4) {
+ /*send headphone detect*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_HP_DETECT);
+ }
+ if (intsts & 0x8) {
+ /*send headset detect*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_HS_DETECT);
+ }
+ break;
+ case SND_MX:
+ if (intsts & 0x2) {
+ pmic_reg.reg_addr = 0x201;
+ ret_val = sst_sc_reg_access(&pmic_reg, PMIC_READ, 1);
+ if (0 != ret_val) {
+ /*pmic communication fails*/
+ sst_err("pmic commn failed \n");
+ return IRQ_HANDLED;
+ }
+ intsts = pmic_reg.value;
+ if (intsts & 0xc0)
+ /*send jack detect/undetect*/
+ sst_dbg("to handle jack sensing\n");
+ }
+ break;
+ case SND_NC:
+ if (intsts & 0x1) {
+ /*send headset detect/undetect*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_HS_DETECT);
+ }
+ if (intsts & 0x2) {
+ /*send headphone detect/undetect*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_HP_DETECT);
+ }
+ if (intsts & 0x4) {
+ /*send short push*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_SHORT_PRESS);
+ }
+ if (intsts & 0x8) {
+ /*send long push*/
+ ret_val = snd_intelmad_generate_netlink(1,
+ AUDIO_EVENT_LONG_PRESS);
+ }
+ break;
+ }
+ return IRQ_HANDLED;
+}
+static int __devinit snd_intelmad_register_irq(
+ struct snd_intelmad *intelmaddata)
+{
+ int ret_val = 0;
+ u32 regbase = AUDINT_BASE, regsize = 8;
+
+ /* interpret irq field */
+ sst_dbg("irq = 0x%x\n", intelmaddata->irq);
+
+ if ((intelmaddata->irq) & PMIC_SOUND_IRQ_TYPE_MASK) {
+ /* irq -> GPE_ID */
+ intelmaddata->irq = intelmaddata->irq &
+ (~PMIC_SOUND_IRQ_TYPE_MASK);
+ sst_dbg("gpe = 0x%x\n", intelmaddata->irq);
+ ret_val = request_gpe(intelmaddata->irq,
+ (gpio_function_t)snd_intelmad_intr_handler,
+ intelmaddata, 0);
+ if (ret_val) {
+ sst_dbg("cannot register gpe\n");
+ return ret_val;
+ }
+
+ } else {
+ /* irq -> system irq */
+ ret_val = request_irq(intelmaddata->irq,
+ snd_intelmad_intr_handler,
+ IRQF_SHARED, DRIVER_NAME,
+ intelmaddata);
+ if (0 != ret_val) {
+ sst_dbg("cannot register IRQ\n");
+ return ret_val;
+ }
+ }
+
+ intelmaddata->int_base = ioremap_nocache(regbase, regsize);
+ return ret_val;
+}
+static int __devinit snd_intelmad_register_netlink(void)
+{
+ int ret_val = 0;
+
+ ret_val = genl_register_family(&audio_event_genl_family);
+ if (0 != ret_val) {
+ 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) {
+ 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 = 0;
+ struct sc_reg_access pmic_reg = {0,};
+
+ pmic_reg.reg_addr = 0;
+ ret_val = sst_sc_reg_access(&pmic_reg, PMIC_READ, 1);
+
+ if (0 != ret_val)
+ return ret_val;
+
+ vendor_id = pmic_reg.value & (MASK2|MASK1|MASK0);
+ sst_info("orginal reg n extrated vendor id = 0x%x %d\n",
+ pmic_reg.value, vendor_id);
+ if (vendor_id < 0 || vendor_id > 2) {
+ sst_err("vendor card not supported!!\n");
+ return -EIO;
+ }
+ intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES;
+ intelmaddata->sstdrv_ops->vendor_id = vendor_id;
+ intelmaddata->sstdrv_ops->scard_ops = &v[vendor_id];
+
+ /*registering with SST driver to get access to SST APIs to use*/
+ ret_val = register_sst_card(intelmaddata->sstdrv_ops);
+ if (0 != ret_val) {
+ sst_err("sst card registration failed\n");
+ return ret_val;
+ }
+
+ 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 (0 != 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_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 = NULL;
+ 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 < ARRAY_SIZE(snd_intelmad_controls); idx++) {
+ ret_val = snd_ctl_add(card,
+ snd_ctl_new1(&snd_intelmad_controls[idx],
+ intelmaddata));
+ sst_dbg("mixer[idx]=%d added \n", idx);
+ if (0 != ret_val)
+ 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;
+
+ 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*/
+ 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 = 0;
+ 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 = NULL;
+ int ret_val = 0;
+ struct snd_intelmad *intelmaddata = NULL;
+
+ sst_dbg("called \n");
+
+ /*allocate memory for saving internal context and working*/
+ intelmaddata = kzalloc(sizeof(*intelmaddata), GFP_KERNEL);
+ if (NULL == intelmaddata)
+ return -ENOMEM;
+
+ /*allocate memory for LPE API set*/
+ intelmaddata->sstdrv_ops = kzalloc(sizeof(struct intel_sst_card_ops),
+ GFP_KERNEL);
+ if (NULL == intelmaddata->sstdrv_ops) {
+ sst_err("mem alloctn fail \n");
+ kfree(intelmaddata);
+ return -ENOMEM;
+ }
+
+ /*create a card instance with ALSA framework*/
+ card = snd_card_new(card_index, card_id, THIS_MODULE, 0);
+ if (NULL == card)
+ return -ENOMEM;
+
+ 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;
+ strncpy(card->driver, INTEL_MAD, strlen(INTEL_MAD));
+ strncpy(card->shortname, INTEL_MAD, strlen(INTEL_MAD));
+
+#ifdef REG_IRQ
+ ret_val = snd_intelmad_register_irq(intelmaddata);
+ if (0 != ret_val) {
+ sst_err("snd_intelmad_register_irq fail\n");
+ goto free_allocs;
+ }
+ ret_val = snd_intelmad_register_netlink();
+ if (0 == ret_val) {
+ sst_dbg("...complete\n");
+ return ret_val;
+ }
+#endif
+
+ /*internal function call to register device with ALSA*/
+ ret_val = snd_intelmad_create(intelmaddata, card);
+ if (0 != ret_val) {
+ sst_err("snd_intelmad_create failed\n");
+ goto free_allocs;
+ }
+
+ ret_val = snd_intelmad_pcm(card, intelmaddata);
+ if (0 != ret_val) {
+ sst_err("snd_intelmad_pcm failed\n");
+ goto free_allocs;
+ }
+
+ ret_val = snd_intelmad_mixer(intelmaddata);
+ if (0 != ret_val) {
+ sst_err("snd_intelmad_mixer failed\n");
+ goto free_allocs;
+ }
+
+ card->private_data = &intelmaddata;
+ snd_card_set_dev(card, &spi->dev);
+ ret_val = snd_card_register(card);
+ if (0 != ret_val)
+ goto free_allocs;
+
+
+ intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES;
+ /*registering with LPE driver to get access to LPE APIsito use*/
+ ret_val = snd_intelmad_sst_register(intelmaddata);
+ if (0 == ret_val) {
+ ret_val = intelmaddata->sstdrv_ops->scard_ops->init_card();
+ intelmaddata->pmic_status = PMIC_INIT;
+ sst_dbg("...complete\n");
+ return ret_val;
+ }
+
+ sst_err("registering with LPE failed \n");
+
+free_allocs:
+ /*TODO: unregister IRQ*/
+ 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*/
+ 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)
+{
+ 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)
+{
+ sst_dbg("called\n");
+ spi_unregister_driver(&snd_intelmad_driver);
+}
+
+module_init(alsa_card_intelmad_init)
+module_exit(alsa_card_intelmad_exit)
+
--
1.5.4.5
6
31
[alsa-devel] [PATCH] AC97 atmel: add support for AT91(common with AVR32).
by Sedji Gaouaou 07 Sep '09
by Sedji Gaouaou 07 Sep '09
07 Sep '09
This patch add AC97 support for ATMEL AT91 boards, using the AVR32 code.
It is based on Takashi git tree(sound-2.6/for-next).
Regards,
Sedji
Signed-off-by: Sedji Gaouaou <sedji.gaouaou(a)atmel.com>
---
sound/atmel/Kconfig | 2 +-
sound/atmel/ac97c.c | 364 +++++++++++++++++++++++++++++++++++++--------------
2 files changed, 268 insertions(+), 98 deletions(-)
diff --git a/sound/atmel/Kconfig b/sound/atmel/Kconfig
index 6c228a9..94de43a 100644
--- a/sound/atmel/Kconfig
+++ b/sound/atmel/Kconfig
@@ -12,7 +12,7 @@ config SND_ATMEL_AC97C
tristate "Atmel AC97 Controller (AC97C) driver"
select SND_PCM
select SND_AC97_CODEC
- depends on DW_DMAC && AVR32
+ depends on (DW_DMAC && AVR32) || ARCH_AT91
help
ALSA sound driver for the Atmel AC97 controller.
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 0c0f877..379d7ea 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -31,6 +32,10 @@
#include <linux/dw_dmac.h>
+#include <mach/cpu.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
#include "ac97c.h"
enum {
@@ -63,6 +68,7 @@ struct atmel_ac97c {
u64 cur_format;
unsigned int cur_rate;
unsigned long flags;
+ int period;
/* Serialize access to opened variable */
spinlock_t lock;
void __iomem *regs;
@@ -167,6 +173,7 @@ static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
mutex_lock(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
+ chip->period = 0;
if (chip->cur_rate) {
runtime->hw.rate_min = chip->cur_rate;
runtime->hw.rate_max = chip->cur_rate;
@@ -186,6 +193,7 @@ static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
mutex_lock(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
+ chip->period = 0;
if (chip->cur_rate) {
runtime->hw.rate_min = chip->cur_rate;
runtime->hw.rate_max = chip->cur_rate;
@@ -239,12 +247,14 @@ static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
retval = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.tx_chan);
+ if(cpu_is_at32ap7000()) {
+ if (retval < 0)
+ return retval;
+ /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+ if (retval == 1)
+ if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.tx_chan);
+ }
/* Set restrictions to params. */
mutex_lock(&opened_mutex);
@@ -263,12 +273,14 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
retval = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
+ if(cpu_is_at32ap7000()) {
+ if (retval < 0)
+ return retval;
+ /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+ if (retval == 1)
+ if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.rx_chan);
+ }
/* Set restrictions to params. */
mutex_lock(&opened_mutex);
@@ -282,16 +294,20 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.tx_chan);
+ if(cpu_is_at32ap7000()) {
+ if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.tx_chan);
+ }
return snd_pcm_lib_free_pages(substream);
}
static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
+ if(cpu_is_at32ap7000()) {
+ if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.rx_chan);
+ }
return snd_pcm_lib_free_pages(substream);
}
@@ -299,6 +315,7 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ int block_size = frames_to_bytes(runtime, runtime->period_size);
unsigned long word = ac97c_readl(chip, OCA);
int retval;
@@ -324,7 +341,9 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
- word |= AC97C_CMR_CEM_LITTLE;
+ if(cpu_is_at32ap7000()) {
+ word |= AC97C_CMR_CEM_LITTLE;
+ }
break;
case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
word &= ~(AC97C_CMR_CEM_LITTLE);
@@ -363,9 +382,18 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
runtime->rate);
- if (!test_bit(DMA_TX_READY, &chip->flags))
- retval = atmel_ac97c_prepare_dma(chip, substream,
- DMA_TO_DEVICE);
+ if(cpu_is_at32ap7000()) {
+ if (!test_bit(DMA_TX_READY, &chip->flags))
+ retval = atmel_ac97c_prepare_dma(chip, substream,
+ DMA_TO_DEVICE);
+ } else {
+ /* Initialize and start the PDC */
+ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
+ writel(runtime->dma_addr + block_size,
+ chip->regs + ATMEL_PDC_TNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
+ }
return retval;
}
@@ -374,6 +402,7 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ int block_size = frames_to_bytes(runtime, runtime->period_size);
unsigned long word = ac97c_readl(chip, ICA);
int retval;
@@ -415,11 +444,15 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
word |= AC97C_CSR_OVRUN;
ac97c_writel(chip, CAMR, word);
-
/* Enable channel A event interrupt */
word = ac97c_readl(chip, IMR);
word |= AC97C_SR_CAEVT;
- ac97c_writel(chip, IER, word);
+ ac97c_writel(chip, IER, /*word*/AC97C_SR_CAEVT);
+
+ /* Enable channel A event interrupt */
+ /*word = ac97c_readl(chip, IMR);
+ word |= AC97C_SR_CAEVT;
+ ac97c_writel(chip, IER, word);*/
/* set variable rate if needed */
if (runtime->rate != 48000) {
@@ -438,9 +471,18 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
runtime->rate);
- if (!test_bit(DMA_RX_READY, &chip->flags))
- retval = atmel_ac97c_prepare_dma(chip, substream,
- DMA_FROM_DEVICE);
+ if(cpu_is_at32ap7000()) {
+ if (!test_bit(DMA_RX_READY, &chip->flags))
+ retval = atmel_ac97c_prepare_dma(chip, substream,
+ DMA_FROM_DEVICE);
+ } else {
+ /* Initialize and start the PDC */
+ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
+ writel(runtime->dma_addr + block_size,
+ chip->regs + ATMEL_PDC_RNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
+ }
return retval;
}
@@ -449,7 +491,7 @@ static int
atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- unsigned long camr;
+ unsigned long camr, ptcr = 0;
int retval = 0;
camr = ac97c_readl(chip, CAMR);
@@ -458,15 +500,23 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
case SNDRV_PCM_TRIGGER_START:
- retval = dw_dma_cyclic_start(chip->dma.tx_chan);
- if (retval)
- goto out;
- camr |= AC97C_CMR_CENA;
+ if(cpu_is_at32ap7000()) {
+ retval = dw_dma_cyclic_start(chip->dma.tx_chan);
+ if (retval)
+ goto out;
+ } else {
+ ptcr = ATMEL_PDC_TXTEN;
+ }
+ camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
case SNDRV_PCM_TRIGGER_STOP:
- dw_dma_cyclic_stop(chip->dma.tx_chan);
+ if(cpu_is_at32ap7000()) {
+ dw_dma_cyclic_stop(chip->dma.tx_chan);
+ } else {
+ ptcr = ATMEL_PDC_TXTDIS;
+ }
if (chip->opened <= 1)
camr &= ~AC97C_CMR_CENA;
break;
@@ -476,6 +526,8 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
}
ac97c_writel(chip, CAMR, camr);
+ if(!cpu_is_at32ap7000())
+ writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
out:
return retval;
}
@@ -484,7 +536,7 @@ static int
atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- unsigned long camr;
+ unsigned long camr, ptcr = 0;
int retval = 0;
camr = ac97c_readl(chip, CAMR);
@@ -493,15 +545,24 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
case SNDRV_PCM_TRIGGER_START:
- retval = dw_dma_cyclic_start(chip->dma.rx_chan);
- if (retval)
- goto out;
+ if(cpu_is_at32ap7000()) {
+ retval = dw_dma_cyclic_start(chip->dma.rx_chan);
+ if (retval)
+ goto out;
+ } else {
+ ptcr = ATMEL_PDC_RXTEN;
+ }
camr |= AC97C_CMR_CENA;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
case SNDRV_PCM_TRIGGER_STOP:
- dw_dma_cyclic_stop(chip->dma.rx_chan);
+ if(cpu_is_at32ap7000()) {
+ dw_dma_cyclic_stop(chip->dma.rx_chan);
+ }
+ else {
+ ptcr = ATMEL_PDC_RXTDIS;
+ }
if (chip->opened <= 1)
camr &= ~AC97C_CMR_CENA;
break;
@@ -511,6 +572,8 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
}
ac97c_writel(chip, CAMR, camr);
+ if(!cpu_is_at32ap7000())
+ writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
out:
return retval;
}
@@ -523,7 +586,11 @@ atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frames;
unsigned long bytes;
- bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
+ if(cpu_is_at32ap7000()) {
+ bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
+ } else {
+ bytes = readl(chip->regs + ATMEL_PDC_TPR);
+ }
bytes -= runtime->dma_addr;
frames = bytes_to_frames(runtime, bytes);
@@ -540,7 +607,11 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frames;
unsigned long bytes;
- bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
+ if(cpu_is_at32ap7000()) {
+ bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
+ } else {
+ bytes = readl(chip->regs + ATMEL_PDC_RPR);
+ }
bytes -= runtime->dma_addr;
frames = bytes_to_frames(runtime, bytes);
@@ -578,20 +649,66 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
u32 sr = ac97c_readl(chip, SR);
u32 casr = ac97c_readl(chip, CASR);
u32 cosr = ac97c_readl(chip, COSR);
+ u32 camr = ac97c_readl(chip, CAMR);
if (sr & AC97C_SR_CAEVT) {
- dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
+ struct snd_pcm_runtime *runtime;
+ int offset, next_period, block_size;
+ dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
casr & AC97C_CSR_OVRUN ? " OVRUN" : "",
casr & AC97C_CSR_RXRDY ? " RXRDY" : "",
casr & AC97C_CSR_UNRUN ? " UNRUN" : "",
casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
casr & AC97C_CSR_TXRDY ? " TXRDY" : "",
!casr ? " NONE" : "");
+ if(!cpu_is_at32ap7000()) {
+ if ((casr & camr) & AC97C_CSR_ENDTX) {
+ runtime = chip->playback_substream->runtime;
+ block_size = frames_to_bytes(runtime,
+ runtime->period_size);
+ chip->period++;
+
+ if (chip->period == runtime->periods)
+ chip->period = 0;
+ next_period = chip->period + 1;
+ if (next_period == runtime->periods)
+ next_period = 0;
+
+ offset = block_size * next_period;
+
+ writel(runtime->dma_addr + offset,
+ chip->regs + ATMEL_PDC_TNPR);
+ writel(block_size / 2,
+ chip->regs + ATMEL_PDC_TNCR);
+
+ snd_pcm_period_elapsed(chip->playback_substream);
+ }
+ if ((casr & camr) & AC97C_CSR_ENDRX) {
+ runtime = chip->capture_substream->runtime;
+ block_size = frames_to_bytes(runtime,
+ runtime->period_size);
+ chip->period++;
+
+ if (chip->period == runtime->periods)
+ chip->period = 0;
+ next_period = chip->period + 1;
+ if (next_period == runtime->periods)
+ next_period = 0;
+
+ offset = block_size * next_period;
+
+ writel(runtime->dma_addr + offset,
+ chip->regs + ATMEL_PDC_RNPR);
+ writel(block_size / 2,
+ chip->regs + ATMEL_PDC_RNCR);
+ snd_pcm_period_elapsed(chip->capture_substream);
+ }
+ }
retval = IRQ_HANDLED;
}
if (sr & AC97C_SR_COEVT) {
- dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
+ dev_dbg(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
cosr & AC97C_CSR_OVRUN ? " OVRUN" : "",
cosr & AC97C_CSR_RXRDY ? " RXRDY" : "",
cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
@@ -608,15 +725,50 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
return retval;
}
+static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
+ /* Playback */
+ {
+ .exclusive = 1,
+ .r = { {
+ .slots = ((1 << AC97_SLOT_PCM_LEFT)
+ | (1 << AC97_SLOT_PCM_RIGHT)),
+ } },
+ },
+ /* PCM in */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = ((1 << AC97_SLOT_PCM_LEFT)
+ | (1 << AC97_SLOT_PCM_RIGHT)),
+ } }
+ },
+ /* Mic in */
+ {
+ .stream = 1,
+ .exclusive = 1,
+ .r = { {
+ .slots = (1<<AC97_SLOT_MIC),
+ } }
+ },
+};
+
static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
{
struct snd_pcm *pcm;
struct snd_pcm_hardware hw = atmel_ac97c_hw;
- int capture, playback, retval;
+ int capture, playback, retval, err;
capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ if(!cpu_is_at32ap7000()) {
+ err = snd_ac97_pcm_assign(chip->ac97_bus,
+ ARRAY_SIZE(at91_ac97_pcm_defs),
+ at91_ac97_pcm_defs);
+ if (err)
+ return err;
+ }
retval = snd_pcm_new(chip->card, chip->card->shortname,
chip->pdev->id, playback, capture, &pcm);
if (retval)
@@ -775,7 +927,12 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
return -ENXIO;
}
- pclk = clk_get(&pdev->dev, "pclk");
+ if(cpu_is_at32ap7000()) {
+ pclk = clk_get(&pdev->dev, "pclk");
+ } else {
+ pclk = clk_get(&pdev->dev, "ac97_clk");
+ }
+
if (IS_ERR(pclk)) {
dev_dbg(&pdev->dev, "no peripheral clock\n");
return PTR_ERR(pclk);
@@ -844,44 +1001,51 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
goto err_ac97_bus;
}
- if (pdata->rx_dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->rx_dws;
- dma_cap_mask_t mask;
+ if(cpu_is_at32ap7000()) {
+ if (pdata->rx_dws.dma_dev) {
+ struct dw_dma_slave *dws = &pdata->rx_dws;
+ dma_cap_mask_t mask;
- dws->rx_reg = regs->start + AC97C_CARHR + 2;
+ dws->rx_reg = regs->start + AC97C_CARHR + 2;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
- chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
+ chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
- dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
+ dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
dev_name(&chip->dma.rx_chan->dev->device));
- set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- }
+ set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ }
- if (pdata->tx_dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->tx_dws;
- dma_cap_mask_t mask;
+ if (pdata->tx_dws.dma_dev) {
+ struct dw_dma_slave *dws = &pdata->tx_dws;
+ dma_cap_mask_t mask;
- dws->tx_reg = regs->start + AC97C_CATHR + 2;
+ dws->tx_reg = regs->start + AC97C_CATHR + 2;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
- chip->dma.tx_chan = dma_request_channel(mask, filter, dws);
+ chip->dma.tx_chan = dma_request_channel(mask, filter, dws);
- dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
- dev_name(&chip->dma.tx_chan->dev->device));
- set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- }
+ dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
+ dev_name(&chip->dma.tx_chan->dev->device));
+ set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ }
- if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
- !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
- dev_dbg(&pdev->dev, "DMA not available\n");
- retval = -ENODEV;
- goto err_dma;
- }
+ if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
+ !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
+ dev_dbg(&pdev->dev, "DMA not available\n");
+ retval = -ENODEV;
+ goto err_dma;
+ }
+ } else {
+ /* Just pretend that we have DMA channel(for at91 i is actually
+ * the PDC */
+ set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ }
retval = atmel_ac97c_pcm_new(chip);
if (retval) {
@@ -897,20 +1061,22 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, card);
- dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p\n",
- chip->regs);
+ dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n",
+ chip->regs, irq);
return 0;
err_dma:
- if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.rx_chan);
- if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.tx_chan);
- clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- chip->dma.rx_chan = NULL;
- chip->dma.tx_chan = NULL;
+ if(cpu_is_at32ap7000()) {
+ if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.tx_chan);
+ clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ chip->dma.rx_chan = NULL;
+ chip->dma.tx_chan = NULL;
+ }
err_ac97_bus:
snd_card_set_dev(card, NULL);
@@ -934,12 +1100,13 @@ static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
struct snd_card *card = platform_get_drvdata(pdev);
struct atmel_ac97c *chip = card->private_data;
- if (test_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_stop(chip->dma.rx_chan);
- if (test_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_stop(chip->dma.tx_chan);
+ if(cpu_is_at32ap7000()) {
+ if (test_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_stop(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_stop(chip->dma.tx_chan);
+ }
clk_disable(chip->pclk);
-
return 0;
}
@@ -949,11 +1116,12 @@ static int atmel_ac97c_resume(struct platform_device *pdev)
struct atmel_ac97c *chip = card->private_data;
clk_enable(chip->pclk);
- if (test_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_start(chip->dma.rx_chan);
- if (test_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_start(chip->dma.tx_chan);
-
+ if(cpu_is_at32ap7000()) {
+ if (test_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_start(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_READY, &chip->flags))
+ dw_dma_cyclic_start(chip->dma.tx_chan);
+ }
return 0;
}
#else
@@ -978,14 +1146,16 @@ static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
iounmap(chip->regs);
free_irq(chip->irq, chip);
- if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.rx_chan);
- if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.tx_chan);
- clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- chip->dma.rx_chan = NULL;
- chip->dma.tx_chan = NULL;
+ if(cpu_is_at32ap7000()) {
+ if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.rx_chan);
+ if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
+ dma_release_channel(chip->dma.tx_chan);
+ clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+ clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ chip->dma.rx_chan = NULL;
+ chip->dma.tx_chan = NULL;
+ }
snd_card_set_dev(card, NULL);
snd_card_free(card);
--
1.5.3.7
2
9
04 Sep '09
Hello,
Is there a way to reduce latency on audiocard if the dmix plugin is in
use?
I am asking because there is a strange problem with asistive
technologies which are outputing through alsa.
E. G. speech-dispatcher takes almost a half sec to stop the
speech-synthesizer.
If the user navigates quickly through menus, there are simultane outputs
at a time.
(tested with an usb logitech headset, snd-intel8x0 based card,
snd-hda-intel).
The same problem can be reproduced by running mplayer with alsa output
driver and pressing
quickly left/right arrow.
You should hear several samples at a time.
Using an sblive card which doesn't use dmix doesn't have such problems.
Using the dmix plugin should audio much faster to avoid this problem.
Please help!!!
Regards
Halim
2
8
[alsa-devel] [PATCH v2 1/2] Davinci: DM365: Add platform device for McBSP
by miguel.aguilar@ridgerun.com 02 Sep '09
by miguel.aguilar@ridgerun.com 02 Sep '09
02 Sep '09
From: Miguel Aguilar <miguel.aguilar(a)ridgerun.com>
1) Registers the platform device for McBSP on dm365.
2) Add platform data to DM365 EVM board file.
Signed-off-by: Miguel Aguilar <miguel.aguilar(a)ridgerun.com>
---
arch/arm/mach-davinci/board-dm365-evm.c | 4 ++
arch/arm/mach-davinci/dm365.c | 45 +++++++++++++++++++++++++++-
arch/arm/mach-davinci/include/mach/asp.h | 3 ++
arch/arm/mach-davinci/include/mach/dm365.h | 2 +
4 files changed, 53 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c
index f6adf79..fd2db78 100644
--- a/arch/arm/mach-davinci/board-dm365-evm.c
+++ b/arch/arm/mach-davinci/board-dm365-evm.c
@@ -177,6 +177,8 @@ static struct at24_platform_data eeprom_info = {
.context = (void *)0x7f00,
};
+static struct snd_platform_data dm365_evm_snd_data;
+
static struct i2c_board_info i2c_info[] = {
{
I2C_BOARD_INFO("dm365evm_keys", 0x25),
@@ -476,6 +478,8 @@ static __init void dm365_evm_init(void)
/* maybe setup mmc1/etc ... _after_ mmc0 */
evm_init_cpld();
+
+ dm365_init_asp(&dm365_evm_snd_data);
}
static __init void dm365_evm_irq_init(void)
diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index e815174..c8bff14 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -32,6 +32,7 @@
#include <mach/time.h>
#include <mach/serial.h>
#include <mach/common.h>
+#include <mach/asp.h>
#include "clock.h"
#include "mux.h"
@@ -456,7 +457,7 @@ static struct davinci_clk dm365_clks[] = {
CLK(NULL, "usb", &usb_clk),
CLK("davinci_emac.1", NULL, &emac_clk),
CLK("voice_codec", NULL, &voicecodec_clk),
- CLK("soc-audio.0", NULL, &asp0_clk),
+ CLK("davinci-asp.0", NULL, &asp0_clk),
CLK(NULL, "rto", &rto_clk),
CLK(NULL, "mjcp", &mjcp_clk),
CLK(NULL, NULL, NULL),
@@ -603,6 +604,9 @@ INT_CFG(DM365, INT_IMX1_ENABLE, 24, 1, 1, false)
INT_CFG(DM365, INT_IMX1_DISABLE, 24, 1, 0, false)
INT_CFG(DM365, INT_NSF_ENABLE, 25, 1, 1, false)
INT_CFG(DM365, INT_NSF_DISABLE, 25, 1, 0, false)
+
+EVT_CFG(DM365, EVT2_ASP_TX, 0, 1, 0, false)
+EVT_CFG(DM365, EVT3_ASP_RX, 1, 1, 0, false)
#endif
};
@@ -806,6 +810,31 @@ static struct platform_device dm365_edma_device = {
.resource = edma_resources,
};
+static struct resource dm365_asp_resources[] = {
+ {
+ .start = DAVINCI_DM365_ASP0_BASE,
+ .end = DAVINCI_DM365_ASP0_BASE + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = DAVINCI_DMA_ASP0_TX,
+ .end = DAVINCI_DMA_ASP0_TX,
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ .start = DAVINCI_DMA_ASP0_RX,
+ .end = DAVINCI_DMA_ASP0_RX,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct platform_device dm365_asp_device = {
+ .name = "davinci-asp",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(dm365_asp_resources),
+ .resource = dm365_asp_resources,
+};
+
static struct map_desc dm365_io_desc[] = {
{
.virtual = IO_VIRT,
@@ -907,6 +936,20 @@ static struct davinci_soc_info davinci_soc_info_dm365 = {
.sram_len = SZ_32K,
};
+void __init dm365_init_asp(struct snd_platform_data *pdata)
+{
+ davinci_cfg_reg(DM365_MCBSP0_BDX);
+ davinci_cfg_reg(DM365_MCBSP0_X);
+ davinci_cfg_reg(DM365_MCBSP0_BFSX);
+ davinci_cfg_reg(DM365_MCBSP0_BDR);
+ davinci_cfg_reg(DM365_MCBSP0_R);
+ davinci_cfg_reg(DM365_MCBSP0_BFSR);
+ davinci_cfg_reg(DM365_EVT2_ASP_TX);
+ davinci_cfg_reg(DM365_EVT3_ASP_RX);
+ dm365_asp_device.dev.platform_data = pdata;
+ platform_device_register(&dm365_asp_device);
+}
+
void __init dm365_init(void)
{
davinci_common_init(&davinci_soc_info_dm365);
diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h
index 18e4ce3..fbcbed0 100644
--- a/arch/arm/mach-davinci/include/mach/asp.h
+++ b/arch/arm/mach-davinci/include/mach/asp.h
@@ -15,6 +15,9 @@
#define DAVINCI_DM646X_MCASP0_REG_BASE 0x01D01000
#define DAVINCI_DM646X_MCASP1_REG_BASE 0x01D01800
+/* Bases of dm365 register banks */
+#define DAVINCI_DM365_ASP0_BASE 0x01D02000
+
/* Bases of da850/da830 McASP0 register banks */
#define DAVINCI_DA8XX_MCASP0_REG_BASE 0x01D00000
diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h
index 09db434..2291c0d 100644
--- a/arch/arm/mach-davinci/include/mach/dm365.h
+++ b/arch/arm/mach-davinci/include/mach/dm365.h
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/emac.h>
+#include <mach/asp.h>
#define DM365_EMAC_BASE (0x01D07000)
#define DM365_EMAC_CNTRL_OFFSET (0x0000)
@@ -25,5 +26,6 @@
#define DM365_EMAC_CNTRL_RAM_SIZE (0x2000)
void __init dm365_init(void);
+void __init dm365_init_asp(struct snd_platform_data *pdata);
#endif /* __ASM_ARCH_DM365_H */
--
1.6.0.4
4
5
On Wednesday 05 August 2009 23:21:53 Christian Esken wrote:
> > All in all, I can produce a working environment with recent 32 and 64 bit
> > kernels. This is good news. :-)
> >
> > Below is the summary on the tested distributions.
> >
> > Christian
> >
> >
> > So we got this:
> > Kubuntu Karmic Alpha-2 32 Bit, 2.6.30 (works)
> > Kubuntu Karmic Alpha-2 64 Bit, 2.6.30 (works)
> > SuSE 11.1 64 Bit, 2.6.27 (does not work)
> > SuSE 11.1 64 Bit, 2.6.27 XEN (does not work)
> > SuSE 11.2 Milestone3 64Bit, 2.6.30 (test canceled, due to unrelated
> > problems) => I'll see whether I can redo the SuSE11.2 tests, after doing
> > a bugreport at openSuSE.
OK, that last issue was solved, and I now redid the test:
SuSE 11.2 Milestone6 64Bit, 2.6.31-rc6 (works)
I succesfully tested all analog (5.1) channels, using: speaker-test -D
surround51 -c 6
I used ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/snapshot/alsa-
driver-snapshot.tar.bz2 from 24.08.2009. I am just wondering, whether the new
ALSA driver is it still planned for the 2.6.31 kernel?!?
Christian
3
4