Alsa-devel
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 8 participants
- 51270 discussions

[alsa-devel] [PATCH 3/6] pulse: get rid of redundant state variable
by Lennart Poettering 05 Aug '09
by Lennart Poettering 05 Aug '09
05 Aug '09
snd_pulse_t::state was mostly shadowing the state of
pa_context_get_state(snd_pulse_t::context), so get rid of it and use the
state of the context directly.
---
pulse/pulse.c | 14 +++-----------
pulse/pulse.h | 6 ------
2 files changed, 3 insertions(+), 17 deletions(-)
diff --git a/pulse/pulse.c b/pulse/pulse.c
index c313182..9c05171 100644
--- a/pulse/pulse.c
+++ b/pulse/pulse.c
@@ -59,12 +59,6 @@ int pulse_wait_operation(snd_pulse_t * p, pa_operation * o)
assert(p);
assert(o);
- if (p->state != PULSE_STATE_READY)
- return -EBADFD;
-
- if (!p->mainloop)
- return -EBADFD;
-
for (;;) {
int err;
@@ -124,8 +118,6 @@ snd_pulse_t *pulse_new(void)
if (!p)
return NULL;
- p->state = PULSE_STATE_INIT;
-
if (pipe(fd)) {
free(p);
return NULL;
@@ -192,13 +184,15 @@ void pulse_free(snd_pulse_t * p)
int pulse_connect(snd_pulse_t * p, const char *server)
{
int err;
+ pa_context_state_t state;
assert(p);
if (!p->context || !p->mainloop)
return -EBADFD;
- if (p->state != PULSE_STATE_INIT)
+ state = pa_context_get_state(p->context);
+ if (state != PA_CONTEXT_UNCONNECTED)
return -EBADFD;
pa_threaded_mainloop_lock(p->mainloop);
@@ -221,8 +215,6 @@ int pulse_connect(snd_pulse_t * p, const char *server)
pa_threaded_mainloop_unlock(p->mainloop);
- p->state = PULSE_STATE_READY;
-
return 0;
error:
diff --git a/pulse/pulse.h b/pulse/pulse.h
index 51f9a11..e98124f 100644
--- a/pulse/pulse.h
+++ b/pulse/pulse.h
@@ -31,12 +31,6 @@ typedef struct snd_pulse {
pa_context *context;
int thread_fd, main_fd;
-
- enum {
- PULSE_STATE_INIT,
- PULSE_STATE_READY,
- } state;
-
} snd_pulse_t;
int pulse_check_connection(snd_pulse_t * p);
--
1.6.4
Lennart
--
Lennart Poettering Red Hat, Inc.
lennart [at] poettering [dot] net ICQ# 11060553
http://0pointer.net/lennart/ GnuPG 0x1A015CC4
1
0

[alsa-devel] [PATCH 2/6] pulse: move a couple of PCM related functions from pulse.c to pcm_pulse.c
by Lennart Poettering 05 Aug '09
by Lennart Poettering 05 Aug '09
05 Aug '09
A number of functions in pulse.c are only relevant for the PCM handling,
so let's move them to pcm_pulse.c. This allows us to simplify their
argument lists a bit.
---
pulse/pcm_pulse.c | 118 ++++++++++++++++++++++++++++++++++++++++++----------
pulse/pulse.c | 55 -------------------------
pulse/pulse.h | 4 --
3 files changed, 95 insertions(+), 82 deletions(-)
diff --git a/pulse/pcm_pulse.c b/pulse/pcm_pulse.c
index 24347f9..a625f55 100644
--- a/pulse/pcm_pulse.c
+++ b/pulse/pcm_pulse.c
@@ -49,6 +49,32 @@ typedef struct snd_pcm_pulse {
pa_buffer_attr buffer_attr;
} snd_pcm_pulse_t;
+static int check_stream(snd_pcm_pulse_t *pcm)
+{
+ int err;
+ pa_stream_state_t state;
+
+ assert(pcm);
+
+ if (!pcm->p)
+ return -EBADFD;
+
+ err = pulse_check_connection(pcm->p);
+ if (err < 0)
+ return err;
+
+ if (!pcm->stream)
+ return -EBADFD;
+
+ state = pa_stream_get_state(pcm->stream);
+ if (!PA_STREAM_IS_GOOD(state))
+ return -EIO;
+
+ err = 0;
+
+ return err;
+}
+
static int update_ptr(snd_pcm_pulse_t *pcm)
{
size_t size;
@@ -118,9 +144,53 @@ static int update_active(snd_pcm_pulse_t *pcm) {
else
pulse_poll_deactivate(pcm->p);
+ return ret;
+}
+
+static int wait_stream_state(snd_pcm_pulse_t *pcm, pa_stream_state_t target)
+{
+ pa_stream_state_t state;
+
+ assert(pcm);
+
+ if (!pcm->p)
+ return -EBADFD;
+
+ for (;;) {
+ int err;
+
+ err = pulse_check_connection(pcm->p);
+ if (err < 0)
+ return err;
+
+ if (!pcm->stream)
+ return -EBADFD;
+
+ state = pa_stream_get_state(pcm->stream);
+ if (state == target)
+ break;
+
+ if (!PA_STREAM_IS_GOOD(state))
+ return -EIO;
+
+ pa_threaded_mainloop_wait(pcm->p->mainloop);
+ }
+
return 0;
}
+static void stream_success_cb(pa_stream * p, int success, void *userdata)
+{
+ snd_pcm_pulse_t *pcm = userdata;
+
+ assert(pcm);
+
+ if (!pcm->p)
+ return;
+
+ pa_threaded_mainloop_signal(pcm->p->mainloop, 0);
+}
+
static int pulse_start(snd_pcm_ioplug_t * io)
{
snd_pcm_pulse_t *pcm = io->private_data;
@@ -138,18 +208,13 @@ static int pulse_start(snd_pcm_ioplug_t * io)
if (err < 0)
goto finish;
- if (pcm->stream == NULL)
- goto finish;
-
- o = pa_stream_cork(pcm->stream, 0, pulse_stream_success_cb,
- pcm->p);
+ o = pa_stream_cork(pcm->stream, 0, stream_success_cb, pcm);
if (!o) {
err = -EIO;
goto finish;
}
- u = pa_stream_trigger(pcm->stream, pulse_stream_success_cb,
- pcm->p);
+ u = pa_stream_trigger(pcm->stream, stream_success_cb, pcm);
pcm->underrun = 0;
err_o = pulse_wait_operation(pcm->p, o);
@@ -165,7 +230,6 @@ static int pulse_start(snd_pcm_ioplug_t * io)
goto finish;
}
-
finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
@@ -189,18 +253,13 @@ static int pulse_stop(snd_pcm_ioplug_t * io)
if (err < 0)
goto finish;
- if (pcm->stream == NULL)
- goto finish;
-
- o = pa_stream_cork(pcm->stream, 1, pulse_stream_success_cb,
- pcm->p);
+ o = pa_stream_cork(pcm->stream, 1, stream_success_cb, pcm);
if (!o) {
err = -EIO;
goto finish;
}
- u = pa_stream_flush(pcm->stream, pulse_stream_success_cb,
- pcm->p);
+ u = pa_stream_flush(pcm->stream, stream_success_cb, pcm);
if (!u) {
pa_operation_unref(o);
err = -EIO;
@@ -241,7 +300,7 @@ static int pulse_drain(snd_pcm_ioplug_t * io)
if (err < 0)
goto finish;
- o = pa_stream_drain(pcm->stream, pulse_stream_success_cb, pcm->p);
+ o = pa_stream_drain(pcm->stream, stream_success_cb, pcm);
if (!o) {
err = -EIO;
goto finish;
@@ -504,6 +563,23 @@ finish:
return ret;
}
+static void stream_state_cb(pa_stream * p, void *userdata)
+{
+ snd_pcm_pulse_t *pcm = userdata;
+ pa_stream_state_t state;
+
+ assert(pcm);
+
+ if (!pcm->p)
+ return;
+
+ state = pa_stream_get_state(p);
+ if (!PA_STREAM_IS_GOOD(state))
+ pulse_poll_activate(pcm->p);
+
+ pa_threaded_mainloop_signal(pcm->p->mainloop, 0);
+}
+
static void stream_request_cb(pa_stream * p, size_t length, void *userdata)
{
snd_pcm_pulse_t *pcm = userdata;
@@ -586,8 +662,7 @@ static int pulse_prepare(snd_pcm_ioplug_t * io)
if (pcm->stream) {
pa_stream_disconnect(pcm->stream);
- pulse_wait_stream_state(pcm->p, pcm->stream,
- PA_STREAM_TERMINATED);
+ wait_stream_state(pcm, PA_STREAM_TERMINATED);
pa_stream_unref(pcm->stream);
pcm->stream = NULL;
}
@@ -620,9 +695,7 @@ static int pulse_prepare(snd_pcm_ioplug_t * io)
goto finish;
}
- pa_stream_set_state_callback(pcm->stream, pulse_stream_state_cb,
- pcm->p);
-
+ pa_stream_set_state_callback(pcm->stream, stream_state_cb, pcm);
pa_stream_set_latency_update_callback(pcm->stream, stream_latency_cb, pcm);
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
@@ -659,8 +732,7 @@ static int pulse_prepare(snd_pcm_ioplug_t * io)
goto finish;
}
- err =
- pulse_wait_stream_state(pcm->p, pcm->stream, PA_STREAM_READY);
+ err = wait_stream_state(pcm, PA_STREAM_READY);
if (err < 0) {
SNDERR("PulseAudio: Unable to create stream: %s\n", pa_strerror(pa_context_errno(pcm->p->context)));
pa_stream_unref(pcm->stream);
diff --git a/pulse/pulse.c b/pulse/pulse.c
index 6f58a7e..c313182 100644
--- a/pulse/pulse.c
+++ b/pulse/pulse.c
@@ -44,26 +44,6 @@ int pulse_check_connection(snd_pulse_t * p)
return 0;
}
-void pulse_stream_state_cb(pa_stream * s, void *userdata)
-{
- snd_pulse_t *p = userdata;
-
- assert(s);
- assert(p);
-
- pa_threaded_mainloop_signal(p->mainloop, 0);
-}
-
-void pulse_stream_success_cb(pa_stream * s, int success, void *userdata)
-{
- snd_pulse_t *p = userdata;
-
- assert(s);
- assert(p);
-
- pa_threaded_mainloop_signal(p->mainloop, 0);
-}
-
void pulse_context_success_cb(pa_context * c, int success, void *userdata)
{
snd_pulse_t *p = userdata;
@@ -101,41 +81,6 @@ int pulse_wait_operation(snd_pulse_t * p, pa_operation * o)
return 0;
}
-int pulse_wait_stream_state(snd_pulse_t * p, pa_stream * stream,
- pa_stream_state_t target)
-{
- pa_stream_state_t state;
-
- assert(p);
- assert(stream);
-
- if (p->state != PULSE_STATE_READY)
- return -EBADFD;
-
- if (!p->mainloop)
- return -EBADFD;
-
- for (;;) {
- int err;
-
- err = pulse_check_connection(p);
- if (err < 0)
- return err;
-
- state = pa_stream_get_state(stream);
-
- if (state == target)
- break;
-
- if (!PA_STREAM_IS_GOOD(state))
- return -EIO;
-
- pa_threaded_mainloop_wait(p->mainloop);
- }
-
- return 0;
-}
-
static void context_state_cb(pa_context * c, void *userdata)
{
snd_pulse_t *p = userdata;
diff --git a/pulse/pulse.h b/pulse/pulse.h
index 7bf1a5b..51f9a11 100644
--- a/pulse/pulse.h
+++ b/pulse/pulse.h
@@ -41,13 +41,9 @@ typedef struct snd_pulse {
int pulse_check_connection(snd_pulse_t * p);
-void pulse_stream_state_cb(pa_stream * s, void *userdata);
-void pulse_stream_success_cb(pa_stream * s, int success, void *userdata);
void pulse_context_success_cb(pa_context * c, int success, void *userdata);
int pulse_wait_operation(snd_pulse_t * p, pa_operation * o);
-int pulse_wait_stream_state(snd_pulse_t * p, pa_stream * stream,
- pa_stream_state_t target);
snd_pulse_t *pulse_new(void);
void pulse_free(snd_pulse_t * p);
--
1.6.4
Lennart
--
Lennart Poettering Red Hat, Inc.
lennart [at] poettering [dot] net ICQ# 11060553
http://0pointer.net/lennart/ GnuPG 0x1A015CC4
1
0

04 Aug '09
This adds support for DAI platform for the SSI present in MXC platforms.
It currently does not support i.MX3, the only thing necessary to do
this is to export DMA data for i.MX3 interface which I haven't done
because I don't have a i.MX3 based board available.
It has been tested on i.MX27 board.
Signed-off-by: Javier Martin <javier.martin(a)vista-silicon.com>
---
sound/soc/imx/Kconfig | 2 +
sound/soc/imx/Makefile | 2 +
sound/soc/imx/mxc-ssi.c | 868 +++++++++++++++++++++++++++++++++++++++++++++++
sound/soc/imx/mxc-ssi.h | 238 +++++++++++++
4 files changed, 1110 insertions(+), 0 deletions(-)
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index a1bf053..886dadd 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -6,6 +6,8 @@ config SND_MX1_MX2_SOC
Say Y or M if you want to add support for codecs attached to
the MX1 or MX2 SSI interface.
+config SND_MXC_SOC_SSI
+ tristate
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index c390f0f..6552cb2 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -1,4 +1,6 @@
# i.MX Platform Support
snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o
+snd-soc-mxc-ssi-objs := mxc-ssi.o
obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o
+obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o
diff --git a/sound/soc/imx/mxc-ssi.c b/sound/soc/imx/mxc-ssi.c
new file mode 100644
index 0000000..3806ff2
--- /dev/null
+++ b/sound/soc/imx/mxc-ssi.c
@@ -0,0 +1,868 @@
+/*
+ * mxc-ssi.c -- SSI driver for Freescale IMX
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood(a)wolfsonmicro.com or linux(a)wolfsonmicro.com
+ *
+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * TODO:
+ * Need to rework SSI register defs when new defs go into mainline.
+ * Add support for TDM and FIFO 1.
+ * Add support for i.mx3x DMA interface.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma-mx1-mx2.h>
+#include <asm/mach-types.h>
+
+#include "mxc-ssi.h"
+#include "mx1_mx2-pcm.h"
+
+#define SSI1_PORT 0
+#define SSI2_PORT 1
+
+static int ssi_active[2] = {0, 0};
+
+/* DMA information for mx1_mx2 platforms */
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = {
+ .name = "SSI1 PCM Stereo out 0",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI1_BASE_ADDR + STX0,
+ .event_id = DMA_REQ_SSI1_TX0,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = {
+ .name = "SSI1 PCM Stereo out 1",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI1_BASE_ADDR + STX1,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = {
+ .name = "SSI1 PCM Stereo in 0",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI1_BASE_ADDR + SRX0,
+ .event_id = DMA_REQ_SSI1_RX0,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = {
+ .name = "SSI1 PCM Stereo in 1",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI1_BASE_ADDR + SRX1,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = {
+ .name = "SSI2 PCM Stereo out 0",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI2_BASE_ADDR + STX0,
+ .event_id = DMA_REQ_SSI2_TX0,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = {
+ .name = "SSI2 PCM Stereo out 1",
+ .transfer_type = DMA_MODE_WRITE,
+ .per_address = SSI2_BASE_ADDR + STX1,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .watermark_level = TXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = {
+ .name = "SSI2 PCM Stereo in 0",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI2_BASE_ADDR + SRX0,
+ .event_id = DMA_REQ_SSI2_RX0,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = {
+ .name = "SSI2 PCM Stereo in 1",
+ .transfer_type = DMA_MODE_READ,
+ .per_address = SSI2_BASE_ADDR + SRX1,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .watermark_level = RXFIFO_WATERMARK,
+ .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+ .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+};
+
+static struct clk *ssi_clk0, *ssi_clk1;
+
+int get_ssi_clk(int ssi, struct device *dev)
+{
+ switch (ssi) {
+ case 0:
+ ssi_clk0 = clk_get(dev, "ssi1");
+ if (IS_ERR(ssi_clk0))
+ return PTR_ERR(ssi_clk0);
+ return 0;
+ case 1:
+ ssi_clk1 = clk_get(dev, "ssi2");
+ if (IS_ERR(ssi_clk1))
+ return PTR_ERR(ssi_clk1);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(get_ssi_clk);
+
+void put_ssi_clk(int ssi)
+{
+ switch (ssi) {
+ case 0:
+ clk_put(ssi_clk0);
+ ssi_clk0 = NULL;
+ break;
+ case 1:
+ clk_put(ssi_clk1);
+ ssi_clk1 = NULL;
+ break;
+ }
+}
+EXPORT_SYMBOL(put_ssi_clk);
+
+/*
+ * SSI system clock configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ u32 scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ scr = SSI1_SCR;
+ pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr);
+ } else {
+ scr = SSI2_SCR;
+ pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr);
+ }
+
+ if (scr & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+
+ switch (clk_id) {
+ case IMX_SSP_SYS_CLK:
+ if (dir == SND_SOC_CLOCK_OUT) {
+ scr |= SSI_SCR_SYS_CLK_EN;
+ pr_debug("%s: clk of is output\n", __func__);
+ } else {
+ scr &= ~SSI_SCR_SYS_CLK_EN;
+ pr_debug("%s: clk of is input\n", __func__);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ pr_debug("%s: writeback of SSI1_SCR\n", __func__);
+ SSI1_SCR = scr;
+ } else {
+ pr_debug("%s: writeback of SSI2_SCR\n", __func__);
+ SSI2_SCR = scr;
+ }
+
+ return 0;
+}
+
+/*
+ * SSI Clock dividers
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ u32 stccr, srccr;
+
+ pr_debug("%s\n", __func__);
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ if (SSI1_SCR & SSI_SCR_SSIEN)
+ return 0;
+ srccr = SSI1_STCCR;
+ stccr = SSI1_STCCR;
+ } else {
+ if (SSI2_SCR & SSI_SCR_SSIEN)
+ return 0;
+ srccr = SSI2_STCCR;
+ stccr = SSI2_STCCR;
+ }
+
+ switch (div_id) {
+ case IMX_SSI_TX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ case IMX_SSI_RX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STCCR = stccr;
+ SSI1_SRCCR = srccr;
+ } else {
+ SSI2_STCCR = stccr;
+ SSI2_SRCCR = srccr;
+ }
+ return 0;
+}
+
+/*
+ * SSI Network Mode or TDM slots configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int mask, int slots)
+{
+ u32 stmsk, srmsk, stccr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ if (SSI1_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ stccr = SSI1_STCCR;
+ } else {
+ if (SSI2_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ stccr = SSI2_STCCR;
+ }
+
+ stmsk = srmsk = mask;
+ stccr &= ~SSI_STCCR_DC_MASK;
+ stccr |= SSI_STCCR_DC(slots - 1);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STMSK = stmsk;
+ SSI1_SRMSK = srmsk;
+ SSI1_SRCCR = SSI1_STCCR = stccr;
+ } else {
+ SSI2_STMSK = stmsk;
+ SSI2_SRMSK = srmsk;
+ SSI2_SRCCR = SSI2_STCCR = stccr;
+ }
+
+ return 0;
+}
+
+/*
+ * SSI DAI format configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ * Note: We don't use the I2S modes but instead manually configure the
+ * SSI for I2S.
+ */
+static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ u32 stcr = 0, srcr = 0, scr;
+
+ /*
+ * This is done to avoid this function to modify
+ * previous set values in stcr
+ */
+ stcr = SSI1_STCR;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
+ else
+ scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
+
+ if (scr & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* data on rising edge of bclk, frame low 1clk before data */
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* data on rising edge of bclk, frame high with data */
+ stcr |= SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RXBIT0;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* data on rising edge of bclk, frame high with data */
+ stcr |= SSI_STCR_TFSL;
+ srcr |= SSI_SRCR_RFSL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* data on rising edge of bclk, frame high 1clk before data */
+ stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+ srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
+ break;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ stcr |= SSI_STCR_TFSI;
+ stcr &= ~SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI;
+ srcr &= ~SSI_SRCR_RSCKP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ stcr &= ~SSI_STCR_TFSI;
+ stcr |= SSI_STCR_TSCKP;
+ srcr &= ~SSI_SRCR_RFSI;
+ srcr |= SSI_SRCR_RSCKP;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
+ srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ stcr |= SSI_STCR_TFDIR;
+ srcr |= SSI_SRCR_RFDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ stcr |= SSI_STCR_TXDIR;
+ srcr |= SSI_SRCR_RXDIR;
+ break;
+ }
+
+ /* sync */
+ if (!(fmt & SND_SOC_DAIFMT_ASYNC))
+ scr |= SSI_SCR_SYN;
+
+ /* tdm - only for stereo atm */
+ if (fmt & SND_SOC_DAIFMT_TDM)
+ scr |= SSI_SCR_NET;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STCR = stcr;
+ SSI1_SRCR = srcr;
+ SSI1_SCR = scr;
+ } else {
+ SSI2_STCR = stcr;
+ SSI2_SRCR = srcr;
+ SSI2_SCR = scr;
+ }
+
+ return 0;
+}
+
+static int imx_ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* set up TX DMA params */
+ switch (cpu_dai->id) {
+ case IMX_DAI_SSI0:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
+ break;
+ case IMX_DAI_SSI1:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
+ break;
+ case IMX_DAI_SSI2:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
+ break;
+ case IMX_DAI_SSI3:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
+ }
+ pr_debug("%s: (playback)\n", __func__);
+ } else {
+ /* set up RX DMA params */
+ switch (cpu_dai->id) {
+ case IMX_DAI_SSI0:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
+ break;
+ case IMX_DAI_SSI1:
+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
+ break;
+ case IMX_DAI_SSI2:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
+ break;
+ case IMX_DAI_SSI3:
+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
+ }
+ pr_debug("%s: (capture)\n", __func__);
+ }
+
+ /*
+ * we cant really change any SSI values after SSI is enabled
+ * need to fix in software for max flexibility - lrg
+ */
+ if (cpu_dai->active) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+
+ /* reset the SSI port - Sect 45.4.4 */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+
+ if (!ssi_clk0)
+ return -EINVAL;
+
+ if (ssi_active[SSI1_PORT]++) {
+ pr_debug("%s: exit before reset\n", __func__);
+ return 0;
+ }
+
+ /* SSI1 Reset */
+ SSI1_SCR = 0;
+
+ SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
+ } else {
+
+ if (!ssi_clk1)
+ return -EINVAL;
+
+ if (ssi_active[SSI2_PORT]++) {
+ pr_debug("%s: exit before reset\n", __func__);
+ return 0;
+ }
+
+ /* SSI2 Reset */
+ SSI2_SCR = 0;
+
+ SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
+ }
+
+ return 0;
+}
+
+int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 stccr, stcr, sier;
+
+ pr_debug("%s\n", __func__);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
+ stcr = SSI1_STCR;
+ sier = SSI1_SIER;
+ } else {
+ stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
+ stcr = SSI2_STCR;
+ sier = SSI2_SIER;
+ }
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stccr |= SSI_STCCR_WL(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ stccr |= SSI_STCCR_WL(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ stccr |= SSI_STCCR_WL(24);
+ break;
+ }
+
+ /* enable interrupts */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ stcr |= SSI_STCR_TFEN0;
+ else
+ stcr |= SSI_STCR_TFEN1;
+ sier |= SSI_SIER_TDMAE;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_STCR = stcr;
+ SSI1_STCCR = stccr;
+ SSI1_SIER = sier;
+ } else {
+ SSI2_STCR = stcr;
+ SSI2_STCCR = stccr;
+ SSI2_SIER = sier;
+ }
+
+ return 0;
+}
+
+int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 srccr, srcr, sier;
+
+ pr_debug("%s\n", __func__);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
+ srcr = SSI1_SRCR;
+ sier = SSI1_SIER;
+ } else {
+ srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
+ srcr = SSI2_SRCR;
+ sier = SSI2_SIER;
+ }
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srccr |= SSI_SRCCR_WL(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srccr |= SSI_SRCCR_WL(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srccr |= SSI_SRCCR_WL(24);
+ break;
+ }
+
+ /* enable interrupts */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ srcr |= SSI_SRCR_RFEN0;
+ else
+ srcr |= SSI_SRCR_RFEN1;
+ sier |= SSI_SIER_RDMAE;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ SSI1_SRCR = srcr;
+ SSI1_SRCCR = srccr;
+ SSI1_SIER = sier;
+ } else {
+ SSI2_SRCR = srcr;
+ SSI2_SRCCR = srccr;
+ SSI2_SIER = sier;
+ }
+
+ return 0;
+}
+
+/*
+ * Should only be called when port is inactive (i.e. SSIEN = 0),
+ * although can be called multiple times by upper layers.
+ */
+int imx_ssi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ int ret;
+
+ /* cant change any parameters when SSI is running */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ if (SSI1_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ } else {
+ if (SSI2_SCR & SSI_SCR_SSIEN) {
+ printk(KERN_WARNING "Warning ssi already enabled\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Configure both tx and rx params with the same settings. This is
+ * really a harware restriction because SSI must be disabled until
+ * we can change those values. If there is an active audio stream in
+ * one direction, enabling the other direction with different
+ * settings would mean disturbing the running one.
+ */
+ ret = imx_ssi_hw_tx_params(substream, params);
+ if (ret < 0)
+ return ret;
+ return imx_ssi_hw_rx_params(substream, params);
+}
+
+int imx_ssi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ /* Enable clks here to follow SSI recommended init sequence */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+ ret = clk_enable(ssi_clk0);
+ if (ret < 0)
+ printk(KERN_ERR "Unable to enable ssi_clk0\n");
+ } else {
+ ret = clk_enable(ssi_clk1);
+ if (ret < 0)
+ printk(KERN_ERR "Unable to enable ssi_clk1\n");
+ }
+
+ return 0;
+}
+
+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ scr = SSI1_SCR;
+ else
+ scr = SSI2_SCR;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
+ else
+ scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ scr &= ~SSI_SCR_TE;
+ else
+ scr &= ~SSI_SCR_RE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ SSI1_SCR = scr;
+ else
+ SSI2_SCR = scr;
+
+ return 0;
+}
+
+static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ /* shutdown SSI if neither Tx or Rx is active */
+ if (!cpu_dai->active) {
+
+ if (cpu_dai->id == IMX_DAI_SSI0 ||
+ cpu_dai->id == IMX_DAI_SSI2) {
+
+ if (--ssi_active[SSI1_PORT] > 1)
+ return;
+
+ SSI1_SCR = 0;
+ clk_disable(ssi_clk0);
+ } else {
+ if (--ssi_active[SSI2_PORT])
+ return;
+ SSI2_SCR = 0;
+ clk_disable(ssi_clk1);
+ }
+ }
+}
+
+#ifdef CONFIG_PM
+static int imx_ssi_suspend(struct platform_device *dev,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static int imx_ssi_resume(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+#else
+#define imx_ssi_suspend NULL
+#define imx_ssi_resume NULL
+#endif
+
+#define IMX_SSI_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define IMX_SSI_BITS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
+ .startup = imx_ssi_startup,
+ .shutdown = imx_ssi_shutdown,
+ .trigger = imx_ssi_trigger,
+ .prepare = imx_ssi_prepare,
+ .hw_params = imx_ssi_hw_params,
+ .set_sysclk = imx_ssi_set_dai_sysclk,
+ .set_clkdiv = imx_ssi_set_dai_clkdiv,
+ .set_fmt = imx_ssi_set_dai_fmt,
+ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
+};
+
+struct snd_soc_dai imx_ssi_pcm_dai[] = {
+{
+ .name = "imx-i2s-1-0",
+ .id = IMX_DAI_SSI0,
+ .suspend = imx_ssi_suspend,
+ .resume = imx_ssi_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+{
+ .name = "imx-i2s-2-0",
+ .id = IMX_DAI_SSI1,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+{
+ .name = "imx-i2s-1-1",
+ .id = IMX_DAI_SSI2,
+ .suspend = imx_ssi_suspend,
+ .resume = imx_ssi_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+{
+ .name = "imx-i2s-2-1",
+ .id = IMX_DAI_SSI3,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .formats = IMX_SSI_BITS,
+ .rates = IMX_SSI_RATES,},
+ .ops = &imx_ssi_pcm_dai_ops,
+},
+};
+EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
+
+static int __init imx_ssi_init(void)
+{
+ return snd_soc_register_dais(imx_ssi_pcm_dai,
+ ARRAY_SIZE(imx_ssi_pcm_dai));
+}
+
+static void __exit imx_ssi_exit(void)
+{
+ snd_soc_unregister_dais(imx_ssi_pcm_dai,
+ ARRAY_SIZE(imx_ssi_pcm_dai));
+}
+
+module_init(imx_ssi_init);
+module_exit(imx_ssi_exit);
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood(a)wolfsonmicro.com");
+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.h b/sound/soc/imx/mxc-ssi.h
new file mode 100644
index 0000000..12bbdc9
--- /dev/null
+++ b/sound/soc/imx/mxc-ssi.h
@@ -0,0 +1,238 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _IMX_SSI_H
+#define _IMX_SSI_H
+
+#include <mach/hardware.h>
+
+/* SSI regs definition - MOVE to /arch/arm/plat-mxc/include/mach/
when stable */
+#define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR)
+#define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR)
+
+#define STX0 0x00
+#define STX1 0x04
+#define SRX0 0x08
+#define SRX1 0x0c
+#define SCR 0x10
+#define SISR 0x14
+#define SIER 0x18
+#define STCR 0x1c
+#define SRCR 0x20
+#define STCCR 0x24
+#define SRCCR 0x28
+#define SFCSR 0x2c
+#define STR 0x30
+#define SOR 0x34
+#define SACNT 0x38
+#define SACADD 0x3c
+#define SACDAT 0x40
+#define SATAG 0x44
+#define STMSK 0x48
+#define SRMSK 0x4c
+
+#define SSI1_STX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX0)))
+#define SSI1_STX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX1)))
+#define SSI1_SRX0 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX0)))
+#define SSI1_SRX1 (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX1)))
+#define SSI1_SCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SCR)))
+#define SSI1_SISR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SISR)))
+#define SSI1_SIER (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SIER)))
+#define SSI1_STCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCR)))
+#define SSI1_SRCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCR)))
+#define SSI1_STCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCCR)))
+#define SSI1_SRCCR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCCR)))
+#define SSI1_SFCSR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SFCSR)))
+#define SSI1_STR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STR)))
+#define SSI1_SOR (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SOR)))
+#define SSI1_SACNT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACNT)))
+#define SSI1_SACADD (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACADD)))
+#define SSI1_SACDAT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACDAT)))
+#define SSI1_SATAG (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SATAG)))
+#define SSI1_STMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STMSK)))
+#define SSI1_SRMSK (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRMSK)))
+
+
+#define SSI2_STX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX0)))
+#define SSI2_STX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX1)))
+#define SSI2_SRX0 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX0)))
+#define SSI2_SRX1 (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX1)))
+#define SSI2_SCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SCR)))
+#define SSI2_SISR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SISR)))
+#define SSI2_SIER (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SIER)))
+#define SSI2_STCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCR)))
+#define SSI2_SRCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCR)))
+#define SSI2_STCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCCR)))
+#define SSI2_SRCCR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCCR)))
+#define SSI2_SFCSR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SFCSR)))
+#define SSI2_STR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STR)))
+#define SSI2_SOR (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SOR)))
+#define SSI2_SACNT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACNT)))
+#define SSI2_SACADD (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACADD)))
+#define SSI2_SACDAT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACDAT)))
+#define SSI2_SATAG (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SATAG)))
+#define SSI2_STMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STMSK)))
+#define SSI2_SRMSK (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRMSK)))
+
+#define SSI_SCR_CLK_IST (1 << 9)
+#define SSI_SCR_TCH_EN (1 << 8)
+#define SSI_SCR_SYS_CLK_EN (1 << 7)
+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
+#define SSI_SCR_SYN (1 << 4)
+#define SSI_SCR_NET (1 << 3)
+#define SSI_SCR_RE (1 << 2)
+#define SSI_SCR_TE (1 << 1)
+#define SSI_SCR_SSIEN (1 << 0)
+
+#define SSI_SISR_CMDAU (1 << 18)
+#define SSI_SISR_CMDDU (1 << 17)
+#define SSI_SISR_RXT (1 << 16)
+#define SSI_SISR_RDR1 (1 << 15)
+#define SSI_SISR_RDR0 (1 << 14)
+#define SSI_SISR_TDE1 (1 << 13)
+#define SSI_SISR_TDE0 (1 << 12)
+#define SSI_SISR_ROE1 (1 << 11)
+#define SSI_SISR_ROE0 (1 << 10)
+#define SSI_SISR_TUE1 (1 << 9)
+#define SSI_SISR_TUE0 (1 << 8)
+#define SSI_SISR_TFS (1 << 7)
+#define SSI_SISR_RFS (1 << 6)
+#define SSI_SISR_TLS (1 << 5)
+#define SSI_SISR_RLS (1 << 4)
+#define SSI_SISR_RFF1 (1 << 3)
+#define SSI_SISR_RFF0 (1 << 2)
+#define SSI_SISR_TFE1 (1 << 1)
+#define SSI_SISR_TFE0 (1 << 0)
+
+#define SSI_SIER_RDMAE (1 << 22)
+#define SSI_SIER_RIE (1 << 21)
+#define SSI_SIER_TDMAE (1 << 20)
+#define SSI_SIER_TIE (1 << 19)
+#define SSI_SIER_CMDAU_EN (1 << 18)
+#define SSI_SIER_CMDDU_EN (1 << 17)
+#define SSI_SIER_RXT_EN (1 << 16)
+#define SSI_SIER_RDR1_EN (1 << 15)
+#define SSI_SIER_RDR0_EN (1 << 14)
+#define SSI_SIER_TDE1_EN (1 << 13)
+#define SSI_SIER_TDE0_EN (1 << 12)
+#define SSI_SIER_ROE1_EN (1 << 11)
+#define SSI_SIER_ROE0_EN (1 << 10)
+#define SSI_SIER_TUE1_EN (1 << 9)
+#define SSI_SIER_TUE0_EN (1 << 8)
+#define SSI_SIER_TFS_EN (1 << 7)
+#define SSI_SIER_RFS_EN (1 << 6)
+#define SSI_SIER_TLS_EN (1 << 5)
+#define SSI_SIER_RLS_EN (1 << 4)
+#define SSI_SIER_RFF1_EN (1 << 3)
+#define SSI_SIER_RFF0_EN (1 << 2)
+#define SSI_SIER_TFE1_EN (1 << 1)
+#define SSI_SIER_TFE0_EN (1 << 0)
+
+#define SSI_STCR_TXBIT0 (1 << 9)
+#define SSI_STCR_TFEN1 (1 << 8)
+#define SSI_STCR_TFEN0 (1 << 7)
+#define SSI_STCR_TFDIR (1 << 6)
+#define SSI_STCR_TXDIR (1 << 5)
+#define SSI_STCR_TSHFD (1 << 4)
+#define SSI_STCR_TSCKP (1 << 3)
+#define SSI_STCR_TFSI (1 << 2)
+#define SSI_STCR_TFSL (1 << 1)
+#define SSI_STCR_TEFS (1 << 0)
+
+#define SSI_SRCR_RXBIT0 (1 << 9)
+#define SSI_SRCR_RFEN1 (1 << 8)
+#define SSI_SRCR_RFEN0 (1 << 7)
+#define SSI_SRCR_RFDIR (1 << 6)
+#define SSI_SRCR_RXDIR (1 << 5)
+#define SSI_SRCR_RSHFD (1 << 4)
+#define SSI_SRCR_RSCKP (1 << 3)
+#define SSI_SRCR_RFSI (1 << 2)
+#define SSI_SRCR_RFSL (1 << 1)
+#define SSI_SRCR_REFS (1 << 0)
+
+#define SSI_STCCR_DIV2 (1 << 18)
+#define SSI_STCCR_PSR (1 << 15)
+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_STCCR_WL_MASK (0xf << 13)
+#define SSI_STCCR_DC_MASK (0x1f << 8)
+#define SSI_STCCR_PM_MASK (0xff << 0)
+
+#define SSI_SRCCR_DIV2 (1 << 18)
+#define SSI_SRCCR_PSR (1 << 15)
+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_SRCCR_WL_MASK (0xf << 13)
+#define SSI_SRCCR_DC_MASK (0x1f << 8)
+#define SSI_SRCCR_PM_MASK (0xff << 0)
+
+
+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
+
+#define SSI_STR_TEST (1 << 15)
+#define SSI_STR_RCK2TCK (1 << 14)
+#define SSI_STR_RFS2TFS (1 << 13)
+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
+#define SSI_STR_TXD2RXD (1 << 7)
+#define SSI_STR_TCK2RCK (1 << 6)
+#define SSI_STR_TFS2RFS (1 << 5)
+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
+
+#define SSI_SOR_CLKOFF (1 << 6)
+#define SSI_SOR_RX_CLR (1 << 5)
+#define SSI_SOR_TX_CLR (1 << 4)
+#define SSI_SOR_INIT (1 << 3)
+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
+#define SSI_SOR_SYNRST (1 << 0)
+
+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
+#define SSI_SACNT_WR (x << 4)
+#define SSI_SACNT_RD (x << 3)
+#define SSI_SACNT_TIF (x << 2)
+#define SSI_SACNT_FV (x << 1)
+#define SSI_SACNT_AC97EN (x << 0)
+
+/* Watermarks for FIFO's */
+#define TXFIFO_WATERMARK 0x4
+#define RXFIFO_WATERMARK 0x4
+
+/* i.MX DAI SSP ID's */
+#define IMX_DAI_SSI0 0 /* SSI1 FIFO 0 */
+#define IMX_DAI_SSI1 1 /* SSI1 FIFO 1 */
+#define IMX_DAI_SSI2 2 /* SSI2 FIFO 0 */
+#define IMX_DAI_SSI3 3 /* SSI2 FIFO 1 */
+
+/* SSI clock sources */
+#define IMX_SSP_SYS_CLK 0
+
+/* SSI audio dividers */
+#define IMX_SSI_TX_DIV_2 0
+#define IMX_SSI_TX_DIV_PSR 1
+#define IMX_SSI_TX_DIV_PM 2
+#define IMX_SSI_RX_DIV_2 3
+#define IMX_SSI_RX_DIV_PSR 4
+#define IMX_SSI_RX_DIV_PM 5
+
+
+/* SSI Div 2 */
+#define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2)
+#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
+
+extern struct snd_soc_dai imx_ssi_pcm_dai[4];
+extern int get_ssi_clk(int ssi, struct device *dev);
+extern void put_ssi_clk(int ssi);
+#endif
---
--
Javier Martin
Vista Silicon S.L.
Universidad de Cantabria
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
2
2

[alsa-devel] [PATCH 1/3] ASoC: add DMA platform driver for MX1x and MX2x
by javier Martin 04 Aug '09
by javier Martin 04 Aug '09
04 Aug '09
This adds support for DMA platform valid for i.MX1 and i.MX2 platforms.
This is not valid for i.MX3 since it doesn't share the same DMA
interface than i.MX1 and i.MX2.
It has been tested on i.MX27 board.
Signed-off-by: Javier Martin <javier.martin(a)vista-silicon.com>
---
sound/soc/Makefile | 1 +
sound/soc/imx/Kconfig | 11 +
sound/soc/imx/Makefile | 4 +
sound/soc/imx/mx1_mx2-pcm.c | 487 +++++++++++++++++++++++++++++++++++++++++++
sound/soc/imx/mx1_mx2-pcm.h | 48 +++++
5 files changed, 551 insertions(+), 0 deletions(-)
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 0237879..8120b52 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += sh/
+obj-$(CONFIG_SND_SOC) += imx/
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
new file mode 100644
index 0000000..a1bf053
--- /dev/null
+++ b/sound/soc/imx/Kconfig
@@ -0,0 +1,11 @@
+config SND_MX1_MX2_SOC
+ tristate "SoC Audio for Freecale i.MX1x i.MX2x CPUs"
+ depends on (ARCH_MX2 || ARCH_MX1) && SND
+ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the MX1 or MX2 SSI interface.
+
+
+
+
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
new file mode 100644
index 0000000..c390f0f
--- /dev/null
+++ b/sound/soc/imx/Makefile
@@ -0,0 +1,4 @@
+# i.MX Platform Support
+snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o
+
+obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o
diff --git a/sound/soc/imx/mx1_mx2-pcm.c b/sound/soc/imx/mx1_mx2-pcm.c
new file mode 100644
index 0000000..94807f8
--- /dev/null
+++ b/sound/soc/imx/mx1_mx2-pcm.c
@@ -0,0 +1,487 @@
+/*
+ * mx1_mx2-pcm.c -- ALSA SoC interface for Freescale i.MX1x, i.MX2x CPUs
+ *
+ * Copyright 2009 Vista Silicon S.L.
+ * Author: Javier Martin
+ * javier.martin(a)vista-silicon.com
+ *
+ * Based on mxc-pcm.c by Liam Girdwood.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/dma-mx1-mx2.h>
+
+#include "mx1_mx2-pcm.h"
+
+
+static const struct snd_pcm_hardware mx1_mx2_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .buffer_bytes_max = 32 * 1024,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8 * 1024,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+struct mx1_mx2_runtime_data {
+ int dma_ch;
+ int active;
+ unsigned int period;
+ unsigned int periods;
+ int tx_spin;
+ spinlock_t dma_lock;
+ struct mx1_mx2_pcm_dma_params *dma_params;
+};
+
+
+/**
+ * This function stops the current dma transfer for playback
+ * and clears the dma pointers.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static int audio_stop_dma(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prtd->dma_lock, flags);
+
+ pr_debug("%s\n", __func__);
+
+ prtd->active = 0;
+ prtd->period = 0;
+ prtd->periods = 0;
+
+ /* this stops the dma channel and clears the buffer ptrs */
+
+ imx_dma_disable(prtd->dma_ch);
+
+ spin_unlock_irqrestore(&prtd->dma_lock, flags);
+
+ return 0;
+}
+
+/**
+ * This function is called whenever a new audio block needs to be
+ * transferred to the codec. The function receives the address and the size
+ * of the new block and start a new DMA transfer.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static int dma_new_period(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+ unsigned int dma_size;
+ unsigned int offset;
+ int ret = 0;
+ dma_addr_t mem_addr;
+ unsigned int dev_addr;
+
+ if (prtd->active) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * prtd->period;
+
+ pr_debug("%s: period (%d) out of (%d)\n", __func__,
+ prtd->period,
+ runtime->periods);
+ pr_debug("period_size %d frames\n offset %d bytes\n",
+ (unsigned int)runtime->period_size,
+ offset);
+ pr_debug("dma_size %d bytes\n", dma_size);
+
+ snd_BUG_ON(dma_size > mx1_mx2_pcm_hardware.period_bytes_max);
+
+ mem_addr = (dma_addr_t)(runtime->dma_addr + offset);
+ dev_addr = prtd->dma_params->per_address;
+ pr_debug("%s: mem_addr is %x\n dev_addr is %x\n",
+ __func__, mem_addr, dev_addr);
+
+ ret = imx_dma_setup_single(prtd->dma_ch, mem_addr,
+ dma_size, dev_addr,
+ prtd->dma_params->transfer_type);
+ if (ret < 0) {
+ printk(KERN_ERR "Error configuring DMA\n");
+ return ret;
+ }
+ imx_dma_enable(prtd->dma_ch);
+
+ pr_debug("%s: transfer enabled\nmem_addr = %x\n",
+ __func__, (unsigned int) mem_addr);
+ pr_debug("dev_addr = %x\ndma_size = %d\n",
+ (unsigned int) dev_addr, dma_size);
+
+ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+ prtd->period++;
+ prtd->period %= runtime->periods;
+ }
+ return ret;
+}
+
+
+/**
+ * This is a callback which will be called
+ * when a TX transfer finishes. The call occurs
+ * in interrupt context.
+ *
+ * @param dat pointer to the structure of the current stream.
+ *
+ */
+static void audio_dma_irq(int channel, void *data)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ struct mx1_mx2_runtime_data *prtd;
+ unsigned int dma_size;
+ unsigned int previous_period;
+ unsigned int offset;
+
+ substream = data;
+ runtime = substream->runtime;
+ prtd = runtime->private_data;
+ previous_period = prtd->periods;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * previous_period;
+
+ prtd->tx_spin = 0;
+ prtd->periods++;
+ prtd->periods %= runtime->periods;
+
+ pr_debug("%s: irq per %d offset %x\n", __func__, prtd->periods, offset);
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (prtd->active)
+ snd_pcm_period_elapsed(substream);
+
+ /*
+ * Trig next DMA transfer
+ */
+ dma_new_period(substream);
+}
+
+/**
+ * This function configures the hardware to allow audio
+ * playback operations. It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+snd_mx1_mx2_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+
+ prtd->period = 0;
+ prtd->periods = 0;
+
+ return 0;
+}
+
+static int mx1_mx2_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ ret = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to malloc pcm pages\n", __func__);
+ return ret;
+ }
+
+ pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_addr 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_addr);
+ pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_area 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_area);
+ pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_bytes 0x(%x)\n",
+ __func__, (unsigned int)runtime->dma_bytes);
+
+ return ret;
+}
+
+static int mx1_mx2_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+
+ imx_dma_free(prtd->dma_ch);
+
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static int mx1_mx2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mx1_mx2_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->tx_spin = 0;
+ /* requested stream startup */
+ prtd->active = 1;
+ pr_debug("%s: starting dma_new_period\n", __func__);
+ ret = dma_new_period(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ /* requested stream shutdown */
+ pr_debug("%s: stopping dma transfer\n", __func__);
+ ret = audio_stop_dma(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+mx1_mx2_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+ unsigned int offset = 0;
+
+ /* tx_spin value is used here to check if a transfer is active */
+ if (prtd->tx_spin) {
+ offset = (runtime->period_size * (prtd->periods)) +
+ (runtime->period_size >> 1);
+ if (offset >= runtime->buffer_size)
+ offset = runtime->period_size >> 1;
+ } else {
+ offset = (runtime->period_size * (prtd->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ }
+ pr_debug("%s: pointer offset %x\n", __func__, offset);
+
+ return offset;
+}
+
+static int mx1_mx2_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mx1_mx2_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &mx1_mx2_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(struct mx1_mx2_runtime_data), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ runtime->private_data = prtd;
+
+ if (!dma_data)
+ return -ENODEV;
+
+ prtd->dma_params = dma_data;
+
+ pr_debug("%s: Requesting dma channel (%s)\n", __func__,
+ prtd->dma_params->name);
+ prtd->dma_ch = imx_dma_request_by_prio(prtd->dma_params->name,
+ DMA_PRIO_HIGH);
+ if (prtd->dma_ch < 0) {
+ printk(KERN_ERR "Error requesting dma channel\n");
+ return ret;
+ }
+ imx_dma_config_burstlen(prtd->dma_ch,
+ prtd->dma_params->watermark_level);
+
+ ret = imx_dma_config_channel(prtd->dma_ch,
+ prtd->dma_params->per_config,
+ prtd->dma_params->mem_config,
+ prtd->dma_params->event_id, 0);
+
+ if (ret) {
+ pr_debug(KERN_ERR "Error configuring dma channel %d\n",
+ prtd->dma_ch);
+ return ret;
+ }
+
+ pr_debug("%s: Setting tx dma callback function\n", __func__);
+ ret = imx_dma_setup_handlers(prtd->dma_ch,
+ audio_dma_irq, NULL,
+ (void *)substream);
+ if (ret < 0) {
+ printk(KERN_ERR "Error setting dma callback function\n");
+ return ret;
+ }
+ return 0;
+
+ out:
+ return ret;
+}
+
+static int mx1_mx2_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mx1_mx2_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+
+ return 0;
+}
+
+static int mx1_mx2_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+struct snd_pcm_ops mx1_mx2_pcm_ops = {
+ .open = mx1_mx2_pcm_open,
+ .close = mx1_mx2_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = mx1_mx2_pcm_hw_params,
+ .hw_free = mx1_mx2_pcm_hw_free,
+ .prepare = snd_mx1_mx2_prepare,
+ .trigger = mx1_mx2_pcm_trigger,
+ .pointer = mx1_mx2_pcm_pointer,
+ .mmap = mx1_mx2_pcm_mmap,
+};
+
+static u64 mx1_mx2_pcm_dmamask = 0xffffffff;
+
+static int mx1_mx2_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = mx1_mx2_pcm_hardware.buffer_bytes_max;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ /* Reserve uncached-buffered memory area for DMA */
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+
+ pr_debug("%s: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+ __func__, (void *) buf->area, (void *) buf->addr, size);
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void mx1_mx2_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+int mx1_mx2_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &mx1_mx2_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ pr_debug("%s: preallocate playback buffer\n", __func__);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ pr_debug("%s: preallocate capture buffer\n", __func__);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform mx1_mx2_soc_platform = {
+ .name = "mx1_mx2-audio",
+ .pcm_ops = &mx1_mx2_pcm_ops,
+ .pcm_new = mx1_mx2_pcm_new,
+ .pcm_free = mx1_mx2_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(mx1_mx2_soc_platform);
+
+static int __init mx1_mx2_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&mx1_mx2_soc_platform);
+}
+module_init(mx1_mx2_soc_platform_init);
+
+static void __exit mx1_mx2_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&mx1_mx2_soc_platform);
+}
+module_exit(mx1_mx2_soc_platform_exit);
+
+MODULE_AUTHOR("Javier Martin, javier.martin(a)vista-silicon.com");
+MODULE_DESCRIPTION("Freescale i.MX2x, i.MX1x PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mx1_mx2-pcm.h b/sound/soc/imx/mx1_mx2-pcm.h
new file mode 100644
index 0000000..e1e3a3f
--- /dev/null
+++ b/sound/soc/imx/mx1_mx2-pcm.h
@@ -0,0 +1,48 @@
+/*
+ * mx1_mx2-pcm.h :- ASoC platform header for Freescale i.MX1x, i.MX2x
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MXC_PCM_H
+#define _MXC_PCM_H
+
+/* AUDMUX register definitions */
+#define AUDMUX_IO_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define DAM_HPCR1 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x00)))
+#define DAM_HPCR2 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x04)))
+#define DAM_HPCR3 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x08)))
+#define DAM_PPCR1 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x10)))
+#define DAM_PPCR2 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x14)))
+#define DAM_PPCR3 (*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x1C)))
+
+#define AUDMUX_HPCR_TFSDIR (1 << 31)
+#define AUDMUX_HPCR_TCLKDIR (1 << 30)
+#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0xff) << 26)
+#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
+#define AUDMUX_HPCR_SYN (1 << 12)
+
+#define AUDMUX_PPCR_TFSDIR (1 << 31)
+#define AUDMUX_PPCR_TCLKDIR (1 << 30)
+#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0xff) << 26)
+#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
+#define AUDMUX_PPCR_SYN (1 << 12)
+
+/* DMA information for mx1_mx2 platforms */
+struct mx1_mx2_pcm_dma_params {
+ char *name; /* stream identifier */
+ unsigned int transfer_type; /* READ or WRITE DMA transfer */
+ dma_addr_t per_address; /* physical address of SSI fifo */
+ int event_id; /* fixed DMA number for SSI fifo */
+ int watermark_level; /* SSI fifo watermark level */
+ int per_config; /* DMA Config flags for peripheral */
+ int mem_config; /* DMA Config flags for RAM */
+ };
+
+/* platform data */
+extern struct snd_soc_platform mx1_mx2_soc_platform;
+
+#endif
--
--
Javier Martin
Vista Silicon S.L.
Universidad de Cantabria
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
2
1

[alsa-devel] [PATCH - BT and FM audio for zoom2 1/2] Add clock-only codec to provide McBSP clock source.
by sean.mcneilï¼ ti.com 04 Aug '09
by sean.mcneilï¼ ti.com 04 Aug '09
04 Aug '09
From: Sean McNeil <sean.mcneil(a)ti.com>
Signed-off-by: Sean McNeil <sean.mcneil(a)ti.com>
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 818fb37..c343ceb 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -56,7 +56,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_AVTXL2PGA (0xC) */
0x00, /* REG_AVTXR2PGA (0xD) */
0x01, /* REG_AUDIO_IF (0xE) */
- 0x00, /* REG_VOICE_IF (0xF) */
+ 0x04, /* REG_VOICE_IF (0xF) */
0x00, /* REG_ARXR1PGA (0x10) */
0x00, /* REG_ARXL1PGA (0x11) */
0x6c, /* REG_ARXR2PGA (0x12) */
@@ -118,25 +118,32 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_SW_SHADOW (0x4A) - Shadow, non HW register */
};
+struct substream_item {
+ struct list_head started;
+ struct list_head configured;
+ struct snd_pcm_substream *substream;
+ int use256FS;
+};
+
/* codec private data */
struct twl4030_priv {
+ struct mutex mutex;
+
+ unsigned int extClock;
unsigned int bypass_state;
unsigned int codec_powered;
unsigned int codec_muted;
- struct snd_pcm_substream *master_substream;
- struct snd_pcm_substream *slave_substream;
-
- unsigned int configured;
- unsigned int rate;
- unsigned int sample_bits;
- unsigned int channels;
+ struct list_head started_list;
+ struct list_head config_list;
unsigned int sysclk;
/* Headset output state handling */
unsigned int hsl_enabled;
unsigned int hsr_enabled;
+
+ struct snd_pcm_hw_params params;
};
/*
@@ -966,7 +973,7 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
unsigned short val;
unsigned short mask, bitmask;
- if (twl4030->configured) {
+ if (!list_empty(&twl4030->config_list)) {
printk(KERN_ERR "twl4030 operation mode cannot be "
"changed on-the-fly\n");
return -EBUSY;
@@ -1534,34 +1541,68 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static void twl4030_constraints(struct twl4030_priv *twl4030,
- struct snd_pcm_substream *mst_substream)
+static unsigned int twl4030_rate_min(struct substream_item *item,
+ unsigned int rate)
{
- struct snd_pcm_substream *slv_substream;
-
- /* Pick the stream, which need to be constrained */
- if (mst_substream == twl4030->master_substream)
- slv_substream = twl4030->slave_substream;
- else if (mst_substream == twl4030->slave_substream)
- slv_substream = twl4030->master_substream;
- else /* This should not happen.. */
- return;
+ static const unsigned int table[] = {
+ 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 96000};
+ unsigned int value = rate;
+
+ if (item->use256FS) {
+ int i;
+ rate *= 256;
+ for (i = 0; i < ARRAY_SIZE(table); i++)
+ if (rate % table[i] == 0) {
+ value = table[i];
+ break;
+ }
+ }
+ return value;
+}
- /* Set the constraints according to the already configured stream */
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- twl4030->rate,
- twl4030->rate);
-
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- twl4030->sample_bits,
- twl4030->sample_bits);
-
- snd_pcm_hw_constraint_minmax(slv_substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- twl4030->channels,
- twl4030->channels);
+static unsigned int twl4030_rate_max(struct substream_item *item,
+ unsigned int rate)
+{
+ static const unsigned int table[] = {
+ 96000, 48000, 44100, 32000, 24000,
+ 22050, 16000, 12000, 11025, 8000};
+ unsigned int value = rate;
+
+ if (item->use256FS) {
+ int i;
+ rate *= 256;
+ for (i = 0; i < ARRAY_SIZE(table); i++)
+ if (rate % table[i] == 0) {
+ value = table[i];
+ break;
+ }
+ }
+ return value;
+}
+
+static void twl4030_constraints(struct twl4030_priv *twl4030)
+{
+ struct substream_item *item;
+ unsigned int value;
+
+ list_for_each_entry(item, &twl4030->started_list, started) {
+
+ /* Set constraints according to the already configured stream */
+ value = params_rate(&twl4030->params);
+ if (value)
+ snd_pcm_hw_constraint_minmax(item->substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ twl4030_rate_min(item, value),
+ twl4030_rate_max(item, value));
+
+ value = hw_param_interval(&twl4030->params,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+ if (value && !item->use256FS)
+ snd_pcm_hw_constraint_minmax(item->substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ value, value);
+ }
}
/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
@@ -1586,6 +1627,53 @@ static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
twl4030_write(codec, TWL4030_REG_OPTION, reg);
}
+static int twl4030_new_substream(struct twl4030_priv *twl4030,
+ struct snd_pcm_substream *substream, int use256FS)
+{
+ struct substream_item *item;
+
+ item = kzalloc(sizeof(struct snd_pcm_substream), GFP_KERNEL);
+ if (!item)
+ return -ENOMEM;
+
+ item->substream = substream;
+ item->use256FS = use256FS;
+
+ mutex_lock(&twl4030->mutex);
+ list_add_tail(&item->started, &twl4030->started_list);
+ twl4030->extClock += item->use256FS;
+ mutex_unlock(&twl4030->mutex);
+
+ return 0;
+}
+
+static void twl4030_del_substream(struct twl4030_priv *twl4030,
+ struct snd_pcm_substream *substream)
+{
+ struct substream_item *item;
+
+ mutex_lock(&twl4030->mutex);
+
+ list_for_each_entry(item, &twl4030->config_list, configured) {
+ if (item->substream == substream) {
+ printk(KERN_ERR "TWL4030 deleted substream still configured!\n");
+ list_del(&item->configured);
+ break;
+ }
+ }
+
+ list_for_each_entry(item, &twl4030->started_list, started) {
+ if (item->substream == substream) {
+ list_del(&item->started);
+ twl4030->extClock -= item->use256FS;
+ kfree(item);
+ break;
+ }
+ }
+
+ mutex_unlock(&twl4030->mutex);
+}
+
static int twl4030_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -1594,82 +1682,45 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = socdev->card->codec;
struct twl4030_priv *twl4030 = codec->private_data;
- if (twl4030->master_substream) {
- twl4030->slave_substream = substream;
- /* The DAI has one configuration for playback and capture, so
- * if the DAI has been already configured then constrain this
- * substream to match it. */
- if (twl4030->configured)
- twl4030_constraints(twl4030, twl4030->master_substream);
- } else {
- if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
- TWL4030_OPTION_1)) {
- /* In option2 4 channel is not supported, set the
- * constraint for the first stream for channels, the
- * second stream will 'inherit' this cosntraint */
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- 2, 2);
- }
- twl4030->master_substream = substream;
+ if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
+ TWL4030_OPTION_1)) {
+ /* In option2 4 channel is not supported, set the
+ * constraint for the first stream for channels, the
+ * second stream will 'inherit' this cosntraint */
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 2, 2);
}
- return 0;
+ return twl4030_new_substream(twl4030, substream, 0);
}
static void twl4030_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct twl4030_priv *twl4030 = codec->private_data;
- if (twl4030->master_substream == substream)
- twl4030->master_substream = twl4030->slave_substream;
-
- twl4030->slave_substream = NULL;
-
- /* If all streams are closed, or the remaining stream has not yet
- * been configured than set the DAI as not configured. */
- if (!twl4030->master_substream)
- twl4030->configured = 0;
- else if (!twl4030->master_substream->runtime->channels)
- twl4030->configured = 0;
+ twl4030_del_substream(twl4030, substream);
/* If the closing substream had 4 channel, do the necessary cleanup */
if (substream->runtime->channels == 4)
twl4030_tdm_enable(codec, substream->stream, 0);
}
-static int twl4030_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+int twl4030_set_rate(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->card->codec;
struct twl4030_priv *twl4030 = codec->private_data;
- u8 mode, old_mode, format, old_format;
+ u8 mode, old_mode;
- /* If the substream has 4 channel, do the necessary setup */
- if (params_channels(params) == 4) {
- format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
- mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
-
- /* Safety check: are we in the correct operating mode and
- * the interface is in TDM mode? */
- if ((mode & TWL4030_OPTION_1) &&
- ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM))
- twl4030_tdm_enable(codec, substream->stream, 1);
- else
- return -EINVAL;
+ if (params_rate(&twl4030->params) &&
+ params_rate(&twl4030->params) != params_rate(params)) {
+ return -EBUSY;
}
- if (twl4030->configured)
- /* Ignoring hw_params for already configured DAI */
- return 0;
-
/* bit rate */
old_mode = twl4030_read_reg_cache(codec,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
@@ -1712,6 +1763,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ params_rate(&twl4030->params) = params_rate(params);
+
if (mode != old_mode) {
/* change rate and set CODECPDZ */
twl4030_codec_enable(codec, 0);
@@ -1719,10 +1772,51 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
twl4030_codec_enable(codec, 1);
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(twl4030_set_rate);
+
+int twl4030_get_clock_divisor(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct twl4030_priv *twl4030 = codec->private_data;
+ int clock, divisor;
+
+ clock = params_rate(&twl4030->params) * 256;
+ divisor = clock / params_rate(params);
+ divisor /= params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ divisor /= 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ divisor /= 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ divisor /= 24;
+ break;
+ default:
+ printk(KERN_ERR "TWL4030 get_clock_divisor: unknown format %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ return divisor;
+}
+EXPORT_SYMBOL_GPL(twl4030_get_clock_divisor);
+
+static int twl4030_set_format(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct twl4030_priv *twl4030 = codec->private_data;
+ u8 format, old_format;
+
/* sample size */
old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
- format = old_format;
- format &= ~TWL4030_DATA_WIDTH;
+ format = old_format & ~TWL4030_DATA_WIDTH;
+
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
format |= TWL4030_DATA_WIDTH_16S_16W;
@@ -1736,31 +1830,96 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (format != old_format) {
+ if (format == old_format)
+ return 0;
- /* clear CODECPDZ before changing format (codec requirement) */
- twl4030_codec_enable(codec, 0);
+ if (params_format(&twl4030->params) &&
+ params_format(&twl4030->params) != params_format(params))
+ return -EBUSY;
- /* change format */
- twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+ *hw_param_mask(&twl4030->params, SNDRV_PCM_HW_PARAM_FORMAT) =
+ *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- /* set CODECPDZ afterwards */
- twl4030_codec_enable(codec, 1);
+ /* clear CODECPDZ before changing format (codec requirement) */
+ twl4030_codec_enable(codec, 0);
+
+ /* change format */
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+ /* set CODECPDZ afterwards */
+ twl4030_codec_enable(codec, 1);
+
+ return 0;
+}
+
+static int twl4030_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+ int rval;
+
+ mutex_lock(&twl4030->mutex);
+
+ /* If the substream has 4 channel, do the necessary setup */
+ if (params_channels(params) == 4) {
+ /* Safety check: are we in the correct operating mode? */
+ if ((twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
+ TWL4030_OPTION_1)) {
+ twl4030_tdm_enable(codec, substream->stream, 1);
+ } else {
+ mutex_unlock(&twl4030->mutex);
+ return -EINVAL;
+ }
}
- /* Store the important parameters for the DAI configuration and set
- * the DAI as configured */
- twl4030->configured = 1;
- twl4030->rate = params_rate(params);
- twl4030->sample_bits = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
- twl4030->channels = params_channels(params);
+ rval = twl4030_set_rate(codec, params);
+ if (rval < 0) {
+ mutex_unlock(&twl4030->mutex);
+ return rval;
+ }
- /* If both playback and capture streams are open, and one of them
+ rval = twl4030_set_format(codec, params);
+ if (rval < 0) {
+ mutex_unlock(&twl4030->mutex);
+ return rval;
+ }
+
+ /* If any other streams are currently open, and one of them
* is setting the hw parameters right now (since we are here), set
- * constraints to the other stream to match the current one. */
- if (twl4030->slave_substream)
- twl4030_constraints(twl4030, substream);
+ * constraints to the other stream(s) to match the current one. */
+ twl4030_constraints(twl4030);
+
+ mutex_unlock(&twl4030->mutex);
+
+ return 0;
+}
+
+static int twl4030_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+ struct substream_item *item;
+
+ mutex_lock(&twl4030->mutex);
+
+ list_for_each_entry(item, &twl4030->config_list, configured) {
+ if (item->substream == substream) {
+ list_del(&item->configured);
+ break;
+ }
+ }
+
+ if (list_empty(&twl4030->config_list))
+ memset(&twl4030->params, 0, sizeof(twl4030->params));
+
+ mutex_unlock(&twl4030->mutex);
return 0;
}
@@ -1797,10 +1956,39 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+static int twl4030_set_ext_clock(struct snd_soc_codec *codec, int enable)
+{
+ u8 old_format, format;
+
+ /* get format */
+ old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+
+ if (enable)
+ format = old_format | TWL4030_CLK256FS_EN;
+ else
+ format = old_format & ~TWL4030_CLK256FS_EN;
+
+ if (format != old_format) {
+
+ /* clear CODECPDZ before changing format (codec requirement) */
+ twl4030_codec_enable(codec, 0);
+
+ /* change format */
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+ /* set CODECPDZ afterwards */
+ twl4030_codec_enable(codec, 1);
+ }
+
+ return 0;
+}
+
static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+ int use256FS = 0;
u8 old_format, format;
/* get format */
@@ -1811,11 +1999,10 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
format &= ~(TWL4030_AIF_SLAVE_EN);
- format &= ~(TWL4030_CLK256FS_EN);
break;
case SND_SOC_DAIFMT_CBS_CFS:
format |= TWL4030_AIF_SLAVE_EN;
- format |= TWL4030_CLK256FS_EN;
+ use256FS = 1;
break;
default:
return -EINVAL;
@@ -1846,7 +2033,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
twl4030_codec_enable(codec, 1);
}
- return 0;
+ return twl4030_set_ext_clock(codec, use256FS | twl4030->extClock);
}
static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
@@ -1890,6 +2077,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
u8 infreq;
u8 mode;
@@ -1917,7 +2105,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
return -EINVAL;
}
- return 0;
+ return twl4030_new_substream(twl4030, substream, 1);
}
static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
@@ -1926,6 +2114,9 @@ static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+
+ twl4030_del_substream(twl4030, substream);
/* Enable voice digital filters */
twl4030_voice_enable(codec, substream->stream, 0);
@@ -1937,15 +2128,18 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
u8 old_mode, mode;
/* Enable voice digital filters */
twl4030_voice_enable(codec, substream->stream, 1);
+ mutex_lock(&twl4030->mutex);
+
/* bit rate */
- old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
- & ~(TWL4030_CODECPDZ);
- mode = old_mode;
+ old_mode = twl4030_read_reg_cache(codec,
+ TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
+ mode = old_mode & ~TWL4030_APLL_RATE;
switch (params_rate(params)) {
case 8000:
@@ -1955,6 +2149,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
mode |= TWL4030_SEL_16K;
break;
default:
+ mutex_unlock(&twl4030->mutex);
printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n",
params_rate(params));
return -EINVAL;
@@ -1967,6 +2162,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
twl4030_codec_enable(codec, 1);
}
+ mutex_unlock(&twl4030->mutex);
return 0;
}
@@ -1996,19 +2192,22 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+ int use256FS = 0;
u8 old_format, format;
/* get format */
old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
- format = old_format;
+ format = old_format & ~TWL4030_VIF_TRI_EN;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBS_CFM:
format &= ~(TWL4030_VIF_SLAVE_EN);
break;
case SND_SOC_DAIFMT_CBS_CFS:
format |= TWL4030_VIF_SLAVE_EN;
+ use256FS = 1;
break;
default:
return -EINVAL;
@@ -2033,7 +2232,59 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
twl4030_codec_enable(codec, 1);
}
- return 0;
+ return twl4030_set_ext_clock(codec, use256FS | twl4030->extClock);
+}
+
+static int twl4030_clock_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+
+ return twl4030_new_substream(twl4030, substream, 1);
+}
+
+static int twl4030_clock_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct twl4030_priv *twl4030 = codec->private_data;
+ int rval;
+
+ mutex_lock(&twl4030->mutex);
+
+ rval = twl4030_set_rate(codec, params);
+
+ /* See if we are a multiple of the current FS. If so, then still OK. */
+ if (rval) {
+ int divisor = twl4030_get_clock_divisor(codec, params);
+ int clock = params_rate(&twl4030->params) * 256;
+ int remainder = clock % params_rate(params);
+
+ if (remainder == 0 && divisor <= 256)
+ rval = 0;
+ }
+
+ /* If any other streams are currently open, and one of them
+ * is setting the hw parameters right now (since we are here), set
+ * constraints to the other stream(s) to match the current one. */
+ twl4030_constraints(twl4030);
+
+ mutex_unlock(&twl4030->mutex);
+
+ return rval;
+}
+
+static int twl4030_clock_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ return twl4030_set_ext_clock(codec, 1);
}
static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
@@ -2056,6 +2307,7 @@ static struct snd_soc_dai_ops twl4030_dai_ops = {
.startup = twl4030_startup,
.shutdown = twl4030_shutdown,
.hw_params = twl4030_hw_params,
+ .hw_free = twl4030_hw_free,
.set_sysclk = twl4030_set_dai_sysclk,
.set_fmt = twl4030_set_dai_fmt,
.set_tristate = twl4030_set_tristate,
@@ -2065,26 +2317,38 @@ static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
.startup = twl4030_voice_startup,
.shutdown = twl4030_voice_shutdown,
.hw_params = twl4030_voice_hw_params,
+ .hw_free = twl4030_hw_free,
.set_sysclk = twl4030_voice_set_dai_sysclk,
.set_fmt = twl4030_voice_set_dai_fmt,
.set_tristate = twl4030_voice_set_tristate,
};
+static struct snd_soc_dai_ops twl4030_dai_clock_ops = {
+ .startup = twl4030_clock_startup,
+ .shutdown = twl4030_shutdown,
+ .hw_params = twl4030_clock_hw_params,
+ .hw_free = twl4030_hw_free,
+ .set_sysclk = twl4030_set_dai_sysclk,
+ .set_fmt = twl4030_clock_set_dai_fmt,
+};
+
struct snd_soc_dai twl4030_dai[] = {
{
.name = "twl4030",
.playback = {
.stream_name = "HiFi Playback",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 4,
.rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
- .formats = TWL4030_FORMATS,},
+ .formats = TWL4030_FORMATS,
+ },
.capture = {
.stream_name = "Capture",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 4,
.rates = TWL4030_RATES,
- .formats = TWL4030_FORMATS,},
+ .formats = TWL4030_FORMATS,
+ },
.ops = &twl4030_dai_ops,
},
{
@@ -2092,17 +2356,37 @@ struct snd_soc_dai twl4030_dai[] = {
.playback = {
.stream_name = "Voice Playback",
.channels_min = 1,
- .channels_max = 1,
+ .channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
.ops = &twl4030_dai_voice_ops,
},
+{
+ .name = "twl4030 Clock",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL4030_RATES,
+ .formats = SNDRV_PCM_FMTBIT_U8 | TWL4030_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TWL4030_RATES,
+ .formats = SNDRV_PCM_FMTBIT_U8 | TWL4030_FORMATS,
+ },
+ .ops = &twl4030_dai_clock_ops,
+},
};
EXPORT_SYMBOL_GPL(twl4030_dai);
@@ -2220,6 +2504,9 @@ static int twl4030_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ mutex_init(&twl4030->mutex);
+ INIT_LIST_HEAD(&twl4030->started_list);
+ INIT_LIST_HEAD(&twl4030->config_list);
codec->private_data = twl4030;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
@@ -2257,13 +2544,13 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
static int __init twl4030_modinit(void)
{
- return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ return snd_soc_register_dais(twl4030_dai, ARRAY_SIZE(twl4030_dai));
}
module_init(twl4030_modinit);
static void __exit twl4030_exit(void)
{
- snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+ snd_soc_unregister_dais(twl4030_dai, ARRAY_SIZE(twl4030_dai));
}
module_exit(twl4030_exit);
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 2b4bfa2..fa23613 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -267,10 +267,15 @@
#define TWL4030_DAI_HIFI 0
#define TWL4030_DAI_VOICE 1
+#define TWL4030_DAI_CLOCK 2
-extern struct snd_soc_dai twl4030_dai[2];
+extern struct snd_soc_dai twl4030_dai[];
extern struct snd_soc_codec_device soc_codec_dev_twl4030;
+extern int twl4030_set_rate(struct snd_soc_codec *, struct snd_pcm_hw_params *);
+extern int twl4030_get_clock_divisor(struct snd_soc_codec *,
+ struct snd_pcm_hw_params *);
+
struct twl4030_setup_data {
unsigned int ramp_delay_value;
unsigned int sysclk;
--
1.6.0.4
3
3

[alsa-devel] [PATCH 1/8] ASoC: Begin to factor out register cache I/O functions
by Mark Brown 03 Aug '09
by Mark Brown 03 Aug '09
03 Aug '09
A lot of CODECs share the same register data formats and therefore
replicate the code to manage access to and caching of the register
map. In order to reduce code duplication centralised versions of
this code will be introduced with drivers able to configure the use
of the common code by calling the new snd_soc_codec_set_cache_io()
API call during startup.
As an initial user the 7 bit address/9 bit data format used by many
Wolfson devices is supported for write only CODECs and the drivers
with straightforward register cache implementations are converted to
use it.
Signed-off-by: Mark Brown <broonie(a)opensource.wolfsonmicro.com>
---
include/sound/soc.h | 2 +
sound/soc/Makefile | 2 +-
sound/soc/codecs/wm8510.c | 141 +++++++++++---------------------
sound/soc/codecs/wm8728.c | 78 +++++-------------
sound/soc/codecs/wm8731.c | 108 +++++++------------------
sound/soc/codecs/wm8750.c | 121 ++++++++++------------------
sound/soc/codecs/wm8960.c | 194 +++++++++++++++++----------------------------
sound/soc/codecs/wm8971.c | 121 ++++++++++------------------
sound/soc/codecs/wm8988.c | 105 ++++++++-----------------
sound/soc/soc-cache.c | 105 ++++++++++++++++++++++++
10 files changed, 400 insertions(+), 577 deletions(-)
create mode 100644 sound/soc/soc-cache.c
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 94fcc65..27409dd 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -192,6 +192,8 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform);
int snd_soc_register_codec(struct snd_soc_codec *codec);
void snd_soc_unregister_codec(struct snd_soc_codec *codec);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+ int addr_bits, int data_bits);
#ifdef CONFIG_PM
int snd_soc_suspend_device(struct device *dev);
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 6f1e28d..4eaf48a 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index c8b8dba..7a169bf 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -58,55 +58,7 @@ static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
#define WM8510_POWER1_BIASEN 0x08
#define WM8510_POWER1_BUFIOEN 0x10
-/*
- * read wm8510 register cache
- */
-static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == WM8510_RESET)
- return 0;
- if (reg >= WM8510_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8510 register cache
- */
-static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= WM8510_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the WM8510 register space
- */
-static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8510 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8510_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
+#define wm8510_reset(c) snd_soc_write(c, WM8510_RESET, 0)
static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
@@ -327,27 +279,27 @@ static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
- wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+ reg = snd_soc_read(codec, WM8510_CLOCK);
+ snd_soc_write(codec, WM8510_CLOCK, reg & 0x0ff);
/* Turn off PLL */
- reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
- wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+ reg = snd_soc_read(codec, WM8510_POWER1);
+ snd_soc_write(codec, WM8510_POWER1, reg & 0x1df);
return 0;
}
pll_factors(freq_out*4, freq_in);
- wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
- wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
- wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
- wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
- reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
- wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+ snd_soc_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+ snd_soc_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+ snd_soc_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+ reg = snd_soc_read(codec, WM8510_POWER1);
+ snd_soc_write(codec, WM8510_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
- wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+ reg = snd_soc_read(codec, WM8510_CLOCK);
+ snd_soc_write(codec, WM8510_CLOCK, reg | 0x100);
return 0;
}
@@ -363,24 +315,24 @@ static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8510_OPCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
- wm8510_write(codec, WM8510_GPIO, reg | div);
+ reg = snd_soc_read(codec, WM8510_GPIO) & 0x1cf;
+ snd_soc_write(codec, WM8510_GPIO, reg | div);
break;
case WM8510_MCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f;
- wm8510_write(codec, WM8510_CLOCK, reg | div);
+ reg = snd_soc_read(codec, WM8510_CLOCK) & 0x11f;
+ snd_soc_write(codec, WM8510_CLOCK, reg | div);
break;
case WM8510_ADCCLK:
- reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
- wm8510_write(codec, WM8510_ADC, reg | div);
+ reg = snd_soc_read(codec, WM8510_ADC) & 0x1f7;
+ snd_soc_write(codec, WM8510_ADC, reg | div);
break;
case WM8510_DACCLK:
- reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
- wm8510_write(codec, WM8510_DAC, reg | div);
+ reg = snd_soc_read(codec, WM8510_DAC) & 0x1f7;
+ snd_soc_write(codec, WM8510_DAC, reg | div);
break;
case WM8510_BCLKDIV:
- reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
- wm8510_write(codec, WM8510_CLOCK, reg | div);
+ reg = snd_soc_read(codec, WM8510_CLOCK) & 0x1e3;
+ snd_soc_write(codec, WM8510_CLOCK, reg | div);
break;
default:
return -EINVAL;
@@ -394,7 +346,7 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 iface = 0;
- u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+ u16 clk = snd_soc_read(codec, WM8510_CLOCK) & 0x1fe;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -441,8 +393,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8510_write(codec, WM8510_IFACE, iface);
- wm8510_write(codec, WM8510_CLOCK, clk);
+ snd_soc_write(codec, WM8510_IFACE, iface);
+ snd_soc_write(codec, WM8510_CLOCK, clk);
return 0;
}
@@ -453,8 +405,8 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
- u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+ u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f;
+ u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1;
/* bit size */
switch (params_format(params)) {
@@ -493,20 +445,20 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8510_write(codec, WM8510_IFACE, iface);
- wm8510_write(codec, WM8510_ADD, adn);
+ snd_soc_write(codec, WM8510_IFACE, iface);
+ snd_soc_write(codec, WM8510_ADD, adn);
return 0;
}
static int wm8510_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+ u16 mute_reg = snd_soc_read(codec, WM8510_DAC) & 0xffbf;
if (mute)
- wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+ snd_soc_write(codec, WM8510_DAC, mute_reg | 0x40);
else
- wm8510_write(codec, WM8510_DAC, mute_reg);
+ snd_soc_write(codec, WM8510_DAC, mute_reg);
return 0;
}
@@ -514,13 +466,13 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute)
static int wm8510_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3;
+ u16 power1 = snd_soc_read(codec, WM8510_POWER1) & ~0x3;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
power1 |= 0x1; /* VMID 50k */
- wm8510_write(codec, WM8510_POWER1, power1);
+ snd_soc_write(codec, WM8510_POWER1, power1);
break;
case SND_SOC_BIAS_STANDBY:
@@ -528,18 +480,18 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Initial cap charge at VMID 5k */
- wm8510_write(codec, WM8510_POWER1, power1 | 0x3);
+ snd_soc_write(codec, WM8510_POWER1, power1 | 0x3);
mdelay(100);
}
power1 |= 0x2; /* VMID 500k */
- wm8510_write(codec, WM8510_POWER1, power1);
+ snd_soc_write(codec, WM8510_POWER1, power1);
break;
case SND_SOC_BIAS_OFF:
- wm8510_write(codec, WM8510_POWER1, 0);
- wm8510_write(codec, WM8510_POWER2, 0);
- wm8510_write(codec, WM8510_POWER3, 0);
+ snd_soc_write(codec, WM8510_POWER1, 0);
+ snd_soc_write(codec, WM8510_POWER2, 0);
+ snd_soc_write(codec, WM8510_POWER3, 0);
break;
}
@@ -619,8 +571,6 @@ static int wm8510_init(struct snd_soc_device *socdev)
codec->name = "WM8510";
codec->owner = THIS_MODULE;
- codec->read = wm8510_read_reg_cache;
- codec->write = wm8510_write;
codec->set_bias_level = wm8510_set_bias_level;
codec->dai = &wm8510_dai;
codec->num_dai = 1;
@@ -630,13 +580,20 @@ static int wm8510_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8510: failed to set cache I/O: %d\n",
+ ret);
+ goto err;
+ }
+
wm8510_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8510: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* power on device */
@@ -655,7 +612,7 @@ static int wm8510_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index e7ff212..66da44b 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -43,45 +43,6 @@ static const u16 wm8728_reg_defaults[] = {
0x100,
};
-static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
- return cache[reg];
-}
-
-static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
- cache[reg] = value;
-}
-
-/*
- * write to the WM8728 register space
- */
-static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8728 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8728_write_reg_cache(codec, reg, value);
-
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
static const struct snd_kcontrol_new wm8728_snd_controls[] = {
@@ -121,12 +82,12 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec)
static int wm8728_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+ u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL);
if (mute)
- wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
+ snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1);
else
- wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
+ snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1);
return 0;
}
@@ -138,7 +99,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+ u16 dac = snd_soc_read(codec, WM8728_DACCTL);
dac &= ~0x18;
@@ -155,7 +116,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- wm8728_write(codec, WM8728_DACCTL, dac);
+ snd_soc_write(codec, WM8728_DACCTL, dac);
return 0;
}
@@ -164,7 +125,7 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
+ u16 iface = snd_soc_read(codec, WM8728_IFCTL);
/* Currently only I2S is supported by the driver, though the
* hardware is more flexible.
@@ -204,7 +165,7 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8728_write(codec, WM8728_IFCTL, iface);
+ snd_soc_write(codec, WM8728_IFCTL, iface);
return 0;
}
@@ -220,19 +181,19 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Power everything up... */
- reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
- wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
+ reg = snd_soc_read(codec, WM8728_DACCTL);
+ snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
/* ..then sync in the register cache. */
for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
- wm8728_write(codec, i,
- wm8728_read_reg_cache(codec, i));
+ snd_soc_write(codec, i,
+ snd_soc_read(codec, i));
}
break;
case SND_SOC_BIAS_OFF:
- reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
- wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
+ reg = snd_soc_read(codec, WM8728_DACCTL);
+ snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
break;
}
codec->bias_level = level;
@@ -294,8 +255,6 @@ static int wm8728_init(struct snd_soc_device *socdev)
codec->name = "WM8728";
codec->owner = THIS_MODULE;
- codec->read = wm8728_read_reg_cache;
- codec->write = wm8728_write;
codec->set_bias_level = wm8728_set_bias_level;
codec->dai = &wm8728_dai;
codec->num_dai = 1;
@@ -307,11 +266,18 @@ static int wm8728_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n",
+ ret);
+ goto err;
+ }
+
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8728: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* power on device */
@@ -331,7 +297,7 @@ static int wm8728_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index dfbc1bb..4eb84ff 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -50,60 +50,12 @@ static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
* There is no point in caching the reset register
*/
static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
+ 0x0000, 0x0000
};
-/*
- * read wm8731 register cache
- */
-static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == WM8731_RESET)
- return 0;
- if (reg >= WM8731_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8731 register cache
- */
-static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= WM8731_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-/*
- * write to the WM8731 register space
- */
-static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8731 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8731_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
+#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0)
static const char *wm8731_input_select[] = {"Line In", "Mic"};
static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
@@ -260,12 +212,12 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8731_priv *wm8731 = codec->private_data;
- u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
+ u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3;
int i = get_coeff(wm8731->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
- wm8731_write(codec, WM8731_SRATE, srate);
+ snd_soc_write(codec, WM8731_SRATE, srate);
/* bit size */
switch (params_format(params)) {
@@ -279,7 +231,7 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
break;
}
- wm8731_write(codec, WM8731_IFACE, iface);
+ snd_soc_write(codec, WM8731_IFACE, iface);
return 0;
}
@@ -291,7 +243,7 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
- wm8731_write(codec, WM8731_ACTIVE, 0x0001);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0001);
return 0;
}
@@ -306,19 +258,19 @@ static void wm8731_shutdown(struct snd_pcm_substream *substream,
/* deactivate */
if (!codec->active) {
udelay(50);
- wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
}
}
static int wm8731_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7;
if (mute)
- wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);
+ snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8);
else
- wm8731_write(codec, WM8731_APDIGI, mute_reg);
+ snd_soc_write(codec, WM8731_APDIGI, mute_reg);
return 0;
}
@@ -396,7 +348,7 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- wm8731_write(codec, WM8731_IFACE, iface);
+ snd_soc_write(codec, WM8731_IFACE, iface);
return 0;
}
@@ -412,12 +364,12 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* Clear PWROFF, gate CLKOUT, everything else as-is */
- reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
- wm8731_write(codec, WM8731_PWR, reg | 0x0040);
+ reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f;
+ snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
- wm8731_write(codec, WM8731_ACTIVE, 0x0);
- wm8731_write(codec, WM8731_PWR, 0xffff);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8731_PWR, 0xffff);
break;
}
codec->bias_level = level;
@@ -466,7 +418,7 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
- wm8731_write(codec, WM8731_ACTIVE, 0x0);
+ snd_soc_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -556,7 +508,6 @@ static int wm8731_register(struct wm8731_priv *wm8731)
{
int ret;
struct snd_soc_codec *codec = &wm8731->codec;
- u16 reg;
if (wm8731_codec) {
dev_err(codec->dev, "Another WM8731 is registered\n");
@@ -571,8 +522,6 @@ static int wm8731_register(struct wm8731_priv *wm8731)
codec->private_data = wm8731;
codec->name = "WM8731";
codec->owner = THIS_MODULE;
- codec->read = wm8731_read_reg_cache;
- codec->write = wm8731_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8731_set_bias_level;
codec->dai = &wm8731_dai;
@@ -582,6 +531,12 @@ static int wm8731_register(struct wm8731_priv *wm8731)
memcpy(codec->reg_cache, wm8731_reg, sizeof(wm8731_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
ret = wm8731_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
@@ -593,18 +548,13 @@ static int wm8731_register(struct wm8731_priv *wm8731)
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
- reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
- wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
- wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
- wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
- reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
- wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
+ snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0);
+ snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0);
/* Disable bypass path by default */
- reg = wm8731_read_reg_cache(codec, WM8731_APANA);
- wm8731_write(codec, WM8731_APANA, reg & ~0x4);
+ snd_soc_update_bits(codec, WM8731_APANA, 0x4, 0);
wm8731_codec = codec;
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index b64509b..ed09043 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -55,50 +55,7 @@ static const u16 wm8750_reg[] = {
0x0079, 0x0079, 0x0079, /* 40 */
};
-/*
- * read wm8750 register cache
- */
-static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8750_CACHE_REGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8750 register cache
- */
-static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8750_CACHE_REGNUM)
- return;
- cache[reg] = value;
-}
-
-static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8753 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8750_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0)
+#define wm8750_reset(c) snd_soc_write(c, WM8750_RESET, 0)
/*
* WM8750 Controls
@@ -594,7 +551,7 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8750_write(codec, WM8750_IFACE, iface);
+ snd_soc_write(codec, WM8750_IFACE, iface);
return 0;
}
@@ -606,8 +563,8 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8750_priv *wm8750 = codec->private_data;
- u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
- u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
+ u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
int coeff = get_coeff(wm8750->sysclk, params_rate(params));
/* bit size */
@@ -626,9 +583,9 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- wm8750_write(codec, WM8750_IFACE, iface);
+ snd_soc_write(codec, WM8750_IFACE, iface);
if (coeff >= 0)
- wm8750_write(codec, WM8750_SRATE, srate |
+ snd_soc_write(codec, WM8750_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
@@ -637,35 +594,35 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8750_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8750_ADCDAC) & 0xfff7;
if (mute)
- wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
+ snd_soc_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
else
- wm8750_write(codec, WM8750_ADCDAC, mute_reg);
+ snd_soc_write(codec, WM8750_ADCDAC, mute_reg);
return 0;
}
static int wm8750_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
+ u16 pwr_reg = snd_soc_read(codec, WM8750_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
- wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
break;
case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
- wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
+ snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
break;
case SND_SOC_BIAS_OFF:
- wm8750_write(codec, WM8750_PWR1, 0x0001);
+ snd_soc_write(codec, WM8750_PWR1, 0x0001);
break;
}
codec->bias_level = level;
@@ -761,8 +718,6 @@ static int wm8750_init(struct snd_soc_device *socdev)
codec->name = "WM8750";
codec->owner = THIS_MODULE;
- codec->read = wm8750_read_reg_cache;
- codec->write = wm8750_write;
codec->set_bias_level = wm8750_set_bias_level;
codec->dai = &wm8750_dai;
codec->num_dai = 1;
@@ -771,13 +726,23 @@ static int wm8750_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
- wm8750_reset(codec);
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
+ ret = wm8750_reset(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8750: failed to reset: %d\n", ret);
+ goto err;
+ }
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8750: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* charge output caps */
@@ -786,22 +751,22 @@ static int wm8750_init(struct snd_soc_device *socdev)
schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
/* set the update bits */
- reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
- wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
- wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
- wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
- wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
- wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
- wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
- wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
- reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
- wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LDAC);
+ snd_soc_write(codec, WM8750_LDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_RDAC);
+ snd_soc_write(codec, WM8750_RDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LOUT1V);
+ snd_soc_write(codec, WM8750_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_ROUT1V);
+ snd_soc_write(codec, WM8750_ROUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LOUT2V);
+ snd_soc_write(codec, WM8750_LOUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_ROUT2V);
+ snd_soc_write(codec, WM8750_ROUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_LINVOL);
+ snd_soc_write(codec, WM8750_LINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8750_RINVOL);
+ snd_soc_write(codec, WM8750_RINVOL, reg | 0x0100);
snd_soc_add_controls(codec, wm8750_snd_controls,
ARRAY_SIZE(wm8750_snd_controls));
@@ -816,7 +781,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index d1769e6..c529ffc 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -69,61 +69,7 @@ struct wm8960_priv {
struct snd_soc_codec codec;
};
-/*
- * read wm8960 register cache
- */
-static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg == WM8960_RESET)
- return 0;
- if (reg >= WM8960_CACHEREGNUM)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8960 register cache
- */
-static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg >= WM8960_CACHEREGNUM)
- return;
- cache[reg] = value;
-}
-
-static inline unsigned int wm8960_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- return wm8960_read_reg_cache(codec, reg);
-}
-
-/*
- * write to the WM8960 register space
- */
-static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8960 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8960_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8960_reset(c) wm8960_write(c, WM8960_RESET, 0)
+#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)
/* enumerated controls */
static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
@@ -420,7 +366,7 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
/* set iface */
- wm8960_write(codec, WM8960_IFACE1, iface);
+ snd_soc_write(codec, WM8960_IFACE1, iface);
return 0;
}
@@ -431,7 +377,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
- u16 iface = wm8960_read(codec, WM8960_IFACE1) & 0xfff3;
+ u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
/* bit size */
switch (params_format(params)) {
@@ -446,19 +392,19 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
}
/* set iface */
- wm8960_write(codec, WM8960_IFACE1, iface);
+ snd_soc_write(codec, WM8960_IFACE1, iface);
return 0;
}
static int wm8960_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8960_read(codec, WM8960_DACCTL1) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8960_DACCTL1) & 0xfff7;
if (mute)
- wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
+ snd_soc_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
else
- wm8960_write(codec, WM8960_DACCTL1, mute_reg);
+ snd_soc_write(codec, WM8960_DACCTL1, mute_reg);
return 0;
}
@@ -474,16 +420,16 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* Set VMID to 2x50k */
- reg = wm8960_read(codec, WM8960_POWER1);
+ reg = snd_soc_read(codec, WM8960_POWER1);
reg &= ~0x180;
reg |= 0x80;
- wm8960_write(codec, WM8960_POWER1, reg);
+ snd_soc_write(codec, WM8960_POWER1, reg);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Enable anti-pop features */
- wm8960_write(codec, WM8960_APOP1,
+ snd_soc_write(codec, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN | WM8960_BUFIOEN);
@@ -491,43 +437,43 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
reg = WM8960_DISOP;
if (pdata)
reg |= pdata->dres << 4;
- wm8960_write(codec, WM8960_APOP2, reg);
+ snd_soc_write(codec, WM8960_APOP2, reg);
msleep(400);
- wm8960_write(codec, WM8960_APOP2, 0);
+ snd_soc_write(codec, WM8960_APOP2, 0);
/* Enable & ramp VMID at 2x50k */
- reg = wm8960_read(codec, WM8960_POWER1);
+ reg = snd_soc_read(codec, WM8960_POWER1);
reg |= 0x80;
- wm8960_write(codec, WM8960_POWER1, reg);
+ snd_soc_write(codec, WM8960_POWER1, reg);
msleep(100);
/* Enable VREF */
- wm8960_write(codec, WM8960_POWER1, reg | WM8960_VREF);
+ snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);
/* Disable anti-pop features */
- wm8960_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
+ snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
}
/* Set VMID to 2x250k */
- reg = wm8960_read(codec, WM8960_POWER1);
+ reg = snd_soc_read(codec, WM8960_POWER1);
reg &= ~0x180;
reg |= 0x100;
- wm8960_write(codec, WM8960_POWER1, reg);
+ snd_soc_write(codec, WM8960_POWER1, reg);
break;
case SND_SOC_BIAS_OFF:
/* Enable anti-pop features */
- wm8960_write(codec, WM8960_APOP1,
+ snd_soc_write(codec, WM8960_APOP1,
WM8960_POBCTRL | WM8960_SOFT_ST |
WM8960_BUFDCOPEN | WM8960_BUFIOEN);
/* Disable VMID and VREF, let them discharge */
- wm8960_write(codec, WM8960_POWER1, 0);
+ snd_soc_write(codec, WM8960_POWER1, 0);
msleep(600);
- wm8960_write(codec, WM8960_APOP1, 0);
+ snd_soc_write(codec, WM8960_APOP1, 0);
break;
}
@@ -610,33 +556,33 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
/* Disable the PLL: even if we are changing the frequency the
* PLL needs to be disabled while we do so. */
- wm8960_write(codec, WM8960_CLOCK1,
- wm8960_read(codec, WM8960_CLOCK1) & ~1);
- wm8960_write(codec, WM8960_POWER2,
- wm8960_read(codec, WM8960_POWER2) & ~1);
+ snd_soc_write(codec, WM8960_CLOCK1,
+ snd_soc_read(codec, WM8960_CLOCK1) & ~1);
+ snd_soc_write(codec, WM8960_POWER2,
+ snd_soc_read(codec, WM8960_POWER2) & ~1);
if (!freq_in || !freq_out)
return 0;
- reg = wm8960_read(codec, WM8960_PLL1) & ~0x3f;
+ reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
reg |= pll_div.pre_div << 4;
reg |= pll_div.n;
if (pll_div.k) {
reg |= 0x20;
- wm8960_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
- wm8960_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
- wm8960_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
+ snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
+ snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
}
- wm8960_write(codec, WM8960_PLL1, reg);
+ snd_soc_write(codec, WM8960_PLL1, reg);
/* Turn it on */
- wm8960_write(codec, WM8960_POWER2,
- wm8960_read(codec, WM8960_POWER2) | 1);
+ snd_soc_write(codec, WM8960_POWER2,
+ snd_soc_read(codec, WM8960_POWER2) | 1);
msleep(250);
- wm8960_write(codec, WM8960_CLOCK1,
- wm8960_read(codec, WM8960_CLOCK1) | 1);
+ snd_soc_write(codec, WM8960_CLOCK1,
+ snd_soc_read(codec, WM8960_CLOCK1) | 1);
return 0;
}
@@ -649,28 +595,28 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
switch (div_id) {
case WM8960_SYSCLKSEL:
- reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1fe;
- wm8960_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1fe;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
break;
case WM8960_SYSCLKDIV:
- reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1f9;
- wm8960_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
break;
case WM8960_DACDIV:
- reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1c7;
- wm8960_write(codec, WM8960_CLOCK1, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
+ snd_soc_write(codec, WM8960_CLOCK1, reg | div);
break;
case WM8960_OPCLKDIV:
- reg = wm8960_read(codec, WM8960_PLL1) & 0x03f;
- wm8960_write(codec, WM8960_PLL1, reg | div);
+ reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
+ snd_soc_write(codec, WM8960_PLL1, reg | div);
break;
case WM8960_DCLKDIV:
- reg = wm8960_read(codec, WM8960_CLOCK2) & 0x03f;
- wm8960_write(codec, WM8960_CLOCK2, reg | div);
+ reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
+ snd_soc_write(codec, WM8960_CLOCK2, reg | div);
break;
case WM8960_TOCLKSEL:
- reg = wm8960_read(codec, WM8960_ADDCTL1) & 0x1fd;
- wm8960_write(codec, WM8960_ADDCTL1, reg | div);
+ reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
+ snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
break;
default:
return -EINVAL;
@@ -830,8 +776,6 @@ static int wm8960_register(struct wm8960_priv *wm8960)
codec->private_data = wm8960;
codec->name = "WM8960";
codec->owner = THIS_MODULE;
- codec->read = wm8960_read_reg_cache;
- codec->write = wm8960_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8960_set_bias_level;
codec->dai = &wm8960_dai;
@@ -841,6 +785,12 @@ static int wm8960_register(struct wm8960_priv *wm8960)
memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
ret = wm8960_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
@@ -852,26 +802,26 @@ static int wm8960_register(struct wm8960_priv *wm8960)
wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
- reg = wm8960_read(codec, WM8960_LINVOL);
- wm8960_write(codec, WM8960_LINVOL, reg | 0x100);
- reg = wm8960_read(codec, WM8960_RINVOL);
- wm8960_write(codec, WM8960_RINVOL, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LADC);
- wm8960_write(codec, WM8960_LADC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_RADC);
- wm8960_write(codec, WM8960_RADC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LDAC);
- wm8960_write(codec, WM8960_LDAC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_RDAC);
- wm8960_write(codec, WM8960_RDAC, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LOUT1);
- wm8960_write(codec, WM8960_LOUT1, reg | 0x100);
- reg = wm8960_read(codec, WM8960_ROUT1);
- wm8960_write(codec, WM8960_ROUT1, reg | 0x100);
- reg = wm8960_read(codec, WM8960_LOUT2);
- wm8960_write(codec, WM8960_LOUT2, reg | 0x100);
- reg = wm8960_read(codec, WM8960_ROUT2);
- wm8960_write(codec, WM8960_ROUT2, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LINVOL);
+ snd_soc_write(codec, WM8960_LINVOL, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_RINVOL);
+ snd_soc_write(codec, WM8960_RINVOL, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LADC);
+ snd_soc_write(codec, WM8960_LADC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_RADC);
+ snd_soc_write(codec, WM8960_RADC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LDAC);
+ snd_soc_write(codec, WM8960_LDAC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_RDAC);
+ snd_soc_write(codec, WM8960_RDAC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LOUT1);
+ snd_soc_write(codec, WM8960_LOUT1, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_ROUT1);
+ snd_soc_write(codec, WM8960_ROUT1, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_LOUT2);
+ snd_soc_write(codec, WM8960_LOUT2, reg | 0x100);
+ reg = snd_soc_read(codec, WM8960_ROUT2);
+ snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);
wm8960_codec = codec;
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 032dca2..53f3bc9 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -59,44 +59,7 @@ static const u16 wm8971_reg[] = {
0x0079, 0x0079, 0x0079, /* 40 */
};
-static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg < WM8971_REG_COUNT)
- return cache[reg];
-
- return -1;
-}
-
-static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg < WM8971_REG_COUNT)
- cache[reg] = value;
-}
-
-static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8753 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8971_write_reg_cache (codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0)
+#define wm8971_reset(c) snd_soc_write(c, WM8971_RESET, 0)
/* WM8971 Controls */
static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
@@ -521,7 +484,7 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8971_write(codec, WM8971_IFACE, iface);
+ snd_soc_write(codec, WM8971_IFACE, iface);
return 0;
}
@@ -533,8 +496,8 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8971_priv *wm8971 = codec->private_data;
- u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
- u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
+ u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
int coeff = get_coeff(wm8971->sysclk, params_rate(params));
/* bit size */
@@ -553,9 +516,9 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- wm8971_write(codec, WM8971_IFACE, iface);
+ snd_soc_write(codec, WM8971_IFACE, iface);
if (coeff >= 0)
- wm8971_write(codec, WM8971_SRATE, srate |
+ snd_soc_write(codec, WM8971_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
@@ -564,33 +527,33 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8971_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8971_ADCDAC) & 0xfff7;
if (mute)
- wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+ snd_soc_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
else
- wm8971_write(codec, WM8971_ADCDAC, mute_reg);
+ snd_soc_write(codec, WM8971_ADCDAC, mute_reg);
return 0;
}
static int wm8971_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+ u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* mute dac and set vmid to 500k, enable VREF */
- wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
break;
case SND_SOC_BIAS_OFF:
- wm8971_write(codec, WM8971_PWR1, 0x0001);
+ snd_soc_write(codec, WM8971_PWR1, 0x0001);
break;
}
codec->bias_level = level;
@@ -667,8 +630,8 @@ static int wm8971_resume(struct platform_device *pdev)
/* charge wm8971 caps */
if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
- reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
- wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+ reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+ snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
codec->bias_level = SND_SOC_BIAS_ON;
queue_delayed_work(wm8971_workq, &codec->delayed_work,
msecs_to_jiffies(1000));
@@ -684,8 +647,6 @@ static int wm8971_init(struct snd_soc_device *socdev)
codec->name = "WM8971";
codec->owner = THIS_MODULE;
- codec->read = wm8971_read_reg_cache;
- codec->write = wm8971_write;
codec->set_bias_level = wm8971_set_bias_level;
codec->dai = &wm8971_dai;
codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
@@ -695,42 +656,48 @@ static int wm8971_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
wm8971_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8971: failed to create pcms\n");
- goto pcm_err;
+ goto err;
}
/* charge output caps - set vmid to 5k for quick power up */
- reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
- wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+ reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+ snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
codec->bias_level = SND_SOC_BIAS_STANDBY;
queue_delayed_work(wm8971_workq, &codec->delayed_work,
msecs_to_jiffies(1000));
/* set the update bits */
- reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
- wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
- wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
-
- reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
- wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
- wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
-
- reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
- wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
- wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
-
- reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
- wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
- reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
- wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_LDAC);
+ snd_soc_write(codec, WM8971_LDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_RDAC);
+ snd_soc_write(codec, WM8971_RDAC, reg | 0x0100);
+
+ reg = snd_soc_read(codec, WM8971_LOUT1V);
+ snd_soc_write(codec, WM8971_LOUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_ROUT1V);
+ snd_soc_write(codec, WM8971_ROUT1V, reg | 0x0100);
+
+ reg = snd_soc_read(codec, WM8971_LOUT2V);
+ snd_soc_write(codec, WM8971_LOUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_ROUT2V);
+ snd_soc_write(codec, WM8971_ROUT2V, reg | 0x0100);
+
+ reg = snd_soc_read(codec, WM8971_LINVOL);
+ snd_soc_write(codec, WM8971_LINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8971_RINVOL);
+ snd_soc_write(codec, WM8971_RINVOL, reg | 0x0100);
snd_soc_add_controls(codec, wm8971_snd_controls,
ARRAY_SIZE(wm8971_snd_controls));
@@ -745,7 +712,7 @@ static int wm8971_init(struct snd_soc_device *socdev)
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-pcm_err:
+err:
kfree(codec->reg_cache);
return ret;
}
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 6f15acd..7d5b807 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -57,50 +57,7 @@ struct wm8988_priv {
};
-/*
- * read wm8988 register cache
- */
-static inline unsigned int wm8988_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8988_NUM_REG)
- return -1;
- return cache[reg];
-}
-
-/*
- * write wm8988 register cache
- */
-static inline void wm8988_write_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- u16 *cache = codec->reg_cache;
- if (reg > WM8988_NUM_REG)
- return;
- cache[reg] = value;
-}
-
-static int wm8988_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- u8 data[2];
-
- /* data is
- * D15..D9 WM8753 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
- wm8988_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
-}
-
-#define wm8988_reset(c) wm8988_write(c, WM8988_RESET, 0)
+#define wm8988_reset(c) snd_soc_write(c, WM8988_RESET, 0)
/*
* WM8988 Controls
@@ -226,15 +183,15 @@ static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
- u16 adctl2 = wm8988_read_reg_cache(codec, WM8988_ADCTL2);
+ u16 adctl2 = snd_soc_read(codec, WM8988_ADCTL2);
/* Use the DAC to gate LRC if active, otherwise use ADC */
- if (wm8988_read_reg_cache(codec, WM8988_PWR2) & 0x180)
+ if (snd_soc_read(codec, WM8988_PWR2) & 0x180)
adctl2 &= ~0x4;
else
adctl2 |= 0x4;
- return wm8988_write(codec, WM8988_ADCTL2, adctl2);
+ return snd_soc_write(codec, WM8988_ADCTL2, adctl2);
}
static const char *wm8988_line_texts[] = {
@@ -619,7 +576,7 @@ static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- wm8988_write(codec, WM8988_IFACE, iface);
+ snd_soc_write(codec, WM8988_IFACE, iface);
return 0;
}
@@ -653,8 +610,8 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8988_priv *wm8988 = codec->private_data;
- u16 iface = wm8988_read_reg_cache(codec, WM8988_IFACE) & 0x1f3;
- u16 srate = wm8988_read_reg_cache(codec, WM8988_SRATE) & 0x180;
+ u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
+ u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
int coeff;
coeff = get_coeff(wm8988->sysclk, params_rate(params));
@@ -685,9 +642,9 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- wm8988_write(codec, WM8988_IFACE, iface);
+ snd_soc_write(codec, WM8988_IFACE, iface);
if (coeff >= 0)
- wm8988_write(codec, WM8988_SRATE, srate |
+ snd_soc_write(codec, WM8988_SRATE, srate |
(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
return 0;
@@ -696,19 +653,19 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
static int wm8988_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = wm8988_read_reg_cache(codec, WM8988_ADCDAC) & 0xfff7;
+ u16 mute_reg = snd_soc_read(codec, WM8988_ADCDAC) & 0xfff7;
if (mute)
- wm8988_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
+ snd_soc_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
else
- wm8988_write(codec, WM8988_ADCDAC, mute_reg);
+ snd_soc_write(codec, WM8988_ADCDAC, mute_reg);
return 0;
}
static int wm8988_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- u16 pwr_reg = wm8988_read_reg_cache(codec, WM8988_PWR1) & ~0x1c1;
+ u16 pwr_reg = snd_soc_read(codec, WM8988_PWR1) & ~0x1c1;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -716,24 +673,24 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* VREF, VMID=2x5k */
- wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
/* Charge caps */
msleep(100);
}
/* VREF, VMID=2*500k, digital stopped */
- wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
+ snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
break;
case SND_SOC_BIAS_OFF:
- wm8988_write(codec, WM8988_PWR1, 0x0000);
+ snd_soc_write(codec, WM8988_PWR1, 0x0000);
break;
}
codec->bias_level = level;
@@ -887,8 +844,6 @@ static int wm8988_register(struct wm8988_priv *wm8988)
codec->private_data = wm8988;
codec->name = "WM8988";
codec->owner = THIS_MODULE;
- codec->read = wm8988_read_reg_cache;
- codec->write = wm8988_write;
codec->dai = &wm8988_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
@@ -899,6 +854,12 @@ static int wm8988_register(struct wm8988_priv *wm8988)
memcpy(codec->reg_cache, wm8988_reg,
sizeof(wm8988_reg));
+ ret = snd_soc_codec_set_cache_io(codec, 7, 9);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ goto err;
+ }
+
ret = wm8988_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
@@ -906,16 +867,16 @@ static int wm8988_register(struct wm8988_priv *wm8988)
}
/* set the update bits (we always update left then right) */
- reg = wm8988_read_reg_cache(codec, WM8988_RADC);
- wm8988_write(codec, WM8988_RADC, reg | 0x100);
- reg = wm8988_read_reg_cache(codec, WM8988_RDAC);
- wm8988_write(codec, WM8988_RDAC, reg | 0x0100);
- reg = wm8988_read_reg_cache(codec, WM8988_ROUT1V);
- wm8988_write(codec, WM8988_ROUT1V, reg | 0x0100);
- reg = wm8988_read_reg_cache(codec, WM8988_ROUT2V);
- wm8988_write(codec, WM8988_ROUT2V, reg | 0x0100);
- reg = wm8988_read_reg_cache(codec, WM8988_RINVOL);
- wm8988_write(codec, WM8988_RINVOL, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_RADC);
+ snd_soc_write(codec, WM8988_RADC, reg | 0x100);
+ reg = snd_soc_read(codec, WM8988_RDAC);
+ snd_soc_write(codec, WM8988_RDAC, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_ROUT1V);
+ snd_soc_write(codec, WM8988_ROUT1V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_ROUT2V);
+ snd_soc_write(codec, WM8988_ROUT2V, reg | 0x0100);
+ reg = snd_soc_read(codec, WM8988_RINVOL);
+ snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100);
wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
new file mode 100644
index 0000000..4eb4333
--- /dev/null
+++ b/sound/soc/soc-cache.c
@@ -0,0 +1,105 @@
+/*
+ * soc-cache.c -- ASoC register cache helpers
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie(a)opensource.wolfsonmicro.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <sound/soc.h>
+
+static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= codec->reg_cache_size)
+ return -1;
+ return cache[reg];
+}
+
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ u8 data[2];
+ int ret;
+
+ BUG_ON(codec->volatile_register);
+
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ if (reg < codec->reg_cache_size)
+ cache[reg] = value;
+ ret = codec->hw_write(codec->control_data, data, 2);
+ if (ret == 2)
+ return 0;
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+
+static struct {
+ int addr_bits;
+ int data_bits;
+ int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+ unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+} io_types[] = {
+ { 7, 9, snd_soc_7_9_write, snd_soc_7_9_read },
+};
+
+/**
+ * snd_soc_codec_set_cache_io: Set up standard I/O functions.
+ *
+ * @codec: CODEC to configure.
+ * @type: Type of cache.
+ * @addr_bits: Number of bits of register address data.
+ * @data_bits: Number of bits of data per register.
+ *
+ * Register formats are frequently shared between many I2C and SPI
+ * devices. In order to promote code reuse the ASoC core provides
+ * some standard implementations of CODEC read and write operations
+ * which can be set up using this function.
+ *
+ * The caller is responsible for allocating and initialising the
+ * actual cache.
+ *
+ * Note that at present this code cannot be used by CODECs with
+ * volatile registers.
+ */
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+ int addr_bits, int data_bits)
+{
+ int i;
+
+ /* We don't support volatile registers yet - refactoring of
+ * the hw_read operation will be required to do so. */
+ if (codec->volatile_register) {
+ printk(KERN_ERR "Volatile registers not yet supported\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(io_types); i++)
+ if (io_types[i].addr_bits == addr_bits &&
+ io_types[i].data_bits == data_bits)
+ break;
+ if (i == ARRAY_SIZE(io_types)) {
+ printk(KERN_ERR
+ "No I/O functions for %d bit address %d bit data\n",
+ addr_bits, data_bits);
+ return -EINVAL;
+ }
+
+ codec->write = io_types[i].write;
+ codec->read = io_types[i].read;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
--
1.6.3.3
1
7

03 Aug '09
Instead of hitting an assert when any of the plugin functions is called
in an invalid context we should return a clean error to make sure
programs are not unnecessarily aborted.
This should fix issues such as http://pulseaudio.org/ticket/595
---
pulse/ctl_pulse.c | 35 +++++++++++++++++------
pulse/pcm_pulse.c | 79 +++++++++++++++++++++++++++++++++++++++++------------
pulse/pulse.c | 34 ++++++++++++++++-------
3 files changed, 111 insertions(+), 37 deletions(-)
diff --git a/pulse/ctl_pulse.c b/pulse/ctl_pulse.c
index c6cf9e2..1b057ef 100644
--- a/pulse/ctl_pulse.c
+++ b/pulse/ctl_pulse.c
@@ -125,8 +125,9 @@ static void event_cb(pa_context * c, pa_subscription_event_type_t t,
pa_operation *o;
assert(ctl);
- assert(ctl->p);
- assert(ctl->p->context);
+
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return;
o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->sink,
sink_info_cb, ctl);
@@ -148,8 +149,9 @@ static int pulse_update_volume(snd_ctl_pulse_t * ctl)
pa_operation *o;
assert(ctl);
- assert(ctl->p);
- assert(ctl->p->context);
+
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
o = pa_context_get_sink_info_by_name(ctl->p->context, ctl->sink,
sink_info_cb, ctl);
@@ -203,6 +205,9 @@ static int pulse_elem_list(snd_ctl_ext_t * ext, unsigned int offset,
assert(ctl);
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
+
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
pa_threaded_mainloop_lock(ctl->p->mainloop);
@@ -260,7 +265,9 @@ static int pulse_get_attribute(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
return -EINVAL;
assert(ctl);
- assert(ctl->p);
+
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
pa_threaded_mainloop_lock(ctl->p->mainloop);
@@ -311,7 +318,9 @@ static int pulse_read_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
pa_cvolume *vol = NULL;
assert(ctl);
- assert(ctl->p);
+
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
pa_threaded_mainloop_lock(ctl->p->mainloop);
@@ -361,7 +370,9 @@ static int pulse_write_integer(snd_ctl_ext_t * ext, snd_ctl_ext_key_t key,
pa_cvolume *vol = NULL;
assert(ctl);
- assert(ctl->p && ctl->p->context);
+
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
pa_threaded_mainloop_lock(ctl->p->mainloop);
@@ -465,6 +476,9 @@ static void pulse_subscribe_events(snd_ctl_ext_t * ext, int subscribe)
assert(ctl);
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return;
+
pa_threaded_mainloop_lock(ctl->p->mainloop);
ctl->subscribed = !!(subscribe & SND_CTL_EVENT_MASK_VALUE);
@@ -481,6 +495,9 @@ static int pulse_read_event(snd_ctl_ext_t * ext, snd_ctl_elem_id_t * id,
assert(ctl);
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
+
pa_threaded_mainloop_lock(ctl->p->mainloop);
if (!ctl->updated || !ctl->subscribed)
@@ -525,8 +542,8 @@ static int pulse_ctl_poll_revents(snd_ctl_ext_t * ext, struct pollfd *pfd,
snd_ctl_pulse_t *ctl = ext->private_data;
int err = 0;
- assert(ctl);
- assert(ctl->p);
+ if (!ctl->p || !ctl->p->mainloop || !ctl->p->context)
+ return -EBADFD;
pa_threaded_mainloop_lock(ctl->p->mainloop);
diff --git a/pulse/pcm_pulse.c b/pulse/pcm_pulse.c
index c276839..24347f9 100644
--- a/pulse/pcm_pulse.c
+++ b/pulse/pcm_pulse.c
@@ -106,6 +106,9 @@ static int update_active(snd_pcm_pulse_t *pcm) {
assert(pcm);
+ if (!pcm->p)
+ return -EBADFD;
+
ret = check_active(pcm);
if (ret < 0)
return ret;
@@ -125,7 +128,9 @@ static int pulse_start(snd_pcm_ioplug_t * io)
int err = 0, err_o = 0, err_u = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
@@ -174,7 +179,9 @@ static int pulse_stop(snd_pcm_ioplug_t * io)
int err = 0, err_o = 0, err_u = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
@@ -224,7 +231,9 @@ static int pulse_drain(snd_pcm_ioplug_t * io)
int err = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
@@ -259,7 +268,9 @@ static snd_pcm_sframes_t pulse_pointer(snd_pcm_ioplug_t * io)
snd_pcm_sframes_t ret = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
if (io->state == SND_PCM_STATE_XRUN)
return -EPIPE;
@@ -269,7 +280,10 @@ static snd_pcm_sframes_t pulse_pointer(snd_pcm_ioplug_t * io)
pa_threaded_mainloop_lock(pcm->p->mainloop);
- assert(pcm->stream);
+ if (!pcm->stream) {
+ ret = -EBADFD;
+ goto finish;
+ }
ret = pulse_check_connection(pcm->p);
if (ret < 0)
@@ -305,11 +319,16 @@ static int pulse_delay(snd_pcm_ioplug_t * io, snd_pcm_sframes_t * delayp)
pa_usec_t lat = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
- assert(pcm->stream);
+ if (!pcm->stream) {
+ err = -EBADFD;
+ goto finish;
+ }
for (;;) {
err = pulse_check_connection(pcm->p);
@@ -354,11 +373,16 @@ static snd_pcm_sframes_t pulse_write(snd_pcm_ioplug_t * io,
snd_pcm_sframes_t ret = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
- assert(pcm->stream);
+ if (!pcm->stream) {
+ ret = -EBADFD;
+ goto finish;
+ }
ret = pulse_check_connection(pcm->p);
if (ret < 0)
@@ -409,11 +433,16 @@ static snd_pcm_sframes_t pulse_read(snd_pcm_ioplug_t * io,
snd_pcm_sframes_t ret = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
- assert(pcm->stream);
+ if (!pcm->stream) {
+ ret = -EBADFD;
+ goto finish;
+ }
ret = pulse_check_connection(pcm->p);
if (ret < 0)
@@ -480,7 +509,9 @@ static void stream_request_cb(pa_stream * p, size_t length, void *userdata)
snd_pcm_pulse_t *pcm = userdata;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return;
update_active(pcm);
}
@@ -490,7 +521,9 @@ static void stream_underrun_cb(pa_stream * p, void *userdata)
snd_pcm_pulse_t *pcm = userdata;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return;
pcm->underrun = 1;
}
@@ -499,7 +532,9 @@ static void stream_latency_cb(pa_stream *p, void *userdata) {
snd_pcm_pulse_t *pcm = userdata;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return;
pa_threaded_mainloop_signal(pcm->p->mainloop, 0);
}
@@ -512,7 +547,9 @@ static int pulse_pcm_poll_revents(snd_pcm_ioplug_t * io,
snd_pcm_pulse_t *pcm = io->private_data;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
@@ -541,7 +578,9 @@ static int pulse_prepare(snd_pcm_ioplug_t * io)
unsigned c, d;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
@@ -645,7 +684,9 @@ static int pulse_hw_params(snd_pcm_ioplug_t * io,
int err = 0;
assert(pcm);
- assert(pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
@@ -745,7 +786,9 @@ static int pulse_pause(snd_pcm_ioplug_t * io, int enable)
int err = 0;
assert (pcm);
- assert (pcm->p);
+
+ if (!pcm->p)
+ return -EBADFD;
pa_threaded_mainloop_lock(pcm->p->mainloop);
diff --git a/pulse/pulse.c b/pulse/pulse.c
index 3940238..95d8dde 100644
--- a/pulse/pulse.c
+++ b/pulse/pulse.c
@@ -32,8 +32,9 @@ int pulse_check_connection(snd_pulse_t * p)
pa_context_state_t state;
assert(p);
- assert(p->context);
- assert(p->mainloop);
+
+ if (!p->context || !p->mainloop)
+ return -EBADFD;
state = pa_context_get_state(p->context);
@@ -77,8 +78,12 @@ int pulse_wait_operation(snd_pulse_t * p, pa_operation * o)
{
assert(p);
assert(o);
- assert(p->state == PULSE_STATE_READY);
- assert(p->mainloop);
+
+ if (p->state != PULSE_STATE_READY)
+ return -EBADFD;
+
+ if (!p->mainloop)
+ return -EBADFD;
for (;;) {
int err;
@@ -103,8 +108,12 @@ int pulse_wait_stream_state(snd_pulse_t * p, pa_stream * stream,
assert(p);
assert(stream);
- assert(p->state == PULSE_STATE_READY);
- assert(p->mainloop);
+
+ if (p->state != PULSE_STATE_READY)
+ return -EBADFD;
+
+ if (!p->mainloop)
+ return -EBADFD;
for (;;) {
int err;
@@ -197,7 +206,9 @@ snd_pulse_t *pulse_new(void)
p->context =
pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), buf);
- assert(p->context);
+
+ if (!p->context)
+ goto fail;
pa_context_set_state_callback(p->context, context_state_cb, p);
@@ -246,9 +257,12 @@ int pulse_connect(snd_pulse_t * p, const char *server)
int err;
assert(p);
- assert(p->context);
- assert(p->mainloop);
- assert(p->state == PULSE_STATE_INIT);
+
+ if (!p->context || !p->mainloop)
+ return -EBADFD;
+
+ if (p->state != PULSE_STATE_INIT)
+ return -EBADFD;
pa_threaded_mainloop_lock(p->mainloop);
--
1.6.3.3
Lennart
--
Lennart Poettering Red Hat, Inc.
lennart [at] poettering [dot] net ICQ# 11060553
http://0pointer.net/lennart/ GnuPG 0x1A015CC4
3
4
As I described in one of my previous mails, I have been trying to port
an old ASoC driver for kernel 2.6.22 to latest stable 2.6.30 kernel.
I am currently facing a big problem which make some repeated samples
appear with a regular pattern:
- frequency of bad samples = 2 * period_size
- bad samples in audio stream are taken from "buffer_size" samples
back in the original file.
You can reproduce this with:
aplay -t raw -f S16_LE -r8000 8k16bitpcm.wav
(http://en.wikipedia.org/wiki/WAV to get this file)
Here I submit two patches:
- One which adds ssi and dma drivers plus a custom machine driver
(imx27asoc_list.patch).
- Another patch which adds wm8974 codec support taken from ASoC
wm8974-upstream branch (wm8974_list.patch).
Note that the machine driver attached uses wm8974 codec, thus second
patch is needed. Please, note also that patches are currently quite
dirty, and in an early development state. The purpose of this mail is
not to even consider to include them in any branch right now, but to
gather some help to solve the problem. As soon as I have it solved I
will clean all the stuff and try to prepare it for mainline.
--
Javier Martin
Vista Silicon S.L.
Universidad de Cantabria
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
1
1
Hi all,
I have some questions about ALSA - LADSPA data exchange in ALSA plugin. As I
understand it, LADSPA only use 32 bit floating point, while ALSA can use
many types of buffer, S8, S16_LE and so on. My question is how this buffer
converted?
As I work with OMAP processor I want to use DSP for signal processing. We
want the LADSPA plugin connect to the DSP and get plugin advantage. But DSP
only work on fixed point arithmatic, so we need to convert the 32 bit float
in LADSPA to an integer type buffer. I think I can achieve it by following
ALSA-LADSPA buffer conversion. How I can do this?
Thank you for your help.
Best regards,
Arif
1
0
Hi all
Thanks in advance,
I am newbie to alsa
Currently working on porting android to omap34xx
Right now porting alsa.
This is a general question.
Please check the below log and specify any suggestions, why is it not
listing any of my capture devices.
What should be done to recognise my capture devices?
Do i have to do it in kernel level or user level?(since my amixer is listing
all my elements, given below the log is my amixer output for verification).
Any suggestions are welcome
thanks once agiain,
log
========================
D/AudioHardwareALSA( 799): Mixer: element name: 'Carkit'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC Voice Analog Downlink'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC Voice Digital
Downlink'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC1 Analog'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC1 Digital Coarse'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC1 Digital Fine'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC2 Analog'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC2 Digital Coarse'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC2 Digital Fine'
D/AudioHardwareALSA( 799): Mixer: element name: 'Earpiece'
D/AudioHardwareALSA( 799): Mixer: element name: 'Headset'
D/AudioHardwareALSA( 799): Mixer: element name: 'Left Digital Loopback'
D/AudioHardwareALSA( 799): Mixer: element name: 'PreDriv'
D/AudioHardwareALSA( 799): Mixer: element name: 'Right Digital Loopback'
D/AudioHardwareALSA( 799): Mixer: element name: 'Voice Digital Loopback'
D/AudioHardwareALSA( 799): Mixer: master 'PCM' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Earpiece' found.
D/AudioHardwareALSA( 799): Mixer: route 'Speaker' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Bluetooth' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Headphone' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Bluetooth A2DP' not found.
D/AudioHardwareALSA( 799): Mixer: element name: 'Analog'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC Voice Analog Downlink'
D/AudioHardwareALSA( 799): Mixer: element name: 'DAC Voice Digital
Downlink'
D/AudioHardwareALSA( 799): Mixer: element name: 'Left Digital Loopback'
D/AudioHardwareALSA( 799): Mixer: element name: 'Right Digital Loopback'
D/AudioHardwareALSA( 799): Mixer: element name: 'TX1 Digital'
D/AudioHardwareALSA( 799): Mixer: element name: 'TX2 Digital'
D/AudioHardwareALSA( 799): Mixer: element name: 'Voice Digital Loopback'
D/AudioHardwareALSA( 799): Mixer: master 'Capture' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Capture' not found.
D/AudioHardwareALSA( 799): Mixer: route '' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Bluetooth Capture' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Capture' not found.
D/AudioHardwareALSA( 799): Mixer: route 'Bluetooth A2DP Capture' not found.
D/AudioHardwareALSA( 799): mixer initialized.
===========================
amixer output
=================
numid=18,iface=MIXER,name='Analog Capture Volume'
numid=23,iface=MIXER,name='Analog Left Capture Route'
numid=22,iface=MIXER,name='Analog Right Capture Route'
numid=14,iface=MIXER,name='Carkit Playback Volume'
numid=34,iface=MIXER,name='CarkitL Mixer AudioL1'
numid=35,iface=MIXER,name='CarkitL Mixer AudioL2'
numid=33,iface=MIXER,name='CarkitL Mixer Voice'
numid=31,iface=MIXER,name='CarkitR Mixer AudioR1'
numid=32,iface=MIXER,name='CarkitR Mixer AudioR2'
numid=30,iface=MIXER,name='CarkitR Mixer Voice'
numid=11,iface=MIXER,name='DAC Voice Analog Downlink Switch'
numid=10,iface=MIXER,name='DAC Voice Analog Downlink Volume'
numid=9,iface=MIXER,name='DAC Voice Digital Downlink Volume'
numid=7,iface=MIXER,name='DAC1 Analog Playback Switch'
numid=5,iface=MIXER,name='DAC1 Analog Playback Volume'
numid=3,iface=MIXER,name='DAC1 Digital Coarse Playback Volume'
numid=1,iface=MIXER,name='DAC1 Digital Fine Playback Volume'
numid=8,iface=MIXER,name='DAC2 Analog Playback Switch'
numid=6,iface=MIXER,name='DAC2 Analog Playback Volume'
numid=4,iface=MIXER,name='DAC2 Digital Coarse Playback Volume'
numid=2,iface=MIXER,name='DAC2 Digital Fine Playback Volume'
numid=51,iface=MIXER,name='Earpiece Mixer AudioL1'
numid=52,iface=MIXER,name='Earpiece Mixer AudioL2'
numid=53,iface=MIXER,name='Earpiece Mixer AudioR1'
numid=50,iface=MIXER,name='Earpiece Mixer Voice'
numid=15,iface=MIXER,name='Earpiece Playback Volume'
numid=19,iface=MIXER,name='HS ramp delay'
numid=29,iface=MIXER,name='HandsfreeL Mux'
numid=28,iface=MIXER,name='HandsfreeR Mux'
numid=13,iface=MIXER,name='Headset Playback Volume'
numid=40,iface=MIXER,name='HeadsetL Mixer AudioL1'
numid=41,iface=MIXER,name='HeadsetL Mixer AudioL2'
numid=39,iface=MIXER,name='HeadsetL Mixer Voice'
numid=37,iface=MIXER,name='HeadsetR Mixer AudioR1'
numid=38,iface=MIXER,name='HeadsetR Mixer AudioR2'
numid=36,iface=MIXER,name='HeadsetR Mixer Voice'
numid=56,iface=MIXER,name='Left Digital Loopback Volume'
numid=60,iface=MIXER,name='Left1 Analog Loopback Switch'
numid=58,iface=MIXER,name='Left2 Analog Loopback Switch'
numid=12,iface=MIXER,name='PreDriv Playback Volume'
numid=47,iface=MIXER,name='PredriveL Mixer AudioL1'
numid=48,iface=MIXER,name='PredriveL Mixer AudioL2'
numid=49,iface=MIXER,name='PredriveL Mixer AudioR2'
numid=46,iface=MIXER,name='PredriveL Mixer Voice'
numid=45,iface=MIXER,name='PredriveR Mixer AudioL2'
numid=43,iface=MIXER,name='PredriveR Mixer AudioR1'
numid=44,iface=MIXER,name='PredriveR Mixer AudioR2'
numid=42,iface=MIXER,name='PredriveR Mixer Voice'
numid=55,iface=MIXER,name='Right Digital Loopback Volume'
numid=61,iface=MIXER,name='Right1 Analog Loopback Switch'
numid=59,iface=MIXER,name='Right2 Analog Loopback Switch'
numid=25,iface=MIXER,name='TX1 Capture Route'
numid=16,iface=MIXER,name='TX1 Digital Capture Volume'
numid=24,iface=MIXER,name='TX2 Capture Route'
numid=17,iface=MIXER,name='TX2 Digital Capture Volume'
numid=21,iface=MIXER,name='Vibra H-bridge direction'
numid=20,iface=MIXER,name='Vibra H-bridge mode'
numid=27,iface=MIXER,name='Vibra Mux'
numid=26,iface=MIXER,name='Vibra Route'
numid=57,iface=MIXER,name='Voice Analog Loopback Switch'
numid=54,iface=MIXER,name='Voice Digital Loopback Volume'
===================
--
Regards
Venkam Balakrishna
2
1