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
July 2009
- 135 participants
- 252 discussions
24 Jun '11
Hello!
The background: I'm just designing an ATMEL AT91RM9200 based board which
got two TLV320AIC33 on board. It is either possible
to hook up the two TLV320AIC33 to a separate SSC (synchronous serial
control) channel each or use one SSC channel and configure it for TDM (time
division multiplex) mode. There is already a driver for
TLV320AIC14k/TLV320AIC2k in the development tree, so adding support for the
TLV320AIC33 should be feasible.
What I'm not sure about is the fact if ASOC is prepared for using channels,
especially SSC, in TDM mode. Is the core ASOC structure prepared to handle
that?
Would we be better off instantiate one drive twice each time with different
parameters for the SSC channel?
Thanks for any feedback.
--
MfG / Regards
Friedrich Lobenstock
_________________________________________________________
SCOTTY Group Austria GmbH
a SCOTTY Group plc company, http://www.scottygroup.com/
_________________________________________________________
4
3
Is the ADAT output of the IDT (sigmatel) STAC927x HDA codec supported in
alsa ?
If not could someone please try to add this incredibly nice feature in
the existing alsa driver ?
Thank you very much
4
13
This patch set is against git://git.alsa-project.org/alsa-kernel.git
but the last patch in series won't compile until the sram allocator patch
is available.
8
44
Hello
> That is more or less what I had figured out, but it how are playback_1 to
> > _16 channels routed to DSP0-15? It seems that it depends on the setting
> of
> > Front, Surround, LFE, etc. mixer controls. If they are turned up, I
> don't
> > understand what happens, sound to playback channels appears on various
> DSP
> > buses, at different levels.
>
> Really? Well, I didn't much experiment with surround mode. In source code,
> I
> can see that there are up to playback 8 channels at hw:0,0 with can be
> controlled by faders in alsamixer. I tried surround40 and surroun51 on my
> emu1616m and it worked as expected. The channles were mapped to first
> couple
> of DSP ports in a correct order. In fact this should work the same as
> audigy
> does, except the result is passed to DSP ports instead of physical
> outputs.
>
On my card, playback channels 1 & 2 (hw:0,3) appear on all DSP busses
associated with surround.
Say you turn up the "Side" fader in alsamixer, channels 1 & 2 will appear at
the relevant DSPs (6-7, I think)
Whatever, for now I am just keeping all surround controls down (that is,
Front, Surround, Center, Side & LFE).
> Anyway, I think if there is another layer of routing done in audigy DSP,
> it
> shouldn't be used in EMU specific application. Or at least not for now.
> The
> most important thing is to provide all the switches and FPGA routing
> scheme
> for each EMU model. Or, to be precise, three routing schemes for each EMU
> model, because for "double speed" (e.g. 96kHz) and "quadro speed" (e.g.
> 192kHz) modes of operation the numebr of both physical and virtual
> channels
> changes.
>
I think the switches and enums for the card models are rather trivial, one
can handle it with a customized version
of qamix (done with a simple XML file), for example. A separate application
would not be necessary for only that.
> > If I turn the Front/Surround/LFE/etc. faders down, I can mix playback
> > channels to DSP using the "Multichannel PCM Send/Routing/Volume" faders,
> in
> > a more or less intuitive way, besides that routing values do not map
> > directly to DSP buses (i.e. 14-15 map to DSP0-1, but 2-3 map to DSP2-3).
>
Handling of this controls motivated me to write the application in the first
place.
No sound at all will appear if I don't set the appropiate controls for each
playback channel: Multichannel PCM Volume (full), Multichannel PCM Send (an
index of 8 controls, the first 4 select the target DSP) & Multichannel PCM
Send Volume (again faders 0-3 of 8)
By the way, only DSP0-7 work in this way. I have no idea how to make DSP8-31
work.
On which card you did tested it? The driver was primary written for the
> 1820M/1212M V1, so some things may not work well on other models and need
> to
> be fixed.
I have a 1212m, don't know which version, but I think it's V1-- it does have
firewire.
I am using driver version 1.0.16.
Thanks,
Camilo.
3
3
16 Jan '10
Hi Guys,
Recently I was investigating an issue with capturing audio using USB
Audio Class device on a sh4-based board. "Bad voice quality" was
reported...
Finally I have traced the problem to something which is (unfortunately)
well known to sh developers as a D-cache aliasing (or synonym) problem.
Briefly speaking: due to some MMU design decisions, one can have two
different virtual address pointing to the same physical location, which
is fine, but going via different cache slots! So if there was a value of
"0" in the memory and user "A" will write "1" there, user "B" will still
read "0"...
The solution is to ensure all TLB entries (so virtual memory areas) are
beginning from a 16kB-aligned virtual address. Otherwise it is necessary
to flush the cache between accesses from "A" and "B" sides.
And now. The USB Audio Class driver (sound/usb/usbaudio.c) is allocating
the sound buffer like this...
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
{
[...]
runtime->dma_area = vmalloc(size);
[...]
}
... and vmalloc will return a page(4k)-aligned pointer, possibly not
16k-aligned one. This is the source of all evil ;-)
When using RW transfers everything is fine, as data is memcopied between
two independent buffers.
In a case of MMAP mode we will end up with such a situation:
1. The driver will memcpy data from an URB to the buffer. It will
populate several cache lines. Let's call it a "kernel cache".
2. As the library has mapped the non-16k-aligned address via different
cache line ("user cache"), it will read some rubbish from the physical
memory, populating the "user cache" by the way.
3. Some time later the "kernel cache" is flushed, writing the data to
the memory.
4. Some new data from URB is entering "kernel cache".
5. The library will access mmap-ed area again, going via the "user
cache", which may or may not reflect the correct data (depending on the
fact was the "user cache" flushed or not) etc.
Of course this cycle is completely not deterministic, so some of the
"kernel cache" lines will be flushed before being accessed from user
space, other not... The final effect is... hmm... bizarre :-) First, of
course, you will get a (probably loud) glitch (rubbish from buffer's
underlying memory, before the first valid data is written back), and
then something that could be described as an "echo" ;-) I mean - you are
capturing "1 2 3 4 5 6 7..." and the result is (G stands for Glitch ;-)
"G 1 23 3 4 4 6 67..."
As a quick-and-dirty work-around I have modified
snd_pcm_alloc_vmalloc_buffer() to allocate always 12kB more and then use
16k-aligned pointer:
static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
if (runtime->dma_addr) {
if (runtime->dma_bytes >= size)
return 0; /* already large enough */
vfree((void *)runtime->dma_addr);
}
runtime->dma_addr = (unsigned long)vmalloc(size + 12 * 1024);
if (!runtime->dma_addr)
return -ENOMEM;
runtime->dma_area = (void *)ALIGN(runtime->dma_addr, 16 * 1024);
runtime->dma_bytes = size;
return 0;
}
Of course it cannot be regarded as anything more as a hack. And
definitely not as a solution... So my question is:
Any idea how the "proper solution" should look like? I see no obvious
method to get an arch-independent "mmap-compliant" buffer. This problem
was found on a sh arch, using an USB Audio Class device, however out
there some other architectures are suffering from this MMU "feature" as
well (ie. see http://docs.hp.com/en/B3906-90006/ch07s09.html) and
possibly other drivers could behave "wrongly" (quoted as the driver is
actually innocent...)
To be honest I just have absolutely no idea what to do with this
all! :-O
I hope I was clear enough in the description... Any feedback, advise,
idea etc. will be more than appreciated.
Cheers
Paweł
3
11
Hi,
I'm planning to build a linux-based, small sized PC for hearing aid
acousticians.
It would be highly unfavorable, if the connection between Receiver and
PC was
analog, because of the lower quality and the high number of cables.
I searched for a appropriate mainboard, and found the DG45FC, which
fits my needs
perfectly, apart from the missing audio over HDMI support.
Is there any plan to support multichannel LPCM output over HDMI on the
Intel G45
platform?
If not, is there anyone willing to implement HDMI support for the G45
chipset if
I donate the needed hardware (permanently)?
But if there's no chance to implement it in the near future, has
anyone experience
with an onboard chipset with multichannel LPCM over HDMI and ALSA
support?
Thanks,
Alexander Werner
7
10
Am Sonntag 12 Juli 2009 10:24:35 schrieb Christian Esken:
> Am Montag 06 Juli 2009 08:26:11 schrieb Takashi Iwai:
> > At Sun, 5 Jul 2009 13:29:48 +0200,
> >
> > Christian Esken wrote:
> > > Am Sonntag 05 Juli 2009 09:44:33 schrieb Takashi Iwai:
> > > > At Sun, 5 Jul 2009 00:19:55 +0200,
> > > >
> > > > Christian Esken wrote:
> > > > > Am Samstag 27 Juni 2009 14:05:45 schrieb Christian Esken:
> > > > > > Hello,
> > > > > >
> > > > > > unfortunately I cannot playback anything (PCM) with the new SB
> > > > > > X-Fi driver. Driver:
> > > > > > ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/snapshot/alsa-
> > > > > > driver-snapshot.tar.bz2
> > > > > > Distribution: openSuSE 11.1
> > > > > > Kernel: 2.6.27.23-0.1-xen
> > > > > >
> > > > > > My card is a "X-Fi Titanium" (PCI device code: 1102:000b). The
> > > > > > card is recognized by the driver, and any mixer (alsamixer, kmix)
> > > > > > also recognizes the card.
> > > > > >
> > > > > > But playing back isn't possible. I tried speaker-test, audacity,
> > > > > > amarok and aplay. Most applications hang, some also produce an
> > > > > > error in the /var/log/messages (see
> > > > > > below).
> > > > > >
> > > > > > I then used "./configure --with-debug=full --with-cards=ctxfi"
> > > > > > due to the possible hd-audio conflict, but it didn't make a
> > > > > > difference.
> > > > > >
> > > > > > Is there anything else I could try?
> > > > >
> > > > > I tested the same openSuSE version with a non-Xen 64 bit kernel,
> > > > > and it doesn't work either.
> > > > >
> > > > > And I tried with this:
> > > > > Driver: alsa-driver-20090704.tar.bz2
> > > > > Distribution: Kubuntu Karmic Alpha-2
> > > > > Kernel: 2.6.30 (32 Bit)
> > > > >
> > > > > Under the latter setup the X-Fi Titanium works (Simple 2 channel
> > > > > test with speaker-test, audacity, amarok). :-))
> > > >
> > > > How many RAM do you have?
> > > > Could you boot with the limited memory size?
> > >
> > > It is just 2GB. So I am not limited by the 32 bit barrier.
> > >
> > > So booting with "limited" memory size doesn't make sense, right?
> >
> > Supposedly. Is it emu20k1 or emu20k2?
>
> I checked it again: It is a 20k2 chip.
>
> /var/log/boot.msg says:
> <7>ALSA /home/chris/Desktop/alsa-driver/pci/ctxfi/ctatc.c:1268: ctxfi: chip
> 20K2 model SB0880 (1102:0041) is found
>
>
> http://pci-ids.ucw.cz/mods/PC/1102/000b shows 1102:0041 as "X-Fi Titanium
> series [EMU20k2]".
I did now test it with a different 64 Bit Linux distribution, and it works
correctly:
Driver: alsa-driver-20090712.tar.bz2
Distribution: Kubuntu Karmic Alpha-2
Kernel: 2.6.30 (64 Bit)
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.0.30 (works)
Kubuntu Karmic Alpha-2 64 Bit, 2.0.30 (works)
SuSE 11.1 64 Bit, 2.0.27 (does not work)
SuSE 11.1 64 Bit, 2.0.27 XEN (does not work)
SuSE 11.2 Milestone3 64Bit, 2.0.30 (test canceled, due to unrelated problems)
=> I'll see whether I can redo the SuSE11.2 tests, after doing a bugreport
at openSuSE.
3
6
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
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