提交 6679ee18 编写于 作者: T Takashi Iwai

Merge branch 'topic/asoc' into for-linus

...@@ -13,3 +13,7 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci.o ...@@ -13,3 +13,7 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci.o
obj-$(CONFIG_MXC_ULPI) += ulpi.o obj-$(CONFIG_MXC_ULPI) += ulpi.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o
ifdef CONFIG_SND_IMX_SOC
obj-y += ssi-fiq.o
obj-y += ssi-fiq-ksym.o
endif
/*
* Exported ksyms for the SSI FIQ handler
*
* Copyright (C) 2009, Sascha Hauer <s.hauer@pengutronix.de>
*
* 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 <mach/ssi.h>
EXPORT_SYMBOL(imx_ssi_fiq_tx_buffer);
EXPORT_SYMBOL(imx_ssi_fiq_rx_buffer);
EXPORT_SYMBOL(imx_ssi_fiq_start);
EXPORT_SYMBOL(imx_ssi_fiq_end);
EXPORT_SYMBOL(imx_ssi_fiq_base);
/*
* Copyright (C) 2009 Sascha Hauer <s.hauer@pengutronix.de>
*
* 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/linkage.h>
#include <asm/assembler.h>
/*
* r8 = bit 0-15: tx offset, bit 16-31: tx buffer size
* r9 = bit 0-15: rx offset, bit 16-31: rx buffer size
*/
#define SSI_STX0 0x00
#define SSI_SRX0 0x08
#define SSI_SISR 0x14
#define SSI_SIER 0x18
#define SSI_SACNT 0x38
#define SSI_SACNT_AC97EN (1 << 0)
#define SSI_SIER_TFE0_EN (1 << 0)
#define SSI_SISR_TFE0 (1 << 0)
#define SSI_SISR_RFF0 (1 << 2)
#define SSI_SIER_RFF0_EN (1 << 2)
.text
.global imx_ssi_fiq_start
.global imx_ssi_fiq_end
.global imx_ssi_fiq_base
.global imx_ssi_fiq_rx_buffer
.global imx_ssi_fiq_tx_buffer
imx_ssi_fiq_start:
ldr r12, imx_ssi_fiq_base
/* TX */
ldr r11, imx_ssi_fiq_tx_buffer
/* shall we send? */
ldr r13, [r12, #SSI_SIER]
tst r13, #SSI_SIER_TFE0_EN
beq 1f
/* TX FIFO empty? */
ldr r13, [r12, #SSI_SISR]
tst r13, #SSI_SISR_TFE0
beq 1f
mov r10, #0x10000
sub r10, #1
and r10, r10, r8 /* r10: current buffer offset */
add r11, r11, r10
ldrh r13, [r11]
strh r13, [r12, #SSI_STX0]
ldrh r13, [r11, #2]
strh r13, [r12, #SSI_STX0]
ldrh r13, [r11, #4]
strh r13, [r12, #SSI_STX0]
ldrh r13, [r11, #6]
strh r13, [r12, #SSI_STX0]
add r10, #8
lsr r13, r8, #16 /* r13: buffer size */
cmp r10, r13
lslgt r8, r13, #16
addle r8, #8
1:
/* RX */
/* shall we receive? */
ldr r13, [r12, #SSI_SIER]
tst r13, #SSI_SIER_RFF0_EN
beq 1f
/* RX FIFO full? */
ldr r13, [r12, #SSI_SISR]
tst r13, #SSI_SISR_RFF0
beq 1f
ldr r11, imx_ssi_fiq_rx_buffer
mov r10, #0x10000
sub r10, #1
and r10, r10, r9 /* r10: current buffer offset */
add r11, r11, r10
ldr r13, [r12, #SSI_SACNT]
tst r13, #SSI_SACNT_AC97EN
ldr r13, [r12, #SSI_SRX0]
strh r13, [r11]
ldr r13, [r12, #SSI_SRX0]
strh r13, [r11, #2]
/* dummy read to skip slot 12 */
ldrne r13, [r12, #SSI_SRX0]
ldr r13, [r12, #SSI_SRX0]
strh r13, [r11, #4]
ldr r13, [r12, #SSI_SRX0]
strh r13, [r11, #6]
/* dummy read to skip slot 12 */
ldrne r13, [r12, #SSI_SRX0]
add r10, #8
lsr r13, r9, #16 /* r13: buffer size */
cmp r10, r13
lslgt r9, r13, #16
addle r9, #8
1:
@ return from FIQ
subs pc, lr, #4
imx_ssi_fiq_base:
.word 0x0
imx_ssi_fiq_rx_buffer:
.word 0x0
imx_ssi_fiq_tx_buffer:
.word 0x0
imx_ssi_fiq_end:
/*
* platform header for the SIU ASoC driver
*
* Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* 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 ASM_SIU_H
#define ASM_SIU_H
#include <asm/dma-sh.h>
struct device;
struct siu_platform {
struct device *dma_dev;
enum sh_dmae_slave_chan_id dma_slave_tx_a;
enum sh_dmae_slave_chan_id dma_slave_rx_a;
enum sh_dmae_slave_chan_id dma_slave_tx_b;
enum sh_dmae_slave_chan_id dma_slave_rx_b;
};
#endif /* ASM_SIU_H */
...@@ -115,7 +115,8 @@ ...@@ -115,7 +115,8 @@
#define twl_has_watchdog() false #define twl_has_watchdog() false
#endif #endif
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) #if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
defined(CONFIG_SND_SOC_TWL6030) || defined(CONFIG_SND_SOC_TWL6030_MODULE)
#define twl_has_codec() true #define twl_has_codec() true
#else #else
#define twl_has_codec() false #define twl_has_codec() false
...@@ -711,8 +712,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) ...@@ -711,8 +712,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child); return PTR_ERR(child);
} }
if (twl_has_codec() && pdata->codec) { if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
child = add_child(1, "twl4030_codec", sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl4030_codec",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
/* Phoenix*/
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
child = add_child(sub_chip_id, "twl6030_codec",
pdata->codec, sizeof(*pdata->codec), pdata->codec, sizeof(*pdata->codec),
false, 0, 0); false, 0, 0);
if (IS_ERR(child)) if (IS_ERR(child))
......
...@@ -547,6 +547,10 @@ struct twl4030_codec_data { ...@@ -547,6 +547,10 @@ struct twl4030_codec_data {
unsigned int audio_mclk; unsigned int audio_mclk;
struct twl4030_codec_audio_data *audio; struct twl4030_codec_audio_data *audio;
struct twl4030_codec_vibra_data *vibra; struct twl4030_codec_vibra_data *vibra;
/* twl6030 */
int audpwron_gpio; /* audio power-on gpio */
int naudint_irq; /* audio interrupt */
}; };
struct twl4030_platform_data { struct twl4030_platform_data {
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/list.h> #include <linux/list.h>
#include <sound/soc.h>
struct snd_pcm_substream; struct snd_pcm_substream;
/* /*
......
...@@ -95,6 +95,21 @@ ...@@ -95,6 +95,21 @@
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.num_kcontrols = 1} .num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
wcontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */ /* path domain with event - event handler must return 0 for success */
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
...@@ -126,6 +141,23 @@ ...@@ -126,6 +141,23 @@
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags}
#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags}
#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
wcontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */ /* events that are pre and post DAPM */
#define SND_SOC_DAPM_PRE(wname, wevent) \ #define SND_SOC_DAPM_PRE(wname, wevent) \
{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
......
...@@ -168,6 +168,23 @@ ...@@ -168,6 +168,23 @@
.get = xhandler_get, .put = xhandler_put, \ .get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&xenum } .private_value = (unsigned long)&xenum }
/*
* Simplified versions of above macros, declaring a struct and calculating
* ARRAY_SIZE internally
*/
#define SOC_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xtexts) \
struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \
ARRAY_SIZE(xtexts), xtexts)
#define SOC_ENUM_SINGLE_DECL(name, xreg, xshift, xtexts) \
SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts)
#define SOC_ENUM_SINGLE_EXT_DECL(name, xtexts) \
struct soc_enum name = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(xtexts), xtexts)
#define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \
struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \
ARRAY_SIZE(xtexts), xtexts, xvalues)
#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
/* /*
* Bias levels * Bias levels
* *
...@@ -253,6 +270,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, ...@@ -253,6 +270,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
/* codec register bit access */ /* codec register bit access */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned int mask, unsigned int value); unsigned int mask, unsigned int value);
int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
unsigned short reg, unsigned int mask,
unsigned int value);
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned int mask, unsigned int value); unsigned int mask, unsigned int value);
...@@ -402,6 +422,10 @@ struct snd_soc_codec { ...@@ -402,6 +422,10 @@ struct snd_soc_codec {
short reg_cache_size; short reg_cache_size;
short reg_cache_step; short reg_cache_step;
unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
unsigned int cache_only:1; /* Suppress writes to hardware */
unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
/* dapm */ /* dapm */
u32 pop_time; u32 pop_time;
struct list_head dapm_widgets; struct list_head dapm_widgets;
...@@ -497,6 +521,8 @@ struct snd_soc_card { ...@@ -497,6 +521,8 @@ struct snd_soc_card {
int (*set_bias_level)(struct snd_soc_card *, int (*set_bias_level)(struct snd_soc_card *,
enum snd_soc_bias_level level); enum snd_soc_bias_level level);
long pmdown_time;
/* CPU <--> Codec DAI links */ /* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link *dai_link;
int num_links; int num_links;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
struct tlv320dac33_platform_data { struct tlv320dac33_platform_data {
int power_gpio; int power_gpio;
u8 burst_bclkdiv;
}; };
#endif /* __TLV320DAC33_PLAT_H */ #endif /* __TLV320DAC33_PLAT_H */
...@@ -23,7 +23,13 @@ ...@@ -23,7 +23,13 @@
#ifndef TPA6130A2_PLAT_H #ifndef TPA6130A2_PLAT_H
#define TPA6130A2_PLAT_H #define TPA6130A2_PLAT_H
enum tpa_model {
TPA6130A2,
TPA6140A2,
};
struct tpa6130a2_platform_data { struct tpa6130a2_platform_data {
enum tpa_model id;
int power_gpio; int power_gpio;
}; };
......
/*
* linux/sound/wm2000.h -- Platform data for WM2000
*
* Copyright 2010 Wolfson Microelectronics. PLC.
*
* 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 __LINUX_SND_WM2000_H
#define __LINUX_SND_WM2000_H
struct wm2000_platform_data {
/** Filename for system-specific image to download to device. */
const char *download_file;
/** Divide MCLK by 2 for system clock? */
unsigned int mclkdiv2:1;
/** Disable speech clarity enhancement, for use when an
* external algorithm is used. */
unsigned int speech_enh_disable:1;
};
#endif
/*
* Platform data for WM8904
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_WM8994_PDATA_H__
#define __MFD_WM8994_PDATA_H__
#define WM8904_DRC_REGS 4
#define WM8904_EQ_REGS 25
/**
* DRC configurations are specified with a label and a set of register
* values to write (the enable bits will be ignored). At runtime an
* enumerated control will be presented for each DRC block allowing
* the user to choose the configration to use.
*
* Configurations may be generated by hand or by using the DRC control
* panel provided by the WISCE - see http://www.wolfsonmicro.com/wisce/
* for details.
*/
struct wm8904_drc_cfg {
const char *name;
u16 regs[WM8904_DRC_REGS];
};
/**
* ReTune Mobile configurations are specified with a label, sample
* rate and set of values to write (the enable bits will be ignored).
*
* Configurations are expected to be generated using the ReTune Mobile
* control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
*/
struct wm8904_retune_mobile_cfg {
const char *name;
unsigned int rate;
u16 regs[WM8904_EQ_REGS];
};
struct wm8904_pdata {
int num_drc_cfgs;
struct wm8904_drc_cfg *drc_cfgs;
int num_retune_mobile_cfgs;
struct wm8904_retune_mobile_cfg *retune_mobile_cfgs;
};
#endif
/* /*
* s3c24xx-ac97.c -- ALSA Soc Audio Layer * Platform data for WM8955
* *
* (c) 2007 Wolfson Microelectronics PLC. * Copyright 2009 Wolfson Microelectronics PLC.
* Author: Graeme Gregory *
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * 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 * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
* *
* Revision history
* 10th Nov 2006 Initial version.
*/ */
#ifndef S3C24XXAC97_H_ #ifndef __WM8955_PDATA_H__
#define S3C24XXAC97_H_ #define __WM8955_PDATA_H__
#define AC_CMD_ADDR(x) (x << 16) struct wm8955_pdata {
#define AC_CMD_DATA(x) (x & 0xffff) /* Configure LOUT2/ROUT2 to drive a speaker */
unsigned int out2_speaker:1;
extern struct snd_soc_dai s3c2443_ac97_dai[]; /* Configure MONOIN+/- in differential mode */
unsigned int monoin_diff:1;
};
#endif /*S3C24XXAC97_H_*/ #endif
...@@ -349,9 +349,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) ...@@ -349,9 +349,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
size, &sport_handle->tx_dma_phy, GFP_KERNEL); size, &sport_handle->tx_dma_phy, GFP_KERNEL);
if (!sport_handle->tx_dma_buf) { if (!sport_handle->tx_dma_buf) {
pr_err("Failed to allocate memory for tx dma \ pr_err("Failed to allocate memory for tx dma buf - Please increase uncached DMA memory region\n");
buf - Please increase uncached DMA \
memory region\n");
return -ENOMEM; return -ENOMEM;
} else } else
memset(sport_handle->tx_dma_buf, 0, size); memset(sport_handle->tx_dma_buf, 0, size);
...@@ -362,9 +360,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) ...@@ -362,9 +360,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \ sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
size, &sport_handle->rx_dma_phy, GFP_KERNEL); size, &sport_handle->rx_dma_phy, GFP_KERNEL);
if (!sport_handle->rx_dma_buf) { if (!sport_handle->rx_dma_buf) {
pr_err("Failed to allocate memory for rx dma \ pr_err("Failed to allocate memory for rx dma buf - Please increase uncached DMA memory region\n");
buf - Please increase uncached DMA \
memory region\n");
return -ENOMEM; return -ENOMEM;
} else } else
memset(sport_handle->rx_dma_buf, 0, size); memset(sport_handle->rx_dma_buf, 0, size);
......
...@@ -207,8 +207,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) ...@@ -207,8 +207,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
buf->area = dma_alloc_coherent(pcm->card->dev, size, buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL); &buf->addr, GFP_KERNEL);
if (!buf->area) { if (!buf->area) {
pr_err("Failed to allocate dma memory \ pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
Please increase uncached DMA memory region\n");
return -ENOMEM; return -ENOMEM;
} }
buf->bytes = size; buf->bytes = size;
......
...@@ -244,8 +244,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) ...@@ -244,8 +244,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
buf->area = dma_alloc_coherent(pcm->card->dev, size * 4, buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
&buf->addr, GFP_KERNEL); &buf->addr, GFP_KERNEL);
if (!buf->area) { if (!buf->area) {
pr_err("Failed to allocate dma memory \ pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
Please increase uncached DMA memory region\n");
return -ENOMEM; return -ENOMEM;
} }
buf->bytes = size; buf->bytes = size;
......
...@@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS ...@@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4671 if I2C select SND_SOC_AK4671 if I2C
select SND_SOC_CS4270 if I2C select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C select SND_SOC_MAX9877 if I2C
select SND_SOC_DA7210 if I2C
select SND_SOC_PCM3008 select SND_SOC_PCM3008
select SND_SOC_SPDIF select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C select SND_SOC_SSM2602 if I2C
...@@ -35,6 +36,7 @@ config SND_SOC_ALL_CODECS ...@@ -35,6 +36,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_TWL4030 if TWL4030_CORE
select SND_SOC_UDA134X select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C select SND_SOC_UDA1380 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400 select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
...@@ -49,14 +51,18 @@ config SND_SOC_ALL_CODECS ...@@ -49,14 +51,18 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
select SND_SOC_WM8940 if I2C select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C select SND_SOC_WM8960 if I2C
select SND_SOC_WM8961 if I2C select SND_SOC_WM8961 if I2C
select SND_SOC_WM8971 if I2C select SND_SOC_WM8971 if I2C
select SND_SOC_WM8974 if I2C select SND_SOC_WM8974 if I2C
select SND_SOC_WM8978 if I2C
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C select SND_SOC_WM8990 if I2C
select SND_SOC_WM8993 if I2C select SND_SOC_WM8993 if I2C
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM9081 if I2C select SND_SOC_WM9081 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS select SND_SOC_WM9712 if SND_SOC_AC97_BUS
...@@ -112,6 +118,9 @@ config SND_SOC_AK4671 ...@@ -112,6 +118,9 @@ config SND_SOC_AK4671
config SND_SOC_CS4270 config SND_SOC_CS4270
tristate tristate
config SND_SOC_DA7210
tristate
# Cirrus Logic CS4270 Codec VD = 3.3V Errata # Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function # Select if you are affected by the errata where the part will not function
# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will # if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
...@@ -203,9 +212,15 @@ config SND_SOC_WM8900 ...@@ -203,9 +212,15 @@ config SND_SOC_WM8900
config SND_SOC_WM8903 config SND_SOC_WM8903
tristate tristate
config SND_SOC_WM8904
tristate
config SND_SOC_WM8940 config SND_SOC_WM8940
tristate tristate
config SND_SOC_WM8955
tristate
config SND_SOC_WM8960 config SND_SOC_WM8960
tristate tristate
...@@ -218,6 +233,9 @@ config SND_SOC_WM8971 ...@@ -218,6 +233,9 @@ config SND_SOC_WM8971
config SND_SOC_WM8974 config SND_SOC_WM8974
tristate tristate
config SND_SOC_WM8978
tristate
config SND_SOC_WM8988 config SND_SOC_WM8988
tristate tristate
...@@ -227,6 +245,9 @@ config SND_SOC_WM8990 ...@@ -227,6 +245,9 @@ config SND_SOC_WM8990
config SND_SOC_WM8993 config SND_SOC_WM8993
tristate tristate
config SND_SOC_WM8994
tristate
config SND_SOC_WM9081 config SND_SOC_WM9081
tristate tristate
...@@ -245,3 +266,6 @@ config SND_SOC_MAX9877 ...@@ -245,3 +266,6 @@ config SND_SOC_MAX9877
config SND_SOC_TPA6130A2 config SND_SOC_TPA6130A2
tristate tristate
config SND_SOC_WM2000
tristate
...@@ -10,6 +10,7 @@ snd-soc-ak4642-objs := ak4642.o ...@@ -10,6 +10,7 @@ snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o snd-soc-ak4671-objs := ak4671.o
snd-soc-cs4270-objs := cs4270.o snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-l3-objs := l3.o snd-soc-l3-objs := l3.o
snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3008-objs := pcm3008.o
snd-soc-spdif-objs := spdif_transciever.o snd-soc-spdif-objs := spdif_transciever.o
...@@ -36,14 +37,18 @@ snd-soc-wm8753-objs := wm8753.o ...@@ -36,14 +37,18 @@ snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8776-objs := wm8776.o snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8900-objs := wm8900.o snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
snd-soc-wm8940-objs := wm8940.o snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8961-objs := wm8961.o snd-soc-wm8961-objs := wm8961.o
snd-soc-wm8971-objs := wm8971.o snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8988-objs := wm8988.o snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8993-objs := wm8993.o snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o
snd-soc-wm9081-objs := wm9081.o snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o snd-soc-wm9712-objs := wm9712.o
...@@ -53,6 +58,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o ...@@ -53,6 +58,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp # Amp
snd-soc-max9877-objs := max9877.o snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-wm2000-objs := wm2000.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
...@@ -66,6 +72,7 @@ obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o ...@@ -66,6 +72,7 @@ obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o 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_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
...@@ -92,14 +99,18 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o ...@@ -92,14 +99,18 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
...@@ -109,3 +120,4 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o ...@@ -109,3 +120,4 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp # Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
...@@ -171,57 +171,35 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, ...@@ -171,57 +171,35 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream,
return 0; return 0;
} }
#ifdef CONFIG_PM
/* static int ad1836_soc_suspend(struct platform_device *pdev,
* interface to read/write ad1836 register pm_message_t state)
*/
#define AD1836_SPI_REG_SHFT 12
#define AD1836_SPI_READ (1 << 11)
#define AD1836_SPI_VAL_MSK 0x3FF
/*
* write to the ad1836 register space
*/
static int ad1836_write_reg(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{ {
u16 *reg_cache = codec->reg_cache; struct snd_soc_device *socdev = platform_get_drvdata(pdev);
int ret = 0; struct snd_soc_codec *codec = socdev->card->codec;
if (value != reg_cache[reg]) { /* reset clock control mode */
unsigned short buf; u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
struct spi_transfer t = { adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
.tx_buf = &buf,
.len = 2,
};
struct spi_message m;
buf = (reg << AD1836_SPI_REG_SHFT) |
(value & AD1836_SPI_VAL_MSK);
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(codec->control_data, &m);
if (ret == 0)
reg_cache[reg] = value;
}
return ret; return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
} }
/* static int ad1836_soc_resume(struct platform_device *pdev)
* read from the ad1836 register space cache
*/
static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{ {
u16 *reg_cache = codec->reg_cache; struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
if (reg >= codec->reg_cache_size) /* restore clock control mode */
return -EINVAL; u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
adc_ctrl2 |= AD1836_ADC_AUX;
return reg_cache[reg]; return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
} }
#else
#define ad1836_soc_suspend NULL
#define ad1836_soc_resume NULL
#endif
static int __devinit ad1836_spi_probe(struct spi_device *spi) static int __devinit ad1836_spi_probe(struct spi_device *spi)
{ {
...@@ -306,32 +284,38 @@ static int ad1836_register(struct ad1836_priv *ad1836) ...@@ -306,32 +284,38 @@ static int ad1836_register(struct ad1836_priv *ad1836)
codec->owner = THIS_MODULE; codec->owner = THIS_MODULE;
codec->dai = &ad1836_dai; codec->dai = &ad1836_dai;
codec->num_dai = 1; codec->num_dai = 1;
codec->write = ad1836_write_reg;
codec->read = ad1836_read_reg_cache;
INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths); INIT_LIST_HEAD(&codec->dapm_paths);
ad1836_dai.dev = codec->dev; ad1836_dai.dev = codec->dev;
ad1836_codec = codec; ad1836_codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O: %d\n",
ret);
kfree(ad1836);
return ret;
}
/* default setting for ad1836 */ /* default setting for ad1836 */
/* de-emphasis: 48kHz, power-on dac */ /* de-emphasis: 48kHz, power-on dac */
codec->write(codec, AD1836_DAC_CTRL1, 0x300); snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300);
/* unmute dac channels */ /* unmute dac channels */
codec->write(codec, AD1836_DAC_CTRL2, 0x0); snd_soc_write(codec, AD1836_DAC_CTRL2, 0x0);
/* high-pass filter enable, power-on adc */ /* high-pass filter enable, power-on adc */
codec->write(codec, AD1836_ADC_CTRL1, 0x100); snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */ /* unmute adc channles, adc aux mode */
codec->write(codec, AD1836_ADC_CTRL2, 0x180); snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
/* left/right diff:PGA/MUX */ /* left/right diff:PGA/MUX */
codec->write(codec, AD1836_ADC_CTRL3, 0x3A); snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
/* volume */ /* volume */
codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF); snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF); snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF); snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF); snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF); snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF); snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
ret = snd_soc_register_codec(codec); ret = snd_soc_register_codec(codec);
if (ret != 0) { if (ret != 0) {
...@@ -404,6 +388,8 @@ static int ad1836_remove(struct platform_device *pdev) ...@@ -404,6 +388,8 @@ static int ad1836_remove(struct platform_device *pdev)
struct snd_soc_codec_device soc_codec_dev_ad1836 = { struct snd_soc_codec_device soc_codec_dev_ad1836 = {
.probe = ad1836_probe, .probe = ad1836_probe,
.remove = ad1836_remove, .remove = ad1836_remove,
.suspend = ad1836_soc_suspend,
.resume = ad1836_soc_resume,
}; };
EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836); EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#define AD1836_ADC_SERFMT_MASK (7 << 6) #define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6) #define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6) #define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_AUX (0x6 << 6)
#define AD1836_ADC_CTRL3 14 #define AD1836_ADC_CTRL3 14
......
...@@ -46,6 +46,11 @@ struct ad1938_priv { ...@@ -46,6 +46,11 @@ struct ad1938_priv {
u8 reg_cache[AD1938_NUM_REGS]; u8 reg_cache[AD1938_NUM_REGS];
}; };
/* ad1938 register cache & default register settings */
static const u8 ad1938_reg[AD1938_NUM_REGS] = {
0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0,
};
static struct snd_soc_codec *ad1938_codec; static struct snd_soc_codec *ad1938_codec;
struct snd_soc_codec_device soc_codec_dev_ad1938; struct snd_soc_codec_device soc_codec_dev_ad1938;
static int ad1938_register(struct ad1938_priv *ad1938); static int ad1938_register(struct ad1938_priv *ad1938);
...@@ -97,6 +102,7 @@ static const struct snd_kcontrol_new ad1938_snd_controls[] = { ...@@ -97,6 +102,7 @@ static const struct snd_kcontrol_new ad1938_snd_controls[] = {
static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = { static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1), SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD1938_PLL_CLK_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_OUTPUT("DAC1OUT"), SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"), SND_SOC_DAPM_OUTPUT("DAC2OUT"),
...@@ -107,6 +113,8 @@ static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = { ...@@ -107,6 +113,8 @@ static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
}; };
static const struct snd_soc_dapm_route audio_paths[] = { static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "PLL_PWR" },
{ "ADC", NULL, "PLL_PWR" },
{ "DAC", NULL, "ADC_PWR" }, { "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" }, { "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", "DAC1 Switch", "DAC" }, { "DAC1OUT", "DAC1 Switch", "DAC" },
...@@ -126,30 +134,20 @@ static int ad1938_mute(struct snd_soc_dai *dai, int mute) ...@@ -126,30 +134,20 @@ static int ad1938_mute(struct snd_soc_dai *dai, int mute)
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
int reg; int reg;
reg = codec->read(codec, AD1938_DAC_CTRL2); reg = snd_soc_read(codec, AD1938_DAC_CTRL2);
reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg & reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg &
(~AD1938_DAC_MASTER_MUTE); (~AD1938_DAC_MASTER_MUTE);
codec->write(codec, AD1938_DAC_CTRL2, reg); snd_soc_write(codec, AD1938_DAC_CTRL2, reg);
return 0;
}
static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
{
int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg |
AD1938_PLL_POWERDOWN;
codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
return 0; return 0;
} }
static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int mask, int slots, int width) unsigned int rx_mask, int slots, int width)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
int dac_reg = codec->read(codec, AD1938_DAC_CTRL1); int dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1);
int adc_reg = codec->read(codec, AD1938_ADC_CTRL2); int adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2);
dac_reg &= ~AD1938_DAC_CHAN_MASK; dac_reg &= ~AD1938_DAC_CHAN_MASK;
adc_reg &= ~AD1938_ADC_CHAN_MASK; adc_reg &= ~AD1938_ADC_CHAN_MASK;
...@@ -175,8 +173,8 @@ static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, ...@@ -175,8 +173,8 @@ static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL; return -EINVAL;
} }
codec->write(codec, AD1938_DAC_CTRL1, dac_reg); snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg);
codec->write(codec, AD1938_ADC_CTRL2, adc_reg); snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg);
return 0; return 0;
} }
...@@ -187,8 +185,8 @@ static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -187,8 +185,8 @@ static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
int adc_reg, dac_reg; int adc_reg, dac_reg;
adc_reg = codec->read(codec, AD1938_ADC_CTRL2); adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2);
dac_reg = codec->read(codec, AD1938_DAC_CTRL1); dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1);
/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
* with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
...@@ -265,8 +263,8 @@ static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -265,8 +263,8 @@ static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL; return -EINVAL;
} }
codec->write(codec, AD1938_ADC_CTRL2, adc_reg); snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg);
codec->write(codec, AD1938_DAC_CTRL1, dac_reg); snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg);
return 0; return 0;
} }
...@@ -295,134 +293,13 @@ static int ad1938_hw_params(struct snd_pcm_substream *substream, ...@@ -295,134 +293,13 @@ static int ad1938_hw_params(struct snd_pcm_substream *substream,
break; break;
} }
reg = codec->read(codec, AD1938_DAC_CTRL2); reg = snd_soc_read(codec, AD1938_DAC_CTRL2);
reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len; reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
codec->write(codec, AD1938_DAC_CTRL2, reg); snd_soc_write(codec, AD1938_DAC_CTRL2, reg);
reg = codec->read(codec, AD1938_ADC_CTRL1); reg = snd_soc_read(codec, AD1938_ADC_CTRL1);
reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len; reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
codec->write(codec, AD1938_ADC_CTRL1, reg); snd_soc_write(codec, AD1938_ADC_CTRL1, reg);
return 0;
}
static int ad1938_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
ad1938_pll_powerctrl(codec, 1);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
case SND_SOC_BIAS_OFF:
ad1938_pll_powerctrl(codec, 0);
break;
}
codec->bias_level = level;
return 0;
}
/*
* interface to read/write ad1938 register
*/
#define AD1938_SPI_ADDR 0x4
#define AD1938_SPI_READ 0x1
#define AD1938_SPI_BUFLEN 3
/*
* write to the ad1938 register space
*/
static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 *reg_cache = codec->reg_cache;
int ret = 0;
if (value != reg_cache[reg]) {
uint8_t buf[AD1938_SPI_BUFLEN];
struct spi_transfer t = {
.tx_buf = buf,
.len = AD1938_SPI_BUFLEN,
};
struct spi_message m;
buf[0] = AD1938_SPI_ADDR << 1;
buf[1] = reg;
buf[2] = value;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(codec->control_data, &m);
if (ret == 0)
reg_cache[reg] = value;
}
return ret;
}
/*
* read from the ad1938 register space cache
*/
static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 *reg_cache = codec->reg_cache;
if (reg >= codec->reg_cache_size)
return -EINVAL;
return reg_cache[reg];
}
/*
* read from the ad1938 register space
*/
static unsigned int ad1938_read_reg(struct snd_soc_codec *codec,
unsigned int reg)
{
char w_buf[AD1938_SPI_BUFLEN];
char r_buf[AD1938_SPI_BUFLEN];
int ret;
struct spi_transfer t = {
.tx_buf = w_buf,
.rx_buf = r_buf,
.len = AD1938_SPI_BUFLEN,
};
struct spi_message m;
w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
w_buf[1] = reg;
w_buf[2] = 0;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(codec->control_data, &m);
if (ret == 0)
return r_buf[2];
else
return -EIO;
}
static int ad1938_fill_cache(struct snd_soc_codec *codec)
{
int i;
u8 *reg_cache = codec->reg_cache;
struct spi_device *spi = codec->control_data;
for (i = 0; i < codec->reg_cache_size; i++) {
int ret = ad1938_read_reg(codec, i);
if (ret == -EIO) {
dev_err(&spi->dev, "AD1938 SPI read failure\n");
return ret;
}
reg_cache[i] = ret;
}
return 0; return 0;
} }
...@@ -512,32 +389,37 @@ static int ad1938_register(struct ad1938_priv *ad1938) ...@@ -512,32 +389,37 @@ static int ad1938_register(struct ad1938_priv *ad1938)
codec->owner = THIS_MODULE; codec->owner = THIS_MODULE;
codec->dai = &ad1938_dai; codec->dai = &ad1938_dai;
codec->num_dai = 1; codec->num_dai = 1;
codec->write = ad1938_write_reg;
codec->read = ad1938_read_reg_cache;
codec->set_bias_level = ad1938_set_bias_level;
INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths); INIT_LIST_HEAD(&codec->dapm_paths);
ad1938_dai.dev = codec->dev; ad1938_dai.dev = codec->dev;
ad1938_codec = codec; ad1938_codec = codec;
memcpy(codec->reg_cache, ad1938_reg, AD1938_NUM_REGS);
ret = snd_soc_codec_set_cache_io(codec, 16, 8, SND_SOC_SPI);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O: %d\n",
ret);
kfree(ad1938);
return ret;
}
/* default setting for ad1938 */ /* default setting for ad1938 */
/* unmute dac channels */ /* unmute dac channels */
codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0); snd_soc_write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
/* de-emphasis: 48kHz, powedown dac */ /* de-emphasis: 48kHz, powedown dac */
codec->write(codec, AD1938_DAC_CTRL2, 0x1A); snd_soc_write(codec, AD1938_DAC_CTRL2, 0x1A);
/* powerdown dac, dac in tdm mode */ /* powerdown dac, dac in tdm mode */
codec->write(codec, AD1938_DAC_CTRL0, 0x41); snd_soc_write(codec, AD1938_DAC_CTRL0, 0x41);
/* high-pass filter enable */ /* high-pass filter enable */
codec->write(codec, AD1938_ADC_CTRL0, 0x3); snd_soc_write(codec, AD1938_ADC_CTRL0, 0x3);
/* sata delay=1, adc aux mode */ /* sata delay=1, adc aux mode */
codec->write(codec, AD1938_ADC_CTRL1, 0x43); snd_soc_write(codec, AD1938_ADC_CTRL1, 0x43);
/* pll input: mclki/xi */ /* pll input: mclki/xi */
codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D); snd_soc_write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04); snd_soc_write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
ad1938_fill_cache(codec);
ret = snd_soc_register_codec(codec); ret = snd_soc_register_codec(codec);
if (ret != 0) { if (ret != 0) {
...@@ -559,7 +441,6 @@ static int ad1938_register(struct ad1938_priv *ad1938) ...@@ -559,7 +441,6 @@ static int ad1938_register(struct ad1938_priv *ad1938)
static void ad1938_unregister(struct ad1938_priv *ad1938) static void ad1938_unregister(struct ad1938_priv *ad1938)
{ {
ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&ad1938_dai); snd_soc_unregister_dai(&ad1938_dai);
snd_soc_unregister_codec(&ad1938->codec); snd_soc_unregister_codec(&ad1938->codec);
kfree(ad1938); kfree(ad1938);
...@@ -593,7 +474,6 @@ static int ad1938_probe(struct platform_device *pdev) ...@@ -593,7 +474,6 @@ static int ad1938_probe(struct platform_device *pdev)
ARRAY_SIZE(ad1938_dapm_widgets)); ARRAY_SIZE(ad1938_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
pcm_err: pcm_err:
return ret; return ret;
...@@ -610,37 +490,9 @@ static int ad1938_remove(struct platform_device *pdev) ...@@ -610,37 +490,9 @@ static int ad1938_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int ad1938_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;
ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ad1938_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
return 0;
}
#else
#define ad1938_suspend NULL
#define ad1938_resume NULL
#endif
struct snd_soc_codec_device soc_codec_dev_ad1938 = { struct snd_soc_codec_device soc_codec_dev_ad1938 = {
.probe = ad1938_probe, .probe = ad1938_probe,
.remove = ad1938_remove, .remove = ad1938_remove,
.suspend = ad1938_suspend,
.resume = ad1938_resume,
}; };
EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938); EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
......
...@@ -185,9 +185,7 @@ struct snd_soc_dai ak4104_dai = { ...@@ -185,9 +185,7 @@ struct snd_soc_dai ak4104_dai = {
.stream_name = "Playback", .stream_name = "Playback",
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 2,
.rates = SNDRV_PCM_RATE_44100 | .rates = SNDRV_PCM_RATE_8000_192000,
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_32000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE SNDRV_PCM_FMTBIT_S24_LE
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <sound/initval.h> #include <sound/initval.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include "cs4270.h" #include "cs4270.h"
...@@ -106,6 +107,10 @@ ...@@ -106,6 +107,10 @@
#define CS4270_MUTE_DAC_A 0x01 #define CS4270_MUTE_DAC_A 0x01
#define CS4270_MUTE_DAC_B 0x02 #define CS4270_MUTE_DAC_B 0x02
static const char *supply_names[] = {
"va", "vd", "vlc"
};
/* Private data for the CS4270 */ /* Private data for the CS4270 */
struct cs4270_private { struct cs4270_private {
struct snd_soc_codec codec; struct snd_soc_codec codec;
...@@ -114,6 +119,9 @@ struct cs4270_private { ...@@ -114,6 +119,9 @@ struct cs4270_private {
unsigned int mode; /* The mode (I2S or left-justified) */ unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode; unsigned int slave_mode;
unsigned int manual_mute; unsigned int manual_mute;
/* power domain regulators */
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
}; };
/** /**
...@@ -192,6 +200,11 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { ...@@ -192,6 +200,11 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
* This function must be called by the machine driver's 'startup' function, * This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in * otherwise the list of supported sample rates will not be available in
* time for ALSA. * time for ALSA.
*
* For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
* theoretically possible sample rates to be enabled. Call it again with a
* proper value set one the external clock is set (most probably you would do
* that from a machine's driver 'hw_param' hook.
*/ */
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir) int clk_id, unsigned int freq, int dir)
...@@ -205,20 +218,27 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, ...@@ -205,20 +218,27 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
cs4270->mclk = freq; cs4270->mclk = freq;
for (i = 0; i < NUM_MCLK_RATIOS; i++) { if (cs4270->mclk) {
unsigned int rate = freq / cs4270_mode_ratios[i].ratio; for (i = 0; i < NUM_MCLK_RATIOS; i++) {
rates |= snd_pcm_rate_to_rate_bit(rate); unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
if (rate < rate_min) rates |= snd_pcm_rate_to_rate_bit(rate);
rate_min = rate; if (rate < rate_min)
if (rate > rate_max) rate_min = rate;
rate_max = rate; if (rate > rate_max)
} rate_max = rate;
/* FIXME: soc should support a rate list */ }
rates &= ~SNDRV_PCM_RATE_KNOT; /* FIXME: soc should support a rate list */
rates &= ~SNDRV_PCM_RATE_KNOT;
if (!rates) { if (!rates) {
dev_err(codec->dev, "could not find a valid sample rate\n"); dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL; return -EINVAL;
}
} else {
/* enable all possible rates */
rates = SNDRV_PCM_RATE_8000_192000;
rate_min = 8000;
rate_max = 192000;
} }
codec_dai->playback.rates = rates; codec_dai->playback.rates = rates;
...@@ -579,7 +599,8 @@ static int cs4270_probe(struct platform_device *pdev) ...@@ -579,7 +599,8 @@ static int cs4270_probe(struct platform_device *pdev)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = cs4270_codec; struct snd_soc_codec *codec = cs4270_codec;
int ret; struct cs4270_private *cs4270 = codec->private_data;
int i, ret;
/* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */ /* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
socdev->card->codec = codec; socdev->card->codec = codec;
...@@ -599,8 +620,26 @@ static int cs4270_probe(struct platform_device *pdev) ...@@ -599,8 +620,26 @@ static int cs4270_probe(struct platform_device *pdev)
goto error_free_pcms; goto error_free_pcms;
} }
/* get the power supply regulators */
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
cs4270->supplies[i].supply = supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
if (ret < 0)
goto error_free_pcms;
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
if (ret < 0)
goto error_free_regulators;
return 0; return 0;
error_free_regulators:
regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
error_free_pcms: error_free_pcms:
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
...@@ -616,8 +655,12 @@ static int cs4270_probe(struct platform_device *pdev) ...@@ -616,8 +655,12 @@ static int cs4270_probe(struct platform_device *pdev)
static int cs4270_remove(struct platform_device *pdev) static int cs4270_remove(struct platform_device *pdev)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = cs4270_codec;
struct cs4270_private *cs4270 = codec->private_data;
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
return 0; return 0;
}; };
...@@ -799,17 +842,33 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id); ...@@ -799,17 +842,33 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg) static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{ {
struct snd_soc_codec *codec = cs4270_codec; struct snd_soc_codec *codec = cs4270_codec;
int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL; struct cs4270_private *cs4270 = codec->private_data;
int reg, ret;
return snd_soc_write(codec, CS4270_PWRCTL, reg); reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
if (reg < 0)
return reg;
ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
if (ret < 0)
return ret;
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
return 0;
} }
static int cs4270_soc_resume(struct platform_device *pdev) static int cs4270_soc_resume(struct platform_device *pdev)
{ {
struct snd_soc_codec *codec = cs4270_codec; struct snd_soc_codec *codec = cs4270_codec;
struct cs4270_private *cs4270 = codec->private_data;
struct i2c_client *i2c_client = codec->control_data; struct i2c_client *i2c_client = codec->control_data;
int reg; int reg;
regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
/* In case the device was put to hard reset during sleep, we need to /* In case the device was put to hard reset during sleep, we need to
* wait 500ns here before any I2C communication. */ * wait 500ns here before any I2C communication. */
ndelay(500); ndelay(500);
......
/*
* DA7210 ALSA Soc codec driver
*
* Copyright (c) 2009 Dialog Semiconductor
* Written by David Chen <Dajun.chen@diasemi.com>
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <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/tlv.h>
#include <sound/initval.h>
#include <asm/div64.h>
#include "da7210.h"
/* DA7210 register space */
#define DA7210_STATUS 0x02
#define DA7210_STARTUP1 0x03
#define DA7210_MIC_L 0x07
#define DA7210_MIC_R 0x08
#define DA7210_INMIX_L 0x0D
#define DA7210_INMIX_R 0x0E
#define DA7210_ADC_HPF 0x0F
#define DA7210_ADC 0x10
#define DA7210_DAC_HPF 0x14
#define DA7210_DAC_L 0x15
#define DA7210_DAC_R 0x16
#define DA7210_DAC_SEL 0x17
#define DA7210_OUTMIX_L 0x1C
#define DA7210_OUTMIX_R 0x1D
#define DA7210_HP_L_VOL 0x21
#define DA7210_HP_R_VOL 0x22
#define DA7210_HP_CFG 0x23
#define DA7210_DAI_SRC_SEL 0x25
#define DA7210_DAI_CFG1 0x26
#define DA7210_DAI_CFG3 0x28
#define DA7210_PLL_DIV3 0x2B
#define DA7210_PLL 0x2C
/* STARTUP1 bit fields */
#define DA7210_SC_MST_EN (1 << 0)
/* MIC_L bit fields */
#define DA7210_MICBIAS_EN (1 << 6)
#define DA7210_MIC_L_EN (1 << 7)
/* MIC_R bit fields */
#define DA7210_MIC_R_EN (1 << 7)
/* INMIX_L bit fields */
#define DA7210_IN_L_EN (1 << 7)
/* INMIX_R bit fields */
#define DA7210_IN_R_EN (1 << 7)
/* ADC_HPF bit fields */
#define DA7210_ADC_VOICE_EN (1 << 7)
/* ADC bit fields */
#define DA7210_ADC_L_EN (1 << 3)
#define DA7210_ADC_R_EN (1 << 7)
/* DAC_HPF fields */
#define DA7210_DAC_VOICE_EN (1 << 7)
/* DAC_SEL bit fields */
#define DA7210_DAC_L_SRC_DAI_L (4 << 0)
#define DA7210_DAC_L_EN (1 << 3)
#define DA7210_DAC_R_SRC_DAI_R (5 << 4)
#define DA7210_DAC_R_EN (1 << 7)
/* OUTMIX_L bit fields */
#define DA7210_OUT_L_EN (1 << 7)
/* OUTMIX_R bit fields */
#define DA7210_OUT_R_EN (1 << 7)
/* HP_CFG bit fields */
#define DA7210_HP_2CAP_MODE (1 << 1)
#define DA7210_HP_SENSE_EN (1 << 2)
#define DA7210_HP_L_EN (1 << 3)
#define DA7210_HP_MODE (1 << 6)
#define DA7210_HP_R_EN (1 << 7)
/* DAI_SRC_SEL bit fields */
#define DA7210_DAI_OUT_L_SRC (6 << 0)
#define DA7210_DAI_OUT_R_SRC (7 << 4)
/* DAI_CFG1 bit fields */
#define DA7210_DAI_WORD_S16_LE (0 << 0)
#define DA7210_DAI_WORD_S24_LE (2 << 0)
#define DA7210_DAI_FLEN_64BIT (1 << 2)
#define DA7210_DAI_MODE_MASTER (1 << 7)
/* DAI_CFG3 bit fields */
#define DA7210_DAI_FORMAT_I2SMODE (0 << 0)
#define DA7210_DAI_OE (1 << 3)
#define DA7210_DAI_EN (1 << 7)
/*PLL_DIV3 bit fields */
#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4)
#define DA7210_PLL_BYP (1 << 6)
/* PLL bit fields */
#define DA7210_PLL_FS_48000 (11 << 0)
#define DA7210_VERSION "0.0.1"
/* Codec private data */
struct da7210_priv {
struct snd_soc_codec codec;
};
static struct snd_soc_codec *da7210_codec;
/*
* Register cache
*/
static const u8 da7210_reg[] = {
0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R0 - R7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, /* R8 - RF */
0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x54, /* R10 - R17 */
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R18 - R1F */
0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, /* R20 - R27 */
0x04, 0x00, 0x00, 0x30, 0x2A, 0x00, 0x40, 0x00, /* R28 - R2F */
0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, /* R30 - R37 */
0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, /* R38 - R3F */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R40 - R4F */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R48 - R4F */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R50 - R57 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R58 - R5F */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R60 - R67 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R68 - R6F */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R70 - R77 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, /* R78 - R7F */
0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, /* R80 - R87 */
0x00, /* R88 */
};
/*
* Read da7210 register cache
*/
static inline u32 da7210_read_reg_cache(struct snd_soc_codec *codec, u32 reg)
{
u8 *cache = codec->reg_cache;
BUG_ON(reg > ARRAY_SIZE(da7210_reg));
return cache[reg];
}
/*
* Write to the da7210 register space
*/
static int da7210_write(struct snd_soc_codec *codec, u32 reg, u32 value)
{
u8 *cache = codec->reg_cache;
u8 data[2];
BUG_ON(codec->volatile_register);
data[0] = reg & 0xff;
data[1] = value & 0xff;
if (reg >= codec->reg_cache_size)
return -EIO;
if (2 != codec->hw_write(codec->control_data, data, 2))
return -EIO;
cache[reg] = value;
return 0;
}
/*
* Read from the da7210 register space.
*/
static inline u32 da7210_read(struct snd_soc_codec *codec, u32 reg)
{
if (DA7210_STATUS == reg)
return i2c_smbus_read_byte_data(codec->control_data, reg);
return da7210_read_reg_cache(codec, reg);
}
static int da7210_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_codec *codec = dai->codec;
if (is_play) {
/* PlayBack Volume 40 */
snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40);
snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40);
/* Enable Out */
snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
} else {
/* Volume 7 */
snd_soc_update_bits(codec, DA7210_MIC_L, 0x7, 0x7);
snd_soc_update_bits(codec, DA7210_MIC_R, 0x7, 0x7);
/* Enable Mic */
snd_soc_update_bits(codec, DA7210_INMIX_L, 0x1F, 0x1);
snd_soc_update_bits(codec, DA7210_INMIX_R, 0x1F, 0x1);
}
return 0;
}
/*
* Set PCM DAI word length.
*/
static int da7210_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;
u32 dai_cfg1;
u32 reg, mask;
/* set DAI source to Left and Right ADC */
da7210_write(codec, DA7210_DAI_SRC_SEL,
DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
/* Enable DAI */
da7210_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
dai_cfg1 = 0xFC & da7210_read(codec, DA7210_DAI_CFG1);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
break;
case SNDRV_PCM_FORMAT_S24_LE:
dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
break;
default:
return -EINVAL;
}
da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
/* FIXME
*
* It support 48K only now
*/
switch (params_rate(params)) {
case 48000:
if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
reg = DA7210_DAC_HPF;
mask = DA7210_DAC_VOICE_EN;
} else {
reg = DA7210_ADC_HPF;
mask = DA7210_ADC_VOICE_EN;
}
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec, reg, mask, 0);
return 0;
}
/*
* Set DAI mode and Format
*/
static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u32 dai_cfg1;
u32 dai_cfg3;
dai_cfg1 = 0x7f & da7210_read(codec, DA7210_DAI_CFG1);
dai_cfg3 = 0xfc & da7210_read(codec, DA7210_DAI_CFG3);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
dai_cfg1 |= DA7210_DAI_MODE_MASTER;
break;
default:
return -EINVAL;
}
/* FIXME
*
* It support I2S only now
*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
break;
default:
return -EINVAL;
}
/* FIXME
*
* It support 64bit data transmission only now
*/
dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
da7210_write(codec, DA7210_DAI_CFG3, dai_cfg3);
return 0;
}
#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
/* DAI operations */
static struct snd_soc_dai_ops da7210_dai_ops = {
.startup = da7210_startup,
.hw_params = da7210_hw_params,
.set_fmt = da7210_set_dai_fmt,
};
struct snd_soc_dai da7210_dai = {
.name = "DA7210 IIS",
.id = 0,
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = DA7210_FORMATS,
},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = DA7210_FORMATS,
},
.ops = &da7210_dai_ops,
};
EXPORT_SYMBOL_GPL(da7210_dai);
/*
* Initialize the DA7210 driver
* register the mixer and dsp interfaces with the kernel
*/
static int da7210_init(struct da7210_priv *da7210)
{
struct snd_soc_codec *codec = &da7210->codec;
int ret = 0;
if (da7210_codec) {
dev_err(codec->dev, "Another da7210 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = da7210;
codec->name = "DA7210";
codec->owner = THIS_MODULE;
codec->read = da7210_read;
codec->write = da7210_write;
codec->dai = &da7210_dai;
codec->num_dai = 1;
codec->hw_write = (hw_write_t)i2c_master_send;
codec->reg_cache_size = ARRAY_SIZE(da7210_reg);
codec->reg_cache = kmemdup(da7210_reg,
sizeof(da7210_reg), GFP_KERNEL);
if (!codec->reg_cache)
return -ENOMEM;
da7210_dai.dev = codec->dev;
da7210_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret) {
dev_err(codec->dev, "Failed to register CODEC: %d\n", ret);
goto init_err;
}
ret = snd_soc_register_dai(&da7210_dai);
if (ret) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
goto init_err;
}
/* FIXME
*
* This driver use fixed value here
*/
/*
* ADC settings
*/
/* Enable Left & Right MIC PGA and Mic Bias */
da7210_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
da7210_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
/* Enable Left and Right input PGA */
da7210_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
da7210_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
/* Enable Left and Right ADC */
da7210_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
/*
* DAC settings
*/
/* Enable Left and Right DAC */
da7210_write(codec, DA7210_DAC_SEL,
DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
/* Enable Left and Right out PGA */
da7210_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
da7210_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
/* Enable Left and Right HeadPhone PGA */
da7210_write(codec, DA7210_HP_CFG,
DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
/* Diable PLL and bypass it */
da7210_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
/* Bypass PLL and set MCLK freq rang to 10-20MHz */
da7210_write(codec, DA7210_PLL_DIV3,
DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
/* Activate all enabled subsystem */
da7210_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
return ret;
init_err:
kfree(codec->reg_cache);
codec->reg_cache = NULL;
return ret;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da7210_priv *da7210;
struct snd_soc_codec *codec;
int ret;
da7210 = kzalloc(sizeof(struct da7210_priv), GFP_KERNEL);
if (!da7210)
return -ENOMEM;
codec = &da7210->codec;
codec->dev = &i2c->dev;
i2c_set_clientdata(i2c, da7210);
codec->control_data = i2c;
ret = da7210_init(da7210);
if (ret < 0)
pr_err("Failed to initialise da7210 audio codec\n");
return ret;
}
static int __devexit da7210_i2c_remove(struct i2c_client *client)
{
struct da7210_priv *da7210 = i2c_get_clientdata(client);
snd_soc_unregister_dai(&da7210_dai);
kfree(da7210->codec.reg_cache);
kfree(da7210);
da7210_codec = NULL;
return 0;
}
static const struct i2c_device_id da7210_i2c_id[] = {
{ "da7210", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
/* I2C codec control layer */
static struct i2c_driver da7210_i2c_driver = {
.driver = {
.name = "DA7210 I2C Codec",
.owner = THIS_MODULE,
},
.probe = da7210_i2c_probe,
.remove = __devexit_p(da7210_i2c_remove),
.id_table = da7210_i2c_id,
};
#endif
static int da7210_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret;
if (!da7210_codec) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = da7210_codec;
codec = da7210_codec;
/* Register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0)
goto pcm_err;
dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
pcm_err:
return ret;
}
static int da7210_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_da7210 = {
.probe = da7210_probe,
.remove = da7210_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_da7210);
static int __init da7210_modinit(void)
{
int ret = 0;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&da7210_i2c_driver);
#endif
return ret;
}
module_init(da7210_modinit);
static void __exit da7210_exit(void)
{
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&da7210_i2c_driver);
#endif
}
module_exit(da7210_exit);
MODULE_DESCRIPTION("ASoC DA7210 driver");
MODULE_AUTHOR("David Chen, Kuninori Morimoto");
MODULE_LICENSE("GPL");
/*
* da7210.h -- audio driver for da7210
*
* Copyright (c) 2009 Dialog Semiconductor
* Written by David Chen <Dajun.chen@diasemi.com>
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _DA7210_H
#define _DA7210_H
extern struct snd_soc_dai da7210_dai;
extern struct snd_soc_codec_device soc_codec_dev_da7210;
#endif
...@@ -765,9 +765,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -765,9 +765,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec = socdev->card->codec;
struct aic3x_priv *aic3x = codec->private_data; struct aic3x_priv *aic3x = codec->private_data;
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 pll_d = 1; u16 d, pll_d = 1;
u8 reg; u8 reg;
int clk;
/* select data word length */ /* select data word length */
data = data =
...@@ -833,48 +834,70 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, ...@@ -833,48 +834,70 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
if (bypass_pll) if (bypass_pll)
return 0; return 0;
/* Use PLL /* Use PLL, compute apropriate setup for j, d, r and p, the closest
* find an apropriate setup for j, d, r and p by iterating over * one wins the game. Try with d==0 first, next with d!=0.
* p and r - j and d are calculated for each fraction. * Constraints for j are according to the datasheet.
* Up to 128 values are probed, the closest one wins the game.
* The sysclk is divided by 1000 to prevent integer overflows. * The sysclk is divided by 1000 to prevent integer overflows.
*/ */
codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000); codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
for (r = 1; r <= 16; r++) for (r = 1; r <= 16; r++)
for (p = 1; p <= 8; p++) { for (p = 1; p <= 8; p++) {
int clk, tmp = (codec_clk * pll_r * 10) / pll_p; for (j = 4; j <= 55; j++) {
u8 j = tmp / 10000; /* This is actually 1000*((j+(d/10000))*r)/p
u16 d = tmp % 10000; * The term had to be converted to get
* rid of the division by 10000; d = 0 here
*/
int tmp_clk = (1000 * j * r) / p;
/* Check whether this values get closer than
* the best ones we had before
*/
if (abs(codec_clk - tmp_clk) <
abs(codec_clk - last_clk)) {
pll_j = j; pll_d = 0;
pll_r = r; pll_p = p;
last_clk = tmp_clk;
}
/* Early exit for exact matches */
if (tmp_clk == codec_clk)
goto found;
}
}
if (j > 63) /* try with d != 0 */
continue; for (p = 1; p <= 8; p++) {
j = codec_clk * p / 1000;
if (d != 0 && aic3x->sysclk < 10000000) if (j < 4 || j > 11)
continue; continue;
/* This is actually 1000 * ((j + (d/10000)) * r) / p /* do not use codec_clk here since we'd loose precision */
* The term had to be converted to get rid of the d = ((2048 * p * fsref) - j * aic3x->sysclk)
* division by 10000 */ * 100 / (aic3x->sysclk/100);
clk = ((10000 * j * r) + (d * r)) / (10 * p);
/* check whether this values get closer than the best clk = (10000 * j + d) / (10 * p);
* ones we had before */
if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
pll_j = j; pll_d = d; pll_r = r; pll_p = p;
last_clk = clk;
}
/* Early exit for exact matches */ /* check whether this values get closer than the best
if (clk == codec_clk) * ones we had before */
break; if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
pll_j = j; pll_d = d; pll_r = 1; pll_p = p;
last_clk = clk;
} }
/* Early exit for exact matches */
if (clk == codec_clk)
goto found;
}
if (last_clk == 0) { if (last_clk == 0) {
printk(KERN_ERR "%s(): unable to setup PLL\n", __func__); printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
return -EINVAL; return -EINVAL;
} }
found:
data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT); aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
...@@ -58,11 +59,26 @@ enum dac33_state { ...@@ -58,11 +59,26 @@ enum dac33_state {
DAC33_FLUSH, DAC33_FLUSH,
}; };
enum dac33_fifo_modes {
DAC33_FIFO_BYPASS = 0,
DAC33_FIFO_MODE1,
DAC33_FIFO_MODE7,
DAC33_FIFO_LAST_MODE,
};
#define DAC33_NUM_SUPPLIES 3
static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
"AVDD",
"DVDD",
"IOVDD",
};
struct tlv320dac33_priv { struct tlv320dac33_priv {
struct mutex mutex; struct mutex mutex;
struct workqueue_struct *dac33_wq; struct workqueue_struct *dac33_wq;
struct work_struct work; struct work_struct work;
struct snd_soc_codec codec; struct snd_soc_codec codec;
struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
int power_gpio; int power_gpio;
int chip_power; int chip_power;
int irq; int irq;
...@@ -73,8 +89,9 @@ struct tlv320dac33_priv { ...@@ -73,8 +89,9 @@ struct tlv320dac33_priv {
* this */ * this */
unsigned int nsample_max; /* nsample should not be higher than unsigned int nsample_max; /* nsample should not be higher than
* this */ * this */
unsigned int nsample_switch; /* Use FIFO or bypass FIFO switch */ enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */
unsigned int nsample; /* burst read amount from host */ unsigned int nsample; /* burst read amount from host */
u8 burst_bclkdiv; /* BCLK divider value in burst mode */
enum dac33_state state; enum dac33_state state;
}; };
...@@ -297,28 +314,49 @@ static inline void dac33_soft_power(struct snd_soc_codec *codec, int power) ...@@ -297,28 +314,49 @@ static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
dac33_write(codec, DAC33_PWR_CTRL, reg); dac33_write(codec, DAC33_PWR_CTRL, reg);
} }
static void dac33_hard_power(struct snd_soc_codec *codec, int power) static int dac33_hard_power(struct snd_soc_codec *codec, int power)
{ {
struct tlv320dac33_priv *dac33 = codec->private_data; struct tlv320dac33_priv *dac33 = codec->private_data;
int ret;
mutex_lock(&dac33->mutex); mutex_lock(&dac33->mutex);
if (power) { if (power) {
if (dac33->power_gpio >= 0) { ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
gpio_set_value(dac33->power_gpio, 1); dac33->supplies);
dac33->chip_power = 1; if (ret != 0) {
/* Restore registers */ dev_err(codec->dev,
dac33_restore_regs(codec); "Failed to enable supplies: %d\n", ret);
goto exit;
} }
if (dac33->power_gpio >= 0)
gpio_set_value(dac33->power_gpio, 1);
dac33->chip_power = 1;
/* Restore registers */
dac33_restore_regs(codec);
dac33_soft_power(codec, 1); dac33_soft_power(codec, 1);
} else { } else {
dac33_soft_power(codec, 0); dac33_soft_power(codec, 0);
if (dac33->power_gpio >= 0) { if (dac33->power_gpio >= 0)
gpio_set_value(dac33->power_gpio, 0); gpio_set_value(dac33->power_gpio, 0);
dac33->chip_power = 0;
ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
if (ret != 0) {
dev_err(codec->dev,
"Failed to disable supplies: %d\n", ret);
goto exit;
} }
dac33->chip_power = 0;
} }
mutex_unlock(&dac33->mutex);
exit:
mutex_unlock(&dac33->mutex);
return ret;
} }
static int dac33_get_nsample(struct snd_kcontrol *kcontrol, static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
...@@ -351,39 +389,48 @@ static int dac33_set_nsample(struct snd_kcontrol *kcontrol, ...@@ -351,39 +389,48 @@ static int dac33_set_nsample(struct snd_kcontrol *kcontrol,
return ret; return ret;
} }
static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol, static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tlv320dac33_priv *dac33 = codec->private_data; struct tlv320dac33_priv *dac33 = codec->private_data;
ucontrol->value.integer.value[0] = dac33->nsample_switch; ucontrol->value.integer.value[0] = dac33->fifo_mode;
return 0; return 0;
} }
static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol, static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tlv320dac33_priv *dac33 = codec->private_data; struct tlv320dac33_priv *dac33 = codec->private_data;
int ret = 0; int ret = 0;
if (dac33->nsample_switch == ucontrol->value.integer.value[0]) if (dac33->fifo_mode == ucontrol->value.integer.value[0])
return 0; return 0;
/* Do not allow changes while stream is running*/ /* Do not allow changes while stream is running*/
if (codec->active) if (codec->active)
return -EPERM; return -EPERM;
if (ucontrol->value.integer.value[0] < 0 || if (ucontrol->value.integer.value[0] < 0 ||
ucontrol->value.integer.value[0] > 1) ucontrol->value.integer.value[0] >= DAC33_FIFO_LAST_MODE)
ret = -EINVAL; ret = -EINVAL;
else else
dac33->nsample_switch = ucontrol->value.integer.value[0]; dac33->fifo_mode = ucontrol->value.integer.value[0];
return ret; return ret;
} }
/* Codec operation modes */
static const char *dac33_fifo_mode_texts[] = {
"Bypass", "Mode 1", "Mode 7"
};
static const struct soc_enum dac33_fifo_mode_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dac33_fifo_mode_texts),
dac33_fifo_mode_texts);
/* /*
* DACL/R digital volume control: * DACL/R digital volume control:
* from 0 dB to -63.5 in 0.5 dB steps * from 0 dB to -63.5 in 0.5 dB steps
...@@ -406,8 +453,8 @@ static const struct snd_kcontrol_new dac33_snd_controls[] = { ...@@ -406,8 +453,8 @@ static const struct snd_kcontrol_new dac33_snd_controls[] = {
static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = { static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0, SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
dac33_get_nsample, dac33_set_nsample), dac33_get_nsample, dac33_set_nsample),
SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0, SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
dac33_get_nsample_switch, dac33_set_nsample_switch), dac33_get_fifo_mode, dac33_set_fifo_mode),
}; };
/* Analog bypass */ /* Analog bypass */
...@@ -469,6 +516,8 @@ static int dac33_add_widgets(struct snd_soc_codec *codec) ...@@ -469,6 +516,8 @@ static int dac33_add_widgets(struct snd_soc_codec *codec)
static int dac33_set_bias_level(struct snd_soc_codec *codec, static int dac33_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level) enum snd_soc_bias_level level)
{ {
int ret;
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
dac33_soft_power(codec, 1); dac33_soft_power(codec, 1);
...@@ -476,12 +525,19 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, ...@@ -476,12 +525,19 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
break; break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) if (codec->bias_level == SND_SOC_BIAS_OFF) {
dac33_hard_power(codec, 1); ret = dac33_hard_power(codec, 1);
if (ret != 0)
return ret;
}
dac33_soft_power(codec, 0); dac33_soft_power(codec, 0);
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
dac33_hard_power(codec, 0); ret = dac33_hard_power(codec, 0);
if (ret != 0)
return ret;
break; break;
} }
codec->bias_level = level; codec->bias_level = level;
...@@ -489,6 +545,51 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, ...@@ -489,6 +545,51 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
return 0; return 0;
} }
static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
{
struct snd_soc_codec *codec;
codec = &dac33->codec;
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
dac33_write16(codec, DAC33_NSAMPLE_MSB,
DAC33_THRREG(dac33->nsample));
dac33_write16(codec, DAC33_PREFILL_MSB,
DAC33_THRREG(dac33->alarm_threshold));
break;
case DAC33_FIFO_MODE7:
dac33_write16(codec, DAC33_PREFILL_MSB,
DAC33_THRREG(10));
break;
default:
dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
dac33->fifo_mode);
break;
}
}
static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
{
struct snd_soc_codec *codec;
codec = &dac33->codec;
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
dac33_write16(codec, DAC33_NSAMPLE_MSB,
DAC33_THRREG(dac33->nsample));
break;
case DAC33_FIFO_MODE7:
/* At the moment we are not using interrupts in mode7 */
break;
default:
dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
dac33->fifo_mode);
break;
}
}
static void dac33_work(struct work_struct *work) static void dac33_work(struct work_struct *work)
{ {
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
...@@ -502,14 +603,10 @@ static void dac33_work(struct work_struct *work) ...@@ -502,14 +603,10 @@ static void dac33_work(struct work_struct *work)
switch (dac33->state) { switch (dac33->state) {
case DAC33_PREFILL: case DAC33_PREFILL:
dac33->state = DAC33_PLAYBACK; dac33->state = DAC33_PLAYBACK;
dac33_write16(codec, DAC33_NSAMPLE_MSB, dac33_prefill_handler(dac33);
DAC33_THRREG(dac33->nsample));
dac33_write16(codec, DAC33_PREFILL_MSB,
DAC33_THRREG(dac33->alarm_threshold));
break; break;
case DAC33_PLAYBACK: case DAC33_PLAYBACK:
dac33_write16(codec, DAC33_NSAMPLE_MSB, dac33_playback_handler(dac33);
DAC33_THRREG(dac33->nsample));
break; break;
case DAC33_IDLE: case DAC33_IDLE:
break; break;
...@@ -547,7 +644,7 @@ static void dac33_shutdown(struct snd_pcm_substream *substream, ...@@ -547,7 +644,7 @@ static void dac33_shutdown(struct snd_pcm_substream *substream,
unsigned int pwr_ctrl; unsigned int pwr_ctrl;
/* Stop pending workqueue */ /* Stop pending workqueue */
if (dac33->nsample_switch) if (dac33->fifo_mode)
cancel_work_sync(&dac33->work); cancel_work_sync(&dac33->work);
mutex_lock(&dac33->mutex); mutex_lock(&dac33->mutex);
...@@ -603,7 +700,7 @@ static int dac33_hw_params(struct snd_pcm_substream *substream, ...@@ -603,7 +700,7 @@ static int dac33_hw_params(struct snd_pcm_substream *substream,
} }
#define CALC_OSCSET(rate, refclk) ( \ #define CALC_OSCSET(rate, refclk) ( \
((((rate * 10000) / refclk) * 4096) + 5000) / 10000) ((((rate * 10000) / refclk) * 4096) + 7000) / 10000)
#define CALC_RATIOSET(rate, refclk) ( \ #define CALC_RATIOSET(rate, refclk) ( \
((((refclk * 100000) / rate) * 16384) + 50000) / 100000) ((((refclk * 100000) / rate) * 16384) + 50000) / 100000)
...@@ -619,7 +716,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) ...@@ -619,7 +716,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec = socdev->card->codec;
struct tlv320dac33_priv *dac33 = codec->private_data; struct tlv320dac33_priv *dac33 = codec->private_data;
unsigned int oscset, ratioset, pwr_ctrl, reg_tmp; unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
u8 aictrl_a, fifoctrl_a; u8 aictrl_a, aictrl_b, fifoctrl_a;
switch (substream->runtime->rate) { switch (substream->runtime->rate) {
case 44100: case 44100:
...@@ -637,7 +734,10 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) ...@@ -637,7 +734,10 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A); aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK); aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
/* Read FIFO control A, and clear FIFO flush bit */
fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A); fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
fifoctrl_a &= ~DAC33_FIFOFLUSH;
fifoctrl_a &= ~DAC33_WIDTH; fifoctrl_a &= ~DAC33_WIDTH;
switch (substream->runtime->format) { switch (substream->runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
...@@ -675,7 +775,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) ...@@ -675,7 +775,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
dac33_oscwait(codec); dac33_oscwait(codec);
if (dac33->nsample_switch) { if (dac33->fifo_mode) {
/* Generic for all FIFO modes */
/* 50-51 : ASRC Control registers */ /* 50-51 : ASRC Control registers */
dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */ dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */ dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
...@@ -685,38 +786,101 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) ...@@ -685,38 +786,101 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
/* Set interrupts to high active */ /* Set interrupts to high active */
dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH); dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
} else { } else {
/* FIFO bypass mode */
/* 50-51 : ASRC Control registers */ /* 50-51 : ASRC Control registers */
dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP); dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */ dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
} }
if (dac33->nsample_switch) /* Interrupt behaviour configuration */
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
break;
case DAC33_FIFO_MODE7:
/* Disable all interrupts */
dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
break;
default:
/* in FIFO bypass mode, the interrupts are not used */
break;
}
aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
/*
* For mode1:
* Disable the FIFO bypass (Enable the use of FIFO)
* Select nSample mode
* BCLK is only running when data is needed by DAC33
*/
fifoctrl_a &= ~DAC33_FBYPAS; fifoctrl_a &= ~DAC33_FBYPAS;
else fifoctrl_a &= ~DAC33_FAUTO;
aictrl_b &= ~DAC33_BCLKON;
break;
case DAC33_FIFO_MODE7:
/*
* For mode1:
* Disable the FIFO bypass (Enable the use of FIFO)
* Select Threshold mode
* BCLK is only running when data is needed by DAC33
*/
fifoctrl_a &= ~DAC33_FBYPAS;
fifoctrl_a |= DAC33_FAUTO;
aictrl_b &= ~DAC33_BCLKON;
break;
default:
/*
* For FIFO bypass mode:
* Enable the FIFO bypass (Disable the FIFO use)
* Set the BCLK as continous
*/
fifoctrl_a |= DAC33_FBYPAS; fifoctrl_a |= DAC33_FBYPAS;
dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a); aictrl_b |= DAC33_BCLKON;
break;
}
dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a); dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B); dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
if (dac33->nsample_switch)
reg_tmp &= ~DAC33_BCLKON;
else
reg_tmp |= DAC33_BCLKON;
dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
if (dac33->nsample_switch) { /*
/* 20: BCLK divide ratio */ * BCLK divide ratio
dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3); * 0: 1.5
* 1: 1
* 2: 2
* ...
* 254: 254
* 255: 255
*/
if (dac33->fifo_mode)
dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C,
dac33->burst_bclkdiv);
else
dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
dac33_write16(codec, DAC33_ATHR_MSB, dac33_write16(codec, DAC33_ATHR_MSB,
DAC33_THRREG(dac33->alarm_threshold)); DAC33_THRREG(dac33->alarm_threshold));
} else { break;
dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32); case DAC33_FIFO_MODE7:
/*
* Configure the threshold levels, and leave 10 sample space
* at the bottom, and also at the top of the FIFO
*/
dac33_write16(codec, DAC33_UTHR_MSB,
DAC33_THRREG(DAC33_BUFFER_SIZE_SAMPLES - 10));
dac33_write16(codec, DAC33_LTHR_MSB,
DAC33_THRREG(10));
break;
default:
break;
} }
mutex_unlock(&dac33->mutex); mutex_unlock(&dac33->mutex);
...@@ -789,7 +953,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -789,7 +953,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (dac33->nsample_switch) { if (dac33->fifo_mode) {
dac33->state = DAC33_PREFILL; dac33->state = DAC33_PREFILL;
queue_work(dac33->dac33_wq, &dac33->work); queue_work(dac33->dac33_wq, &dac33->work);
} }
...@@ -797,7 +961,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -797,7 +961,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (dac33->nsample_switch) { if (dac33->fifo_mode) {
dac33->state = DAC33_FLUSH; dac33->state = DAC33_FLUSH;
queue_work(dac33->dac33_wq, &dac33->work); queue_work(dac33->dac33_wq, &dac33->work);
} }
...@@ -843,6 +1007,7 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -843,6 +1007,7 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt) unsigned int fmt)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
struct tlv320dac33_priv *dac33 = codec->private_data;
u8 aictrl_a, aictrl_b; u8 aictrl_a, aictrl_b;
aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A); aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
...@@ -855,7 +1020,11 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -855,7 +1020,11 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
break; break;
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
/* Codec Slave */ /* Codec Slave */
aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK); if (dac33->fifo_mode) {
dev_err(codec->dev, "FIFO mode requires master mode\n");
return -EINVAL;
} else
aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -959,6 +1128,9 @@ static int dac33_soc_probe(struct platform_device *pdev) ...@@ -959,6 +1128,9 @@ static int dac33_soc_probe(struct platform_device *pdev)
/* power on device */ /* power on device */
dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY); dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration has enabled regulator an extra time */
regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
return 0; return 0;
pcm_err: pcm_err:
...@@ -1033,13 +1205,13 @@ struct snd_soc_dai dac33_dai = { ...@@ -1033,13 +1205,13 @@ struct snd_soc_dai dac33_dai = {
}; };
EXPORT_SYMBOL_GPL(dac33_dai); EXPORT_SYMBOL_GPL(dac33_dai);
static int dac33_i2c_probe(struct i2c_client *client, static int __devinit dac33_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct tlv320dac33_platform_data *pdata; struct tlv320dac33_platform_data *pdata;
struct tlv320dac33_priv *dac33; struct tlv320dac33_priv *dac33;
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
int ret = 0; int ret, i;
if (client->dev.platform_data == NULL) { if (client->dev.platform_data == NULL) {
dev_err(&client->dev, "Platform data not set\n"); dev_err(&client->dev, "Platform data not set\n");
...@@ -1080,10 +1252,11 @@ static int dac33_i2c_probe(struct i2c_client *client, ...@@ -1080,10 +1252,11 @@ static int dac33_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, dac33); i2c_set_clientdata(client, dac33);
dac33->power_gpio = pdata->power_gpio; dac33->power_gpio = pdata->power_gpio;
dac33->burst_bclkdiv = pdata->burst_bclkdiv;
dac33->irq = client->irq; dac33->irq = client->irq;
dac33->nsample = NSAMPLE_MAX; dac33->nsample = NSAMPLE_MAX;
/* Disable FIFO use by default */ /* Disable FIFO use by default */
dac33->nsample_switch = 0; dac33->fifo_mode = DAC33_FIFO_BYPASS;
tlv320dac33_codec = codec; tlv320dac33_codec = codec;
...@@ -1130,6 +1303,24 @@ static int dac33_i2c_probe(struct i2c_client *client, ...@@ -1130,6 +1303,24 @@ static int dac33_i2c_probe(struct i2c_client *client,
} }
} }
for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
dac33->supplies[i].supply = dac33_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(dac33->supplies),
dac33->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err_get;
}
ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_enable;
}
ret = snd_soc_register_codec(codec); ret = snd_soc_register_codec(codec);
if (ret != 0) { if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret); dev_err(codec->dev, "Failed to register codec: %d\n", ret);
...@@ -1149,6 +1340,10 @@ static int dac33_i2c_probe(struct i2c_client *client, ...@@ -1149,6 +1340,10 @@ static int dac33_i2c_probe(struct i2c_client *client,
return ret; return ret;
error_codec: error_codec:
regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
err_enable:
regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
err_get:
if (dac33->irq >= 0) { if (dac33->irq >= 0) {
free_irq(dac33->irq, &dac33->codec); free_irq(dac33->irq, &dac33->codec);
destroy_workqueue(dac33->dac33_wq); destroy_workqueue(dac33->dac33_wq);
...@@ -1165,7 +1360,7 @@ static int dac33_i2c_probe(struct i2c_client *client, ...@@ -1165,7 +1360,7 @@ static int dac33_i2c_probe(struct i2c_client *client,
return ret; return ret;
} }
static int dac33_i2c_remove(struct i2c_client *client) static int __devexit dac33_i2c_remove(struct i2c_client *client)
{ {
struct tlv320dac33_priv *dac33; struct tlv320dac33_priv *dac33;
...@@ -1177,6 +1372,8 @@ static int dac33_i2c_remove(struct i2c_client *client) ...@@ -1177,6 +1372,8 @@ static int dac33_i2c_remove(struct i2c_client *client)
if (dac33->irq >= 0) if (dac33->irq >= 0)
free_irq(dac33->irq, &dac33->codec); free_irq(dac33->irq, &dac33->codec);
regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
destroy_workqueue(dac33->dac33_wq); destroy_workqueue(dac33->dac33_wq);
snd_soc_unregister_dai(&dac33_dai); snd_soc_unregister_dai(&dac33_dai);
snd_soc_unregister_codec(&dac33->codec); snd_soc_unregister_codec(&dac33->codec);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <sound/tpa6130a2-plat.h> #include <sound/tpa6130a2-plat.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/soc-dapm.h> #include <sound/soc-dapm.h>
...@@ -34,10 +35,22 @@ ...@@ -34,10 +35,22 @@
static struct i2c_client *tpa6130a2_client; static struct i2c_client *tpa6130a2_client;
#define TPA6130A2_NUM_SUPPLIES 2
static const char *tpa6130a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
"CPVSS",
"Vdd",
};
static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
"HPVdd",
"AVdd",
};
/* This struct is used to save the context */ /* This struct is used to save the context */
struct tpa6130a2_data { struct tpa6130a2_data {
struct mutex mutex; struct mutex mutex;
unsigned char regs[TPA6130A2_CACHEREGNUM]; unsigned char regs[TPA6130A2_CACHEREGNUM];
struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES];
int power_gpio; int power_gpio;
unsigned char power_state; unsigned char power_state;
}; };
...@@ -106,10 +119,11 @@ static void tpa6130a2_initialize(void) ...@@ -106,10 +119,11 @@ static void tpa6130a2_initialize(void)
tpa6130a2_i2c_write(i, data->regs[i]); tpa6130a2_i2c_write(i, data->regs[i]);
} }
static void tpa6130a2_power(int power) static int tpa6130a2_power(int power)
{ {
struct tpa6130a2_data *data; struct tpa6130a2_data *data;
u8 val; u8 val;
int ret;
BUG_ON(tpa6130a2_client == NULL); BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client); data = i2c_get_clientdata(tpa6130a2_client);
...@@ -117,11 +131,20 @@ static void tpa6130a2_power(int power) ...@@ -117,11 +131,20 @@ static void tpa6130a2_power(int power)
mutex_lock(&data->mutex); mutex_lock(&data->mutex);
if (power) { if (power) {
/* Power on */ /* Power on */
if (data->power_gpio >= 0) { if (data->power_gpio >= 0)
gpio_set_value(data->power_gpio, 1); gpio_set_value(data->power_gpio, 1);
data->power_state = 1;
tpa6130a2_initialize(); ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies),
data->supplies);
if (ret != 0) {
dev_err(&tpa6130a2_client->dev,
"Failed to enable supplies: %d\n", ret);
goto exit;
} }
data->power_state = 1;
tpa6130a2_initialize();
/* Clear SWS */ /* Clear SWS */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL); val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val &= ~TPA6130A2_SWS; val &= ~TPA6130A2_SWS;
...@@ -131,13 +154,25 @@ static void tpa6130a2_power(int power) ...@@ -131,13 +154,25 @@ static void tpa6130a2_power(int power)
val = tpa6130a2_read(TPA6130A2_REG_CONTROL); val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val |= TPA6130A2_SWS; val |= TPA6130A2_SWS;
tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
/* Power off */ /* Power off */
if (data->power_gpio >= 0) { if (data->power_gpio >= 0)
gpio_set_value(data->power_gpio, 0); gpio_set_value(data->power_gpio, 0);
data->power_state = 0;
ret = regulator_bulk_disable(ARRAY_SIZE(data->supplies),
data->supplies);
if (ret != 0) {
dev_err(&tpa6130a2_client->dev,
"Failed to disable supplies: %d\n", ret);
goto exit;
} }
data->power_state = 0;
} }
exit:
mutex_unlock(&data->mutex); mutex_unlock(&data->mutex);
return ret;
} }
static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol, static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
...@@ -237,12 +272,8 @@ static const struct snd_kcontrol_new tpa6130a2_controls[] = { ...@@ -237,12 +272,8 @@ static const struct snd_kcontrol_new tpa6130a2_controls[] = {
*/ */
static void tpa6130a2_channel_enable(u8 channel, int enable) static void tpa6130a2_channel_enable(u8 channel, int enable)
{ {
struct tpa6130a2_data *data;
u8 val; u8 val;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
if (enable) { if (enable) {
/* Enable channel */ /* Enable channel */
/* Enable amplifier */ /* Enable amplifier */
...@@ -299,15 +330,17 @@ static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w, ...@@ -299,15 +330,17 @@ static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w, static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) struct snd_kcontrol *kcontrol, int event)
{ {
int ret = 0;
switch (event) { switch (event) {
case SND_SOC_DAPM_POST_PMU: case SND_SOC_DAPM_POST_PMU:
tpa6130a2_power(1); ret = tpa6130a2_power(1);
break; break;
case SND_SOC_DAPM_POST_PMD: case SND_SOC_DAPM_POST_PMD:
tpa6130a2_power(0); ret = tpa6130a2_power(0);
break; break;
} }
return 0; return ret;
} }
static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
...@@ -346,13 +379,13 @@ int tpa6130a2_add_controls(struct snd_soc_codec *codec) ...@@ -346,13 +379,13 @@ int tpa6130a2_add_controls(struct snd_soc_codec *codec)
} }
EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
static int tpa6130a2_probe(struct i2c_client *client, static int __devinit tpa6130a2_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct device *dev; struct device *dev;
struct tpa6130a2_data *data; struct tpa6130a2_data *data;
struct tpa6130a2_platform_data *pdata; struct tpa6130a2_platform_data *pdata;
int ret; int i, ret;
dev = &client->dev; dev = &client->dev;
...@@ -387,15 +420,38 @@ static int tpa6130a2_probe(struct i2c_client *client, ...@@ -387,15 +420,38 @@ static int tpa6130a2_probe(struct i2c_client *client,
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Failed to request power GPIO (%d)\n", dev_err(dev, "Failed to request power GPIO (%d)\n",
data->power_gpio); data->power_gpio);
goto fail; goto err_gpio;
} }
gpio_direction_output(data->power_gpio, 0); gpio_direction_output(data->power_gpio, 0);
} else {
data->power_state = 1;
tpa6130a2_initialize();
} }
tpa6130a2_power(1); switch (pdata->id) {
case TPA6130A2:
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6130a2_supply_names[i];
break;
case TPA6140A2:
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6140a2_supply_names[i];;
break;
default:
dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
pdata->id);
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6130a2_supply_names[i];
}
ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
data->supplies);
if (ret != 0) {
dev_err(dev, "Failed to request supplies: %d\n", ret);
goto err_regulator;
}
ret = tpa6130a2_power(1);
if (ret != 0)
goto err_power;
/* Read version */ /* Read version */
ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) & ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
...@@ -404,10 +460,18 @@ static int tpa6130a2_probe(struct i2c_client *client, ...@@ -404,10 +460,18 @@ static int tpa6130a2_probe(struct i2c_client *client,
dev_warn(dev, "UNTESTED version detected (%d)\n", ret); dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
/* Disable the chip */ /* Disable the chip */
tpa6130a2_power(0); ret = tpa6130a2_power(0);
if (ret != 0)
goto err_power;
return 0; return 0;
fail:
err_power:
regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
err_regulator:
if (data->power_gpio >= 0)
gpio_free(data->power_gpio);
err_gpio:
kfree(data); kfree(data);
i2c_set_clientdata(tpa6130a2_client, NULL); i2c_set_clientdata(tpa6130a2_client, NULL);
tpa6130a2_client = NULL; tpa6130a2_client = NULL;
...@@ -415,7 +479,7 @@ static int tpa6130a2_probe(struct i2c_client *client, ...@@ -415,7 +479,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
return ret; return ret;
} }
static int tpa6130a2_remove(struct i2c_client *client) static int __devexit tpa6130a2_remove(struct i2c_client *client)
{ {
struct tpa6130a2_data *data = i2c_get_clientdata(client); struct tpa6130a2_data *data = i2c_get_clientdata(client);
...@@ -423,6 +487,9 @@ static int tpa6130a2_remove(struct i2c_client *client) ...@@ -423,6 +487,9 @@ static int tpa6130a2_remove(struct i2c_client *client)
if (data->power_gpio >= 0) if (data->power_gpio >= 0)
gpio_free(data->power_gpio); gpio_free(data->power_gpio);
regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
kfree(data); kfree(data);
tpa6130a2_client = NULL; tpa6130a2_client = NULL;
......
...@@ -55,7 +55,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { ...@@ -55,7 +55,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x0c, /* REG_ATXR1PGA (0xB) */ 0x0c, /* REG_ATXR1PGA (0xB) */
0x00, /* REG_AVTXL2PGA (0xC) */ 0x00, /* REG_AVTXL2PGA (0xC) */
0x00, /* REG_AVTXR2PGA (0xD) */ 0x00, /* REG_AVTXR2PGA (0xD) */
0x01, /* REG_AUDIO_IF (0xE) */ 0x00, /* REG_AUDIO_IF (0xE) */
0x00, /* REG_VOICE_IF (0xF) */ 0x00, /* REG_VOICE_IF (0xF) */
0x00, /* REG_ARXR1PGA (0x10) */ 0x00, /* REG_ARXR1PGA (0x10) */
0x00, /* REG_ARXL1PGA (0x11) */ 0x00, /* REG_ARXL1PGA (0x11) */
...@@ -64,19 +64,19 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { ...@@ -64,19 +64,19 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_VRXPGA (0x14) */ 0x00, /* REG_VRXPGA (0x14) */
0x00, /* REG_VSTPGA (0x15) */ 0x00, /* REG_VSTPGA (0x15) */
0x00, /* REG_VRX2ARXPGA (0x16) */ 0x00, /* REG_VRX2ARXPGA (0x16) */
0x0c, /* REG_AVDAC_CTL (0x17) */ 0x00, /* REG_AVDAC_CTL (0x17) */
0x00, /* REG_ARX2VTXPGA (0x18) */ 0x00, /* REG_ARX2VTXPGA (0x18) */
0x00, /* REG_ARXL1_APGA_CTL (0x19) */ 0x00, /* REG_ARXL1_APGA_CTL (0x19) */
0x00, /* REG_ARXR1_APGA_CTL (0x1A) */ 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
0x4b, /* REG_ARXL2_APGA_CTL (0x1B) */ 0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */
0x4b, /* REG_ARXR2_APGA_CTL (0x1C) */ 0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */
0x00, /* REG_ATX2ARXPGA (0x1D) */ 0x00, /* REG_ATX2ARXPGA (0x1D) */
0x00, /* REG_BT_IF (0x1E) */ 0x00, /* REG_BT_IF (0x1E) */
0x00, /* REG_BTPGA (0x1F) */ 0x00, /* REG_BTPGA (0x1F) */
0x00, /* REG_BTSTPGA (0x20) */ 0x00, /* REG_BTSTPGA (0x20) */
0x00, /* REG_EAR_CTL (0x21) */ 0x00, /* REG_EAR_CTL (0x21) */
0x24, /* REG_HS_SEL (0x22) */ 0x00, /* REG_HS_SEL (0x22) */
0x0a, /* REG_HS_GAIN_SET (0x23) */ 0x00, /* REG_HS_GAIN_SET (0x23) */
0x00, /* REG_HS_POPN_SET (0x24) */ 0x00, /* REG_HS_POPN_SET (0x24) */
0x00, /* REG_PREDL_CTL (0x25) */ 0x00, /* REG_PREDL_CTL (0x25) */
0x00, /* REG_PREDR_CTL (0x26) */ 0x00, /* REG_PREDR_CTL (0x26) */
...@@ -99,7 +99,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { ...@@ -99,7 +99,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
0x16, /* REG_APLL_CTL (0x3A) */ 0x06, /* REG_APLL_CTL (0x3A) */
0x00, /* REG_DTMF_CTL (0x3B) */ 0x00, /* REG_DTMF_CTL (0x3B) */
0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */ 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */
0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */ 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
...@@ -1203,6 +1203,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { ...@@ -1203,6 +1203,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event, SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIF Enable", TWL4030_REG_AUDIO_IF, 0, 0, NULL, 0),
/* Output MIXER controls */ /* Output MIXER controls */
/* Earpiece */ /* Earpiece */
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
...@@ -1337,6 +1339,11 @@ static const struct snd_soc_dapm_route intercon[] = { ...@@ -1337,6 +1339,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital L2 Playback Mixer", NULL, "APLL Enable"}, {"Digital L2 Playback Mixer", NULL, "APLL Enable"},
{"Digital Voice Playback Mixer", NULL, "APLL Enable"}, {"Digital Voice Playback Mixer", NULL, "APLL Enable"},
{"Digital R1 Playback Mixer", NULL, "AIF Enable"},
{"Digital L1 Playback Mixer", NULL, "AIF Enable"},
{"Digital R2 Playback Mixer", NULL, "AIF Enable"},
{"Digital L2 Playback Mixer", NULL, "AIF Enable"},
{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
...@@ -1455,6 +1462,11 @@ static const struct snd_soc_dapm_route intercon[] = { ...@@ -1455,6 +1462,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "APLL Enable"}, {"ADC Virtual Left2", NULL, "APLL Enable"},
{"ADC Virtual Right2", NULL, "APLL Enable"}, {"ADC Virtual Right2", NULL, "APLL Enable"},
{"ADC Virtual Left1", NULL, "AIF Enable"},
{"ADC Virtual Right1", NULL, "AIF Enable"},
{"ADC Virtual Left2", NULL, "AIF Enable"},
{"ADC Virtual Right2", NULL, "AIF Enable"},
/* Analog bypass routes */ /* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right"}, {"Right1 Analog Loopback", "Switch", "Analog Right"},
{"Left1 Analog Loopback", "Switch", "Analog Left"}, {"Left1 Analog Loopback", "Switch", "Analog Left"},
...@@ -2152,8 +2164,6 @@ static int twl4030_soc_remove(struct platform_device *pdev) ...@@ -2152,8 +2164,6 @@ static int twl4030_soc_remove(struct platform_device *pdev)
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev); snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);
return 0; return 0;
} }
...@@ -2192,7 +2202,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) ...@@ -2192,7 +2202,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
codec->write = twl4030_write; codec->write = twl4030_write;
codec->set_bias_level = twl4030_set_bias_level; codec->set_bias_level = twl4030_set_bias_level;
codec->dai = twl4030_dai; codec->dai = twl4030_dai;
codec->num_dai = ARRAY_SIZE(twl4030_dai), codec->num_dai = ARRAY_SIZE(twl4030_dai);
codec->reg_cache_size = sizeof(twl4030_reg); codec->reg_cache_size = sizeof(twl4030_reg);
codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
GFP_KERNEL); GFP_KERNEL);
...@@ -2237,6 +2247,9 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev) ...@@ -2237,6 +2247,9 @@ static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{ {
struct twl4030_priv *twl4030 = platform_get_drvdata(pdev); struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
snd_soc_unregister_codec(&twl4030->codec);
kfree(twl4030->codec.reg_cache);
kfree(twl4030); kfree(twl4030);
twl4030_codec = NULL; twl4030_codec = NULL;
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
/* Register descriptions are here */ /* Register descriptions are here */
#include <linux/mfd/twl4030-codec.h> #include <linux/mfd/twl4030-codec.h>
/* Sgadow register used by the audio driver */ /* Shadow register used by the audio driver */
#define TWL4030_REG_SW_SHADOW 0x4A #define TWL4030_REG_SW_SHADOW 0x4A
#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1) #define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
......
此差异已折叠。
/*
* wm2000.h -- WM2000 Soc Audio driver
*
* 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 _WM2000_H
#define _WM2000_H
struct wm2000_setup_data {
unsigned short i2c_address;
int mclk_div; /* Set to a non-zero value if MCLK_DIV_2 required */
};
extern int wm2000_add_controls(struct snd_soc_codec *codec);
extern struct snd_soc_dai wm2000_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm2000;
#define WM2000_REG_SYS_START 0x8000
#define WM2000_REG_SPEECH_CLARITY 0x8fef
#define WM2000_REG_SYS_WATCHDOG 0x8ff6
#define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7
#define WM2000_REG_ANA_VMID_PU_TIME 0x8ff8
#define WM2000_REG_CAT_FLTR_INDX 0x8ff9
#define WM2000_REG_CAT_GAIN_0 0x8ffa
#define WM2000_REG_SYS_STATUS 0x8ffc
#define WM2000_REG_SYS_MODE_CNTRL 0x8ffd
#define WM2000_REG_SYS_START0 0x8ffe
#define WM2000_REG_SYS_START1 0x8fff
#define WM2000_REG_ID1 0xf000
#define WM2000_REG_ID2 0xf001
#define WM2000_REG_REVISON 0xf002
#define WM2000_REG_SYS_CTL1 0xf003
#define WM2000_REG_SYS_CTL2 0xf004
#define WM2000_REG_ANC_STAT 0xf005
#define WM2000_REG_IF_CTL 0xf006
/* SPEECH_CLARITY */
#define WM2000_SPEECH_CLARITY 0x01
/* SYS_STATUS */
#define WM2000_STATUS_MOUSE_ACTIVE 0x40
#define WM2000_STATUS_CAT_FREQ_COMPLETE 0x20
#define WM2000_STATUS_CAT_GAIN_COMPLETE 0x10
#define WM2000_STATUS_THERMAL_SHUTDOWN_COMPLETE 0x08
#define WM2000_STATUS_ANC_DISABLED 0x04
#define WM2000_STATUS_POWER_DOWN_COMPLETE 0x02
#define WM2000_STATUS_BOOT_COMPLETE 0x01
/* SYS_MODE_CNTRL */
#define WM2000_MODE_ANA_SEQ_INCLUDE 0x80
#define WM2000_MODE_MOUSE_ENABLE 0x40
#define WM2000_MODE_CAT_FREQ_ENABLE 0x20
#define WM2000_MODE_CAT_GAIN_ENABLE 0x10
#define WM2000_MODE_BYPASS_ENTRY 0x08
#define WM2000_MODE_STANDBY_ENTRY 0x04
#define WM2000_MODE_THERMAL_ENABLE 0x02
#define WM2000_MODE_POWER_DOWN 0x01
/* SYS_CTL1 */
#define WM2000_SYS_STBY 0x01
/* SYS_CTL2 */
#define WM2000_MCLK_DIV2_ENA_CLR 0x80
#define WM2000_MCLK_DIV2_ENA_SET 0x40
#define WM2000_ANC_ENG_CLR 0x20
#define WM2000_ANC_ENG_SET 0x10
#define WM2000_ANC_INT_N_CLR 0x08
#define WM2000_ANC_INT_N_SET 0x04
#define WM2000_RAM_CLR 0x02
#define WM2000_RAM_SET 0x01
/* ANC_STAT */
#define WM2000_ANC_ENG_IDLE 0x01
#endif
...@@ -44,23 +44,16 @@ struct snd_soc_dai wm8727_dai = { ...@@ -44,23 +44,16 @@ struct snd_soc_dai wm8727_dai = {
}; };
EXPORT_SYMBOL_GPL(wm8727_dai); EXPORT_SYMBOL_GPL(wm8727_dai);
static struct snd_soc_codec *wm8727_codec;
static int wm8727_soc_probe(struct platform_device *pdev) static int wm8727_soc_probe(struct platform_device *pdev)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0; int ret = 0;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); BUG_ON(!wm8727_codec);
if (codec == NULL)
return -ENOMEM; socdev->card->codec = wm8727_codec;
mutex_init(&codec->mutex);
codec->name = "WM8727";
codec->owner = THIS_MODULE;
codec->dai = &wm8727_dai;
codec->num_dai = 1;
socdev->card->codec = codec;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
/* register pcms */ /* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
...@@ -80,12 +73,9 @@ static int wm8727_soc_probe(struct platform_device *pdev) ...@@ -80,12 +73,9 @@ static int wm8727_soc_probe(struct platform_device *pdev)
static int wm8727_soc_remove(struct platform_device *pdev) static int wm8727_soc_remove(struct platform_device *pdev)
{ {
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;
snd_soc_free_pcms(socdev); snd_soc_free_pcms(socdev);
kfree(codec);
return 0; return 0;
} }
...@@ -98,13 +88,55 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727); ...@@ -98,13 +88,55 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
static __devinit int wm8727_platform_probe(struct platform_device *pdev) static __devinit int wm8727_platform_probe(struct platform_device *pdev)
{ {
struct snd_soc_codec *codec;
int ret;
if (wm8727_codec) {
dev_err(&pdev->dev, "Another WM8727 is registered\n");
return -EBUSY;
}
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8727_codec = codec;
platform_set_drvdata(pdev, codec);
mutex_init(&codec->mutex);
codec->dev = &pdev->dev;
codec->name = "WM8727";
codec->owner = THIS_MODULE;
codec->dai = &wm8727_dai;
codec->num_dai = 1;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
wm8727_dai.dev = &pdev->dev; wm8727_dai.dev = &pdev->dev;
return snd_soc_register_dai(&wm8727_dai);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register CODEC: %d\n", ret);
goto err;
}
ret = snd_soc_register_dai(&wm8727_dai);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
goto err_codec;
}
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(codec);
return ret;
} }
static int __devexit wm8727_platform_remove(struct platform_device *pdev) static int __devexit wm8727_platform_remove(struct platform_device *pdev)
{ {
snd_soc_unregister_dai(&wm8727_dai); snd_soc_unregister_dai(&wm8727_dai);
snd_soc_unregister_codec(platform_get_drvdata(pdev));
return 0; return 0;
} }
......
...@@ -456,6 +456,9 @@ static int wm8731_resume(struct platform_device *pdev) ...@@ -456,6 +456,9 @@ static int wm8731_resume(struct platform_device *pdev)
/* Sync reg_cache with the hardware */ /* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) { for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
if (cache[i] == wm8731_reg[i])
continue;
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff; data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2); codec->hw_write(codec->control_data, data, 2);
......
...@@ -1507,10 +1507,6 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -1507,10 +1507,6 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec = socdev->card->codec;
/* we only need to suspend if we are a valid card */
if (!codec->card)
return 0;
wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0; return 0;
} }
...@@ -1523,10 +1519,6 @@ static int wm8753_resume(struct platform_device *pdev) ...@@ -1523,10 +1519,6 @@ static int wm8753_resume(struct platform_device *pdev)
u8 data[2]; u8 data[2];
u16 *cache = codec->reg_cache; u16 *cache = codec->reg_cache;
/* we only need to resume if we are a valid card */
if (!codec->card)
return 0;
/* Sync reg_cache with the hardware */ /* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) { for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
if (i + 1 == WM8753_RESET) if (i + 1 == WM8753_RESET)
......
...@@ -406,6 +406,8 @@ static int wm8776_resume(struct platform_device *pdev) ...@@ -406,6 +406,8 @@ static int wm8776_resume(struct platform_device *pdev)
/* Sync reg_cache with the hardware */ /* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) { for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) {
if (cache[i] == wm8776_reg[i])
continue;
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff; data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2); codec->hw_write(codec->control_data, data, 2);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -1022,6 +1022,9 @@ static int wm8961_resume(struct platform_device *pdev) ...@@ -1022,6 +1022,9 @@ static int wm8961_resume(struct platform_device *pdev)
int i; int i;
for (i = 0; i < codec->reg_cache_size; i++) { for (i = 0; i < codec->reg_cache_size; i++) {
if (reg_cache[i] == wm8961_reg_defaults[i])
continue;
if (i == WM8961_SOFTWARE_RESET) if (i == WM8961_SOFTWARE_RESET)
continue; continue;
......
...@@ -170,6 +170,10 @@ SOC_ENUM("Aux Mode", wm8974_auxmode), ...@@ -170,6 +170,10 @@ SOC_ENUM("Aux Mode", wm8974_auxmode),
SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1), SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
/* DAC / ADC oversampling */
SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
}; };
/* Speaker Output Mixer */ /* Speaker Output Mixer */
...@@ -381,14 +385,6 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, ...@@ -381,14 +385,6 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f; reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
snd_soc_write(codec, WM8974_CLOCK, reg | div); snd_soc_write(codec, WM8974_CLOCK, reg | div);
break; break;
case WM8974_ADCCLK:
reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
snd_soc_write(codec, WM8974_ADC, reg | div);
break;
case WM8974_DACCLK:
reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
snd_soc_write(codec, WM8974_DAC, reg | div);
break;
case WM8974_BCLKDIV: case WM8974_BCLKDIV:
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3; reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
snd_soc_write(codec, WM8974_CLOCK, reg | div); snd_soc_write(codec, WM8974_CLOCK, reg | div);
......
...@@ -57,17 +57,7 @@ ...@@ -57,17 +57,7 @@
/* Clock divider Id's */ /* Clock divider Id's */
#define WM8974_OPCLKDIV 0 #define WM8974_OPCLKDIV 0
#define WM8974_MCLKDIV 1 #define WM8974_MCLKDIV 1
#define WM8974_ADCCLK 2 #define WM8974_BCLKDIV 2
#define WM8974_DACCLK 3
#define WM8974_BCLKDIV 4
/* DAC clock dividers */
#define WM8974_DACCLK_F2 (1 << 3)
#define WM8974_DACCLK_F4 (0 << 3)
/* ADC clock dividers */
#define WM8974_ADCCLK_F2 (1 << 3)
#define WM8974_ADCCLK_F4 (0 << 3)
/* PLL Out dividers */ /* PLL Out dividers */
#define WM8974_OPCLKDIV_1 (0 << 4) #define WM8974_OPCLKDIV_1 (0 << 4)
......
此差异已折叠。
/*
* wm8978.h -- codec driver for WM8978
*
* Copyright 2009 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*
* 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 __WM8978_H__
#define __WM8978_H__
/*
* Register values.
*/
#define WM8978_RESET 0x00
#define WM8978_POWER_MANAGEMENT_1 0x01
#define WM8978_POWER_MANAGEMENT_2 0x02
#define WM8978_POWER_MANAGEMENT_3 0x03
#define WM8978_AUDIO_INTERFACE 0x04
#define WM8978_COMPANDING_CONTROL 0x05
#define WM8978_CLOCKING 0x06
#define WM8978_ADDITIONAL_CONTROL 0x07
#define WM8978_GPIO_CONTROL 0x08
#define WM8978_JACK_DETECT_CONTROL_1 0x09
#define WM8978_DAC_CONTROL 0x0A
#define WM8978_LEFT_DAC_DIGITAL_VOLUME 0x0B
#define WM8978_RIGHT_DAC_DIGITAL_VOLUME 0x0C
#define WM8978_JACK_DETECT_CONTROL_2 0x0D
#define WM8978_ADC_CONTROL 0x0E
#define WM8978_LEFT_ADC_DIGITAL_VOLUME 0x0F
#define WM8978_RIGHT_ADC_DIGITAL_VOLUME 0x10
#define WM8978_EQ1 0x12
#define WM8978_EQ2 0x13
#define WM8978_EQ3 0x14
#define WM8978_EQ4 0x15
#define WM8978_EQ5 0x16
#define WM8978_DAC_LIMITER_1 0x18
#define WM8978_DAC_LIMITER_2 0x19
#define WM8978_NOTCH_FILTER_1 0x1b
#define WM8978_NOTCH_FILTER_2 0x1c
#define WM8978_NOTCH_FILTER_3 0x1d
#define WM8978_NOTCH_FILTER_4 0x1e
#define WM8978_ALC_CONTROL_1 0x20
#define WM8978_ALC_CONTROL_2 0x21
#define WM8978_ALC_CONTROL_3 0x22
#define WM8978_NOISE_GATE 0x23
#define WM8978_PLL_N 0x24
#define WM8978_PLL_K1 0x25
#define WM8978_PLL_K2 0x26
#define WM8978_PLL_K3 0x27
#define WM8978_3D_CONTROL 0x29
#define WM8978_BEEP_CONTROL 0x2b
#define WM8978_INPUT_CONTROL 0x2c
#define WM8978_LEFT_INP_PGA_CONTROL 0x2d
#define WM8978_RIGHT_INP_PGA_CONTROL 0x2e
#define WM8978_LEFT_ADC_BOOST_CONTROL 0x2f
#define WM8978_RIGHT_ADC_BOOST_CONTROL 0x30
#define WM8978_OUTPUT_CONTROL 0x31
#define WM8978_LEFT_MIXER_CONTROL 0x32
#define WM8978_RIGHT_MIXER_CONTROL 0x33
#define WM8978_LOUT1_HP_CONTROL 0x34
#define WM8978_ROUT1_HP_CONTROL 0x35
#define WM8978_LOUT2_SPK_CONTROL 0x36
#define WM8978_ROUT2_SPK_CONTROL 0x37
#define WM8978_OUT3_MIXER_CONTROL 0x38
#define WM8978_OUT4_MIXER_CONTROL 0x39
#define WM8978_CACHEREGNUM 58
/* Clock divider Id's */
enum wm8978_clk_id {
WM8978_OPCLKRATE,
WM8978_BCLKDIV,
};
enum wm8978_sysclk_src {
WM8978_PLL,
WM8978_MCLK
};
extern struct snd_soc_dai wm8978_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8978;
#endif /* __WM8978_H__ */
...@@ -1319,10 +1319,6 @@ static int wm8990_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -1319,10 +1319,6 @@ static int wm8990_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec; struct snd_soc_codec *codec = socdev->card->codec;
/* we only need to suspend if we are a valid card */
if (!codec->card)
return 0;
wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF); wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0; return 0;
} }
...@@ -1335,10 +1331,6 @@ static int wm8990_resume(struct platform_device *pdev) ...@@ -1335,10 +1331,6 @@ static int wm8990_resume(struct platform_device *pdev)
u8 data[2]; u8 data[2];
u16 *cache = codec->reg_cache; u16 *cache = codec->reg_cache;
/* we only need to resume if we are a valid card */
if (!codec->card)
return 0;
/* Sync reg_cache with the hardware */ /* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) { for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
if (i + 1 == WM8990_RESET) if (i + 1 == WM8990_RESET)
......
此差异已折叠。
此差异已折叠。
/*
* wm8994.h -- WM8994 Soc Audio driver
*
* 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 _WM8994_H
#define _WM8994_H
#include <sound/soc.h>
extern struct snd_soc_codec_device soc_codec_dev_wm8994;
extern struct snd_soc_dai wm8994_dai[];
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
#define WM8994_SYSCLK_MCLK2 2
#define WM8994_SYSCLK_FLL1 3
#define WM8994_SYSCLK_FLL2 4
#define WM8994_FLL1 1
#define WM8994_FLL2 2
#endif
...@@ -23,13 +23,12 @@ ...@@ -23,13 +23,12 @@
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/soc-dapm.h> #include <sound/soc-dapm.h>
#include "wm9713.h" #include "wm9713.h"
#define WM9713_VERSION "0.15"
struct wm9713_priv { struct wm9713_priv {
u32 pll_in; /* PLL input frequency */ u32 pll_in; /* PLL input frequency */
}; };
...@@ -115,15 +114,27 @@ SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 ...@@ -115,15 +114,27 @@ SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18
SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
}; };
static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0);
static unsigned int mic_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
};
static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv),
SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1,
out_tlv),
SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1), SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv),
SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1), SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv),
SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
SOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv),
SOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv),
SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
...@@ -133,7 +144,7 @@ SOC_ENUM("Capture Volume Steps", wm9713_enum[5]), ...@@ -133,7 +144,7 @@ SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1), SOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv),
SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
...@@ -154,28 +165,43 @@ SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), ...@@ -154,28 +165,43 @@ SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1), SOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv),
SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1), SOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv),
SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1), SOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv),
SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1), SOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv),
SOC_SINGLE("Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1), SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1,
SOC_SINGLE("Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1), misc_tlv),
SOC_SINGLE("Beep Playback Mono Volume", AC97_AUX, 4, 7, 1), SOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1,
misc_tlv),
SOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv),
SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1), SOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1,
misc_tlv),
SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
SOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1,
misc_tlv),
SOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1,
misc_tlv),
SOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1,
misc_tlv),
SOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1,
misc_tlv),
SOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1,
misc_tlv),
SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
SOC_ENUM("Bass Control", wm9713_enum[16]), SOC_ENUM("Bass Control", wm9713_enum[16]),
SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
...@@ -1186,8 +1212,6 @@ static int wm9713_soc_probe(struct platform_device *pdev) ...@@ -1186,8 +1212,6 @@ static int wm9713_soc_probe(struct platform_device *pdev)
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
int ret = 0, reg; int ret = 0, reg;
printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
GFP_KERNEL); GFP_KERNEL);
if (socdev->card->codec == NULL) if (socdev->card->codec == NULL)
......
...@@ -68,24 +68,77 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec) ...@@ -68,24 +68,77 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec)
int count = 0; int count = 0;
dev_dbg(codec->dev, "Waiting for DC servo...\n"); dev_dbg(codec->dev, "Waiting for DC servo...\n");
do { do {
count++; count++;
msleep(1); msleep(1);
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0); reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
dev_dbg(codec->dev, "DC servo status: %x\n", reg); dev_dbg(codec->dev, "DC servo: %x\n", reg);
} while ((reg & WM8993_DCS_CAL_COMPLETE_MASK) } while (reg & WM8993_DCS_DATAPATH_BUSY);
!= WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
if ((reg & WM8993_DCS_CAL_COMPLETE_MASK) if (reg & WM8993_DCS_DATAPATH_BUSY)
!= WM8993_DCS_CAL_COMPLETE_MASK)
dev_err(codec->dev, "Timed out waiting for DC Servo\n"); dev_err(codec->dev, "Timed out waiting for DC Servo\n");
} }
/*
* Startup calibration of the DC servo
*/
static void calibrate_dc_servo(struct snd_soc_codec *codec)
{
struct wm_hubs_data *hubs = codec->private_data;
u16 reg, dcs_cfg;
/* Set for 32 series updates */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_SERIES_NO_01_MASK,
32 << WM8993_DCS_SERIES_NO_01_SHIFT);
/* Enable the DC servo. Write all bits to avoid triggering startup
* or write calibration.
*/
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
0xFFFF,
WM8993_DCS_ENA_CHAN_0 |
WM8993_DCS_ENA_CHAN_1 |
WM8993_DCS_TRIG_SERIES_1 |
WM8993_DCS_TRIG_SERIES_0);
wait_for_dc_servo(codec);
/* Apply correction to DC servo result */
if (hubs->dcs_codes) {
dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
hubs->dcs_codes);
/* HPOUT1L */
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) &
WM8993_DCS_INTEG_CHAN_0_MASK;;
reg += hubs->dcs_codes;
dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
/* HPOUT1R */
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) &
WM8993_DCS_INTEG_CHAN_1_MASK;
reg += hubs->dcs_codes;
dcs_cfg |= reg;
/* Do it */
snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1,
WM8993_DCS_TRIG_DAC_WR_0 |
WM8993_DCS_TRIG_DAC_WR_1);
wait_for_dc_servo(codec);
}
}
/* /*
* Update the DC servo calibration on gain changes * Update the DC servo calibration on gain changes
*/ */
static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int ret; int ret;
...@@ -251,6 +304,47 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1, ...@@ -251,6 +304,47 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
line_tlv), line_tlv),
}; };
static int hp_supply_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct wm_hubs_data *hubs = codec->private_data;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
switch (hubs->hp_startup_mode) {
case 0:
break;
case 1:
/* Enable the headphone amp */
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA |
WM8993_HPOUT1R_ENA,
WM8993_HPOUT1L_ENA |
WM8993_HPOUT1R_ENA);
/* Enable the second stage */
snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY);
break;
default:
dev_err(codec->dev, "Unknown HP startup mode %d\n",
hubs->hp_startup_mode);
break;
}
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, 0);
break;
}
return 0;
}
static int hp_event(struct snd_soc_dapm_widget *w, static int hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) struct snd_kcontrol *kcontrol, int event)
{ {
...@@ -271,14 +365,11 @@ static int hp_event(struct snd_soc_dapm_widget *w, ...@@ -271,14 +365,11 @@ static int hp_event(struct snd_soc_dapm_widget *w,
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg); snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
/* Start the DC servo */ /* Smallest supported update interval */
snd_soc_update_bits(codec, WM8993_DC_SERVO_0, snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
0xFFFF, WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
WM8993_DCS_ENA_CHAN_0 |
WM8993_DCS_ENA_CHAN_1 | calibrate_dc_servo(codec);
WM8993_DCS_TRIG_STARTUP_1 |
WM8993_DCS_TRIG_STARTUP_0);
wait_for_dc_servo(codec);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT | reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT; WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
...@@ -286,23 +377,19 @@ static int hp_event(struct snd_soc_dapm_widget *w, ...@@ -286,23 +377,19 @@ static int hp_event(struct snd_soc_dapm_widget *w,
break; break;
case SND_SOC_DAPM_PRE_PMD: case SND_SOC_DAPM_PRE_PMD:
reg &= ~(WM8993_HPOUT1L_RMV_SHORT | snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY | WM8993_HPOUT1L_DLY |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1R_DLY |
WM8993_HPOUT1R_RMV_SHORT | WM8993_HPOUT1L_RMV_SHORT |
WM8993_HPOUT1R_DLY | WM8993_HPOUT1R_RMV_SHORT, 0);
WM8993_HPOUT1R_OUTP);
snd_soc_update_bits(codec, WM8993_DC_SERVO_0, snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
0xffff, 0); WM8993_HPOUT1L_OUTP |
WM8993_HPOUT1R_OUTP, 0);
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1, snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0); 0);
snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, 0);
break; break;
} }
...@@ -473,6 +560,8 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0, ...@@ -473,6 +560,8 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0), SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
NULL, 0, NULL, 0,
hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
...@@ -626,6 +715,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = { ...@@ -626,6 +715,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Headphone PGA", NULL, "Left Headphone Mux" }, { "Headphone PGA", NULL, "Left Headphone Mux" },
{ "Headphone PGA", NULL, "Right Headphone Mux" }, { "Headphone PGA", NULL, "Right Headphone Mux" },
{ "Headphone PGA", NULL, "CLK_SYS" }, { "Headphone PGA", NULL, "CLK_SYS" },
{ "Headphone PGA", NULL, "Headphone Supply" },
{ "HPOUT1L", NULL, "Headphone PGA" }, { "HPOUT1L", NULL, "Headphone PGA" },
{ "HPOUT1R", NULL, "Headphone PGA" }, { "HPOUT1R", NULL, "Headphone PGA" },
...@@ -753,6 +843,12 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec, ...@@ -753,6 +843,12 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
WM8993_LINEOUT2_MODE, WM8993_LINEOUT2_MODE,
WM8993_LINEOUT2_MODE); WM8993_LINEOUT2_MODE);
/* If the line outputs are differential then we aren't presenting
* VMID as an output and can disable it.
*/
if (lineout1_diff && lineout2_diff)
codec->idle_bias_off = 1;
if (lineout1fb) if (lineout1fb)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
......
...@@ -18,6 +18,12 @@ struct snd_soc_codec; ...@@ -18,6 +18,12 @@ struct snd_soc_codec;
extern const unsigned int wm_hubs_spkmix_tlv[]; extern const unsigned int wm_hubs_spkmix_tlv[];
/* This *must* be the first element of the codec->private_data struct */
struct wm_hubs_data {
int dcs_codes;
int hp_startup_mode;
};
extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *); extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int); extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *, extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
......
此差异已折叠。
...@@ -44,6 +44,7 @@ struct davinci_audio_dev { ...@@ -44,6 +44,7 @@ struct davinci_audio_dev {
int sample_rate; int sample_rate;
struct clk *clk; struct clk *clk;
unsigned int codec_fmt; unsigned int codec_fmt;
u8 clk_active;
/* McASP specific data */ /* McASP specific data */
int tdm_slots; int tdm_slots;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册