Commit 6679ee18 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/asoc' into for-linus

parents a91a4aa1 a0b62329
......@@ -13,3 +13,7 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci.o
obj-$(CONFIG_MXC_ULPI) += ulpi.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.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 @@
#define twl_has_watchdog() false
#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
#else
#define twl_has_codec() false
......@@ -711,8 +712,19 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
if (twl_has_codec() && pdata->codec) {
child = add_child(1, "twl4030_codec",
if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
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),
false, 0, 0);
if (IS_ERR(child))
......
......@@ -547,6 +547,10 @@ struct twl4030_codec_data {
unsigned int audio_mclk;
struct twl4030_codec_audio_data *audio;
struct twl4030_codec_vibra_data *vibra;
/* twl6030 */
int audpwron_gpio; /* audio power-on gpio */
int naudint_irq; /* audio interrupt */
};
struct twl4030_platform_data {
......
......@@ -16,6 +16,8 @@
#include <linux/list.h>
#include <sound/soc.h>
struct snd_pcm_substream;
/*
......
......@@ -95,6 +95,21 @@
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.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 */
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
......@@ -126,6 +141,23 @@
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
.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 */
#define SND_SOC_DAPM_PRE(wname, wevent) \
{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
......
......@@ -168,6 +168,23 @@
.get = xhandler_get, .put = xhandler_put, \
.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
*
......@@ -253,6 +270,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
/* codec register bit access */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
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,
unsigned int mask, unsigned int value);
......@@ -402,6 +422,10 @@ struct snd_soc_codec {
short reg_cache_size;
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 */
u32 pop_time;
struct list_head dapm_widgets;
......@@ -497,6 +521,8 @@ struct snd_soc_card {
int (*set_bias_level)(struct snd_soc_card *,
enum snd_soc_bias_level level);
long pmdown_time;
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
......
......@@ -15,6 +15,7 @@
struct tlv320dac33_platform_data {
int power_gpio;
u8 burst_bclkdiv;
};
#endif /* __TLV320DAC33_PLAT_H */
......@@ -23,7 +23,13 @@
#ifndef TPA6130A2_PLAT_H
#define TPA6130A2_PLAT_H
enum tpa_model {
TPA6130A2,
TPA6140A2,
};
struct tpa6130a2_platform_data {
enum tpa_model id;
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.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
* 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.
*
* Revision history
* 10th Nov 2006 Initial version.
*/
#ifndef S3C24XXAC97_H_
#define S3C24XXAC97_H_
#ifndef __WM8955_PDATA_H__
#define __WM8955_PDATA_H__
#define AC_CMD_ADDR(x) (x << 16)
#define AC_CMD_DATA(x) (x & 0xffff)
struct wm8955_pdata {
/* 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)
sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
size, &sport_handle->tx_dma_phy, GFP_KERNEL);
if (!sport_handle->tx_dma_buf) {
pr_err("Failed to allocate memory for tx dma \
buf - Please increase uncached DMA \
memory region\n");
pr_err("Failed to allocate memory for tx dma buf - Please increase uncached DMA memory region\n");
return -ENOMEM;
} else
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)
sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
size, &sport_handle->rx_dma_phy, GFP_KERNEL);
if (!sport_handle->rx_dma_buf) {
pr_err("Failed to allocate memory for rx dma \
buf - Please increase uncached DMA \
memory region\n");
pr_err("Failed to allocate memory for rx dma buf - Please increase uncached DMA memory region\n");
return -ENOMEM;
} else
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)
buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (!buf->area) {
pr_err("Failed to allocate dma memory \
Please increase uncached DMA memory region\n");
pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
return -ENOMEM;
}
buf->bytes = size;
......
......@@ -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->addr, GFP_KERNEL);
if (!buf->area) {
pr_err("Failed to allocate dma memory \
Please increase uncached DMA memory region\n");
pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
return -ENOMEM;
}
buf->bytes = size;
......
......@@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4671 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_DA7210 if I2C
select SND_SOC_PCM3008
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
......@@ -35,6 +36,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TWL4030 if TWL4030_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
......@@ -49,14 +51,18 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
select SND_SOC_WM8961 if I2C
select SND_SOC_WM8971 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_WM8990 if I2C
select SND_SOC_WM8993 if I2C
select SND_SOC_WM8994 if MFD_WM8994
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
......@@ -112,6 +118,9 @@ config SND_SOC_AK4671
config SND_SOC_CS4270
tristate
config SND_SOC_DA7210
tristate
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# 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
......@@ -203,9 +212,15 @@ config SND_SOC_WM8900
config SND_SOC_WM8903
tristate
config SND_SOC_WM8904
tristate
config SND_SOC_WM8940
tristate
config SND_SOC_WM8955
tristate
config SND_SOC_WM8960
tristate
......@@ -218,6 +233,9 @@ config SND_SOC_WM8971
config SND_SOC_WM8974
tristate
config SND_SOC_WM8978
tristate
config SND_SOC_WM8988
tristate
......@@ -227,6 +245,9 @@ config SND_SOC_WM8990
config SND_SOC_WM8993
tristate
config SND_SOC_WM8994
tristate
config SND_SOC_WM9081
tristate
......@@ -245,3 +266,6 @@ config SND_SOC_MAX9877
config SND_SOC_TPA6130A2
tristate
config SND_SOC_WM2000
tristate
......@@ -10,6 +10,7 @@ snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-l3-objs := l3.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-spdif-objs := spdif_transciever.o
......@@ -36,14 +37,18 @@ snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8961-objs := wm8961.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
......@@ -53,6 +58,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.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_AD1836) += snd-soc-ad1836.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_CS4270) += snd-soc-cs4270.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_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.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_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.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_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_WM8990) += snd-soc-wm8990.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_WM9705) += snd-soc-wm9705.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
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.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,
return 0;
}
/*
* interface to read/write ad1836 register
*/
#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)
#ifdef CONFIG_PM
static int ad1836_soc_suspend(struct platform_device *pdev,
pm_message_t state)
{
u16 *reg_cache = codec->reg_cache;
int ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
if (value != reg_cache[reg]) {
unsigned short buf;
struct spi_transfer t = {
.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;
}
/* reset clock control mode */
u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
return ret;
return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
}
/*
* read from the ad1836 register space cache
*/
static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
static int ad1836_soc_resume(struct platform_device *pdev)
{
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)
return -EINVAL;
/* restore clock control mode */
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)
{
......@@ -306,32 +284,38 @@ static int ad1836_register(struct ad1836_priv *ad1836)
codec->owner = THIS_MODULE;
codec->dai = &ad1836_dai;
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_paths);
ad1836_dai.dev = codec->dev;
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 */
/* de-emphasis: 48kHz, power-on dac */
codec->write(codec, AD1836_DAC_CTRL1, 0x300);
snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300);
/* 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 */
codec->write(codec, AD1836_ADC_CTRL1, 0x100);
snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
/* 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 */
codec->write(codec, AD1836_ADC_CTRL3, 0x3A);
snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
/* volume */
codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
......@@ -404,6 +388,8 @@ static int ad1836_remove(struct platform_device *pdev)
struct snd_soc_codec_device soc_codec_dev_ad1836 = {
.probe = ad1836_probe,
.remove = ad1836_remove,
.suspend = ad1836_soc_suspend,
.resume = ad1836_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
......
......@@ -54,6 +54,7 @@
#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_AUX (0x6 << 6)
#define AD1836_ADC_CTRL3 14
......
......@@ -46,6 +46,11 @@ struct ad1938_priv {
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;
struct snd_soc_codec_device soc_codec_dev_ad1938;
static int ad1938_register(struct ad1938_priv *ad1938);
......@@ -97,6 +102,7 @@ static const struct snd_kcontrol_new ad1938_snd_controls[] = {
static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
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_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_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
......@@ -107,6 +113,8 @@ static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "PLL_PWR" },
{ "ADC", NULL, "PLL_PWR" },
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", "DAC1 Switch", "DAC" },
......@@ -126,30 +134,20 @@ static int ad1938_mute(struct snd_soc_dai *dai, int mute)
struct snd_soc_codec *codec = dai->codec;
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 &
(~AD1938_DAC_MASTER_MUTE);
codec->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);
snd_soc_write(codec, AD1938_DAC_CTRL2, reg);
return 0;
}
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;
int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
int dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1);
int adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2);
dac_reg &= ~AD1938_DAC_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,
return -EINVAL;
}
codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg);
snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg);
return 0;
}
......@@ -187,8 +185,8 @@ static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
int adc_reg, dac_reg;
adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2);
dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1);
/* 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)
......@@ -265,8 +263,8 @@ static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg);
snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg);
return 0;
}
......@@ -295,134 +293,13 @@ static int ad1938_hw_params(struct snd_pcm_substream *substream,
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;
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;
codec->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;
}
snd_soc_write(codec, AD1938_ADC_CTRL1, reg);
return 0;
}
......@@ -512,32 +389,37 @@ static int ad1938_register(struct ad1938_priv *ad1938)
codec->owner = THIS_MODULE;
codec->dai = &ad1938_dai;
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_paths);
ad1938_dai.dev = codec->dev;
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 */
/* 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 */
codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
snd_soc_write(codec, AD1938_DAC_CTRL2, 0x1A);
/* 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 */
codec->write(codec, AD1938_ADC_CTRL0, 0x3);
snd_soc_write(codec, AD1938_ADC_CTRL0, 0x3);
/* 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 */
codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
ad1938_fill_cache(codec);
snd_soc_write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
snd_soc_write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
......@@ -559,7 +441,6 @@ static int ad1938_register(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_codec(&ad1938->codec);
kfree(ad1938);
......@@ -593,7 +474,6 @@ static int ad1938_probe(struct platform_device *pdev)
ARRAY_SIZE(ad1938_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
pcm_err:
return ret;
......@@ -610,37 +490,9 @@ static int ad1938_remove(struct platform_device *pdev)
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 = {
.probe = ad1938_probe,
.remove = ad1938_remove,
.suspend = ad1938_suspend,
.resume = ad1938_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
......
......@@ -185,9 +185,7 @@ struct snd_soc_dai ak4104_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_32000,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE
......
......@@ -28,6 +28,7 @@
#include <sound/initval.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include "cs4270.h"
......@@ -106,6 +107,10 @@
#define CS4270_MUTE_DAC_A 0x01
#define CS4270_MUTE_DAC_B 0x02
static const char *supply_names[] = {
"va", "vd", "vlc"
};
/* Private data for the CS4270 */
struct cs4270_private {
struct snd_soc_codec codec;
......@@ -114,6 +119,9 @@ struct cs4270_private {
unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode;
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[] = {
* This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in
* 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,
int clk_id, unsigned int freq, int dir)
......@@ -205,20 +218,27 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
cs4270->mclk = freq;
for (i = 0; i < NUM_MCLK_RATIOS; i++) {
unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
rates |= snd_pcm_rate_to_rate_bit(rate);
if (rate < rate_min)
rate_min = rate;
if (rate > rate_max)
rate_max = rate;
}
/* FIXME: soc should support a rate list */
rates &= ~SNDRV_PCM_RATE_KNOT;
if (cs4270->mclk) {
for (i = 0; i < NUM_MCLK_RATIOS; i++) {
unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
rates |= snd_pcm_rate_to_rate_bit(rate);
if (rate < rate_min)
rate_min = rate;
if (rate > rate_max)
rate_max = rate;
}
/* FIXME: soc should support a rate list */
rates &= ~SNDRV_PCM_RATE_KNOT;
if (!rates) {
dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL;
if (!rates) {
dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL;
}
} else {
/* enable all possible rates */
rates = SNDRV_PCM_RATE_8000_192000;
rate_min = 8000;
rate_max = 192000;
}
codec_dai->playback.rates = rates;
......@@ -579,7 +599,8 @@ static int cs4270_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
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. */
socdev->card->codec = codec;
......@@ -599,8 +620,26 @@ static int cs4270_probe(struct platform_device *pdev)
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;
error_free_regulators:
regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
cs4270->supplies);
error_free_pcms:
snd_soc_free_pcms(socdev);
......@@ -616,8 +655,12 @@ static int cs4270_probe(struct platform_device *pdev)
static int cs4270_remove(struct platform_device *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);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
return 0;
};
......@@ -799,17 +842,33 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
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)
{
struct snd_soc_codec *codec = cs4270_codec;
struct cs4270_private *cs4270 = codec->private_data;
struct i2c_client *i2c_client = codec->control_data;
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
* wait 500ns here before any I2C communication. */
ndelay(500);
......
This diff is collapsed.
/*
* 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,
struct snd_soc_codec *codec = socdev->card->codec;
struct aic3x_priv *aic3x = codec->private_data;
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;
u16 pll_d = 1;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
u8 reg;
int clk;
/* select data word length */
data =
......@@ -833,48 +834,70 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
if (bypass_pll)
return 0;
/* Use PLL
* find an apropriate setup for j, d, r and p by iterating over
* p and r - j and d are calculated for each fraction.
* Up to 128 values are probed, the closest one wins the game.
/* Use PLL, compute apropriate setup for j, d, r and p, the closest
* one wins the game. Try with d==0 first, next with d!=0.
* Constraints for j are according to the datasheet.
* The sysclk is divided by 1000 to prevent integer overflows.
*/
codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
for (r = 1; r <= 16; r++)
for (p = 1; p <= 8; p++) {
int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
u8 j = tmp / 10000;
u16 d = tmp % 10000;
for (j = 4; j <= 55; j++) {
/* This is actually 1000*((j+(d/10000))*r)/p
* 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)
continue;
/* try with d != 0 */
for (p = 1; p <= 8; p++) {
j = codec_clk * p / 1000;
if (d != 0 && aic3x->sysclk < 10000000)
continue;
if (j < 4 || j > 11)
continue;
/* This is actually 1000 * ((j + (d/10000)) * r) / p
* The term had to be converted to get rid of the
* division by 10000 */
clk = ((10000 * j * r) + (d * r)) / (10 * p);
/* do not use codec_clk here since we'd loose precision */
d = ((2048 * p * fsref) - j * aic3x->sysclk)
* 100 / (aic3x->sysclk/100);
/* check whether this values get closer than the best
* 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;
}
clk = (10000 * j + d) / (10 * p);
/* Early exit for exact matches */
if (clk == codec_clk)
break;
/* check whether this values get closer than the best
* ones we had before */
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) {
printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
return -EINVAL;
}
found:
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_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
......
This diff is collapsed.
......@@ -25,6 +25,7 @@
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <sound/tpa6130a2-plat.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
......@@ -34,10 +35,22 @@
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 */
struct tpa6130a2_data {
struct mutex mutex;
unsigned char regs[TPA6130A2_CACHEREGNUM];
struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES];
int power_gpio;
unsigned char power_state;
};
......@@ -106,10 +119,11 @@ static void tpa6130a2_initialize(void)
tpa6130a2_i2c_write(i, data->regs[i]);
}
static void tpa6130a2_power(int power)
static int tpa6130a2_power(int power)
{
struct tpa6130a2_data *data;
u8 val;
int ret;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
......@@ -117,11 +131,20 @@ static void tpa6130a2_power(int power)
mutex_lock(&data->mutex);
if (power) {
/* Power on */
if (data->power_gpio >= 0) {
if (data->power_gpio >= 0)
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 */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val &= ~TPA6130A2_SWS;
......@@ -131,13 +154,25 @@ static void tpa6130a2_power(int power)
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val |= TPA6130A2_SWS;
tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
/* Power off */
if (data->power_gpio >= 0) {
if (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);
return ret;
}
static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
......@@ -237,12 +272,8 @@ static const struct snd_kcontrol_new tpa6130a2_controls[] = {
*/
static void tpa6130a2_channel_enable(u8 channel, int enable)
{
struct tpa6130a2_data *data;
u8 val;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
if (enable) {
/* Enable channel */
/* Enable amplifier */
......@@ -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,
struct snd_kcontrol *kcontrol, int event)
{
int ret = 0;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
tpa6130a2_power(1);
ret = tpa6130a2_power(1);
break;
case SND_SOC_DAPM_POST_PMD:
tpa6130a2_power(0);
ret = tpa6130a2_power(0);
break;
}
return 0;
return ret;
}
static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
......@@ -346,13 +379,13 @@ int tpa6130a2_add_controls(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
static int tpa6130a2_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static int __devinit tpa6130a2_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev;
struct tpa6130a2_data *data;
struct tpa6130a2_platform_data *pdata;
int ret;
int i, ret;
dev = &client->dev;
......@@ -387,15 +420,38 @@ static int tpa6130a2_probe(struct i2c_client *client,
if (ret < 0) {
dev_err(dev, "Failed to request power GPIO (%d)\n",
data->power_gpio);
goto fail;
goto err_gpio;
}
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 */
ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
......@@ -404,10 +460,18 @@ static int tpa6130a2_probe(struct i2c_client *client,
dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
/* Disable the chip */
tpa6130a2_power(0);
ret = tpa6130a2_power(0);
if (ret != 0)
goto err_power;
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);
i2c_set_clientdata(tpa6130a2_client, NULL);
tpa6130a2_client = NULL;
......@@ -415,7 +479,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
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);
......@@ -423,6 +487,9 @@ static int tpa6130a2_remove(struct i2c_client *client)
if (data->power_gpio >= 0)
gpio_free(data->power_gpio);
regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
kfree(data);
tpa6130a2_client = NULL;
......
......@@ -55,7 +55,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x0c, /* REG_ATXR1PGA (0xB) */
0x00, /* REG_AVTXL2PGA (0xC) */
0x00, /* REG_AVTXR2PGA (0xD) */
0x01, /* REG_AUDIO_IF (0xE) */
0x00, /* REG_AUDIO_IF (0xE) */
0x00, /* REG_VOICE_IF (0xF) */
0x00, /* REG_ARXR1PGA (0x10) */
0x00, /* REG_ARXL1PGA (0x11) */
......@@ -64,19 +64,19 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_VRXPGA (0x14) */
0x00, /* REG_VSTPGA (0x15) */
0x00, /* REG_VRX2ARXPGA (0x16) */
0x0c, /* REG_AVDAC_CTL (0x17) */
0x00, /* REG_AVDAC_CTL (0x17) */
0x00, /* REG_ARX2VTXPGA (0x18) */
0x00, /* REG_ARXL1_APGA_CTL (0x19) */
0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
0x4b, /* REG_ARXL2_APGA_CTL (0x1B) */
0x4b, /* REG_ARXR2_APGA_CTL (0x1C) */
0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */
0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */
0x00, /* REG_ATX2ARXPGA (0x1D) */
0x00, /* REG_BT_IF (0x1E) */
0x00, /* REG_BTPGA (0x1F) */
0x00, /* REG_BTSTPGA (0x20) */
0x00, /* REG_EAR_CTL (0x21) */
0x24, /* REG_HS_SEL (0x22) */
0x0a, /* REG_HS_GAIN_SET (0x23) */
0x00, /* REG_HS_SEL (0x22) */
0x00, /* REG_HS_GAIN_SET (0x23) */
0x00, /* REG_HS_POPN_SET (0x24) */
0x00, /* REG_PREDL_CTL (0x25) */
0x00, /* REG_PREDR_CTL (0x26) */
......@@ -99,7 +99,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
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_PGA_CTL2 (0x3C) */
0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
......@@ -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_PRE_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIF Enable", TWL4030_REG_AUDIO_IF, 0, 0, NULL, 0),
/* Output MIXER controls */
/* Earpiece */
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
......@@ -1337,6 +1339,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital L2 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 R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
......@@ -1455,6 +1462,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", 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 */
{"Right1 Analog Loopback", "Switch", "Analog Right"},
{"Left1 Analog Loopback", "Switch", "Analog Left"},
......@@ -2152,8 +2164,6 @@ static int twl4030_soc_remove(struct platform_device *pdev)
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);
return 0;
}
......@@ -2192,7 +2202,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
codec->write = twl4030_write;
codec->set_bias_level = twl4030_set_bias_level;
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 = kmemdup(twl4030_reg, sizeof(twl4030_reg),
GFP_KERNEL);
......@@ -2237,6 +2247,9 @@ static int __devexit twl4030_codec_remove(struct platform_device *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);
twl4030_codec = NULL;
......
......@@ -25,7 +25,7 @@
/* Register descriptions are here */
#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_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
......
This diff is collapsed.
/*
* 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 = {
};
EXPORT_SYMBOL_GPL(wm8727_dai);
static struct snd_soc_codec *wm8727_codec;
static int wm8727_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
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);
BUG_ON(!wm8727_codec);
socdev->card->codec = wm8727_codec;
/* register pcms */
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)
static int wm8727_soc_remove(struct platform_device *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);
kfree(codec);
return 0;
}
......@@ -98,13 +88,55 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
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;
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)
{
snd_soc_unregister_dai(&wm8727_dai);
snd_soc_unregister_codec(platform_get_drvdata(pdev));
return 0;
}
......
......@@ -456,6 +456,9 @@ static int wm8731_resume(struct platform_device *pdev)
/* Sync reg_cache with the hardware */
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[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
......
......@@ -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_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);
return 0;
}
......@@ -1523,10 +1519,6 @@ static int wm8753_resume(struct platform_device *pdev)
u8 data[2];
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 */
for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
if (i + 1 == WM8753_RESET)
......
......@@ -406,6 +406,8 @@ static int wm8776_resume(struct platform_device *pdev)
/* Sync reg_cache with the hardware */
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[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -1022,6 +1022,9 @@ static int wm8961_resume(struct platform_device *pdev)
int i;
for (i = 0; i < codec->reg_cache_size; i++) {
if (reg_cache[i] == wm8961_reg_defaults[i])
continue;
if (i == WM8961_SOFTWARE_RESET)
continue;
......
......@@ -170,6 +170,10 @@ SOC_ENUM("Aux Mode", wm8974_auxmode),
SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
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 */
......@@ -381,14 +385,6 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
snd_soc_write(codec, WM8974_CLOCK, reg | div);
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:
reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
snd_soc_write(codec, WM8974_CLOCK, reg | div);
......
......@@ -57,17 +57,7 @@
/* Clock divider Id's */
#define WM8974_OPCLKDIV 0
#define WM8974_MCLKDIV 1
#define WM8974_ADCCLK 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)
#define WM8974_BCLKDIV 2
/* PLL Out dividers */
#define WM8974_OPCLKDIV_1 (0 << 4)
......
This diff is collapsed.
/*
* 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)
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
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);
return 0;
}
......@@ -1335,10 +1331,6 @@ static int wm8990_resume(struct platform_device *pdev)
u8 data[2];
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 */
for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
if (i + 1 == WM8990_RESET)
......
This diff is collapsed.
This diff is collapsed.
/*
* 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 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "wm9713.h"
#define WM9713_VERSION "0.15"
struct wm9713_priv {
u32 pll_in; /* PLL input frequency */
};
......@@ -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 */
};
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[] = {
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("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("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv),
SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv),
SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
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 Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
......@@ -133,7 +144,7 @@ SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 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 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),
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 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 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 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("Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
SOC_SINGLE("Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1,
misc_tlv),
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 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 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_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
......@@ -1186,8 +1212,6 @@ static int wm9713_soc_probe(struct platform_device *pdev)
struct snd_soc_codec *codec;
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),
GFP_KERNEL);
if (socdev->card->codec == NULL)
......
......@@ -68,24 +68,77 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec)
int count = 0;
dev_dbg(codec->dev, "Waiting for DC servo...\n");
do {
count++;
msleep(1);
reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
dev_dbg(codec->dev, "DC servo status: %x\n", reg);
} while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
!= WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
dev_dbg(codec->dev, "DC servo: %x\n", reg);
} while (reg & WM8993_DCS_DATAPATH_BUSY);
if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
!= WM8993_DCS_CAL_COMPLETE_MASK)
if (reg & WM8993_DCS_DATAPATH_BUSY)
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
*/
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);
int ret;
......@@ -251,6 +304,47 @@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
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,
struct snd_kcontrol *kcontrol, int event)
{
......@@ -271,14 +365,11 @@ static int hp_event(struct snd_soc_dapm_widget *w,
reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
/* Start the DC servo */
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
0xFFFF,
WM8993_DCS_ENA_CHAN_0 |
WM8993_DCS_ENA_CHAN_1 |
WM8993_DCS_TRIG_STARTUP_1 |
WM8993_DCS_TRIG_STARTUP_0);
wait_for_dc_servo(codec);
/* Smallest supported update interval */
snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
calibrate_dc_servo(codec);
reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
......@@ -286,23 +377,19 @@ static int hp_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1L_OUTP |
WM8993_HPOUT1R_RMV_SHORT |
WM8993_HPOUT1R_DLY |
WM8993_HPOUT1R_OUTP);
snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY |
WM8993_HPOUT1L_RMV_SHORT |
WM8993_HPOUT1R_RMV_SHORT, 0);
snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
0xffff, 0);
snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_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,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0);
snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
WM8993_CP_ENA, 0);
break;
}
......@@ -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("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,
NULL, 0,
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[] = {
{ "Headphone PGA", NULL, "Left Headphone Mux" },
{ "Headphone PGA", NULL, "Right Headphone Mux" },
{ "Headphone PGA", NULL, "CLK_SYS" },
{ "Headphone PGA", NULL, "Headphone Supply" },
{ "HPOUT1L", NULL, "Headphone PGA" },
{ "HPOUT1R", NULL, "Headphone PGA" },
......@@ -753,6 +843,12 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
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)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
......
......@@ -18,6 +18,12 @@ struct snd_soc_codec;
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_routes(struct snd_soc_codec *, int, int);
extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
......
This diff is collapsed.
......@@ -44,6 +44,7 @@ struct davinci_audio_dev {
int sample_rate;
struct clk *clk;
unsigned int codec_fmt;
u8 clk_active;
/* McASP specific data */
int tdm_slots;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment