Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
August 2010
- 127 participants
- 319 discussions
13 Dec '10
Hi,
I need to set all mixer stuff to 100% and the output isn't ok.
Volume is too low.
When I switch to another input on my amp. it's about 500% louder.
Please have a look to attached ala-info output.
I tested with opensuse 11.2 and also with a newer kernel for the distro
2.6.34.
Can you please help?
Thx.
Halim
--
Halim Sahin
E-Mail:
halim.sahin (at) t-online.de
2
4
So I've discovered that my sound card has a "PCM Playback Volume"
control, but changing that control does not alter the volume. This is
not only confusing but also a problem for PulseAudio. (PA merges this
control into it's overall volume, and so when the user moves PA's volume
control, in some ranges nothing happens.)
Interestingly enough, this control does not come from the HDA parser, it
is added by alsactl at boot time...! (Through alsa-lib's
snd_ctl_elem_add_integer, which does an snd_ctl_ioctl.)
So the question is, why is alsactl creating volume controls, and why is
userspace allowed to add controls in the first place, if the kernel
can't use them for anything anyway? That doesn't make sense to me.
This a generic problem, but sound card info, if that matters, is
available here:
https://launchpad.net/bugs/625149
Codec: http://launchpadlibrarian.net/54478760/Card0.Codecs.codec.0.txt
--
David Henningsson, Canonical Ltd.
http://launchpad.net/~diwic
9
73
This patch fixes up the au1x audio platform after the multi-component
merge:
- compile fixes and updates to get DB1200 platform audio working again,
- removal of global variables in AC97/I2S/DMA(PCM) modules.
The AC97 part is limited to one instance only for now due to issues
with getting at driver data in the soc_ac97_ops.
Signed-off-by: Manuel Lauss <manuel.lauss(a)googlemail.com>
---
v5: smaller patch
v4: fixed a bug in the previous bugfix, and added DAI drvdata accessors.
v3: fixed a bug which caused cat /proc/iomem to loop endlessly.
v2: prepare PCM,I2S for multiple card operation, use dev_name() for DAI name.
Against Liam's asoc/for-2.6.37 branch.
Tested on DB1200 and DB1300 (here both I2S and AC97 operate as
independent cards), please fold this into the other Au1x multi-component
patches.
Issues I observed with AC97:
* AC97 is limited to a single instance since I cannot get at the driver
data in the AC97 callbacks at all time (or did I miss anything?):
when the AC97 codec calls snd_ac97_mixer(), it calls into the soc_ac97_ops
callbacks; however ac97->bus->card->private_data (suggested by Mark)
is _always_ NULL, so no way to get at the dai and ultimately driver data.
* generic AC97 codec use spits out this kobject warning, which is caused by the
"device_register()" in soc-core.c::soc_ac97_dev_register():
asoc: ac97-hifi <-> au1xpsc-ac97.1 mapping ok
kobject (8fdc59b0): tried to init an initialized object, something is
seriously wrong.
Call Trace:
[<804959c4>] dump_stack+0x8/0x34
[<802a30e4>] kobject_init+0x50/0xcc
[<802ec27c>] device_initialize+0x2c/0x70
[<802ecb64>] device_register+0x14/0x28
[<8039e058>] snd_soc_instantiate_cards+0xa00/0xb10
[<8039e27c>] soc_probe+0x114/0x154
[<802ef0fc>] driver_probe_device+0xe4/0x1a0
[<802ee404>] bus_for_each_drv+0x60/0xb0
[<802ef370>] device_attach+0x74/0xa8
[<802ee1e8>] bus_probe_device+0x30/0x54
[<802ec9a0>] device_add+0x384/0x534
[<802f09a4>] platform_device_add+0x15c/0x1c8
[<805ccb14>] db1200_audio_load+0x70/0x9c
[<801004fc>] do_one_initcall+0xfc/0x1e0
[<805ba32c>] kernel_init+0xc8/0x168
[<80105904>] kernel_thread_helper+0x10/0x18
arch/mips/alchemy/devboards/db1200/platform.c | 6 ++
sound/soc/au1x/db1200.c | 16 +++---
sound/soc/au1x/dbdma2.c | 82 ++++++++-----------------
sound/soc/au1x/psc-ac97.c | 59 +++++++++++-------
sound/soc/au1x/psc-i2s.c | 42 ++++---------
sound/soc/au1x/psc.h | 7 +--
6 files changed, 91 insertions(+), 121 deletions(-)
diff --git a/arch/mips/alchemy/devboards/db1200/platform.c b/arch/mips/alchemy/devboards/db1200/platform.c
index 3fa34c3..fbb5593 100644
--- a/arch/mips/alchemy/devboards/db1200/platform.c
+++ b/arch/mips/alchemy/devboards/db1200/platform.c
@@ -429,6 +429,11 @@ static struct platform_device db1200_audio_dev = {
.resource = au1200_psc1_res,
};
+static struct platform_device db1200_stac_dev = {
+ .name = "ac97-codec",
+ .id = 1, /* on PSC1 */
+};
+
static struct platform_device *db1200_devs[] __initdata = {
NULL, /* PSC0, selected by S6.8 */
&db1200_ide_dev,
@@ -436,6 +441,7 @@ static struct platform_device *db1200_devs[] __initdata = {
&db1200_rtc_dev,
&db1200_nand_dev,
&db1200_audio_dev,
+ &db1200_stac_dev,
};
static int __init db1200_dev_init(void)
diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c
index d8dc822..b62fcd3 100644
--- a/sound/soc/au1x/db1200.c
+++ b/sound/soc/au1x/db1200.c
@@ -27,10 +27,10 @@
static struct snd_soc_dai_link db1200_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
- .cpu_dai_name = "au1xpsc-ac97",
.codec_dai_name = "ac97-hifi",
- .platform_name = "au1xpsc-pcm-audio",
- .codec_name = "ac97-codec",
+ .cpu_dai_name = "au1xpsc_ac97.1",
+ .platform_name = "au1xpsc-pcm.1",
+ .codec_name = "ac97-codec.1",
};
static struct snd_soc_card db1200_ac97_machine = {
@@ -75,10 +75,10 @@ static struct snd_soc_ops db1200_i2s_wm8731_ops = {
static struct snd_soc_dai_link db1200_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
- .cpu_dai_name = "au1xpsc",
- .codec_dai_name = "wm8731-hifi"
- .platform_name = "au1xpsc-pcm-audio",
- .codec_name = "wm8731-codec.0-001a",
+ .codec_dai_name = "wm8731-hifi",
+ .cpu_dai_name = "au1xpsc_i2s.1",
+ .platform_name = "au1xpsc-pcm.1",
+ .codec_name = "wm8731-codec.0-001b",
.ops = &db1200_i2s_wm8731_ops,
};
@@ -97,7 +97,7 @@ static int __init db1200_audio_load(void)
int ret;
ret = -ENOMEM;
- db1200_asoc_dev = platform_device_alloc("soc-audio", -1);
+ db1200_asoc_dev = platform_device_alloc("soc-audio", 1); /* PSC1 */
if (!db1200_asoc_dev)
goto out;
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 00fdb9c..10fdd28 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -10,9 +10,6 @@
*
* DMA glue for Au1x-PSC audio.
*
- * NOTE: all of these drivers can only work with a SINGLE instance
- * of a PSC. Multiple independent audio devices are impossible
- * with ASoC v1.
*/
@@ -61,9 +58,6 @@ struct au1xpsc_audio_dmadata {
int msbits;
};
-/* instance data. There can be only one, MacLeod!!!! */
-static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2];
-
/*
* These settings are somewhat okay, at least on my machine audio plays
* almost skip-free. Especially the 64kB buffer seems to help a LOT.
@@ -199,6 +193,14 @@ out:
return 0;
}
+static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
+{
+ struct snd_soc_pcm_runtime *rtd = ss->private_data;
+ struct au1xpsc_audio_dmadata *pcd =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ return &pcd[SUBSTREAM_TYPE(ss)];
+}
+
static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -211,7 +213,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
goto out;
stype = SUBSTREAM_TYPE(substream);
- pcd = au1xpsc_audio_pcmdma[stype];
+ pcd = to_dmadata(substream);
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
"runtime->min_align %d\n",
@@ -249,8 +251,7 @@ static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct au1xpsc_audio_dmadata *pcd =
- au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)];
+ struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
au1xxx_dbdma_reset(pcd->ddma_chan);
@@ -267,7 +268,7 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan;
+ u32 c = to_dmadata(substream)->ddma_chan;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -287,8 +288,7 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t
au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
{
- return bytes_to_frames(substream->runtime,
- au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos);
+ return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
}
static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
@@ -299,7 +299,7 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
{
- au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]);
+ au1x_pcm_dbdma_free(to_dmadata(substream));
return 0;
}
@@ -329,35 +329,21 @@ static int au1xpsc_pcm_new(struct snd_card *card,
return 0;
}
-static int au1xpsc_pcm_probe(struct snd_soc_platform *platform)
-{
- if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX])
- return -ENODEV;
-
- return 0;
-}
-
/* au1xpsc audio platform */
struct snd_soc_platform_driver au1xpsc_soc_platform = {
- .probe = au1xpsc_pcm_probe,
.ops = &au1xpsc_pcm_ops,
.pcm_new = au1xpsc_pcm_new,
.pcm_free = au1xpsc_pcm_free_dma_buffers,
};
-EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
{
+ struct au1xpsc_audio_dmadata *dmadata;
struct resource *r;
int ret;
- if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX])
- return -EBUSY;
-
- /* TX DMA */
- au1xpsc_audio_pcmdma[PCM_TX]
- = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
- if (!au1xpsc_audio_pcmdma[PCM_TX])
+ dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
+ if (!dmadata)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -365,54 +351,40 @@ static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
ret = -ENODEV;
goto out1;
}
- (au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start;
+ dmadata[PCM_TX].ddma_id = r->start;
/* RX DMA */
- au1xpsc_audio_pcmdma[PCM_RX]
- = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
- if (!au1xpsc_audio_pcmdma[PCM_RX])
- return -ENOMEM;
-
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!r) {
ret = -ENODEV;
- goto out2;
+ goto out1;
}
- (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
+ dmadata[PCM_RX].ddma_id = r->start;
+
+ platform_set_drvdata(pdev, dmadata);
ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
if (!ret)
return ret;
-out2:
- kfree(au1xpsc_audio_pcmdma[PCM_RX]);
- au1xpsc_audio_pcmdma[PCM_RX] = NULL;
out1:
- kfree(au1xpsc_audio_pcmdma[PCM_TX]);
- au1xpsc_audio_pcmdma[PCM_TX] = NULL;
+ kfree(dmadata);
return ret;
}
static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
{
- int i;
+ struct au1xpsc_audio_dmadata *dmadata = platform_get_drvdata(pdev);
snd_soc_unregister_platform(&pdev->dev);
-
- for (i = 0; i < 2; i++) {
- if (au1xpsc_audio_pcmdma[i]) {
- au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
- kfree(au1xpsc_audio_pcmdma[i]);
- au1xpsc_audio_pcmdma[i] = NULL;
- }
- }
+ kfree(dmadata);
return 0;
}
static struct platform_driver au1xpsc_pcm_driver = {
.driver = {
- .name = "au1xpsc-pcm-audio",
+ .name = "au1xpsc-pcm",
.owner = THIS_MODULE,
},
.probe = au1xpsc_pcm_drvprobe,
@@ -421,8 +393,6 @@ static struct platform_driver au1xpsc_pcm_driver = {
static int __init au1xpsc_audio_dbdma_load(void)
{
- au1xpsc_audio_pcmdma[PCM_TX] = NULL;
- au1xpsc_audio_pcmdma[PCM_RX] = NULL;
return platform_driver_register(&au1xpsc_pcm_driver);
}
@@ -460,7 +430,7 @@ struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
res[1].start = res[1].end = id[1];
res[0].flags = res[1].flags = IORESOURCE_DMA;
- pd = platform_device_alloc("au1xpsc-pcm", -1);
+ pd = platform_device_alloc("au1xpsc-pcm", pdev->id);
if (!pd)
goto out;
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 6a9516c..d0db66f 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -10,9 +10,6 @@
*
* Au1xxx-PSC AC97 glue.
*
- * NOTE: all of these drivers can only work with a SINGLE instance
- * of a PSC. Multiple independent audio devices are impossible
- * with ASoC v1.
*/
#include <linux/init.h>
@@ -56,12 +53,29 @@
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
+#if 0
+
+/* this could theoretically work, but ac97->bus->card->private_data can be NULL
+ * when snd_ac97_mixer() is called; I don't know if the rest further down the
+ * chain are always valid either.
+ */
+static inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x)
+{
+ struct snd_soc_card *c = x->bus->card->private_data;
+ return snd_soc_dai_get_drvdata(c->rtd->cpu_dai);
+}
+
+#else
+
+#define ac97_to_pscdata(x) au1xpsc_ac97_workdata
+
+#endif
+
/* AC97 controller reads codec register */
static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
- /* FIXME */
- struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
unsigned short retry, tmo;
unsigned long data;
@@ -102,8 +116,7 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
- /* FIXME */
- struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
unsigned int tmo, retry;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
@@ -134,8 +147,7 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
/* AC97 controller asserts a warm reset */
static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
{
- /* FIXME */
- struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
au_sync();
@@ -146,8 +158,7 @@ static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
{
- /* FIXME */
- struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
int i;
/* disable PSC during cold reset */
@@ -202,8 +213,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- /* FIXME */
- struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
unsigned long r, ro, stat;
int chans, t, stype = SUBSTREAM_TYPE(substream);
@@ -283,8 +293,7 @@ out:
static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
- /* FIXME */
- struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
int ret, stype = SUBSTREAM_TYPE(substream);
ret = 0;
@@ -325,7 +334,7 @@ static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
.hw_params = au1xpsc_ac97_hw_params,
};
-struct snd_soc_dai_driver au1xpsc_ac97_dai = {
+static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
.ac97_control = 1,
.probe = au1xpsc_ac97_probe,
.playback = {
@@ -342,7 +351,6 @@ struct snd_soc_dai_driver au1xpsc_ac97_dai = {
},
.ops = &au1xpsc_ac97_dai_ops,
};
-EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
@@ -351,9 +359,6 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
unsigned long sel;
struct au1xpsc_audio_data *wd;
- if (au1xpsc_ac97_workdata)
- return -EBUSY;
-
wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!wd)
return -ENOMEM;
@@ -387,14 +392,20 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
au_sync();
- ret = snd_soc_register_dai(&pdev->dev, &au1xpsc_ac97_dai);
+ /* name the DAI like this device instance ("au1xpsc-ac97.PSCINDEX") */
+ memcpy(&wd->dai_drv, &au1xpsc_ac97_dai_template,
+ sizeof(struct snd_soc_dai_driver));
+ wd->dai_drv.name = dev_name(&pdev->dev);
+
+ platform_set_drvdata(pdev, wd);
+
+ ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
if (ret)
goto out1;
wd->dmapd = au1xpsc_pcm_add(pdev);
if (wd->dmapd) {
- platform_set_drvdata(pdev, wd);
- au1xpsc_ac97_workdata = wd; /* MDEV */
+ au1xpsc_ac97_workdata = wd;
return 0;
}
@@ -477,7 +488,7 @@ static struct dev_pm_ops au1xpscac97_pmops = {
static struct platform_driver au1xpsc_ac97_driver = {
.driver = {
- .name = "au1xpsc-ac97",
+ .name = "au1xpsc_ac97",
.owner = THIS_MODULE,
.pm = AU1XPSCAC97_PMOPS,
},
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index 94e560a..fca0912 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -10,9 +10,6 @@
*
* Au1xxx-PSC I2S glue.
*
- * NOTE: all of these drivers can only work with a SINGLE instance
- * of a PSC. Multiple independent audio devices are impossible
- * with ASoC v1.
* NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
*/
@@ -54,13 +51,10 @@
((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
-/* instance data. There can be only one, MacLeod!!!! */
-static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
-
static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long ct;
int ret;
@@ -120,7 +114,7 @@ static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
int cfgbits;
unsigned long stat;
@@ -245,7 +239,7 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
+ struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
int ret, stype = SUBSTREAM_TYPE(substream);
switch (cmd) {
@@ -263,19 +257,13 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
-static int au1xpsc_i2s_probe(struct snd_soc_dai *dai)
-{
- return au1xpsc_i2s_workdata ? 0 : -ENODEV;
-}
-
static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
.trigger = au1xpsc_i2s_trigger,
.hw_params = au1xpsc_i2s_hw_params,
.set_fmt = au1xpsc_i2s_set_fmt,
};
-static struct snd_soc_dai_driver au1xpsc_i2s_dai = {
- .probe = au1xpsc_i2s_probe,
+static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = {
.playback = {
.rates = AU1XPSC_I2S_RATES,
.formats = AU1XPSC_I2S_FMTS,
@@ -298,9 +286,6 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
int ret;
struct au1xpsc_audio_data *wd;
- if (au1xpsc_i2s_workdata)
- return -EBUSY;
-
wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!wd)
return -ENOMEM;
@@ -337,17 +322,21 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
* time out.
*/
- ret = snd_soc_register_dai(&pdev->dev, &au1xpsc_i2s_dai);
+ /* name the DAI like this device instance ("au1xpsc-i2s.PSCINDEX") */
+ memcpy(&wd->dai_drv, &au1xpsc_i2s_dai_template,
+ sizeof(struct snd_soc_dai_driver));
+ wd->dai_drv.name = dev_name(&pdev->dev);
+
+ platform_set_drvdata(pdev, wd);
+
+ ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
if (ret)
goto out1;
/* finally add the DMA device for this PSC */
wd->dmapd = au1xpsc_pcm_add(pdev);
- if (wd->dmapd) {
- platform_set_drvdata(pdev, wd);
- au1xpsc_i2s_workdata = wd;
+ if (wd->dmapd)
return 0;
- }
snd_soc_unregister_dai(&pdev->dev);
out1:
@@ -376,8 +365,6 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
release_mem_region(r->start, resource_size(r));
kfree(wd);
- au1xpsc_i2s_workdata = NULL; /* MDEV */
-
return 0;
}
@@ -427,7 +414,7 @@ static struct dev_pm_ops au1xpsci2s_pmops = {
static struct platform_driver au1xpsc_i2s_driver = {
.driver = {
- .name = "au1xpsc",
+ .name = "au1xpsc_i2s",
.owner = THIS_MODULE,
.pm = AU1XPSCI2S_PMOPS,
},
@@ -437,7 +424,6 @@ static struct platform_driver au1xpsc_i2s_driver = {
static int __init au1xpsc_i2s_load(void)
{
- au1xpsc_i2s_workdata = NULL;
return platform_driver_register(&au1xpsc_i2s_driver);
}
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index f281443..b30eadd 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -8,16 +8,11 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * NOTE: all of these drivers can only work with a SINGLE instance
- * of a PSC. Multiple independent audio devices are impossible
- * with ASoC v1.
*/
#ifndef _AU1X_PCM_H
#define _AU1X_PCM_H
-extern struct snd_ac97_bus_ops soc_ac97_ops;
-
/* DBDMA helpers */
extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
@@ -28,6 +23,8 @@ struct au1xpsc_audio_data {
unsigned long cfg;
unsigned long rate;
+ struct snd_soc_dai_driver dai_drv;
+
unsigned long pm[2];
struct mutex lock;
struct platform_device *dmapd;
--
1.7.2
3
10
30 Sep '10
This patch the I2C board information for the WM8994 used in the Aquila
as audio codec and adds the I2C/I2S platform drivers. Additionlly, to
control power consumption have registerd the voltage consumer of WM8994
to the regulator framework. I initialize gpio settting relevant to
operation of audio codec. I explain following comment concering gpio
setting:
- CODEC_XTAL_EN : This gpio enables that the main clock is provided
to operate audio codec.
- MICBIAS_EN : This gpio enable the microphone to input analog
- ADC_EN : This gpio enable the ADC device which is used to detect
the kind of jack. (SND_JACK_HEADPHONE/HEADSET/MECHANICAL/AVOUT)
According to the kind of jack, an electric current is changed.
Signed-off-by : Chanwoo Choi <cw00.choi(a)samsung.com>
Signed-off-by : Joonyoung Shim <jy0922.shim(a)samsung.com>
Signed-off-by : Kyungmin Park <kyungmin.park(a)samsung.com>
---
arch/arm/mach-s5pv210/mach-aquila.c | 177 +++++++++++++++++++++++++++++++++++
1 files changed, 177 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c
index f0f960f..d027f9d 100644
--- a/arch/arm/mach-s5pv210/mach-aquila.c
+++ b/arch/arm/mach-s5pv210/mach-aquila.c
@@ -17,9 +17,11 @@
#include <linux/i2c-gpio.h>
#include <linux/i2c/mcs.h>
#include <linux/mfd/max8998.h>
+#include <linux/mfd/wm8994/pdata.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/gpio.h>
+#include <linux/regulator/fixed.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -397,6 +399,120 @@ static struct max8998_platform_data aquila_max8998_pdata = {
};
#endif
+static struct regulator_consumer_supply wm8994_fixed_voltage0_supplies[] = {
+ {
+ .dev_name = "5-001a",
+ .supply = "DBVDD",
+ }, {
+ .dev_name = "5-001a",
+ .supply = "AVDD2",
+ }, {
+ .dev_name = "5-001a",
+ .supply = "CPVDD",
+ },
+
+};
+
+static struct regulator_consumer_supply wm8994_fixed_voltage1_supplies[] = {
+ {
+ .dev_name = "5-001a",
+ .supply = "SPKVDD1",
+ }, {
+ .dev_name = "5-001a",
+ .supply = "SPKVDD2",
+ },
+};
+
+static struct regulator_init_data wm8994_fixed_voltage0_init_data = {
+ .constraints = {
+ .always_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(wm8994_fixed_voltage0_supplies),
+ .consumer_supplies = wm8994_fixed_voltage0_supplies,
+};
+
+static struct regulator_init_data wm8994_fixed_voltage1_init_data = {
+ .constraints = {
+ .always_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(wm8994_fixed_voltage1_supplies),
+ .consumer_supplies = wm8994_fixed_voltage1_supplies,
+};
+
+static struct fixed_voltage_config wm8994_fixed_voltage0_config = {
+ .supply_name = "VCC_1.8V_PDA",
+ .microvolts = 1800000,
+ .gpio = -EINVAL,
+ .init_data = &wm8994_fixed_voltage0_init_data,
+};
+
+static struct fixed_voltage_config wm8994_fixed_voltage1_config = {
+ .supply_name = "V_BAT",
+ .microvolts = 3700000,
+ .gpio = -EINVAL,
+ .init_data = &wm8994_fixed_voltage1_init_data,
+};
+
+static struct platform_device wm8994_fixed_voltage0 = {
+ .name = "reg-fixed-voltage",
+ .id = 0,
+ .dev = {
+ .platform_data = &wm8994_fixed_voltage0_config,
+ },
+};
+
+static struct platform_device wm8994_fixed_voltage1 = {
+ .name = "reg-fixed-voltage",
+ .id = 1,
+ .dev = {
+ .platform_data = &wm8994_fixed_voltage1_config,
+ },
+};
+
+static struct regulator_consumer_supply wm8994_avdd1_supply = {
+ .dev_name = "5-001a",
+ .supply = "AVDD1",
+};
+
+static struct regulator_consumer_supply wm8994_dcvdd_supply = {
+ .dev_name = "5-001a",
+ .supply = "DCVDD",
+};
+
+static struct regulator_init_data wm8994_ldo1_data = {
+ .constraints = {
+ .name = "AVDD1_3.0V",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &wm8994_avdd1_supply,
+};
+
+static struct regulator_init_data wm8994_ldo2_data = {
+ .constraints = {
+ .name = "DCVDD_1.0V",
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &wm8994_dcvdd_supply,
+};
+
+static struct wm8994_pdata wm8994_platform_data = {
+ /* configure gpio1 function: 0x0001(Logic level input/output) */
+ .gpio_defaults[0] = 0x0001,
+ /* configure gpio3/4/5/7 function for AIF2 voice */
+ .gpio_defaults[2] = 0x8100,
+ .gpio_defaults[3] = 0x8100,
+ .gpio_defaults[4] = 0x8100,
+ .gpio_defaults[6] = 0x0100,
+ /* configure gpio8/9/10/11 function for AIF3 BT */
+ .gpio_defaults[7] = 0x8100,
+ .gpio_defaults[8] = 0x0100,
+ .gpio_defaults[9] = 0x0100,
+ .gpio_defaults[10] = 0x0100,
+ .ldo[0] = { S5PV210_MP03(6), NULL, &wm8994_ldo1_data }, /* XM0FRNB_2 */
+ .ldo[1] = { 0, NULL, &wm8994_ldo2_data },
+};
+
/* GPIO I2C PMIC */
#define AP_I2C_GPIO_PMIC_BUS_4 4
static struct i2c_gpio_platform_data aquila_i2c_gpio_pmic_data = {
@@ -422,6 +538,58 @@ static struct i2c_board_info i2c_gpio_pmic_devs[] __initdata = {
#endif
};
+/* GPIO I2C AP 1.8V */
+#define AP_I2C_GPIO_BUS_5 5
+static struct i2c_gpio_platform_data i2c_gpio5_data = {
+ .sda_pin = S5PV210_MP05(3), /* XM0ADDR_11 */
+ .scl_pin = S5PV210_MP05(2), /* XM0ADDR_10 */
+};
+
+static struct platform_device i2c_gpio5 = {
+ .name = "i2c-gpio",
+ .id = AP_I2C_GPIO_BUS_5,
+ .dev = {
+ .platform_data = &i2c_gpio5_data,
+ },
+};
+
+static struct i2c_board_info i2c_gpio5_devs[] __initdata = {
+ {
+ /* CS/ADDR = low 0x34 (FYI: high = 0x36) */
+ I2C_BOARD_INFO("wm8994", 0x34 >> 1),
+ .platform_data = &wm8994_platform_data,
+ },
+};
+
+static void __init aquila_sound_init(void)
+{
+ unsigned int gpio;
+
+ /* CODEC_XTAL_EN */
+ gpio = S5PV210_GPH3(2); /* XEINT_26 */
+ gpio_request(gpio, "CODEC_XTAL_EN");
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_OUTPUT);
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ gpio_direction_output(gpio, 1);
+
+ /* CLKOUT[9:8] set to 0x3(XUSBXTI) of 0xE010E000(OTHERS)
+ * for 24MHZ
+ */
+ writel(readl(S5P_OTHERS) | (0x3 << 8), S5P_OTHERS);
+
+ /* MICBIAS_EN */
+ gpio = S5PV210_GPJ4(2); /* XMSMRN */
+ gpio_request(gpio, "MICBIAS_EN");
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_OUTPUT);
+ gpio_direction_output(gpio, 1);
+
+ /* ADC_EN */
+ gpio = S5PV210_GPJ3(2);
+ gpio_request(gpio, "ADC_EN");
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_OUTPUT);
+ gpio_direction_output(gpio, 1);
+}
+
/* PMIC Power button */
static struct gpio_keys_button aquila_gpio_keys_table[] = {
{
@@ -523,6 +691,10 @@ static struct platform_device *aquila_devices[] __initdata = {
&s5pc110_device_onenand,
&samsung_device_keypad,
&i2c_gpio10,
+ &wm8994_fixed_voltage0,
+ &wm8994_fixed_voltage1,
+ &s5pv210_device_iis0,
+ &i2c_gpio5,
};
static void __init aquila_map_io(void)
@@ -539,6 +711,11 @@ static void __init aquila_machine_init(void)
i2c_register_board_info(AP_I2C_GPIO_PMIC_BUS_4, i2c_gpio_pmic_devs,
ARRAY_SIZE(i2c_gpio_pmic_devs));
+ /* SOUND */
+ i2c_register_board_info(AP_I2C_GPIO_BUS_5, i2c_gpio5_devs,
+ ARRAY_SIZE(i2c_gpio5_devs));
+ aquila_sound_init();
+
/* FB */
s3c_fb_set_platdata(&aquila_lcd_pdata);
--
1.6.3.3
5
10
This patch set is against git://git.alsa-project.org/alsa-kernel.git
but the last patch in series won't compile until the sram allocator patch
is available.
8
44
Hello everyone
I've been working on Alsamixer-Qt4 lately and compiled
the changes into a new release version 0.4.0.
Most importantly the cards list can be refreshed now
and the style got a little overhaul, too.
A more detailed changelog can be found at the xwmw.org page.
The new version 0.4.0 is available at the known places.
http://xwmw.org/alsamixer-qt4/
http://sourceforge.net/projects/alsamixer-qt4/files/
Any comments are welcome.
Cheers,
Sebastian
5
18
This patch adds the MAX98088 CODEC driver.
Signed-off-by: Peter Hsiang <peter.hsiang at maxim-ic.com>
---
include/sound/max98088.h | 87 ++
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/max98088.c | 2871 +++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/max98088.h | 161 +++
5 files changed, 3125 insertions(+), 0 deletions(-)
create mode 100644 include/sound/max98088.h
create mode 100644 sound/soc/codecs/max98088.c
create mode 100644 sound/soc/codecs/max98088.h
diff --git a/include/sound/max98088.h b/include/sound/max98088.h
new file mode 100644
index 0000000..7a6c53c
--- /dev/null
+++ b/include/sound/max98088.h
@@ -0,0 +1,87 @@
+/*
+ * Platform data for MAX98088
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SOUND_MAX98088_PDATA_H__
+#define __SOUND_MAX98088_PDATA_H__
+
+#define EQ_CFG_MAX 32
+
+/* Equalizer filter response configuration */
+struct max98088_eq_cfg {
+ const char *name;
+ unsigned int rate;
+ u16 band1[5];
+ u16 band2[5];
+ u16 band3[5];
+ u16 band4[5];
+ u16 band5[5];
+};
+
+/* Speaker excursion limiter filter response configurations */
+#define EX_CFG_MAX 32
+
+struct max98088_excursion_cfg {
+ const char *name;
+ unsigned int rate;
+ u16 resp[5];
+};
+
+/* codec platform data */
+struct max98088_pdata {
+
+ /* Equalizers for DAI1 and DAI2 */
+ struct max98088_eq_cfg *eq1_cfg;
+ struct max98088_eq_cfg *eq2_cfg;
+ unsigned int eq1_cfgcnt;
+ unsigned int eq2_cfgcnt;
+
+ /* Excursion limiters for DAI1 and DAI2 */
+ struct max98088_excursion_cfg *ex1_cfg;
+ struct max98088_excursion_cfg *ex2_cfg;
+ unsigned int ex1_cfgcnt;
+ unsigned int ex2_cfgcnt;
+
+ /* Receiver output can be configured as power amplifier or LINE out */
+ /* Set receiver_mode to:
+ * 0 = amplifier output, or
+ * 1 = LINE level output
+ */
+ unsigned int receiver_mode;
+
+ /* Analog/digital microphone configuration:
+ * 0 = analog microphone input (normal setting)
+ * 1 = digital microhpone input
+ */
+ unsigned int digmic_left_enable;
+ unsigned int digmic_right_enable;
+
+ /* Normal microphone connection, or external connection through INA
+ * 0 = normal connection
+ * 1 = INA1 input
+ * 2 = INA2 input
+ */
+ unsigned int extmic_mode;
+
+ /* Bypass option for INA to MIC1 connection
+ * 0 = normal setting
+ * 1 = bypass enabled
+ */
+ unsigned int ina_to_mic1_bypass;
+
+ /* Bypass option for MIC1 to MIC2 connection
+ * 0 = normal setting
+ * 1 = bypass enabled
+ */
+ unsigned int mic1_to_mic2_bypass;
+};
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 155c127..e9a3c74 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -28,6 +28,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA7210 if I2C
select SND_SOC_JZ4740 if SOC_JZ4740
select SND_SOC_MAX9877 if I2C
+ select SND_SOC_MAX98088 if I2C
select SND_SOC_PCM3008
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
@@ -156,6 +157,9 @@ config SND_SOC_L3
config SND_SOC_DA7210
tristate
+config SND_SOC_MAX98088
+ tristate
+
config SND_SOC_PCM3008
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 10d468e..fe9d104 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
@@ -87,6 +88,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 0000000..72f599f
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2871 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+static struct snd_soc_codec *max98088_codec;
+struct snd_soc_codec_device soc_codec_dev_max98088;
+
+/* configurations associated with each channel of DAI stream */
+struct max98088_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+
+ /* Equalizer parameters */
+ int eq_textcnt;
+ const char *eq_texts[EQ_CFG_MAX];
+ int eq_sel;
+ struct soc_enum eq_enum;
+
+ /* Excursion limiter parameters */
+ int ex_textcnt;
+ const char *ex_texts[EX_CFG_MAX];
+ int ex_sel;
+ struct soc_enum ex_enum;
+};
+
+/* codec private data */
+struct max98088_priv {
+ struct snd_soc_codec codec;
+ struct max98088_pdata *pdata;
+ u8 reg_cache[M98088_REG_CNT];
+ unsigned int sysclk;
+ struct max98088_cdata dai[2];
+ u8 power_state;
+ unsigned int ex_mode;
+ unsigned int digmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+ 0x00, /* 00 IRQ status */
+ 0x00, /* 01 MIC status */
+ 0x00, /* 02 jack status */
+ 0x00, /* 03 battery voltage */
+ 0x00, /* 04 */
+ 0x00, /* 05 */
+ 0x00, /* 06 */
+ 0x00, /* 07 */
+ 0x00, /* 08 */
+ 0x00, /* 09 */
+ 0x00, /* 0A */
+ 0x00, /* 0B */
+ 0x00, /* 0C */
+ 0x00, /* 0D */
+ 0x00, /* 0E */
+ 0x00, /* 0F interrupt enable */
+
+ 0x00, /* 10 master clock */
+ 0x00, /* 11 DAI1 clock mode */
+ 0x00, /* 12 DAI1 clock control */
+ 0x00, /* 13 DAI1 clock control */
+ 0x00, /* 14 DAI1 format */
+ 0x00, /* 15 DAI1 clock */
+ 0x00, /* 16 DAI1 config */
+ 0x00, /* 17 DAI1 TDM */
+ 0x00, /* 18 DAI1 filters */
+ 0x00, /* 19 DAI2 clock mode */
+ 0x00, /* 1A DAI2 clock control */
+ 0x00, /* 1B DAI2 clock control */
+ 0x00, /* 1C DAI2 format */
+ 0x00, /* 1D DAI2 clock */
+ 0x00, /* 1E DAI2 config */
+ 0x00, /* 1F DAI2 TDM */
+
+ 0x00, /* 20 DAI2 filters */
+ 0x00, /* 21 data config */
+ 0x00, /* 22 DAC mixer */
+ 0x00, /* 23 left ADC mixer */
+ 0x00, /* 24 right ADC mixer */
+ 0x00, /* 25 left HP mixer */
+ 0x00, /* 26 right HP mixer */
+ 0x00, /* 27 HP control */
+ 0x00, /* 28 left REC mixer */
+ 0x00, /* 29 right REC mixer */
+ 0x00, /* 2A REC control */
+ 0x00, /* 2B left SPK mixer */
+ 0x00, /* 2C right SPK mixer */
+ 0x00, /* 2D SPK control */
+ 0x00, /* 2E sidetone */
+ 0x00, /* 2F DAI1 playback level */
+
+ 0x00, /* 30 DAI1 playback level */
+ 0x00, /* 31 DAI2 playback level */
+ 0x00, /* 32 DAI2 playbakc level */
+ 0x00, /* 33 left ADC level */
+ 0x00, /* 34 right ADC level */
+ 0x00, /* 35 MIC1 level */
+ 0x00, /* 36 MIC2 level */
+ 0x00, /* 37 INA level */
+ 0x00, /* 38 INB level */
+ 0x00, /* 39 left HP volume */
+ 0x00, /* 3A right HP volume */
+ 0x00, /* 3B left REC volume */
+ 0x00, /* 3C right REC volume */
+ 0x00, /* 3D left SPK volume */
+ 0x00, /* 3E right SPK volume */
+ 0x00, /* 3F MIC config */
+
+ 0x00, /* 40 MIC threshold */
+ 0x00, /* 41 excursion limiter filter */
+ 0x00, /* 42 excursion limiter threshold */
+ 0x00, /* 43 ALC */
+ 0x00, /* 44 power limiter threshold */
+ 0x00, /* 45 power limiter config */
+ 0x00, /* 46 distortion limiter config */
+ 0x00, /* 47 audio input */
+ 0x00, /* 48 microphone */
+ 0x00, /* 49 level control */
+ 0x00, /* 4A bypass switches */
+ 0x00, /* 4B jack detect */
+ 0x00, /* 4C input enable */
+ 0x00, /* 4D output enable */
+ 0xF0, /* 4E bias control */
+ 0x00, /* 4F DAC power */
+
+ 0x0F, /* 50 DAC power */
+ 0x00, /* 51 system */
+ 0x00, /* 52 DAI1 EQ1 */
+ 0x00, /* 53 DAI1 EQ1 */
+ 0x00, /* 54 DAI1 EQ1 */
+ 0x00, /* 55 DAI1 EQ1 */
+ 0x00, /* 56 DAI1 EQ1 */
+ 0x00, /* 57 DAI1 EQ1 */
+ 0x00, /* 58 DAI1 EQ1 */
+ 0x00, /* 59 DAI1 EQ1 */
+ 0x00, /* 5A DAI1 EQ1 */
+ 0x00, /* 5B DAI1 EQ1 */
+ 0x00, /* 5C DAI1 EQ2 */
+ 0x00, /* 5D DAI1 EQ2 */
+ 0x00, /* 5E DAI1 EQ2 */
+ 0x00, /* 5F DAI1 EQ2 */
+
+ 0x00, /* 60 DAI1 EQ2 */
+ 0x00, /* 61 DAI1 EQ2 */
+ 0x00, /* 62 DAI1 EQ2 */
+ 0x00, /* 63 DAI1 EQ2 */
+ 0x00, /* 64 DAI1 EQ2 */
+ 0x00, /* 65 DAI1 EQ2 */
+ 0x00, /* 66 DAI1 EQ3 */
+ 0x00, /* 67 DAI1 EQ3 */
+ 0x00, /* 68 DAI1 EQ3 */
+ 0x00, /* 69 DAI1 EQ3 */
+ 0x00, /* 6A DAI1 EQ3 */
+ 0x00, /* 6B DAI1 EQ3 */
+ 0x00, /* 6C DAI1 EQ3 */
+ 0x00, /* 6D DAI1 EQ3 */
+ 0x00, /* 6E DAI1 EQ3 */
+ 0x00, /* 6F DAI1 EQ3 */
+
+ 0x00, /* 70 DAI1 EQ4 */
+ 0x00, /* 71 DAI1 EQ4 */
+ 0x00, /* 72 DAI1 EQ4 */
+ 0x00, /* 73 DAI1 EQ4 */
+ 0x00, /* 74 DAI1 EQ4 */
+ 0x00, /* 75 DAI1 EQ4 */
+ 0x00, /* 76 DAI1 EQ4 */
+ 0x00, /* 77 DAI1 EQ4 */
+ 0x00, /* 78 DAI1 EQ4 */
+ 0x00, /* 79 DAI1 EQ4 */
+ 0x00, /* 7A DAI1 EQ5 */
+ 0x00, /* 7B DAI1 EQ5 */
+ 0x00, /* 7C DAI1 EQ5 */
+ 0x00, /* 7D DAI1 EQ5 */
+ 0x00, /* 7E DAI1 EQ5 */
+ 0x00, /* 7F DAI1 EQ5 */
+
+ 0x00, /* 80 DAI1 EQ5 */
+ 0x00, /* 81 DAI1 EQ5 */
+ 0x00, /* 82 DAI1 EQ5 */
+ 0x00, /* 83 DAI1 EQ5 */
+ 0x00, /* 84 DAI2 EQ1 */
+ 0x00, /* 85 DAI2 EQ1 */
+ 0x00, /* 86 DAI2 EQ1 */
+ 0x00, /* 87 DAI2 EQ1 */
+ 0x00, /* 88 DAI2 EQ1 */
+ 0x00, /* 89 DAI2 EQ1 */
+ 0x00, /* 8A DAI2 EQ1 */
+ 0x00, /* 8B DAI2 EQ1 */
+ 0x00, /* 8C DAI2 EQ1 */
+ 0x00, /* 8D DAI2 EQ1 */
+ 0x00, /* 8E DAI2 EQ2 */
+ 0x00, /* 8F DAI2 EQ2 */
+
+ 0x00, /* 90 DAI2 EQ2 */
+ 0x00, /* 91 DAI2 EQ2 */
+ 0x00, /* 92 DAI2 EQ2 */
+ 0x00, /* 93 DAI2 EQ2 */
+ 0x00, /* 94 DAI2 EQ2 */
+ 0x00, /* 95 DAI2 EQ2 */
+ 0x00, /* 96 DAI2 EQ2 */
+ 0x00, /* 97 DAI2 EQ2 */
+ 0x00, /* 98 DAI2 EQ3 */
+ 0x00, /* 99 DAI2 EQ3 */
+ 0x00, /* 9A DAI2 EQ3 */
+ 0x00, /* 9B DAI2 EQ3 */
+ 0x00, /* 9C DAI2 EQ3 */
+ 0x00, /* 9D DAI2 EQ3 */
+ 0x00, /* 9E DAI2 EQ3 */
+ 0x00, /* 9F DAI2 EQ3 */
+
+ 0x00, /* A0 DAI2 EQ3 */
+ 0x00, /* A1 DAI2 EQ3 */
+ 0x00, /* A2 DAI2 EQ4 */
+ 0x00, /* A3 DAI2 EQ4 */
+ 0x00, /* A4 DAI2 EQ4 */
+ 0x00, /* A5 DAI2 EQ4 */
+ 0x00, /* A6 DAI2 EQ4 */
+ 0x00, /* A7 DAI2 EQ4 */
+ 0x00, /* A8 DAI2 EQ4 */
+ 0x00, /* A9 DAI2 EQ4 */
+ 0x00, /* AA DAI2 EQ4 */
+ 0x00, /* AB DAI2 EQ4 */
+ 0x00, /* AC DAI2 EQ5 */
+ 0x00, /* AD DAI2 EQ5 */
+ 0x00, /* AE DAI2 EQ5 */
+ 0x00, /* AF DAI2 EQ5 */
+
+ 0x00, /* B0 DAI2 EQ5 */
+ 0x00, /* B1 DAI2 EQ5 */
+ 0x00, /* B2 DAI2 EQ5 */
+ 0x00, /* B3 DAI2 EQ5 */
+ 0x00, /* B4 DAI2 EQ5 */
+ 0x00, /* B5 DAI2 EQ5 */
+ 0x00, /* B6 DAI1 biquad */
+ 0x00, /* B7 DAI1 biquad */
+ 0x00, /* B8 DAI1 biquad */
+ 0x00, /* B9 DAI1 biquad */
+ 0x00, /* BA DAI1 biquad */
+ 0x00, /* BB DAI1 biquad */
+ 0x00, /* BC DAI1 biquad */
+ 0x00, /* BD DAI1 biquad */
+ 0x00, /* BE DAI1 biquad */
+ 0x00, /* BF DAI1 biquad */
+
+ 0x00, /* C0 DAI2 biquad */
+ 0x00, /* C1 DAI2 biquad */
+ 0x00, /* C2 DAI2 biquad */
+ 0x00, /* C3 DAI2 biquad */
+ 0x00, /* C4 DAI2 biquad */
+ 0x00, /* C5 DAI2 biquad */
+ 0x00, /* C6 DAI2 biquad */
+ 0x00, /* C7 DAI2 biquad */
+ 0x00, /* C8 DAI2 biquad */
+ 0x00, /* C9 DAI2 biquad */
+ 0x00, /* CA */
+ 0x00, /* CB */
+ 0x00, /* CC */
+ 0x00, /* CD */
+ 0x00, /* CE */
+ 0x00, /* CF */
+
+ 0x00, /* D0 */
+ 0x00, /* D1 */
+ 0x00, /* D2 */
+ 0x00, /* D3 */
+ 0x00, /* D4 */
+ 0x00, /* D5 */
+ 0x00, /* D6 */
+ 0x00, /* D7 */
+ 0x00, /* D8 */
+ 0x00, /* D9 */
+ 0x00, /* DA */
+ 0x70, /* DB */
+ 0x00, /* DC */
+ 0x00, /* DD */
+ 0x00, /* DE */
+ 0x00, /* DF */
+
+ 0x00, /* E0 */
+ 0x00, /* E1 */
+ 0x00, /* E2 */
+ 0x00, /* E3 */
+ 0x00, /* E4 */
+ 0x00, /* E5 */
+ 0x00, /* E6 */
+ 0x00, /* E7 */
+ 0x00, /* E8 */
+ 0x00, /* E9 */
+ 0x00, /* EA */
+ 0x00, /* EB */
+ 0x00, /* EC */
+ 0x00, /* ED */
+ 0x00, /* EE */
+ 0x00, /* EF */
+
+ 0x00, /* F0 */
+ 0x00, /* F1 */
+ 0x00, /* F2 */
+ 0x00, /* F3 */
+ 0x00, /* F4 */
+ 0x00, /* F5 */
+ 0x00, /* F6 */
+ 0x00, /* F7 */
+ 0x00, /* F8 */
+ 0x00, /* F9 */
+ 0x00, /* FA */
+ 0x00, /* FB */
+ 0x00, /* FC */
+ 0x00, /* FD */
+ 0x00, /* FE */
+ 0x00, /* FF */
+};
+
+static struct {
+ int readable;
+ int writable;
+ int vol;
+} max98088_access[M98088_REG_CNT] = {
+ { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+ { 0xFF, 0x00, 1 }, /* 01 MIC status */
+ { 0xFF, 0x00, 1 }, /* 02 jack status */
+ { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+ { 0xFF, 0xFF, 0 }, /* 04 */
+ { 0xFF, 0xFF, 0 }, /* 05 */
+ { 0xFF, 0xFF, 0 }, /* 06 */
+ { 0xFF, 0xFF, 0 }, /* 07 */
+ { 0xFF, 0xFF, 0 }, /* 08 */
+ { 0xFF, 0xFF, 0 }, /* 09 */
+ { 0xFF, 0xFF, 0 }, /* 0A */
+ { 0xFF, 0xFF, 0 }, /* 0B */
+ { 0xFF, 0xFF, 0 }, /* 0C */
+ { 0xFF, 0xFF, 0 }, /* 0D */
+ { 0xFF, 0xFF, 0 }, /* 0E */
+ { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+ { 0xFF, 0xFF, 0 }, /* 10 master clock */
+ { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+ { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+ { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+ { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+ { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+ { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+ { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+ { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+ { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+ { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+ { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+ { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+ { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+ { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+ { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+ { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+ { 0xFF, 0xFF, 0 }, /* 21 data config */
+ { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+ { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+ { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+ { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+ { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+ { 0xFF, 0xFF, 0 }, /* 27 HP control */
+ { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+ { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+ { 0xFF, 0xFF, 0 }, /* 2A REC control */
+ { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+ { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+ { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+ { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+ { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+ { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+ { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+ { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+ { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+ { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+ { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+ { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+ { 0xFF, 0xFF, 0 }, /* 37 INA level */
+ { 0xFF, 0xFF, 0 }, /* 38 INB level */
+ { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+ { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+ { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+ { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+ { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+ { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+ { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+ { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+ { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+ { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+ { 0xFF, 0xFF, 0 }, /* 43 ALC */
+ { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+ { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+ { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+ { 0xFF, 0xFF, 0 }, /* 47 audio input */
+ { 0xFF, 0xFF, 0 }, /* 48 microphone */
+ { 0xFF, 0xFF, 0 }, /* 49 level control */
+ { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+ { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+ { 0xFF, 0xFF, 0 }, /* 4C input enable */
+ { 0xFF, 0xFF, 0 }, /* 4D output enable */
+ { 0xFF, 0xFF, 0 }, /* 4E bias control */
+ { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+ { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+ { 0xFF, 0xFF, 0 }, /* 51 system */
+ { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+ { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+ { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+ { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+ { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+ { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+ { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+ { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+ { 0x00, 0x00, 0 }, /* CA */
+ { 0x00, 0x00, 0 }, /* CB */
+ { 0x00, 0x00, 0 }, /* CC */
+ { 0x00, 0x00, 0 }, /* CD */
+ { 0x00, 0x00, 0 }, /* CE */
+ { 0x00, 0x00, 0 }, /* CF */
+
+ { 0x00, 0x00, 0 }, /* D0 */
+ { 0x00, 0x00, 0 }, /* D1 */
+ { 0x00, 0x00, 0 }, /* D2 */
+ { 0x00, 0x00, 0 }, /* D3 */
+ { 0x00, 0x00, 0 }, /* D4 */
+ { 0x00, 0x00, 0 }, /* D5 */
+ { 0x00, 0x00, 0 }, /* D6 */
+ { 0x00, 0x00, 0 }, /* D7 */
+ { 0x00, 0x00, 0 }, /* D8 */
+ { 0x00, 0x00, 0 }, /* D9 */
+ { 0x00, 0x00, 0 }, /* DA */
+ { 0x70, 0x70, 0 }, /* DB */
+ { 0x00, 0x00, 0 }, /* DC */
+ { 0x00, 0x00, 0 }, /* DD */
+ { 0x00, 0x00, 0 }, /* DE */
+ { 0x00, 0x00, 0 }, /* DF */
+
+ { 0x00, 0x00, 0 }, /* E0 */
+ { 0x00, 0x00, 0 }, /* E1 */
+ { 0x00, 0x00, 0 }, /* E2 */
+ { 0x00, 0x00, 0 }, /* E3 */
+ { 0x00, 0x00, 0 }, /* E4 */
+ { 0x00, 0x00, 0 }, /* E5 */
+ { 0x00, 0x00, 0 }, /* E6 */
+ { 0x00, 0x00, 0 }, /* E7 */
+ { 0x00, 0x00, 0 }, /* E8 */
+ { 0x00, 0x00, 0 }, /* E9 */
+ { 0x00, 0x00, 0 }, /* EA */
+ { 0x00, 0x00, 0 }, /* EB */
+ { 0x00, 0x00, 0 }, /* EC */
+ { 0x00, 0x00, 0 }, /* ED */
+ { 0x00, 0x00, 0 }, /* EE */
+ { 0x00, 0x00, 0 }, /* EF */
+
+ { 0x00, 0x00, 0 }, /* F0 */
+ { 0x00, 0x00, 0 }, /* F1 */
+ { 0x00, 0x00, 0 }, /* F2 */
+ { 0x00, 0x00, 0 }, /* F3 */
+ { 0x00, 0x00, 0 }, /* F4 */
+ { 0x00, 0x00, 0 }, /* F5 */
+ { 0x00, 0x00, 0 }, /* F6 */
+ { 0x00, 0x00, 0 }, /* F7 */
+ { 0x00, 0x00, 0 }, /* F8 */
+ { 0x00, 0x00, 0 }, /* F9 */
+ { 0x00, 0x00, 0 }, /* FA */
+ { 0x00, 0x00, 0 }, /* FB */
+ { 0x00, 0x00, 0 }, /* FC */
+ { 0x00, 0x00, 0 }, /* FD */
+ { 0x00, 0x00, 0 }, /* FE */
+ { 0xFF, 0x00, 1 }, /* FF */
+};
+
+
+static int max98088_volatile_register(unsigned int reg)
+{
+ return max98088_access[reg].vol;
+}
+
+/*
+ * Read the MAX98088 I2C register space
+ * Note: this driver source code is backward compatible to kernel
+ * version 2.6.32.
+ */
+static unsigned int max98088_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct max98088_priv *max98088 = codec->private_data;
+ struct i2c_msg msg[2];
+ struct i2c_client *client;
+ u8 data[2];
+ int ret;
+
+ client = (struct i2c_client *)codec->control_data;
+
+ if (max98088_volatile_register(reg)) {
+ data[0] = reg;
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = &data[0];
+ msg[0].len = 1;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = &data[1];
+ msg[1].len = 1;
+
+ ret = i2c_transfer(client->adapter, &msg[0], 2);
+ return (ret == 2) ? data[1] : -EIO;
+ } else {
+ if (reg > (ARRAY_SIZE(max98088_reg)))
+ return -EIO;
+ return max98088->reg_cache[reg];
+ }
+}
+
+/* Write to the MAX98088 register space (cached) */
+static int max98088_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+ struct max98088_priv *max98088 = codec->private_data;
+
+ data[0] = reg;
+ data[1] = value;
+ if (codec->hw_write(codec->control_data, data, 2) == 2) {
+ max98088->reg_cache[reg] =
+ (value & max98088_access[reg].writable);
+ return 0;
+ } else {
+ return -EIO;
+ }
+}
+
+/* Write to the MAX98088 I2C register space */
+static int max98088_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ data[0] = reg;
+ data[1] = value;
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+/*
+ * For kernels compiled without unsigned long long int division
+ */
+unsigned long long int ulldiv(unsigned long long int dividend,
+ unsigned long long int divisor)
+{
+ unsigned long long int quotient = 0;
+ int shift = 1;
+
+ if (divisor == 0)
+ return 0;
+
+ /* result is 1.0 if divisor and dividend are equal */
+ if (divisor == dividend)
+ return 1;
+
+ /* Normalize divisor */
+ while (!(divisor & 0x8000000000000000ULL)) {
+ divisor <<= 1;
+ ++shift;
+ }
+
+ /* Shift and subtract */
+ while (shift--) {
+ quotient <<= 1;
+
+ if (divisor <= dividend) {
+ dividend -= divisor;
+ ++quotient;
+ }
+ divisor >>= 1;
+ }
+
+ /* Round up */
+ if (dividend > divisor)
+ ++quotient;
+
+ return quotient;
+}
+
+#define INA1_PGA_BIT 0x01
+#define INA2_PGA_BIT 0x02
+#define INB1_PGA_BIT 0x04
+#define INB2_PGA_BIT 0x08
+/*
+ * The INx1 and INx2 PGAs share a power control signal.
+ * This function OR's the two power events to keep an unpowered INx
+ * from turning off it's counterpart.
+ * The control names are used to identify the PGA.
+ */
+static int max98088_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98088_priv *max98088 = codec->private_data;
+ u8 *state = &max98088->power_state;
+ unsigned int val;
+ unsigned int pga;
+ unsigned int mask;
+
+ BUG_ON(w->reg != M98088_REG_4C_PWR_EN_IN);
+
+ if (strncmp(w->name, "INA1", 4) == 0) {
+ pga = INA1_PGA_BIT;
+ mask = INA1_PGA_BIT | INA2_PGA_BIT;
+ } else if (strncmp(w->name, "INA2", 4) == 0) {
+ pga = INA2_PGA_BIT;
+ mask = INA1_PGA_BIT | INA2_PGA_BIT;
+ } else if (strncmp(w->name, "INB1", 4) == 0) {
+ pga = INB1_PGA_BIT;
+ mask = INB1_PGA_BIT | INB2_PGA_BIT;
+ } else if (strncmp(w->name, "INB2", 4) == 0) {
+ pga = INB2_PGA_BIT;
+ mask = INB1_PGA_BIT | INB2_PGA_BIT;
+ } else {
+ return -EINVAL;
+ }
+
+ if (event == SND_SOC_DAPM_POST_PMU) {
+ /* ON */
+ *state |= pga;
+
+ /* Turn on, avoiding unnecessary writes */
+ val = max98088_read(codec, w->reg);
+ if (!(val & (1 << w->shift))) {
+ val |= (1 << w->shift);
+ max98088_write(codec, w->reg, val);
+ }
+ } else if (event == SND_SOC_DAPM_POST_PMD) {
+ /* OFF */
+ *state &= ~pga;
+
+ /* Turn off if both are off, avoiding unnecessary writes */
+ if (!(*state & mask)) {
+ val = max98088_read(codec, w->reg);
+ if (val & (1 << w->shift)) {
+ val &= ~(1 << w->shift);
+ max98088_write(codec, w->reg, val);
+ }
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ if (band > 4)
+ return;
+
+ if (dai > 1)
+ return;
+
+ /* Load the base register address */
+ eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+ max98088_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+ max98088_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+ }
+
+ return;
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_ex_mode[] = {
+ "Off",
+ "100Hz",
+ "400Hz",
+ "600Hz",
+ "800Hz",
+ "1000Hz",
+ "200-400Hz",
+ "400-600Hz",
+ "400-800Hz",
+ "user-400Hz",
+ "user-600Hz",
+ "user-800Hz",
+ "user-1000Hz"
+};
+
+static const unsigned int ex_mode_table[] = {
+ 0x00, /* disabled */
+ (0<<4)|3, /* 100Hz */
+ (1<<4)|0, /* 400Hz */
+ (2<<4)|0, /* 600Hz */
+ (3<<4)|0, /* 800Hz */
+ (4<<4)|0, /* 1000Hz */
+ (1<<4)|1, /* 200-400Hz */
+ (2<<4)|2, /* 400-600Hz */
+ (3<<4)|2, /* 400-800Hz */
+ (1<<4)|3, /* user-400Hz */
+ (2<<4)|3, /* user-600Hz */
+ (3<<4)|3, /* user-800Hz */
+ (4<<4)|3 /* user-1000Hz */
+};
+
+static const struct soc_enum max98088_ex_mode_enum[] = {
+ SOC_ENUM_SINGLE_EXT(13, max98088_ex_mode),
+};
+
+/*
+ * Excursion limiter mode - set mode
+ */
+static int max98088_ex_mode_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ unsigned int *mode = &max98088->ex_mode;
+
+ *mode = ucontrol->value.integer.value[0];
+
+ if (*mode <= ARRAY_SIZE(ex_mode_table))
+ max98088_write(codec, M98088_REG_41_SPKDHP,
+ ex_mode_table[*mode]);
+
+ return 0;
+}
+
+/*
+ * Excursion limiter mode - get mode
+ */
+static int max98088_ex_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ unsigned int *mode = &max98088->ex_mode;
+
+ ucontrol->value.integer.value[0] = *mode;
+ return 0;
+}
+
+/*
+ * Load user programmable mode excursion limiter filter coefficients
+ */
+static void max98088_ex_resp_control(struct snd_soc_codec *codec,
+ int reg, u16 *param)
+{
+ max98088_write(codec, reg, M98088_BYTE1(param[0]));
+ max98088_write(codec, ++reg, M98088_BYTE0(param[0]));
+ max98088_write(codec, ++reg, M98088_BYTE1(param[1]));
+ max98088_write(codec, ++reg, M98088_BYTE0(param[1]));
+ max98088_write(codec, ++reg, M98088_BYTE1(param[2]));
+ max98088_write(codec, ++reg, M98088_BYTE0(param[2]));
+ max98088_write(codec, ++reg, M98088_BYTE1(param[3]));
+ max98088_write(codec, ++reg, M98088_BYTE0(param[3]));
+ max98088_write(codec, ++reg, M98088_BYTE1(param[4]));
+ max98088_write(codec, ++reg, M98088_BYTE0(param[4]));
+}
+
+static const char *max98088_hp_spk_mute[] = {"disable", "enable"};
+static const struct soc_enum max98088_hp_spk_mute_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, max98088_hp_spk_mute),
+};
+/* These defines correlate to the position of the like named value in
+ * max98088_hp_spk_mute[] */
+#define MAX98088_HP_SPK_MUTE_DISABLE 0
+#define MAX98088_HP_SPK_MUTE_ENABLE 1
+
+/*
+ * Mute the speaker
+ */
+static int max98088_spk_mute_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 lReg = max98088_read(codec, M98088_REG_3D_LVL_SPK_L);
+ u8 rReg = max98088_read(codec, M98088_REG_3E_LVL_SPK_R);
+
+ if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+ lReg |= M98088_SP_MUTE;
+ rReg |= M98088_SP_MUTE;
+ } else {
+ lReg &= ~M98088_SP_MUTE;
+ rReg &= ~M98088_SP_MUTE;
+ }
+
+ max98088_write(codec, M98088_REG_3D_LVL_SPK_L, lReg);
+ max98088_write(codec, M98088_REG_3E_LVL_SPK_R, rReg);
+
+ return 0;
+}
+
+/*
+ * Get speaker mute status
+ */
+static int max98088_spk_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 reg = max98088_read(codec, M98088_REG_3D_LVL_SPK_L);
+
+ ucontrol->value.integer.value[0] = (reg & M98088_SP_MUTE) ? 1 : 0;
+
+ return 0;
+}
+
+/*
+ * Mute the headphone
+ */
+static int max98088_hp_mute_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 lReg = max98088_read(codec, M98088_REG_39_LVL_HP_L);
+ u8 rReg = max98088_read(codec, M98088_REG_3A_LVL_HP_R);
+
+ if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+ lReg |= M98088_HP_MUTE;
+ rReg |= M98088_HP_MUTE;
+ } else {
+ lReg &= ~M98088_HP_MUTE;
+ rReg &= ~M98088_HP_MUTE;
+ }
+
+ max98088_write(codec, M98088_REG_39_LVL_HP_L, lReg);
+ max98088_write(codec, M98088_REG_3A_LVL_HP_R, rReg);
+
+ return 0;
+}
+
+/*
+ * Get current headphone mute status
+ */
+static int max98088_hp_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 reg = max98088_read(codec, M98088_REG_39_LVL_HP_L);
+
+ ucontrol->value.integer.value[0] = (reg & M98088_HP_MUTE) ? 1 : 0;
+
+ return 0;
+}
+
+/*
+ * Mute the receiver
+ */
+static int max98088_rec_mute_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 lReg = max98088_read(codec, M98088_REG_3B_LVL_REC_L);
+ u8 rReg = max98088_read(codec, M98088_REG_3C_LVL_REC_R);
+
+ if (MAX98088_HP_SPK_MUTE_ENABLE == ucontrol->value.integer.value[0]) {
+ lReg |= M98088_REC_MUTE;
+ rReg |= M98088_REC_MUTE;
+ } else {
+ lReg &= ~M98088_REC_MUTE;
+ rReg &= ~M98088_REC_MUTE;
+ }
+
+ max98088_write(codec, M98088_REG_3B_LVL_REC_L, lReg);
+ max98088_write(codec, M98088_REG_3C_LVL_REC_R, rReg);
+
+ return 0;
+}
+
+/*
+ * Get current receiver mute status
+ */
+static int max98088_rec_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u8 reg = max98088_read(codec, M98088_REG_3B_LVL_REC_L);
+
+ ucontrol->value.integer.value[0] = (reg & M98088_REC_MUTE) ? 1 : 0;
+ return 0;
+}
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+ "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+ max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_dai1_fltr[] = {
+ "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+ "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static const char *max98088_dcblk[] = {"Off", "On"};
+static const struct soc_enum max98088_dcblk_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_20_DAI2_FILTERS, 0, 2, max98088_dcblk),
+};
+
+/* amixer controls (non-dapm) */
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+ /* analog output levels */
+
+ SOC_DOUBLE_R("Headphone volume", M98088_REG_39_LVL_HP_L,
+ M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+ SOC_DOUBLE_R("Speaker volume", M98088_REG_3D_LVL_SPK_L,
+ M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+ SOC_DOUBLE_R("Receiver volume", M98088_REG_3B_LVL_REC_L,
+ M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+ SOC_ENUM_EXT("Headphone mute", max98088_hp_spk_mute_enum,
+ max98088_hp_mute_get, max98088_hp_mute_set),
+ SOC_ENUM_EXT("Speaker mute", max98088_hp_spk_mute_enum,
+ max98088_spk_mute_get, max98088_spk_mute_set),
+ SOC_ENUM_EXT("Receiver mute", max98088_hp_spk_mute_enum,
+ max98088_rec_mute_get, max98088_rec_mute_set),
+
+ /* analog input levels */
+
+ SOC_SINGLE("MIC1 gain", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+ SOC_SINGLE("MIC2 gain", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+ SOC_SINGLE("MIC1 pre", M98088_REG_35_LVL_MIC1, 5, 3, 0),
+ SOC_SINGLE("MIC2 pre", M98088_REG_36_LVL_MIC2, 5, 3, 0),
+
+ SOC_SINGLE("INA gain", M98088_REG_37_LVL_INA, 0, 7, 1),
+ SOC_SINGLE("INB gain", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+ /* ADC input digital gains and volume control */
+
+ SOC_SINGLE("ADCL volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+ SOC_SINGLE("ADCR volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+ SOC_SINGLE("ADCL gain", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+ SOC_SINGLE("ADCR gain", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+ /* equalizer */
+
+ SOC_SINGLE("EQ1 switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+ /* excursion limiter */
+
+ SOC_ENUM_EXT("EX limiter mode", max98088_ex_mode_enum,
+ max98088_ex_mode_get, max98088_ex_mode_set),
+ SOC_ENUM("EX limiter threshold", max98088_ex_thresh_enum),
+
+ /* voice/music filters */
+
+ SOC_ENUM("DAI1 filter mode", max98088_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC filter", max98088_dai1_dac_filter_enum),
+ SOC_ENUM("DAI1 ADC filter", max98088_dai1_adc_filter_enum),
+ SOC_ENUM("DAI2 DC block", max98088_dcblk_enum),
+
+ /* automatic level control (for both DAI1/DAI2) */
+
+ SOC_SINGLE("ALC switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+ SOC_SINGLE("ALC threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+ SOC_SINGLE("ALC multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+ SOC_SINGLE("ALC release time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+ /* power limiter */
+
+ SOC_SINGLE("PWR limiter threshold", M98088_REG_44_PWRLMT_CFG,
+ 4, 15, 0),
+ SOC_SINGLE("PWR limiter weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+ SOC_SINGLE("PWR limiter time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+ SOC_SINGLE("PWR limiter time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+ /* THD distortion limiter */
+
+ SOC_SINGLE("THD limiter thresh", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+ SOC_SINGLE("THD limiter time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* DAPM sub-tables of the main dapm_widgets */
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_receiver_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INA2", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INB2", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ u16 status;
+
+ BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+ /* powering down headphone gracefully */
+ status = max98088_read(codec, M98088_REG_4D_PWR_EN_OUT);
+ if ((status & M98088_HPEN) == M98088_HPEN) {
+ max98088_hw_write(codec, M98088_REG_4D_PWR_EN_OUT,
+ (status & ~M98088_HPEN));
+ }
+ schedule_timeout_interruptible(msecs_to_jiffies(20));
+
+ return 0;
+}
+
+/* DAPM widgets top level */
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98088_REG_4D_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98088_REG_4D_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+ M98088_REG_4D_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+ M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+ SND_SOC_DAPM_PGA_E("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 7, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 6, 0, NULL, 0, max98088_hp_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_receiver_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_receiver_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_rec_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+ 7, 0, NULL, 0, max98088_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+ 7, 0, NULL, 0, max98088_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+ 6, 0, NULL, 0, max98088_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+ 6, 0, NULL, 0, max98088_pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RECL"),
+ SND_SOC_DAPM_OUTPUT("RECR"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+/* DAPM AUDIO_MAP: */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left HP Mixer", "Left DAC1", "DACL1"},
+ {"Left HP Mixer", "Left DAC2", "DACL2"},
+ {"Left HP Mixer", "Right DAC1", "DACR1"},
+ {"Left HP Mixer", "Right DAC2", "DACR2"},
+ {"Left HP Mixer", "MIC1", "Mic Bias"},
+ {"Left HP Mixer", "MIC2", "Mic Bias"},
+ {"Left HP Mixer", "INA1", "INA1 Input"},
+ {"Left HP Mixer", "INA2", "INA2 Input"},
+ {"Left HP Mixer", "INB1", "INB1 Input"},
+ {"Left HP Mixer", "INB2", "INB2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right HP Mixer", "Left DAC1", "DACL1"},
+ {"Right HP Mixer", "Left DAC2", "DACL2" },
+ {"Right HP Mixer", "Right DAC1", "DACR1"},
+ {"Right HP Mixer", "Right DAC2", "DACR2"},
+ {"Right HP Mixer", "MIC1", "Mic Bias"},
+ {"Right HP Mixer", "MIC2", "Mic Bias"},
+ {"Right HP Mixer", "INA1", "INA1 Input"},
+ {"Right HP Mixer", "INA2", "INA2 Input"},
+ {"Right HP Mixer", "INB1", "INB1 Input"},
+ {"Right HP Mixer", "INB2", "INB2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left SPK Mixer", "Left DAC1", "DACL1"},
+ {"Left SPK Mixer", "Left DAC2", "DACL2"},
+ {"Left SPK Mixer", "Right DAC1", "DACR1"},
+ {"Left SPK Mixer", "Right DAC2", "DACR2"},
+ {"Left SPK Mixer", "MIC1", "Mic Bias"},
+ {"Left SPK Mixer", "MIC2", "Mic Bias"},
+ {"Left SPK Mixer", "INA1", "INA1 Input"},
+ {"Left SPK Mixer", "INA2", "INA2 Input"},
+ {"Left SPK Mixer", "INB1", "INB1 Input"},
+ {"Left SPK Mixer", "INB2", "INB2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right SPK Mixer", "Left DAC1", "DACL1"},
+ {"Right SPK Mixer", "Left DAC2", "DACL2"},
+ {"Right SPK Mixer", "Right DAC1", "DACR1"},
+ {"Right SPK Mixer", "Right DAC2", "DACR2"},
+ {"Right SPK Mixer", "MIC1", "Mic Bias"},
+ {"Right SPK Mixer", "MIC2", "Mic Bias"},
+ {"Right SPK Mixer", "INA1", "INA1 Input"},
+ {"Right SPK Mixer", "INA2", "INA2 Input"},
+ {"Right SPK Mixer", "INB1", "INB1 Input"},
+ {"Right SPK Mixer", "INB2", "INB2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Left REC Mixer", "Left DAC1", "DACL1"},
+ {"Left REC Mixer", "Left DAC2", "DACL2"},
+ {"Left REC Mixer", "Right DAC1", "DACR1"},
+ {"Left REC Mixer", "Right DAC2", "DACR2"},
+ {"Left REC Mixer", "MIC1", "Mic Bias"},
+ {"Left REC Mixer", "MIC2", "Mic Bias"},
+ {"Left REC Mixer", "INA1", "INA1 Input"},
+ {"Left REC Mixer", "INA2", "INA2 Input"},
+ {"Left REC Mixer", "INB1", "INB1 Input"},
+ {"Left REC Mixer", "INB2", "INB2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Right REC Mixer", "Left DAC1", "DACL1"},
+ {"Right REC Mixer", "Left DAC2", "DACL2"},
+ {"Right REC Mixer", "Right DAC1", "DACR1"},
+ {"Right REC Mixer", "Right DAC2", "DACR2"},
+ {"Right REC Mixer", "MIC1", "Mic Bias"},
+ {"Right REC Mixer", "MIC2", "Mic Bias"},
+ {"Right REC Mixer", "INA1", "INA1 Input"},
+ {"Right REC Mixer", "INA2", "INA2 Input"},
+ {"Right REC Mixer", "INB1", "INB1 Input"},
+ {"Right REC Mixer", "INB2", "INB2 Input"},
+
+ {"HP Left Out", NULL, "Left HP Mixer"},
+ {"HP Right Out", NULL, "Right HP Mixer"},
+ {"SPK Left Out", NULL, "Left SPK Mixer"},
+ {"SPK Right Out", NULL, "Right SPK Mixer"},
+ {"REC Left Out", NULL, "Left REC Mixer"},
+ {"REC Right Out", NULL, "Right REC Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RECL", NULL, "REC Left Out"},
+ {"RECR", NULL, "REC Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1", "Mic Bias"},
+ {"Left ADC Mixer", "MIC2", "Mic Bias"},
+ {"Left ADC Mixer", "INA1", "INA1 Input"},
+ {"Left ADC Mixer", "INA2", "INA2 Input"},
+ {"Left ADC Mixer", "INB1", "INB1 Input"},
+ {"Left ADC Mixer", "INB2", "INB2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1", "Mic Bias"},
+ {"Right ADC Mixer", "MIC2", "Mic Bias"},
+ {"Right ADC Mixer", "INA1", "INA1 Input"},
+ {"Right ADC Mixer", "INA2", "INA2 Input"},
+ {"Right ADC Mixer", "INB1", "INB1 Input"},
+ {"Right ADC Mixer", "INB2", "INB2 Input"},
+
+ /* inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+
+ {"INA1 Input", NULL, "INA1"},
+ {"INA2 Input", NULL, "INA2"},
+ {"INB1 Input", NULL, "INB1"},
+ {"INB2 Input", NULL, "INB2"},
+
+ {"Mic Bias", NULL, "MIC1"},
+ {"Mic Bias", NULL, "MIC2"},
+};
+
+/*
+ * Add widgets
+ */
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+ ARRAY_SIZE(max98088_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+/*
+ * Setup DAI1 format
+ */
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+ u8 regval;
+
+ cdata = &max98088->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ /* DAI clock master/slave wrt the codec */
+ switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
+ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ /* MAS: slave */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_MAS, 0);
+ /* slave mode PLL */
+ max98088_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ 0x80);
+ max98088_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk, frm master */
+ /* MAS: master */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ 0, M98088_DAI_MAS);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slv, frm mas */
+ case SND_SOC_DAIFMT_CBM_CFS: /* codec clk mas, frm slv */
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;;
+ }
+
+ /* I2S or TDM */
+ if ((fmt & SND_SOC_DAIFMT_I2S) == SND_SOC_DAIFMT_I2S) {
+ /* TDM: I2S */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_TDM, 0);
+ } else {
+ /* TDM: PCM/TDM */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ 0, M98088_DAI_TDM);
+ }
+
+ /* DAI hardware signal inversions */
+ switch (fmt & SND_SOC_DAIFMT_NB_NF) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* BCI: normal bclk (rise) */
+ /* WCI: normal frame */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_BCI|M98088_DAI_WCI, 0);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* BCI: normal bclk (rise) */
+ /* WCI: invert frame */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_BCI, M98088_DAI_WCI);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* BCI: invert bclk (fall) */
+ /* WCI: normal frame */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_WCI, M98088_DAI_BCI);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* BCI: invert bclk (fall) */
+ /* WCI: invert frame */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ 0, M98088_DAI_BCI|M98088_DAI_WCI);
+ break;
+ }
+
+ regval = (1<<0); /* BSEL : 64*LRCLK (for master mode only) */
+ if (max98088->digmic_mode)
+ regval |= (1<<6); /* OSR : oversample ratio */
+ max98088_write(codec, M98088_REG_15_DAI1_CLOCK, regval);
+
+ max98088_write(codec, M98088_REG_16_DAI1_IOCFG,
+ (1<<6) | /* SEL : map DAI1 to S1 */
+ (0<<5) | /* LTEN : ADC->DAC loop-through enable */
+ (0<<4) | /* LBEN : loopback (0=disable, 1=enable) */
+ (0<<3) | /* DMONO : DAC SDIN (0=stereo, 1=mono) */
+ (0<<2) | /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
+ (1<<1) | /* SDOEN : serial data out ENABLE */
+ (1<<0)); /* SDIEN : serial data in ENABLE */
+
+ max98088_write(codec, M98088_REG_17_DAI1_TDM,
+ (0<<6) | /* SLOTL : L in 1st slot */
+ (1<<4) | /* SLOTR : R in 2nd slot */
+ (0<<0)); /* SLOTDLY : no delay */
+ }
+
+ return 0;
+}
+
+/*
+ * Setup DAI2 format
+ */
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[1];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ /* DAI clock master/slave wrt the codec */
+ switch (fmt & SND_SOC_DAIFMT_CBS_CFS) {
+ case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ /* MAS: slave */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_MAS, 0);
+ /* slave mode PLL */
+ max98088_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ 0x80);
+ max98088_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ /* MAS: master */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ 0, M98088_DAI_MAS);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slv & frm mas */
+ case SND_SOC_DAIFMT_CBM_CFS: /* codec clk mas & frm slv */
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;;
+ }
+
+ /* I2S or TDM */
+ if ((fmt & SND_SOC_DAIFMT_I2S) == SND_SOC_DAIFMT_I2S) {
+ /* TDM: I2S */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_TDM, 0);
+ } else {
+ /* TDM: PCM/TDM */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ 0, M98088_DAI_TDM);
+ }
+
+ /* DAI hardware signal inversions */
+ switch (fmt & SND_SOC_DAIFMT_NB_NF) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* BCI: normal bclk (rise) */
+ /* WCI: normal frame */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_BCI|M98088_DAI_WCI, 0);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* BCI: normal bclk (rise) */
+ /* WCI: invert frame */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_BCI, M98088_DAI_WCI);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* BCI: invert bclk (fall) */
+ /* WCI: normal frame */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_WCI, M98088_DAI_BCI);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* BCI: invert bclk (fall) */
+ /* WCI: invert frame */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ 0, M98088_DAI_BCI|M98088_DAI_WCI);
+ break;
+ }
+
+ max98088_write(codec, M98088_REG_1D_DAI2_CLOCK,
+ (1<<0)); /* BSEL: 64*LRCLK (for master mode only) */
+
+ max98088_write(codec, M98088_REG_1E_DAI2_IOCFG,
+ (2<<6) | /* SEL : map DAI2 to S2 */
+ (0<<4) | /* LBEN : loopback (0=disable) */
+ (0<<3) | /* DMONO : DAC SDIN input */
+ (0<<2) | /* HIZOFF : ADC SDOUT (0=HiZ, 1=Drive) */
+ (1<<1) | /* SDOEN : serial data out ENABLE */
+ (1<<0)); /* SDIEN : serial data in ENABLE */
+
+ max98088_write(codec, M98088_REG_1F_DAI2_TDM,
+ (0<<6) | /* SLOTL : L in 1st slot */
+ (1<<4) | /* SLOTR : R in 2nd slot */
+ (0<<0)); /* SLOTDLY : no delay */
+ }
+
+ return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = codec->private_data;
+
+ if (freq != max98088->sysclk) {
+ max98088->sysclk = freq; /* remember current sysclk */
+
+ /* setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 30MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ max98088_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 30000000)) {
+ max98088_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ /* If codec is currently running, toggle reset */
+ if (max98088_read(codec, M98088_REG_51_PWR_SYS)
+ & M98088_SHDNRUN) {
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN, 0);
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ 0, M98088_SHDNRUN);
+ }
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98088->sysclk = freq;
+ return 0;
+}
+
+
+struct rate_table_struct {
+ u32 rate;
+ u8 sr1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct rate_table_struct rate_table[] = {
+ {8000, 0x1},
+ {11025, 0x2},
+ {16000, 0x3},
+ {22050, 0x4},
+ {24000, 0x5},
+ {32000, 0x6},
+ {44100, 0x7},
+ {48000, 0x8},
+ {88200, 0x9},
+ {96000, 0xA},
+};
+
+static inline int rate_index(int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate)
+ return i;
+ }
+ return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai1_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 max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+ unsigned int rate;
+ u8 reg11val;
+ u16 ni;
+
+ cdata = &max98088->dai[0];
+
+ rate = params_rate(params);
+
+ /* data 16/24 bit width */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /* WS: 16bit */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ /* WS: 24bit */
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ 0, M98088_DAI_WS);
+ break;
+ }
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+ if (rate != cdata->rate) {
+ /* set DAI1 SR1 value for the DSP; FREQ1:0=anyclock */
+ reg11val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
+ max98088_write(codec, M98088_REG_11_DAI1_CLKMODE, reg11val);
+ cdata->rate = rate;
+ }
+
+ /* Configure NI when operating as master */
+ if (max98088_read(codec, M98088_REG_14_DAI1_FORMAT)
+ & M98088_DAI_MAS) {
+ BUG_ON(max98088->sysclk == 0);
+ ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate,
+ (unsigned long long int)max98088->sysclk);
+ max98088_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ (ni >> 8) & 0x7f);
+ max98088_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ ni & 0xff);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ (1<<3), 0);
+ else
+ snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ 0, (1<<3));
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+ return 0;
+}
+
+/* Setup hw params and sample rate */
+static int max98088_dai2_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 max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+ unsigned int rate;
+ u8 reg19val;
+ u16 ni;
+
+ cdata = &max98088->dai[1];
+
+ rate = params_rate(params);
+
+ /* data 16/24 bit width */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /* WS: 16bit */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ /* WS: 24bit */
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ 0, M98088_DAI_WS);
+ break;
+ }
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+ if (rate != cdata->rate) {
+ /* set DAI2 SR2 value for the DSP; FREQ1:0=anyclock */
+ reg19val = (rate_table[rate_index(rate)].sr1 << 4) | 0;
+ max98088_write(codec, M98088_REG_19_DAI2_CLKMODE, reg19val);
+ cdata->rate = rate;
+ }
+
+ /* Configure NI when operating as master */
+ if (max98088_read(codec, M98088_REG_1C_DAI2_FORMAT)
+ & M98088_DAI_MAS) {
+ BUG_ON(max98088->sysclk == 0);
+ ni = (u16)ulldiv(65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate,
+ (unsigned long long int)max98088->sysclk);
+ max98088_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ (ni >> 8) & 0x7f);
+ max98088_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ ni & 0xff);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ (1<<3), 0);
+ else
+ snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ 0, (1<<3));
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, 0, M98088_SHDNRUN);
+
+ return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = codec->private_data;
+ int i;
+
+ /* write back cached values if they're writeable and
+ * different from the hardware default.
+ */
+ for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+ if (!max98088_access[i].writable)
+ continue;
+
+ if (max98088->reg_cache[i] == max98088_reg[i])
+ continue;
+
+ snd_soc_write(codec, i, max98088->reg_cache[i]);
+ }
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ max98088_sync_cache(codec);
+ max98088_write(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN|M98088_PWRSV);
+ break;
+ case SND_SOC_BIAS_OFF:
+ max98088_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+ .set_sysclk = max98088_dai_set_sysclk,
+ .set_fmt = max98088_dai1_set_fmt,
+ .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+ .set_sysclk = max98088_dai_set_sysclk,
+ .set_fmt = max98088_dai2_set_fmt,
+ .hw_params = max98088_dai2_hw_params,
+};
+
+struct snd_soc_dai max98088_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .ops = &max98088_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .ops = &max98088_dai2_ops,
+}
+};
+EXPORT_SYMBOL_GPL(max98088_dai);
+
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *coef_set;
+ int best, best_val, save, i, sel, fs;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[0];
+
+ if (!pdata || !cdata->eq_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+ sel = cdata->eq_sel;
+
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+ if (strcmp(pdata->eq1_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+ abs(pdata->eq1_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq1_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq1_cfg[best].name,
+ pdata->eq1_cfg[best].rate, fs);
+
+ /* Disable EQ while configuring, and save current on/off state */
+ save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+ coef_set = &pdata->eq1_cfg[sel];
+
+ m98088_eq_band(codec, 0, 0, coef_set->band1);
+ m98088_eq_band(codec, 0, 1, coef_set->band2);
+ m98088_eq_band(codec, 0, 2, coef_set->band3);
+ m98088_eq_band(codec, 0, 3, coef_set->band4);
+ m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+ /* restore original on/off state */
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *coef_set;
+ int best, best_val, save, i, sel, fs;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[1];
+
+ if (!pdata || !cdata->eq_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+
+ sel = cdata->eq_sel;
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+ if (strcmp(pdata->eq2_cfg[i].name, cdata->eq_texts[sel]) == 0 &&
+ abs(pdata->eq2_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq2_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq2_cfg[best].name,
+ pdata->eq2_cfg[best].rate, fs);
+
+ /* Disable EQ while configuring, and save current on/off state */
+ save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+ coef_set = &pdata->eq2_cfg[sel];
+
+ m98088_eq_band(codec, 1, 0, coef_set->band1);
+ m98088_eq_band(codec, 1, 1, coef_set->band2);
+ m98088_eq_band(codec, 1, 2, coef_set->band3);
+ m98088_eq_band(codec, 1, 3, coef_set->band4);
+ m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+ /* restore original on/off state */
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+ save);
+}
+
+
+static int max98088_put_eq1_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+
+ cdata = &max98088->dai[0];
+
+ if (sel >= pdata->eq1_cfgcnt)
+ return -EINVAL;
+
+ cdata->eq_sel = sel;
+ max98088_setup_eq1(codec);
+ return 0;
+}
+
+static int max98088_put_eq2_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+
+ cdata = &max98088->dai[1];
+
+ if (sel >= pdata->eq2_cfgcnt)
+ return -EINVAL;
+
+ cdata->eq_sel = sel;
+ max98088_setup_eq2(codec);
+ return 0;
+}
+
+static int max98088_get_eq1_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[0];
+
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+ return 0;
+}
+
+static int max98088_get_eq2_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[1];
+
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+ return 0;
+}
+
+static void max98088_handle_eq1_pdata(struct max98088_priv *max98088)
+{
+ struct snd_soc_codec *codec = &max98088->codec;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int ret, i, j;
+
+ struct snd_kcontrol_new eq1control =
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98088->dai[0].eq_enum,
+ max98088_get_eq1_enum,
+ max98088_put_eq1_enum);
+
+ cdata = &max98088->dai[0];
+
+ /* Build an array of texts for the enum API. The number
+ * of texts is likely fewer than the number of configurations
+ * due to multiple sample rates for the same text name. */
+ cdata->eq_textcnt = 0;
+ for (i = 0; i < pdata->eq1_cfgcnt; i++) {
+ for (j = 0; j < cdata->eq_textcnt; j++) {
+ if (strcmp(pdata->eq1_cfg[i].name,
+ cdata->eq_texts[j]) == 0) {
+ break;
+ }
+ }
+
+ if (j != cdata->eq_textcnt)
+ continue;
+
+ /* ...and remember the new version. */
+ cdata->eq_texts[i] = pdata->eq1_cfg[i].name;
+ cdata->eq_textcnt++;
+
+ if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+ dev_err(codec->dev, "Too many EQ config entries\n");
+ cdata->eq_textcnt--;
+ break;
+ }
+ }
+
+ dev_dbg(codec->dev, "Installed %d EQ1 configurations\n",
+ cdata->eq_textcnt);
+
+ /* now point the soc_enum to .texts array items */
+ cdata->eq_enum.texts = cdata->eq_texts;
+ cdata->eq_enum.max = cdata->eq_textcnt;
+
+ BUG_ON(&max98088->codec == 0);
+ BUG_ON((max98088->codec).card == 0);
+
+ ret = snd_soc_add_controls(&max98088->codec, &eq1control, 1);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_eq2_pdata(struct max98088_priv *max98088)
+{
+ struct snd_soc_codec *codec = &max98088->codec;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int ret, i, j;
+
+ struct snd_kcontrol_new eq2control =
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98088->dai[1].eq_enum,
+ max98088_get_eq2_enum,
+ max98088_put_eq2_enum);
+
+ cdata = &max98088->dai[1];
+
+ /* Build an array of texts for the enum API. The number
+ * of texts is likely fewer than the number of configurations
+ * due to multiple sample rates for the same text name. */
+ cdata->eq_textcnt = 0;
+ for (i = 0; i < pdata->eq2_cfgcnt; i++) {
+ for (j = 0; j < cdata->eq_textcnt; j++) {
+ if (strcmp(pdata->eq2_cfg[i].name,
+ cdata->eq_texts[j]) == 0) {
+ break;
+ }
+ }
+
+ if (j != cdata->eq_textcnt)
+ continue;
+
+ cdata->eq_texts[i] = pdata->eq2_cfg[i].name;
+ cdata->eq_textcnt++;
+
+ if (cdata->eq_textcnt >= EQ_CFG_MAX) {
+ dev_err(codec->dev, "Too many EQ config entries\n");
+ cdata->eq_textcnt--;
+ break;
+ }
+ }
+
+ dev_dbg(codec->dev, "Installed %d EQ2 configurations\n",
+ cdata->eq_textcnt);
+
+ /* now point the soc_enum to .texts array items */
+ cdata->eq_enum.texts = cdata->eq_texts;
+ cdata->eq_enum.max = cdata->eq_textcnt;
+
+ ret = snd_soc_add_controls(&max98088->codec, &eq2control, 1);
+ if (ret != 0)
+ printk(KERN_ERR "Failed to add EQ control: %d\n", ret);
+}
+
+
+static void max98088_setup_ex1(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ struct max98088_excursion_cfg *coef_set;
+ int best, best_val, i, sel, fs;
+
+ cdata = &max98088->dai[0];
+
+ if (!pdata || !cdata->ex_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+
+ sel = cdata->ex_sel;
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->ex1_cfgcnt; i++) {
+ if (strcmp(pdata->ex1_cfg[i].name,
+ cdata->ex_texts[sel]) == 0 &&
+ abs(pdata->ex1_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->ex1_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->ex1_cfg[best].name,
+ pdata->ex1_cfg[best].rate, fs);
+
+ coef_set = &pdata->ex1_cfg[sel];
+
+ max98088_ex_resp_control(codec, M98088_REG_B6_DAI1_BIQUAD_BASE,
+ coef_set->resp);
+}
+
+static int max98088_put_ex1_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+
+ cdata = &max98088->dai[0];
+
+ if (sel >= pdata->ex1_cfgcnt)
+ return -EINVAL;
+
+ cdata->ex_sel = sel;
+ max98088_setup_ex1(codec);
+ return 0;
+}
+
+
+static int max98088_get_ex1_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[0];
+ ucontrol->value.enumerated.item[0] = cdata->ex_sel;
+
+ return 0;
+}
+
+static void max98088_handle_ex1_pdata(struct max98088_priv *max98088)
+{
+ struct snd_soc_codec *codec = &max98088->codec;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int ret, i, j;
+
+ struct snd_kcontrol_new ex1control =
+ SOC_ENUM_EXT("EX1 Limiter Config",
+ max98088->dai[0].ex_enum,
+ max98088_get_ex1_enum,
+ max98088_put_ex1_enum);
+
+ cdata = &max98088->dai[0];
+
+ /* Build an array of texts for the enum API. The number
+ * of texts is likely fewer than the number of configurations
+ * due to multiple sample rates for the same text name. */
+ cdata->ex_textcnt = 0;
+ for (i = 0; i < pdata->ex1_cfgcnt; i++) {
+ for (j = 0; j < cdata->ex_textcnt; j++) {
+ if (strcmp(pdata->ex1_cfg[i].name,
+ cdata->ex_texts[j]) == 0) {
+ break;
+ }
+ }
+
+ if (j != cdata->ex_textcnt)
+ continue;
+
+ /* ...and remember the new version. */
+ cdata->ex_texts[i] = pdata->ex1_cfg[i].name;
+ cdata->ex_textcnt++;
+
+ if (cdata->ex_textcnt >= EX_CFG_MAX) {
+ dev_err(codec->dev, "Too many limiter configs\n");
+ cdata->ex_textcnt--;
+ break;
+ }
+ }
+
+ dev_dbg(codec->dev, "Installed %d EX1 configurations\n",
+ cdata->ex_textcnt);
+
+ /* now point the soc_enum to .texts array */
+ cdata->ex_enum.texts = cdata->ex_texts;
+ cdata->ex_enum.max = cdata->ex_textcnt;
+ ret = snd_soc_add_controls(&max98088->codec, &ex1control, 1);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add limiter control\n");
+}
+
+
+static void max98088_setup_ex2(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ struct max98088_excursion_cfg *coef_set;
+ int best, best_val, i, sel, fs;
+
+ cdata = &max98088->dai[1];
+
+ if (!pdata || !cdata->ex_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+
+ sel = cdata->ex_sel;
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->ex2_cfgcnt; i++) {
+ if (strcmp(pdata->ex2_cfg[i].name,
+ cdata->ex_texts[sel]) == 0 &&
+ abs(pdata->ex2_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->ex2_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->ex2_cfg[best].name,
+ pdata->ex2_cfg[best].rate, fs);
+
+ coef_set = &pdata->ex2_cfg[sel];
+
+ max98088_ex_resp_control(codec, M98088_REG_C0_DAI2_BIQUAD_BASE,
+ coef_set->resp);
+}
+
+static int max98088_put_ex2_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+
+ cdata = &max98088->dai[1];
+
+ if (sel >= pdata->ex2_cfgcnt)
+ return -EINVAL;
+
+ cdata->ex_sel = sel;
+ max98088_setup_ex2(codec);
+ return 0;
+}
+
+static int max98088_get_ex2_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = codec->private_data;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[1];
+ ucontrol->value.enumerated.item[0] = cdata->ex_sel;
+
+ return 0;
+}
+
+static void max98088_handle_ex2_pdata(struct max98088_priv *max98088)
+{
+ struct snd_soc_codec *codec = &max98088->codec;
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_cdata *cdata;
+ int ret, i, j;
+
+ struct snd_kcontrol_new ex2control =
+ SOC_ENUM_EXT("EX2 Limiter Config",
+ max98088->dai[1].ex_enum,
+ max98088_get_ex2_enum,
+ max98088_put_ex2_enum);
+
+ cdata = &max98088->dai[1];
+
+ /* Build an array of texts for the enum API. The number
+ * of texts is likely fewer than the number of configurations
+ * due to multiple sample rates for the same text name. */
+ cdata->ex_textcnt = 0;
+ for (i = 0; i < pdata->ex2_cfgcnt; i++) {
+ for (j = 0; j < cdata->ex_textcnt; j++) {
+ if (strcmp(pdata->ex2_cfg[i].name,
+ cdata->ex_texts[j]) == 0) {
+ break;
+ }
+ }
+
+ if (j != cdata->ex_textcnt)
+ continue;
+
+ /* ...and remember the new version. */
+ cdata->ex_texts[i] = pdata->ex2_cfg[i].name;
+ cdata->ex_textcnt++;
+
+ if (cdata->ex_textcnt >= EX_CFG_MAX) {
+ dev_err(codec->dev, "Too many limiter configs\n");
+ cdata->ex_textcnt--;
+ break;
+ }
+ }
+
+ dev_dbg(codec->dev, "Installed %d EX2 configurations\n",
+ cdata->ex_textcnt);
+
+ /* now point the soc_enum to .texts array items */
+ cdata->ex_enum.texts = cdata->ex_texts;
+ cdata->ex_enum.max = cdata->ex_textcnt;
+
+ ret = snd_soc_add_controls(&max98088->codec, &ex2control, 1);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add limiter control\n");
+}
+
+static void max98088_handle_pdata(struct max98088_priv *max98088)
+{
+ struct snd_soc_codec *codec = &max98088->codec;
+ struct max98088_pdata *pdata = max98088->pdata;
+ u8 regval;
+
+ if (!pdata)
+ return;
+
+ /* Configure digital mic / external mic */
+ /* Digital mic needs REG_15 OSR1=1 */
+ regval = ((0<<6) | /* digital mic clock freq = PCLK/8 */
+ (0<<5) | /* DIGMICL enable */
+ (0<<4) | /* DIGMICR enable */
+ (0<<0)); /* external mic enable */
+
+ if (pdata->digmic_left_enable)
+ regval |= (1<<5); /* digital left mic enabled */
+
+ if (pdata->digmic_right_enable)
+ regval |= (1<<4); /* digital right mic enabled */
+
+ max98088->digmic_mode = (regval ? 1 : 0);
+
+ if (pdata->extmic_mode < 3)
+ regval |= (pdata->extmic_mode << 0); /* external mic enabled */
+
+ max98088_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+ /* configure external mic to mic1 bypass, mic1 to mic2 bypass */
+ regval = 0;
+
+ if (pdata->ina_to_mic1_bypass)
+ regval |= (1<<7);
+
+ if (pdata->mic1_to_mic2_bypass)
+ regval |= (1<<4);
+
+ max98088_write(codec, M98088_REG_4A_CFG_BYPASS, regval);
+
+ /* configure receiver output */
+ regval = ((pdata->receiver_mode) ? (1<<7) : 0);
+ snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+ (1<<7), regval);
+
+ /* configure equalizers */
+ if (pdata->eq1_cfgcnt)
+ max98088_handle_eq1_pdata(max98088);
+
+ if (pdata->eq2_cfgcnt)
+ max98088_handle_eq2_pdata(max98088);
+
+ /* configure excursion limiters */
+ if (pdata->ex1_cfgcnt)
+ max98088_handle_ex1_pdata(max98088);
+
+ if (pdata->ex2_cfgcnt)
+ max98088_handle_ex2_pdata(max98088);
+}
+
+static int max98088_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;
+
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98088_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+ int i;
+ u8 *cache = codec->reg_cache;
+
+ /* toggle reset the shutdown bit of the codec hardware */
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < M98088_REG_CNT; i++) {
+ if (i == M98088_REG_51_PWR_SYS)
+ continue;
+
+ if (!max98088_access[i].writable)
+ continue;
+
+ max98088_hw_write(codec, i, cache[i]);
+ }
+
+ /* now enter into the resume mode bias level */
+ max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+/*
+ * Make sure that a max98088 is attached to the I2C bus.
+ */
+static int max98088_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct max98088_priv *max98088;
+ int ret = 0;
+
+ dev_printk(KERN_INFO, &pdev->dev, "MAX98088 Audio CODEC\n");
+
+ if (!max98088_codec) {
+ dev_err(&pdev->dev, "Codec device is not registered\n");
+ return -EINVAL;
+ }
+
+ socdev->card->codec = max98088_codec;
+ codec = max98088_codec;
+ max98088 = codec->private_data;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create pcms: %d\n", ret);
+ goto pcm_err;
+ }
+
+ snd_soc_add_controls(codec, max98088_snd_controls,
+ ARRAY_SIZE(max98088_snd_controls));
+
+ /* install additional amixer controls: EQ and excursion limiter */
+ if (max98088->pdata)
+ max98088_handle_pdata(max98088);
+ else
+ dev_err(&pdev->dev, "No platform data\n");
+
+ max98088_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return 0;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+pcm_err:
+ return ret;
+}
+
+static int max98088_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->card->codec;
+
+ if (codec->control_data)
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ return 0;
+}
+
+/* expose to machine driver */
+struct snd_soc_codec_device soc_codec_dev_max98088 = {
+ .probe = max98088_probe,
+ .remove = max98088_remove,
+ .suspend = max98088_suspend,
+ .resume = max98088_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98088);
+
+
+static int max98088_register(struct max98088_priv *max98088,
+ enum snd_soc_control_type control)
+
+{
+ int ret, i, version;
+ struct snd_soc_codec *codec = &max98088->codec;
+ struct max98088_cdata *cdata;
+
+ if (max98088_codec) {
+ dev_err(codec->dev, "Another MAX98088 is registered\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->name = "MAX98088";
+ codec->owner = THIS_MODULE;
+ codec->read = max98088_read;
+ codec->write = max98088_write;
+
+ /* setup DAPM event bias level and handler function */
+ codec->bias_level = SND_SOC_BIAS_STANDBY;
+ codec->set_bias_level = max98088_set_bias_level;
+
+ codec->dai = max98088_dai;
+ codec->num_dai = ARRAY_SIZE(max98088_dai);
+ codec->private_data = max98088;
+
+ codec->reg_cache_size = M98088_REG_CNT;
+ codec->reg_cache = &max98088->reg_cache;
+ codec->volatile_register = max98088_volatile_register;
+
+ memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+ version = max98088_read(codec, M98088_REG_FF_REV_ID);
+ dev_dbg(codec->dev, "MAX98088 revision 0x%02X\n", version);
+
+ /* initalize private data */
+
+ max98088->sysclk = (unsigned)-1;
+
+ cdata = &max98088->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_textcnt = 0;
+ cdata->eq_sel = 0;
+ cdata->ex_textcnt = 0;
+ cdata->ex_sel = 0;
+
+ cdata = &max98088->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_textcnt = 0;
+ cdata->eq_sel = 0;
+ cdata->ex_textcnt = 0;
+ cdata->ex_sel = 0;
+
+ max98088_write(codec, M98088_REG_14_DAI1_FORMAT, M98088_DAI_DLY);
+ max98088_write(codec, M98088_REG_1C_DAI2_FORMAT, M98088_DAI_DLY);
+
+ max98088->power_state = 0; /* INA INB power enable state */
+ max98088->ex_mode = 0; /* excursion limiter mode */
+ max98088->digmic_mode = 0; /* 0=normal, 1=digital mic used */
+
+ /* disable device via dapm interface */
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ /* initialize registers cache to hardware default */
+ max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ max98088_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+ max98088_write(codec, M98088_REG_22_MIX_DAC,
+ M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+ M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+ max98088_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+ max98088_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+ /* power on device */
+ max98088_codec = codec;
+ for (i = 0; i < codec->num_dai; i++)
+ max98088_dai[i].dev = codec->dev;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dais(&max98088_dai[0], codec->num_dai);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+ goto err_codec;
+ }
+
+ return ret;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ kfree(max98088);
+
+ return ret;
+}
+
+static void max98088_unregister(struct max98088_priv *max98088)
+{
+ struct snd_soc_codec *codec = &max98088->codec;
+ snd_soc_unregister_dais(max98088_dai, codec->num_dai);
+ snd_soc_unregister_codec(codec);
+ kfree(max98088);
+ max98088_codec = NULL;
+}
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_codec *codec;
+ struct max98088_priv *max98088;
+
+ max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+ if (max98088 == NULL)
+ return -ENOMEM;
+
+ /* codec structure is inside the private data */
+ codec = &(max98088->codec);
+
+ codec->private_data = max98088;
+
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ i2c_set_clientdata(i2c, max98088);
+ codec->control_data = i2c;
+
+ max98088->pdata = i2c->dev.platform_data;
+
+ codec->dev = &i2c->dev;
+
+ return max98088_register(max98088, SND_SOC_I2C);
+}
+
+static int max98088_i2c_remove(struct i2c_client *client)
+{
+ struct max98088_priv *max98088 = i2c_get_clientdata(client);
+ max98088_unregister(max98088);
+ return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+ { "max98088", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+
+static struct i2c_driver max98088_i2c_driver = {
+ .driver = {
+ .name = "MAX98088 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98088_i2c_probe,
+ .remove = __devexit_p(max98088_i2c_remove),
+ .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&max98088_i2c_driver);
+ if (ret)
+ pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+ return ret;
+}
+
+static void __exit max98088_exit(void)
+{
+ i2c_del_driver(&max98088_i2c_driver);
+}
+
+module_init(max98088_init);
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang <peter.hsiang(a)maxim-ic.com>");
+MODULE_AUTHOR("Jesse Marroquin <jesse.marroquin(a)maxim-ic.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 0000000..f7ef4c2
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,161 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * 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 _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS 0x00
+#define M98088_REG_01_MIC_STATUS 0x01
+#define M98088_REG_02_JACK_STAUS 0x02
+#define M98088_REG_03_BATTERY_VOLTAGE 0x03
+#define M98088_REG_0F_IRQ_ENABLE 0x0F
+#define M98088_REG_10_SYS_CLK 0x10
+#define M98088_REG_11_DAI1_CLKMODE 0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI 0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO 0x13
+#define M98088_REG_14_DAI1_FORMAT 0x14
+#define M98088_REG_15_DAI1_CLOCK 0x15
+#define M98088_REG_16_DAI1_IOCFG 0x16
+#define M98088_REG_17_DAI1_TDM 0x17
+#define M98088_REG_18_DAI1_FILTERS 0x18
+#define M98088_REG_19_DAI2_CLKMODE 0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI 0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO 0x1B
+#define M98088_REG_1C_DAI2_FORMAT 0x1C
+#define M98088_REG_1D_DAI2_CLOCK 0x1D
+#define M98088_REG_1E_DAI2_IOCFG 0x1E
+#define M98088_REG_1F_DAI2_TDM 0x1F
+#define M98088_REG_20_DAI2_FILTERS 0x20
+#define M98088_REG_21_SRC 0x21
+#define M98088_REG_22_MIX_DAC 0x22
+#define M98088_REG_23_MIX_ADC_LEFT 0x23
+#define M98088_REG_24_MIX_ADC_RIGHT 0x24
+#define M98088_REG_25_MIX_HP_LEFT 0x25
+#define M98088_REG_26_MIX_HP_RIGHT 0x26
+#define M98088_REG_27_MIX_HP_CNTL 0x27
+#define M98088_REG_28_MIX_REC_LEFT 0x28
+#define M98088_REG_29_MIX_REC_RIGHT 0x29
+#define M98088_REG_2A_MIC_REC_CNTL 0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT 0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT 0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL 0x2D
+#define M98088_REG_2E_LVL_SIDETONE 0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY 0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ 0x30
+#define M98088_REG_31_LVL_DAI2_PLAY 0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ 0x32
+#define M98088_REG_33_LVL_ADC_L 0x33
+#define M98088_REG_34_LVL_ADC_R 0x34
+#define M98088_REG_35_LVL_MIC1 0x35
+#define M98088_REG_36_LVL_MIC2 0x36
+#define M98088_REG_37_LVL_INA 0x37
+#define M98088_REG_38_LVL_INB 0x38
+#define M98088_REG_39_LVL_HP_L 0x39
+#define M98088_REG_3A_LVL_HP_R 0x3A
+#define M98088_REG_3B_LVL_REC_L 0x3B
+#define M98088_REG_3C_LVL_REC_R 0x3C
+#define M98088_REG_3D_LVL_SPK_L 0x3D
+#define M98088_REG_3E_LVL_SPK_R 0x3E
+#define M98088_REG_3F_MICAGC_CFG 0x3F
+#define M98088_REG_40_MICAGC_THRESH 0x40
+#define M98088_REG_41_SPKDHP 0x41
+#define M98088_REG_42_SPKDHP_THRESH 0x42
+#define M98088_REG_43_SPKALC_COMP 0x43
+#define M98088_REG_44_PWRLMT_CFG 0x44
+#define M98088_REG_45_PWRLMT_TIME 0x45
+#define M98088_REG_46_THDLMT_CFG 0x46
+#define M98088_REG_47_CFG_AUDIO_IN 0x47
+#define M98088_REG_48_CFG_MIC 0x48
+#define M98088_REG_49_CFG_LEVEL 0x49
+#define M98088_REG_4A_CFG_BYPASS 0x4A
+#define M98088_REG_4B_CFG_JACKDET 0x4B
+#define M98088_REG_4C_PWR_EN_IN 0x4C
+#define M98088_REG_4D_PWR_EN_OUT 0x4D
+#define M98088_REG_4E_BIAS_CNTL 0x4E
+#define M98088_REG_4F_DAC_BIAS1 0x4F
+#define M98088_REG_50_DAC_BIAS2 0x50
+#define M98088_REG_51_PWR_SYS 0x51
+#define M98088_REG_52_DAI1_EQ_BASE 0x52
+#define M98088_REG_84_DAI2_EQ_BASE 0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE 0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE 0xC0
+#define M98088_REG_FF_REV_ID 0xFF
+
+#define M98088_REG_CNT (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+ #define M98088_DAI_MAS (1<<7)
+ #define M98088_DAI_WCI (1<<6)
+ #define M98088_DAI_BCI (1<<5)
+ #define M98088_DAI_DLY (1<<4)
+ #define M98088_DAI_TDM (1<<2)
+ #define M98088_DAI_FSW (1<<1)
+ #define M98088_DAI_WS (1<<0)
+
+/* M98088_REG_22_MIX_DAC */
+ #define M98088_DAI1L_TO_DACL (1<<7)
+ #define M98088_DAI1R_TO_DACL (1<<6)
+ #define M98088_DAI2L_TO_DACL (1<<5)
+ #define M98088_DAI2R_TO_DACL (1<<4)
+ #define M98088_DAI1L_TO_DACR (1<<3)
+ #define M98088_DAI1R_TO_DACR (1<<2)
+ #define M98088_DAI2L_TO_DACR (1<<1)
+ #define M98088_DAI2R_TO_DACR (1<<0)
+
+/* M98088_REG_3A_LVL_HEADPHONE_R */
+ #define M98088_HP_MUTE (1<<7)
+
+/* M98088_REG_3C_LVL_RECEIVER_R */
+ #define M98088_REC_MUTE (1<<7)
+
+/* M98088_REG_3E_LVL_SPEAKER_R */
+ #define M98088_SP_MUTE (1<<7)
+
+/* M98088_REG_49_CFG_LEVEL */
+ #define M98088_VSEN (1<<6)
+ #define M98088_ZDEN (1<<5)
+ #define M98088_EQ2EN (1<<1)
+ #define M98088_EQ1EN (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+ #define M98088_HPLEN (1<<7)
+ #define M98088_HPREN (1<<6)
+ #define M98088_HPEN ((1<<7)|(1<<6))
+ #define M98088_SPLEN (1<<5)
+ #define M98088_SPREN (1<<4)
+ #define M98088_RECEN (1<<3)
+ #define M98088_DALEN (1<<1)
+ #define M98088_DAREN (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+ #define M98088_SHDNRUN (1<<7)
+ #define M98088_PERFMODE (1<<3)
+ #define M98088_HPPLYBACK (1<<2)
+ #define M98088_PWRSV8K (1<<1)
+ #define M98088_PWRSV (1<<0)
+
+#define M98088_COEFS_PER_BAND 5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+struct max98088_setup_data {
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai max98088_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_max98088;
+
+#endif
--
1.6.3.3
2
5
[alsa-devel] [PATCH 2.6.32.3] hda_intel: ALSA HD Audio patch for Intel Cougar Point DeviceIDs
by Seth Heasley 13 Sep '10
by Seth Heasley 13 Sep '10
13 Sep '10
This patch adds the Intel Cougar Point (PCH) HD Audio Controller DeviceIDs.
Signed-off-by: Seth Heasley <seth.heasley(a)intel.com>
--- linux-2.6.32.3/sound/pci/hda/hda_intel.c.orig 2010-01-06 15:07:45.000000000 -0800
+++ linux-2.6.32.3/sound/pci/hda/hda_intel.c 2010-01-07 13:10:28.000000000 -0800
@@ -116,6 +116,7 @@
"{Intel, ICH9},"
"{Intel, ICH10},"
"{Intel, PCH},"
+ "{Intel, CPT},"
"{Intel, SCH},"
"{ATI, SB450},"
"{ATI, SB600},"
@@ -2648,6 +2649,8 @@
{ PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
/* PCH */
{ PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
+ /* CPT */
+ { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_ICH },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
/* ATI SB 450/600 */
2
5
[alsa-devel] [PATCH] ASoC: multi-component: SAMSUNG: Modify the name of I2S driver
by Chanwoo Choi 11 Sep '10
by Chanwoo Choi 11 Sep '10
11 Sep '10
This patch modify the name of I2S driver(S3C64XX/S3C24xx
/S3C2412) from "*-iis-*" to "*-i2s-*". The mismatch of I2S name
exist in the following sound driver. These files write the
"cpu_dai_name" of I2S as "*-i2s-*".
- sound/soc/s3c24xx/aquila_wm8994.c : "s3c64xx-i2s-v4-dai"
- sound/soc/s3c24xx/goni_wm8994.c : "s3c64xx-i2s-v4-dai"
- sound/soc/s3c24xx/smdk64xx_wm8580.c "s3c64xx-i2s-v4-dai"
- sound/soc/s3c24xx/jive_wm8750.c : "s3c2412-i2s-dai"
- sound/soc/s3c24xx/neo1973_wm8753.c : "s3c24xx-i2s-dai"
- sound/soc/s3c24xx/neo1973_gta02_wm8753.c : "s3c24xx-i2s-dai"
- sound/soc/s3c24xx/s3c24xx_simtec_hermes.c : "s3c24xx-i2s-dai"
- sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c : "s3c24xx-i2s-dai"
- sound/soc/s3c24xx/s3c24xx_uda134x.c : "s3c24xx-i2s-dai"
- sound/soc/s3c24xx/smartq_wm8987.c : "s3c64xx-i2s-dai.0"
or, It is should modify "cpu_dai_name" of sound drvier files
instead of modifying the name of I2S driver.
Signed-off-by: Chanwoo Choi <cw00.choi(a)samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
sound/soc/s3c24xx/s3c2412-i2s.c | 2 +-
sound/soc/s3c24xx/s3c24xx-i2s.c | 2 +-
sound/soc/s3c24xx/s3c64xx-i2s-v4.c | 2 +-
sound/soc/s3c24xx/s3c64xx-i2s.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index aa116f6..f7c23ae 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -188,7 +188,7 @@ static struct platform_driver s3c2412_iis_driver = {
.probe = s3c2412_iis_dev_probe,
.remove = s3c2412_iis_dev_remove,
.driver = {
- .name = "s3c2412-iis-dai",
+ .name = "s3c2412-i2s-dai",
.owner = THIS_MODULE,
},
};
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index a6cbeef..20712ad 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -495,7 +495,7 @@ static struct platform_driver s3c24xx_iis_driver = {
.probe = s3c24xx_iis_dev_probe,
.remove = s3c24xx_iis_dev_remove,
.driver = {
- .name = "s3c24xx-iis-dai",
+ .name = "s3c24xx-i2s-dai",
.owner = THIS_MODULE,
},
};
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s-v4.c b/sound/soc/s3c24xx/s3c64xx-i2s-v4.c
index 885040b..2846ea2 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s-v4.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s-v4.c
@@ -195,7 +195,7 @@ static struct platform_driver s3c64xx_i2sv4_driver = {
.probe = s3c64xx_i2sv4_dev_probe,
.remove = s3c64xx_i2sv4_dev_remove,
.driver = {
- .name = "s3c64xx-iis-v4-dai",
+ .name = "s3c64xx-i2s-v4-dai",
.owner = THIS_MODULE,
},
};
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 161189b..2017639 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -218,7 +218,7 @@ static struct platform_driver s3c64xx_iis_driver = {
.probe = s3c64xx_iis_dev_probe,
.remove = s3c64xx_iis_dev_remove,
.driver = {
- .name = "s3c64xx-iis-dai",
+ .name = "s3c64xx-i2s-dai",
.owner = THIS_MODULE,
},
};
--
1.6.3.3
7
16
[alsa-devel] [PATCH - 1/1] patch_nvhdmi.c: Add missing codec IDs, unify names
by Stephen Warren 10 Sep '10
by Stephen Warren 10 Sep '10
10 Sep '10
* Add missing codec IDs.
* Modify some existing codec names for discrete GPUs to match newly
added IDs. Note: existing names were a mixture of marketing and
engineering GPU names. Equally, there's no reason that codec IDs
have to be specific to a particular GPU or board, so identify
codecs in a less marketing-oriented fashion.
* Reformat codec ID table so it's easier to read, for me at least.
Signed-off-by: Stephen Warren <swarren(a)nvidia.com>
diff --git a/pci/hda/patch_nvhdmi.c b/pci/hda/patch_nvhdmi.c
index a281836..77e2b40 100644
--- a/pci/hda/patch_nvhdmi.c
+++ b/pci/hda/patch_nvhdmi.c
@@ -540,26 +540,32 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
- { .id = 0x10de0002, .name = "MCP77/78 HDMI",
- .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0003, .name = "MCP77/78 HDMI",
- .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0005, .name = "MCP77/78 HDMI",
- .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0006, .name = "MCP77/78 HDMI",
- .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0007, .name = "MCP79/7A HDMI",
- .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de000a, .name = "GT220 HDMI",
- .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000b, .name = "GT21x HDMI",
- .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000c, .name = "MCP89 HDMI",
- .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000d, .name = "GT240 HDMI",
- .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
- { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+ { .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+ { .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+ { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+ { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+ { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
+ { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+ { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+ { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{} /* terminator */
};
@@ -572,6 +578,21 @@ MODULE_ALIAS("snd-hda-codec-id:10de000a");
MODULE_ALIAS("snd-hda-codec-id:10de000b");
MODULE_ALIAS("snd-hda-codec-id:10de000c");
MODULE_ALIAS("snd-hda-codec-id:10de000d");
+MODULE_ALIAS("snd-hda-codec-id:10de0010");
+MODULE_ALIAS("snd-hda-codec-id:10de0011");
+MODULE_ALIAS("snd-hda-codec-id:10de0012");
+MODULE_ALIAS("snd-hda-codec-id:10de0013");
+MODULE_ALIAS("snd-hda-codec-id:10de0014");
+MODULE_ALIAS("snd-hda-codec-id:10de0018");
+MODULE_ALIAS("snd-hda-codec-id:10de0019");
+MODULE_ALIAS("snd-hda-codec-id:10de001a");
+MODULE_ALIAS("snd-hda-codec-id:10de001b");
+MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0040");
+MODULE_ALIAS("snd-hda-codec-id:10de0041");
+MODULE_ALIAS("snd-hda-codec-id:10de0042");
+MODULE_ALIAS("snd-hda-codec-id:10de0043");
+MODULE_ALIAS("snd-hda-codec-id:10de0044");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
--
1.6.0.6
nvpublic
2
5