Merge branch 'topic/asoc' into for-linus

This commit is contained in:
Takashi Iwai 2009-12-04 16:22:41 +01:00
commit baf9226667
154 changed files with 8956 additions and 2445 deletions

View file

@ -51,6 +51,14 @@ struct snd_platform_data {
u32 rx_dma_offset;
enum dma_event_q eventq_no; /* event queue number */
unsigned int codec_fmt;
/*
* Allowing this is more efficient and eliminates left and right swaps
* caused by underruns, but will swap the left and right channels
* when compared to previous behavior.
*/
unsigned enable_channel_combine:1;
unsigned sram_size_playback;
unsigned sram_size_capture;
/* McASP specific fields */
int tdm_slots;

View file

@ -410,6 +410,15 @@ static struct regulator_init_data sdp3430_vpll2 = {
.consumer_supplies = &sdp3430_vdvi_supply,
};
static struct twl4030_codec_audio_data sdp3430_audio = {
.audio_mclk = 26000000,
};
static struct twl4030_codec_data sdp3430_codec = {
.audio_mclk = 26000000,
.audio = &sdp3430_audio,
};
static struct twl4030_platform_data sdp3430_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@ -420,6 +429,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
.madc = &sdp3430_madc_data,
.keypad = &sdp3430_kp_data,
.usb = &sdp3430_usb_data,
.codec = &sdp3430_codec,
.vaux1 = &sdp3430_vaux1,
.vaux2 = &sdp3430_vaux2,

View file

@ -254,6 +254,15 @@ static struct twl4030_usb_data beagle_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
static struct twl4030_codec_audio_data beagle_audio_data = {
.audio_mclk = 26000000,
};
static struct twl4030_codec_data beagle_codec_data = {
.audio_mclk = 26000000,
.audio = &beagle_audio_data,
};
static struct twl4030_platform_data beagle_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@ -261,6 +270,7 @@ static struct twl4030_platform_data beagle_twldata = {
/* platform_data for children goes here */
.usb = &beagle_usb_data,
.gpio = &beagle_gpio_data,
.codec = &beagle_codec_data,
.vmmc1 = &beagle_vmmc1,
.vsim = &beagle_vsim,
.vdac = &beagle_vdac,

View file

@ -194,6 +194,15 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = {
.irq_line = 1,
};
static struct twl4030_codec_audio_data omap3evm_audio_data = {
.audio_mclk = 26000000,
};
static struct twl4030_codec_data omap3evm_codec_data = {
.audio_mclk = 26000000,
.audio = &omap3evm_audio_data,
};
static struct twl4030_platform_data omap3evm_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@ -203,6 +212,7 @@ static struct twl4030_platform_data omap3evm_twldata = {
.madc = &omap3evm_madc_data,
.usb = &omap3evm_usb_data,
.gpio = &omap3evm_gpio_data,
.codec = &omap3evm_codec_data,
};
static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {

View file

@ -281,11 +281,21 @@ static struct twl4030_usb_data omap3pandora_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
static struct twl4030_codec_audio_data omap3pandora_audio_data = {
.audio_mclk = 26000000,
};
static struct twl4030_codec_data omap3pandora_codec_data = {
.audio_mclk = 26000000,
.audio = &omap3pandora_audio_data,
};
static struct twl4030_platform_data omap3pandora_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
.gpio = &omap3pandora_gpio_data,
.usb = &omap3pandora_usb_data,
.codec = &omap3pandora_codec_data,
.vmmc1 = &pandora_vmmc1,
.vmmc2 = &pandora_vmmc2,
.keypad = &pandora_kp_data,

View file

@ -329,6 +329,15 @@ static struct regulator_init_data overo_vmmc1 = {
.consumer_supplies = &overo_vmmc1_supply,
};
static struct twl4030_codec_audio_data overo_audio_data = {
.audio_mclk = 26000000,
};
static struct twl4030_codec_data overo_codec_data = {
.audio_mclk = 26000000,
.audio = &overo_audio_data,
};
/* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
static struct twl4030_platform_data overo_twldata = {
@ -336,6 +345,7 @@ static struct twl4030_platform_data overo_twldata = {
.irq_end = TWL4030_IRQ_END,
.gpio = &overo_gpio_data,
.usb = &overo_usb_data,
.codec = &overo_codec_data,
.vmmc1 = &overo_vmmc1,
};

View file

@ -230,6 +230,15 @@ static struct twl4030_madc_platform_data zoom2_madc_data = {
.irq_line = 1,
};
static struct twl4030_codec_audio_data zoom2_audio_data = {
.audio_mclk = 26000000,
};
static struct twl4030_codec_data zoom2_codec_data = {
.audio_mclk = 26000000,
.audio = &zoom2_audio_data,
};
static struct twl4030_platform_data zoom2_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
@ -240,6 +249,7 @@ static struct twl4030_platform_data zoom2_twldata = {
.usb = &zoom2_usb_data,
.gpio = &zoom2_gpio_data,
.keypad = &zoom2_kp_twl4030_data,
.codec = &zoom2_codec_data,
.vmmc1 = &zoom2_vmmc1,
.vmmc2 = &zoom2_vmmc2,
.vsim = &zoom2_vsim,

View file

@ -48,6 +48,8 @@
#define S3C64XX_PA_IIS1 (0x7F003000)
#define S3C64XX_PA_TIMER (0x7F006000)
#define S3C64XX_PA_IIC0 (0x7F004000)
#define S3C64XX_PA_PCM0 (0x7F009000)
#define S3C64XX_PA_PCM1 (0x7F00A000)
#define S3C64XX_PA_IISV4 (0x7F00D000)
#define S3C64XX_PA_IIC1 (0x7F00F000)

View file

@ -1,45 +1,17 @@
/* arch/arm/mach-s3c2410/include/mach/audio.h
/* arch/arm/plat-s3c/include/plat/audio.h
*
* Copyright (c) 2004-2005 Simtec Electronics
* http://www.simtec.co.uk/products/SWLINUX/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX - Audio platfrom_device info
* Copyright (c) 2009 Samsung Electronics Co. Ltd
* Author: Jaswinder Singh <jassi.brar@samsung.com>
*
* 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_ARCH_AUDIO_H
#define __ASM_ARCH_AUDIO_H __FILE__
/* struct s3c24xx_iis_ops
*
* called from the s3c24xx audio core to deal with the architecture
* or the codec's setup and control.
*
* the pointer to itself is passed through in case the caller wants to
* embed this in an larger structure for easy reference to it's context.
*/
struct s3c24xx_iis_ops {
struct module *owner;
int (*startup)(struct s3c24xx_iis_ops *me);
void (*shutdown)(struct s3c24xx_iis_ops *me);
int (*suspend)(struct s3c24xx_iis_ops *me);
int (*resume)(struct s3c24xx_iis_ops *me);
int (*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
int (*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
int (*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt);
/**
* struct s3c_audio_pdata - common platform data for audio device drivers
* @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
*/
struct s3c_audio_pdata {
int (*cfg_gpio)(struct platform_device *);
};
struct s3c24xx_platdata_iis {
const char *codec_clk;
struct s3c24xx_iis_ops *ops;
int (*match_dev)(struct device *dev);
};
#endif /* __ASM_ARCH_AUDIO_H */

View file

@ -28,6 +28,9 @@ extern struct platform_device s3c64xx_device_iis0;
extern struct platform_device s3c64xx_device_iis1;
extern struct platform_device s3c64xx_device_iisv4;
extern struct platform_device s3c64xx_device_pcm0;
extern struct platform_device s3c64xx_device_pcm1;
extern struct platform_device s3c_device_fb;
extern struct platform_device s3c_device_usb;
extern struct platform_device s3c_device_lcd;

View file

@ -67,6 +67,8 @@
#define S3C2412_IISMOD_BCLK_MASK (3 << 1)
#define S3C2412_IISMOD_8BIT (1 << 0)
#define S3C64XX_IISMOD_CDCLKCON (1 << 12)
#define S3C2412_IISPSR_PSREN (1 << 15)
#define S3C2412_IISFIC_TXFLUSH (1 << 15)

View file

@ -15,9 +15,14 @@
#include <mach/irqs.h>
#include <mach/map.h>
#include <mach/dma.h>
#include <mach/gpio.h>
#include <plat/devs.h>
#include <plat/audio.h>
#include <plat/gpio-bank-d.h>
#include <plat/gpio-bank-e.h>
#include <plat/gpio-cfg.h>
static struct resource s3c64xx_iis0_resource[] = {
[0] = {
@ -66,3 +71,97 @@ struct platform_device s3c64xx_device_iisv4 = {
.resource = s3c64xx_iisv4_resource,
};
EXPORT_SYMBOL(s3c64xx_device_iisv4);
/* PCM Controller platform_devices */
static int s3c64xx_pcm_cfg_gpio(struct platform_device *pdev)
{
switch (pdev->id) {
case 0:
s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_PCM0_SCLK);
s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_PCM0_EXTCLK);
s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_PCM0_FSYNC);
s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_PCM0_SIN);
s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_PCM0_SOUT);
break;
case 1:
s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_PCM1_SCLK);
s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_PCM1_EXTCLK);
s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_PCM1_FSYNC);
s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_PCM1_SIN);
s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_PCM1_SOUT);
break;
default:
printk(KERN_DEBUG "Invalid PCM Controller number!");
return -EINVAL;
}
return 0;
}
static struct resource s3c64xx_pcm0_resource[] = {
[0] = {
.start = S3C64XX_PA_PCM0,
.end = S3C64XX_PA_PCM0 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_PCM0_TX,
.end = DMACH_PCM0_TX,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_PCM0_RX,
.end = DMACH_PCM0_RX,
.flags = IORESOURCE_DMA,
},
};
static struct s3c_audio_pdata s3c_pcm0_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
};
struct platform_device s3c64xx_device_pcm0 = {
.name = "samsung-pcm",
.id = 0,
.num_resources = ARRAY_SIZE(s3c64xx_pcm0_resource),
.resource = s3c64xx_pcm0_resource,
.dev = {
.platform_data = &s3c_pcm0_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_pcm0);
static struct resource s3c64xx_pcm1_resource[] = {
[0] = {
.start = S3C64XX_PA_PCM1,
.end = S3C64XX_PA_PCM1 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_PCM1_TX,
.end = DMACH_PCM1_TX,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_PCM1_RX,
.end = DMACH_PCM1_RX,
.flags = IORESOURCE_DMA,
},
};
static struct s3c_audio_pdata s3c_pcm1_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
};
struct platform_device s3c64xx_device_pcm1 = {
.name = "samsung-pcm",
.id = 1,
.num_resources = ARRAY_SIZE(s3c64xx_pcm1_resource),
.resource = s3c64xx_pcm1_resource,
.dev = {
.platform_data = &s3c_pcm1_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_pcm1);

View file

@ -313,6 +313,9 @@ static struct platform_device fsi_device = {
.dev = {
.platform_data = &fsi_info,
},
.archdata = {
.hwblk_id = HWBLK_SPU, /* FSI needs SPU hwblk */
},
};
/* KEYSC in SoC (Needs SW33-2 set to ON) */

View file

@ -121,6 +121,12 @@ config TWL4030_POWER
and load scripts controling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs.
config TWL4030_CODEC
bool
depends on TWL4030_CORE
select MFD_CORE
default n
config MFD_TMIO
bool
default n

View file

@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o

276
drivers/mfd/twl4030-codec.c Normal file
View file

@ -0,0 +1,276 @@
/*
* MFD driver for twl4030 codec submodule
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl4030-codec.h>
#define TWL4030_CODEC_CELLS 2
static struct platform_device *twl4030_codec_dev;
struct twl4030_codec_resource {
int request_count;
u8 reg;
u8 mask;
};
struct twl4030_codec {
unsigned int audio_mclk;
struct mutex mutex;
struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
struct mfd_cell cells[TWL4030_CODEC_CELLS];
};
/*
* Modify the resource, the function returns the content of the register
* after the modification.
*/
static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
u8 val;
twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
codec->resource[id].reg);
if (enable)
val |= codec->resource[id].mask;
else
val &= ~codec->resource[id].mask;
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
val, codec->resource[id].reg);
return val;
}
static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
u8 val;
twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
codec->resource[id].reg);
return val;
}
/*
* Enable the resource.
* The function returns with error or the content of the register
*/
int twl4030_codec_enable_resource(enum twl4030_codec_res id)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
int val;
if (id >= TWL4030_CODEC_RES_MAX) {
dev_err(&twl4030_codec_dev->dev,
"Invalid resource ID (%u)\n", id);
return -EINVAL;
}
mutex_lock(&codec->mutex);
if (!codec->resource[id].request_count)
/* Resource was disabled, enable it */
val = twl4030_codec_set_resource(id, 1);
else
val = twl4030_codec_get_resource(id);
codec->resource[id].request_count++;
mutex_unlock(&codec->mutex);
return val;
}
EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
/*
* Disable the resource.
* The function returns with error or the content of the register
*/
int twl4030_codec_disable_resource(unsigned id)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
int val;
if (id >= TWL4030_CODEC_RES_MAX) {
dev_err(&twl4030_codec_dev->dev,
"Invalid resource ID (%u)\n", id);
return -EINVAL;
}
mutex_lock(&codec->mutex);
if (!codec->resource[id].request_count) {
dev_err(&twl4030_codec_dev->dev,
"Resource has been disabled already (%u)\n", id);
mutex_unlock(&codec->mutex);
return -EPERM;
}
codec->resource[id].request_count--;
if (!codec->resource[id].request_count)
/* Resource can be disabled now */
val = twl4030_codec_set_resource(id, 0);
else
val = twl4030_codec_get_resource(id);
mutex_unlock(&codec->mutex);
return val;
}
EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
unsigned int twl4030_codec_get_mclk(void)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
return codec->audio_mclk;
}
EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
static int __devinit twl4030_codec_probe(struct platform_device *pdev)
{
struct twl4030_codec *codec;
struct twl4030_codec_data *pdata = pdev->dev.platform_data;
struct mfd_cell *cell = NULL;
int ret, childs = 0;
u8 val;
if (!pdata) {
dev_err(&pdev->dev, "Platform data is missing\n");
return -EINVAL;
}
/* Configure APLL_INFREQ and disable APLL if enabled */
val = 0;
switch (pdata->audio_mclk) {
case 19200000:
val |= TWL4030_APLL_INFREQ_19200KHZ;
break;
case 26000000:
val |= TWL4030_APLL_INFREQ_26000KHZ;
break;
case 38400000:
val |= TWL4030_APLL_INFREQ_38400KHZ;
break;
default:
dev_err(&pdev->dev, "Invalid audio_mclk\n");
return -EINVAL;
}
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
val, TWL4030_REG_APLL_CTL);
codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
if (!codec)
return -ENOMEM;
platform_set_drvdata(pdev, codec);
twl4030_codec_dev = pdev;
mutex_init(&codec->mutex);
codec->audio_mclk = pdata->audio_mclk;
/* Codec power */
codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
/* PLL */
codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
if (pdata->audio) {
cell = &codec->cells[childs];
cell->name = "twl4030_codec_audio";
cell->platform_data = pdata->audio;
cell->data_size = sizeof(*pdata->audio);
childs++;
}
if (pdata->vibra) {
cell = &codec->cells[childs];
cell->name = "twl4030_codec_vibra";
cell->platform_data = pdata->vibra;
cell->data_size = sizeof(*pdata->vibra);
childs++;
}
if (childs)
ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
childs, NULL, 0);
else {
dev_err(&pdev->dev, "No platform data found for childs\n");
ret = -ENODEV;
}
if (!ret)
return 0;
platform_set_drvdata(pdev, NULL);
kfree(codec);
twl4030_codec_dev = NULL;
return ret;
}
static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{
struct twl4030_codec *codec = platform_get_drvdata(pdev);
mfd_remove_devices(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(codec);
twl4030_codec_dev = NULL;
return 0;
}
MODULE_ALIAS("platform:twl4030_codec");
static struct platform_driver twl4030_codec_driver = {
.probe = twl4030_codec_probe,
.remove = __devexit_p(twl4030_codec_remove),
.driver = {
.owner = THIS_MODULE,
.name = "twl4030_codec",
},
};
static int __devinit twl4030_codec_init(void)
{
return platform_driver_register(&twl4030_codec_driver);
}
module_init(twl4030_codec_init);
static void __devexit twl4030_codec_exit(void)
{
platform_driver_unregister(&twl4030_codec_driver);
}
module_exit(twl4030_codec_exit);
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
MODULE_LICENSE("GPL");

View file

@ -114,6 +114,12 @@
#define twl_has_watchdog() false
#endif
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
#define twl_has_codec() true
#else
#define twl_has_codec() false
#endif
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
@ -601,6 +607,14 @@ 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",
pdata->codec, sizeof(*pdata->codec),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_regulator()) {
/*
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
@ -763,7 +777,7 @@ static int twl4030_remove(struct i2c_client *client)
}
/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
static int
static int __init
twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;

View file

@ -401,6 +401,24 @@ struct twl4030_power_data {
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
struct twl4030_codec_audio_data {
unsigned int audio_mclk;
unsigned int ramp_delay_value;
unsigned int hs_extmute:1;
void (*set_hs_extmute)(int mute);
};
struct twl4030_codec_vibra_data {
unsigned int audio_mclk;
unsigned int coexist;
};
struct twl4030_codec_data {
unsigned int audio_mclk;
struct twl4030_codec_audio_data *audio;
struct twl4030_codec_vibra_data *vibra;
};
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_bci_platform_data *bci;
@ -409,6 +427,7 @@ struct twl4030_platform_data {
struct twl4030_keypad_data *keypad;
struct twl4030_usb_data *usb;
struct twl4030_power_data *power;
struct twl4030_codec_data *codec;
/* LDO regulators */
struct regulator_init_data *vdac;

View file

@ -0,0 +1,272 @@
/*
* MFD driver for twl4030 codec submodule
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __TWL4030_CODEC_H__
#define __TWL4030_CODEC_H__
/* Codec registers */
#define TWL4030_REG_CODEC_MODE 0x01
#define TWL4030_REG_OPTION 0x02
#define TWL4030_REG_UNKNOWN 0x03
#define TWL4030_REG_MICBIAS_CTL 0x04
#define TWL4030_REG_ANAMICL 0x05
#define TWL4030_REG_ANAMICR 0x06
#define TWL4030_REG_AVADC_CTL 0x07
#define TWL4030_REG_ADCMICSEL 0x08
#define TWL4030_REG_DIGMIXING 0x09
#define TWL4030_REG_ATXL1PGA 0x0A
#define TWL4030_REG_ATXR1PGA 0x0B
#define TWL4030_REG_AVTXL2PGA 0x0C
#define TWL4030_REG_AVTXR2PGA 0x0D
#define TWL4030_REG_AUDIO_IF 0x0E
#define TWL4030_REG_VOICE_IF 0x0F
#define TWL4030_REG_ARXR1PGA 0x10
#define TWL4030_REG_ARXL1PGA 0x11
#define TWL4030_REG_ARXR2PGA 0x12
#define TWL4030_REG_ARXL2PGA 0x13
#define TWL4030_REG_VRXPGA 0x14
#define TWL4030_REG_VSTPGA 0x15
#define TWL4030_REG_VRX2ARXPGA 0x16
#define TWL4030_REG_AVDAC_CTL 0x17
#define TWL4030_REG_ARX2VTXPGA 0x18
#define TWL4030_REG_ARXL1_APGA_CTL 0x19
#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
#define TWL4030_REG_ATX2ARXPGA 0x1D
#define TWL4030_REG_BT_IF 0x1E
#define TWL4030_REG_BTPGA 0x1F
#define TWL4030_REG_BTSTPGA 0x20
#define TWL4030_REG_EAR_CTL 0x21
#define TWL4030_REG_HS_SEL 0x22
#define TWL4030_REG_HS_GAIN_SET 0x23
#define TWL4030_REG_HS_POPN_SET 0x24
#define TWL4030_REG_PREDL_CTL 0x25
#define TWL4030_REG_PREDR_CTL 0x26
#define TWL4030_REG_PRECKL_CTL 0x27
#define TWL4030_REG_PRECKR_CTL 0x28
#define TWL4030_REG_HFL_CTL 0x29
#define TWL4030_REG_HFR_CTL 0x2A
#define TWL4030_REG_ALC_CTL 0x2B
#define TWL4030_REG_ALC_SET1 0x2C
#define TWL4030_REG_ALC_SET2 0x2D
#define TWL4030_REG_BOOST_CTL 0x2E
#define TWL4030_REG_SOFTVOL_CTL 0x2F
#define TWL4030_REG_DTMF_FREQSEL 0x30
#define TWL4030_REG_DTMF_TONEXT1H 0x31
#define TWL4030_REG_DTMF_TONEXT1L 0x32
#define TWL4030_REG_DTMF_TONEXT2H 0x33
#define TWL4030_REG_DTMF_TONEXT2L 0x34
#define TWL4030_REG_DTMF_TONOFF 0x35
#define TWL4030_REG_DTMF_WANONOFF 0x36
#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
#define TWL4030_REG_APLL_CTL 0x3A
#define TWL4030_REG_DTMF_CTL 0x3B
#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
#define TWL4030_REG_MISC_SET_1 0x3E
#define TWL4030_REG_PCMBTMUX 0x3F
#define TWL4030_REG_RX_PATH_SEL 0x43
#define TWL4030_REG_VDL_APGA_CTL 0x44
#define TWL4030_REG_VIBRA_CTL 0x45
#define TWL4030_REG_VIBRA_SET 0x46
#define TWL4030_REG_VIBRA_PWM_SET 0x47
#define TWL4030_REG_ANAMIC_GAIN 0x48
#define TWL4030_REG_MISC_SET_2 0x49
/* Bitfield Definitions */
/* TWL4030_CODEC_MODE (0x01) Fields */
#define TWL4030_APLL_RATE 0xF0
#define TWL4030_APLL_RATE_8000 0x00
#define TWL4030_APLL_RATE_11025 0x10
#define TWL4030_APLL_RATE_12000 0x20
#define TWL4030_APLL_RATE_16000 0x40
#define TWL4030_APLL_RATE_22050 0x50
#define TWL4030_APLL_RATE_24000 0x60
#define TWL4030_APLL_RATE_32000 0x80
#define TWL4030_APLL_RATE_44100 0x90
#define TWL4030_APLL_RATE_48000 0xA0
#define TWL4030_APLL_RATE_96000 0xE0
#define TWL4030_SEL_16K 0x08
#define TWL4030_CODECPDZ 0x02
#define TWL4030_OPT_MODE 0x01
#define TWL4030_OPTION_1 (1 << 0)
#define TWL4030_OPTION_2 (0 << 0)
/* TWL4030_OPTION (0x02) Fields */
#define TWL4030_ATXL1_EN (1 << 0)
#define TWL4030_ATXR1_EN (1 << 1)
#define TWL4030_ATXL2_VTXL_EN (1 << 2)
#define TWL4030_ATXR2_VTXR_EN (1 << 3)
#define TWL4030_ARXL1_VRX_EN (1 << 4)
#define TWL4030_ARXR1_EN (1 << 5)
#define TWL4030_ARXL2_EN (1 << 6)
#define TWL4030_ARXR2_EN (1 << 7)
/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
#define TWL4030_MICBIAS2_CTL 0x40
#define TWL4030_MICBIAS1_CTL 0x20
#define TWL4030_HSMICBIAS_EN 0x04
#define TWL4030_MICBIAS2_EN 0x02
#define TWL4030_MICBIAS1_EN 0x01
/* ANAMICL (0x05) Fields */
#define TWL4030_CNCL_OFFSET_START 0x80
#define TWL4030_OFFSET_CNCL_SEL 0x60
#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
#define TWL4030_MICAMPL_EN 0x10
#define TWL4030_CKMIC_EN 0x08
#define TWL4030_AUXL_EN 0x04
#define TWL4030_HSMIC_EN 0x02
#define TWL4030_MAINMIC_EN 0x01
/* ANAMICR (0x06) Fields */
#define TWL4030_MICAMPR_EN 0x10
#define TWL4030_AUXR_EN 0x04
#define TWL4030_SUBMIC_EN 0x01
/* AVADC_CTL (0x07) Fields */
#define TWL4030_ADCL_EN 0x08
#define TWL4030_AVADC_CLK_PRIORITY 0x04
#define TWL4030_ADCR_EN 0x02
/* TWL4030_REG_ADCMICSEL (0x08) Fields */
#define TWL4030_DIGMIC1_EN 0x08
#define TWL4030_TX2IN_SEL 0x04
#define TWL4030_DIGMIC0_EN 0x02
#define TWL4030_TX1IN_SEL 0x01
/* AUDIO_IF (0x0E) Fields */
#define TWL4030_AIF_SLAVE_EN 0x80
#define TWL4030_DATA_WIDTH 0x60
#define TWL4030_DATA_WIDTH_16S_16W 0x00
#define TWL4030_DATA_WIDTH_32S_16W 0x40
#define TWL4030_DATA_WIDTH_32S_24W 0x60
#define TWL4030_AIF_FORMAT 0x18
#define TWL4030_AIF_FORMAT_CODEC 0x00
#define TWL4030_AIF_FORMAT_LEFT 0x08
#define TWL4030_AIF_FORMAT_RIGHT 0x10
#define TWL4030_AIF_FORMAT_TDM 0x18
#define TWL4030_AIF_TRI_EN 0x04
#define TWL4030_CLK256FS_EN 0x02
#define TWL4030_AIF_EN 0x01
/* VOICE_IF (0x0F) Fields */
#define TWL4030_VIF_SLAVE_EN 0x80
#define TWL4030_VIF_DIN_EN 0x40
#define TWL4030_VIF_DOUT_EN 0x20
#define TWL4030_VIF_SWAP 0x10
#define TWL4030_VIF_FORMAT 0x08
#define TWL4030_VIF_TRI_EN 0x04
#define TWL4030_VIF_SUB_EN 0x02
#define TWL4030_VIF_EN 0x01
/* EAR_CTL (0x21) */
#define TWL4030_EAR_GAIN 0x30
/* HS_GAIN_SET (0x23) Fields */
#define TWL4030_HSR_GAIN 0x0C
#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
#define TWL4030_HSR_GAIN_0DB 0x08
#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
#define TWL4030_HSL_GAIN 0x03
#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
#define TWL4030_HSL_GAIN_0DB 0x02
#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
/* HS_POPN_SET (0x24) Fields */
#define TWL4030_VMID_EN 0x40
#define TWL4030_EXTMUTE 0x20
#define TWL4030_RAMP_DELAY 0x1C
#define TWL4030_RAMP_DELAY_20MS 0x00
#define TWL4030_RAMP_DELAY_40MS 0x04
#define TWL4030_RAMP_DELAY_81MS 0x08
#define TWL4030_RAMP_DELAY_161MS 0x0C
#define TWL4030_RAMP_DELAY_323MS 0x10
#define TWL4030_RAMP_DELAY_645MS 0x14
#define TWL4030_RAMP_DELAY_1291MS 0x18
#define TWL4030_RAMP_DELAY_2581MS 0x1C
#define TWL4030_RAMP_EN 0x02
/* PREDL_CTL (0x25) */
#define TWL4030_PREDL_GAIN 0x30
/* PREDR_CTL (0x26) */
#define TWL4030_PREDR_GAIN 0x30
/* PRECKL_CTL (0x27) */
#define TWL4030_PRECKL_GAIN 0x30
/* PRECKR_CTL (0x28) */
#define TWL4030_PRECKR_GAIN 0x30
/* HFL_CTL (0x29, 0x2A) Fields */
#define TWL4030_HF_CTL_HB_EN 0x04
#define TWL4030_HF_CTL_LOOP_EN 0x08
#define TWL4030_HF_CTL_RAMP_EN 0x10
#define TWL4030_HF_CTL_REF_EN 0x20
/* APLL_CTL (0x3A) Fields */
#define TWL4030_APLL_EN 0x10
#define TWL4030_APLL_INFREQ 0x0F
#define TWL4030_APLL_INFREQ_19200KHZ 0x05
#define TWL4030_APLL_INFREQ_26000KHZ 0x06
#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
/* REG_MISC_SET_1 (0x3E) Fields */
#define TWL4030_CLK64_EN 0x80
#define TWL4030_SCRAMBLE_EN 0x40
#define TWL4030_FMLOOP_EN 0x20
#define TWL4030_SMOOTH_ANAVOL_EN 0x02
#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
/* VIBRA_CTL (0x45) */
#define TWL4030_VIBRA_EN 0x01
#define TWL4030_VIBRA_DIR 0x02
#define TWL4030_VIBRA_AUDIO_SEL_L1 (0x00 << 2)
#define TWL4030_VIBRA_AUDIO_SEL_R1 (0x01 << 2)
#define TWL4030_VIBRA_AUDIO_SEL_L2 (0x02 << 2)
#define TWL4030_VIBRA_AUDIO_SEL_R2 (0x03 << 2)
#define TWL4030_VIBRA_SEL 0x10
#define TWL4030_VIBRA_DIR_SEL 0x20
/* TWL4030 codec resource IDs */
enum twl4030_codec_res {
TWL4030_CODEC_RES_POWER = 0,
TWL4030_CODEC_RES_APLL,
TWL4030_CODEC_RES_MAX,
};
int twl4030_codec_disable_resource(enum twl4030_codec_res id);
int twl4030_codec_enable_resource(enum twl4030_codec_res id);
unsigned int twl4030_codec_get_mclk(void);
#endif /* End of __TWL4030_CODEC_H__ */

View file

@ -30,6 +30,7 @@ struct snd_pcm_substream;
#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */
#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
#define SND_SOC_DAIFMT_PDM 6 /* Pulse density modulation */
/* left and right justified also known as MSB and LSB respectively */
#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
@ -106,7 +107,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div);
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int pll_id, unsigned int freq_in, unsigned int freq_out);
int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
/* Digital Audio interface formatting */
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@ -114,6 +115,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot);
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
/* Digital Audio Interface mute */
@ -136,8 +141,8 @@ struct snd_soc_dai_ops {
*/
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_dai *dai,
int pll_id, unsigned int freq_in, unsigned int freq_out);
int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
/*
@ -148,6 +153,9 @@ struct snd_soc_dai_ops {
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
int (*set_channel_map)(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*

View file

@ -206,6 +206,12 @@
.get = snd_soc_dapm_get_enum_double, \
.put = snd_soc_dapm_put_enum_double, \
.private_value = (unsigned long)&xenum }
#define SOC_DAPM_ENUM_VIRT(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \
.get = snd_soc_dapm_get_enum_virt, \
.put = snd_soc_dapm_put_enum_virt, \
.private_value = (unsigned long)&xenum }
#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_double, \
@ -260,6 +266,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@ -333,6 +343,10 @@ struct snd_soc_dapm_route {
const char *sink;
const char *control;
const char *source;
/* Note: currently only supported for links where source is a supply */
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
};
/* dapm audio path between two widgets */
@ -349,6 +363,9 @@ struct snd_soc_dapm_path {
u32 connect:1; /* source and sink widgets are connected */
u32 walked:1; /* path has been walked */
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
struct list_head list_source;
struct list_head list_sink;
struct list_head list;

View file

@ -223,15 +223,15 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
#ifdef CONFIG_PM
int snd_soc_suspend_device(struct device *dev);
int snd_soc_resume_device(struct device *dev);
#endif
/* pcm <-> DAI connect */
void snd_soc_free_pcms(struct snd_soc_device *socdev);
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
int snd_soc_init_card(struct snd_soc_device *socdev);
/* Utility functions to get clock rates from various things */
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params);
int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots);
int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
/* set runtime hw params */
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@ -333,6 +333,8 @@ struct snd_soc_jack_gpio {
int debounce_time;
struct snd_soc_jack *jack;
struct work_struct work;
int (*jack_status_check)(void);
};
#endif
@ -413,6 +415,7 @@ struct snd_soc_codec {
unsigned int num_dai;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_codec_root;
struct dentry *debugfs_reg;
struct dentry *debugfs_pop_time;
struct dentry *debugfs_dapm;

View file

@ -0,0 +1,20 @@
/*
* Platform header for Texas Instruments TLV320DAC33 codec driver
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
* 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 __TLV320DAC33_PLAT_H
#define __TLV320DAC33_PLAT_H
struct tlv320dac33_platform_data {
int power_gpio;
};
#endif /* __TLV320DAC33_PLAT_H */

View file

@ -0,0 +1,30 @@
/*
* TPA6130A2 driver platform header
*
* Copyright (C) Nokia Corporation
*
* Written by Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef TPA6130A2_PLAT_H
#define TPA6130A2_PLAT_H
struct tpa6130a2_platform_data {
int power_gpio;
};
#endif

View file

@ -1,4 +1,4 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/

View file

@ -268,7 +268,7 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
ret = snd_soc_dai_set_pll(codec_dai, 0,
ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
clk_get_rate(CODEC_CLK), pll_out);
if (ret < 0) {
pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",

View file

@ -207,7 +207,7 @@ static int __init at91sam9g20ek_init(void)
struct clk *pllb;
int ret;
if (!machine_is_at91sam9g20ek())
if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
return -ENODEV;
/*

View file

@ -2,7 +2,7 @@
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <mano@roarinelk.homelinux.net>
* Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@ -332,6 +332,30 @@ static int au1xpsc_pcm_new(struct snd_card *card,
}
static int au1xpsc_pcm_probe(struct platform_device *pdev)
{
if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX])
return -ENODEV;
return 0;
}
static int au1xpsc_pcm_remove(struct platform_device *pdev)
{
return 0;
}
/* au1xpsc audio platform */
struct snd_soc_platform au1xpsc_soc_platform = {
.name = "au1xpsc-pcm-dbdma",
.probe = au1xpsc_pcm_probe,
.remove = au1xpsc_pcm_remove,
.pcm_ops = &au1xpsc_pcm_ops,
.pcm_new = au1xpsc_pcm_new,
.pcm_free = au1xpsc_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
{
struct resource *r;
int ret;
@ -365,7 +389,9 @@ static int au1xpsc_pcm_probe(struct platform_device *pdev)
}
(au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
return 0;
ret = snd_soc_register_platform(&au1xpsc_soc_platform);
if (!ret)
return ret;
out2:
kfree(au1xpsc_audio_pcmdma[PCM_RX]);
@ -376,10 +402,12 @@ out1:
return ret;
}
static int au1xpsc_pcm_remove(struct platform_device *pdev)
static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
{
int i;
snd_soc_unregister_platform(&au1xpsc_soc_platform);
for (i = 0; i < 2; i++) {
if (au1xpsc_audio_pcmdma[i]) {
au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
@ -391,32 +419,81 @@ static int au1xpsc_pcm_remove(struct platform_device *pdev)
return 0;
}
/* au1xpsc audio platform */
struct snd_soc_platform au1xpsc_soc_platform = {
.name = "au1xpsc-pcm-dbdma",
.probe = au1xpsc_pcm_probe,
.remove = au1xpsc_pcm_remove,
.pcm_ops = &au1xpsc_pcm_ops,
.pcm_new = au1xpsc_pcm_new,
.pcm_free = au1xpsc_pcm_free_dma_buffers,
static struct platform_driver au1xpsc_pcm_driver = {
.driver = {
.name = "au1xpsc-pcm",
.owner = THIS_MODULE,
},
.probe = au1xpsc_pcm_drvprobe,
.remove = __devexit_p(au1xpsc_pcm_drvremove),
};
EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
static int __init au1xpsc_audio_dbdma_init(void)
static int __init au1xpsc_audio_dbdma_load(void)
{
au1xpsc_audio_pcmdma[PCM_TX] = NULL;
au1xpsc_audio_pcmdma[PCM_RX] = NULL;
return snd_soc_register_platform(&au1xpsc_soc_platform);
return platform_driver_register(&au1xpsc_pcm_driver);
}
static void __exit au1xpsc_audio_dbdma_exit(void)
static void __exit au1xpsc_audio_dbdma_unload(void)
{
snd_soc_unregister_platform(&au1xpsc_soc_platform);
platform_driver_unregister(&au1xpsc_pcm_driver);
}
module_init(au1xpsc_audio_dbdma_init);
module_exit(au1xpsc_audio_dbdma_exit);
module_init(au1xpsc_audio_dbdma_load);
module_exit(au1xpsc_audio_dbdma_unload);
struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
{
struct resource *res, *r;
struct platform_device *pd;
int id[2];
int ret;
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!r)
return NULL;
id[0] = r->start;
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!r)
return NULL;
id[1] = r->start;
res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
if (!res)
return NULL;
res[0].start = res[0].end = id[0];
res[1].start = res[1].end = id[1];
res[0].flags = res[1].flags = IORESOURCE_DMA;
pd = platform_device_alloc("au1xpsc-pcm", -1);
if (!pd)
goto out;
pd->resource = res;
pd->num_resources = 2;
ret = platform_device_add(pd);
if (!ret)
return pd;
platform_device_put(pd);
out:
kfree(res);
return NULL;
}
EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
void au1xpsc_pcm_destroy(struct platform_device *dmapd)
{
if (dmapd)
platform_device_unregister(dmapd);
}
EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
MODULE_AUTHOR("Manuel Lauss");

View file

@ -61,7 +61,8 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned short data, retry, tmo;
unsigned short retry, tmo;
unsigned long data;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
@ -74,20 +75,26 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
AC97_CDC(pscdata));
au_sync();
tmo = 2000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
&& --tmo)
udelay(2);
tmo = 20;
do {
udelay(21);
if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
break;
} while (--tmo);
data = au_readl(AC97_CDC(pscdata)) & 0xffff;
data = au_readl(AC97_CDC(pscdata));
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
mutex_unlock(&pscdata->lock);
if (reg != ((data >> 16) & 0x7f))
tmo = 1; /* wrong register, try again */
} while (--retry && !tmo);
return retry ? data : 0xffff;
return retry ? data & 0xffff : 0xffff;
}
/* AC97 controller writes to codec register */
@ -109,10 +116,12 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
AC97_CDC(pscdata));
au_sync();
tmo = 2000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
&& --tmo)
udelay(2);
tmo = 20;
do {
udelay(21);
if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
break;
} while (--tmo);
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
@ -195,7 +204,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned long r, ro, stat;
int chans, stype = SUBSTREAM_TYPE(substream);
int chans, t, stype = SUBSTREAM_TYPE(substream);
chans = params_channels(params);
@ -237,8 +246,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
au_sync();
/* ...wait for it... */
while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
asm volatile ("nop");
t = 100;
while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
msleep(1);
if (!t)
printk(KERN_ERR "PSC-AC97: can't disable!\n");
/* ...write config... */
au_writel(r, AC97_CFG(pscdata));
@ -249,8 +262,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
au_sync();
/* ...and wait for ready bit */
while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
asm volatile ("nop");
t = 100;
while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
msleep(1);
if (!t)
printk(KERN_ERR "PSC-AC97: can't enable!\n");
mutex_unlock(&pscdata->lock);
@ -300,109 +317,12 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
static int au1xpsc_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
int ret;
struct resource *r;
unsigned long sel;
if (au1xpsc_ac97_workdata)
return -EBUSY;
au1xpsc_ac97_workdata =
kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!au1xpsc_ac97_workdata)
return -ENOMEM;
mutex_init(&au1xpsc_ac97_workdata->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
goto out0;
}
ret = -EBUSY;
au1xpsc_ac97_workdata->ioarea =
request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_ac97");
if (!au1xpsc_ac97_workdata->ioarea)
goto out0;
au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
if (!au1xpsc_ac97_workdata->mmio)
goto out1;
/* configuration: max dma trigger threshold, enable ac97 */
au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
PSC_AC97CFG_TT_FIFO8 |
PSC_AC97CFG_DE_ENABLE;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
*/
sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
au_sync();
au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
au_sync();
/* next up: cold reset. Dont check for PSC-ready now since
* there may not be any codec clock yet.
*/
return 0;
out1:
release_resource(au1xpsc_ac97_workdata->ioarea);
kfree(au1xpsc_ac97_workdata->ioarea);
out0:
kfree(au1xpsc_ac97_workdata);
au1xpsc_ac97_workdata = NULL;
return ret;
return au1xpsc_ac97_workdata ? 0 : -ENODEV;
}
static void au1xpsc_ac97_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
/* disable PSC completely */
au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
au_sync();
iounmap(au1xpsc_ac97_workdata->mmio);
release_resource(au1xpsc_ac97_workdata->ioarea);
kfree(au1xpsc_ac97_workdata->ioarea);
kfree(au1xpsc_ac97_workdata);
au1xpsc_ac97_workdata = NULL;
}
static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
{
/* save interesting registers and disable PSC */
au1xpsc_ac97_workdata->pm[0] =
au_readl(PSC_SEL(au1xpsc_ac97_workdata));
au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
au_sync();
return 0;
}
static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
{
/* restore PSC clock config */
au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
PSC_SEL(au1xpsc_ac97_workdata));
au_sync();
/* after this point the ac97 core will cold-reset the codec.
* During cold-reset the PSC is reinitialized and the last
* configuration set up in hw_params() is restored.
*/
return 0;
}
static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
@ -415,8 +335,6 @@ struct snd_soc_dai au1xpsc_ac97_dai = {
.ac97_control = 1,
.probe = au1xpsc_ac97_probe,
.remove = au1xpsc_ac97_remove,
.suspend = au1xpsc_ac97_suspend,
.resume = au1xpsc_ac97_resume,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@ -433,20 +351,165 @@ struct snd_soc_dai au1xpsc_ac97_dai = {
};
EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
static int __init au1xpsc_ac97_init(void)
static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
int ret;
struct resource *r;
unsigned long sel;
struct au1xpsc_audio_data *wd;
if (au1xpsc_ac97_workdata)
return -EBUSY;
wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!wd)
return -ENOMEM;
mutex_init(&wd->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
goto out0;
}
ret = -EBUSY;
wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_ac97");
if (!wd->ioarea)
goto out0;
wd->mmio = ioremap(r->start, 0xffff);
if (!wd->mmio)
goto out1;
/* configuration: max dma trigger threshold, enable ac97 */
wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
PSC_AC97CFG_DE_ENABLE;
/* preserve PSC clock source set up by platform */
sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
au_writel(0, PSC_SEL(wd));
au_sync();
au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
au_sync();
ret = snd_soc_register_dai(&au1xpsc_ac97_dai);
if (ret)
goto out1;
wd->dmapd = au1xpsc_pcm_add(pdev);
if (wd->dmapd) {
platform_set_drvdata(pdev, wd);
au1xpsc_ac97_workdata = wd; /* MDEV */
return 0;
}
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
out1:
release_resource(wd->ioarea);
kfree(wd->ioarea);
out0:
kfree(wd);
return ret;
}
static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
if (wd->dmapd)
au1xpsc_pcm_destroy(wd->dmapd);
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
/* disable PSC completely */
au_writel(0, AC97_CFG(wd));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
iounmap(wd->mmio);
release_resource(wd->ioarea);
kfree(wd->ioarea);
kfree(wd);
au1xpsc_ac97_workdata = NULL; /* MDEV */
return 0;
}
#ifdef CONFIG_PM
static int au1xpsc_ac97_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* save interesting registers and disable PSC */
wd->pm[0] = au_readl(PSC_SEL(wd));
au_writel(0, AC97_CFG(wd));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
return 0;
}
static int au1xpsc_ac97_drvresume(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* restore PSC clock config */
au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
au_sync();
/* after this point the ac97 core will cold-reset the codec.
* During cold-reset the PSC is reinitialized and the last
* configuration set up in hw_params() is restored.
*/
return 0;
}
static struct dev_pm_ops au1xpscac97_pmops = {
.suspend = au1xpsc_ac97_drvsuspend,
.resume = au1xpsc_ac97_drvresume,
};
#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
#else
#define AU1XPSCAC97_PMOPS NULL
#endif
static struct platform_driver au1xpsc_ac97_driver = {
.driver = {
.name = "au1xpsc_ac97",
.owner = THIS_MODULE,
.pm = AU1XPSCAC97_PMOPS,
},
.probe = au1xpsc_ac97_drvprobe,
.remove = __devexit_p(au1xpsc_ac97_drvremove),
};
static int __init au1xpsc_ac97_load(void)
{
au1xpsc_ac97_workdata = NULL;
return snd_soc_register_dai(&au1xpsc_ac97_dai);
return platform_driver_register(&au1xpsc_ac97_driver);
}
static void __exit au1xpsc_ac97_exit(void)
static void __exit au1xpsc_ac97_unload(void)
{
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
platform_driver_unregister(&au1xpsc_ac97_driver);
}
module_init(au1xpsc_ac97_init);
module_exit(au1xpsc_ac97_exit);
module_init(au1xpsc_ac97_load);
module_exit(au1xpsc_ac97_unload);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
MODULE_AUTHOR("Manuel Lauss");

View file

@ -2,7 +2,7 @@
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <mano@roarinelk.homelinux.net>
* Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@ -265,106 +265,12 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
static int au1xpsc_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
struct resource *r;
unsigned long sel;
int ret;
if (au1xpsc_i2s_workdata)
return -EBUSY;
au1xpsc_i2s_workdata =
kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!au1xpsc_i2s_workdata)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
goto out0;
}
ret = -EBUSY;
au1xpsc_i2s_workdata->ioarea =
request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_i2s");
if (!au1xpsc_i2s_workdata->ioarea)
goto out0;
au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
if (!au1xpsc_i2s_workdata->mmio)
goto out1;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
*/
sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
au_sync();
au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
au_sync();
/* preconfigure: set max rx/tx fifo depths */
au1xpsc_i2s_workdata->cfg |=
PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
/* don't wait for I2S core to become ready now; clocks may not
* be running yet; depending on clock input for PSC a wait might
* time out.
*/
return 0;
out1:
release_resource(au1xpsc_i2s_workdata->ioarea);
kfree(au1xpsc_i2s_workdata->ioarea);
out0:
kfree(au1xpsc_i2s_workdata);
au1xpsc_i2s_workdata = NULL;
return ret;
return au1xpsc_i2s_workdata ? 0 : -ENODEV;
}
static void au1xpsc_i2s_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
au_sync();
iounmap(au1xpsc_i2s_workdata->mmio);
release_resource(au1xpsc_i2s_workdata->ioarea);
kfree(au1xpsc_i2s_workdata->ioarea);
kfree(au1xpsc_i2s_workdata);
au1xpsc_i2s_workdata = NULL;
}
static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
{
/* save interesting register and disable PSC */
au1xpsc_i2s_workdata->pm[0] =
au_readl(PSC_SEL(au1xpsc_i2s_workdata));
au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
au_sync();
return 0;
}
static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
{
/* select I2S mode and PSC clock */
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
au_sync();
au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
au_sync();
au_writel(au1xpsc_i2s_workdata->pm[0],
PSC_SEL(au1xpsc_i2s_workdata));
au_sync();
return 0;
}
static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
@ -377,8 +283,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
.name = "au1xpsc_i2s",
.probe = au1xpsc_i2s_probe,
.remove = au1xpsc_i2s_remove,
.suspend = au1xpsc_i2s_suspend,
.resume = au1xpsc_i2s_resume,
.playback = {
.rates = AU1XPSC_I2S_RATES,
.formats = AU1XPSC_I2S_FMTS,
@ -395,20 +299,167 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
};
EXPORT_SYMBOL(au1xpsc_i2s_dai);
static int __init au1xpsc_i2s_init(void)
static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
struct resource *r;
unsigned long sel;
int ret;
struct au1xpsc_audio_data *wd;
if (au1xpsc_i2s_workdata)
return -EBUSY;
wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!wd)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
goto out0;
}
ret = -EBUSY;
wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_i2s");
if (!wd->ioarea)
goto out0;
wd->mmio = ioremap(r->start, 0xffff);
if (!wd->mmio)
goto out1;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
*/
sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
au_writel(0, I2S_CFG(wd));
au_sync();
/* preconfigure: set max rx/tx fifo depths */
wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
/* don't wait for I2S core to become ready now; clocks may not
* be running yet; depending on clock input for PSC a wait might
* time out.
*/
ret = snd_soc_register_dai(&au1xpsc_i2s_dai);
if (ret)
goto out1;
/* finally add the DMA device for this PSC */
wd->dmapd = au1xpsc_pcm_add(pdev);
if (wd->dmapd) {
platform_set_drvdata(pdev, wd);
au1xpsc_i2s_workdata = wd;
return 0;
}
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
out1:
release_resource(wd->ioarea);
kfree(wd->ioarea);
out0:
kfree(wd);
return ret;
}
static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
if (wd->dmapd)
au1xpsc_pcm_destroy(wd->dmapd);
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
au_writel(0, I2S_CFG(wd));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
iounmap(wd->mmio);
release_resource(wd->ioarea);
kfree(wd->ioarea);
kfree(wd);
au1xpsc_i2s_workdata = NULL; /* MDEV */
return 0;
}
#ifdef CONFIG_PM
static int au1xpsc_i2s_drvsuspend(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* save interesting register and disable PSC */
wd->pm[0] = au_readl(PSC_SEL(wd));
au_writel(0, I2S_CFG(wd));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
return 0;
}
static int au1xpsc_i2s_drvresume(struct device *dev)
{
struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
/* select I2S mode and PSC clock */
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
au_sync();
au_writel(0, PSC_SEL(wd));
au_sync();
au_writel(wd->pm[0], PSC_SEL(wd));
au_sync();
return 0;
}
static struct dev_pm_ops au1xpsci2s_pmops = {
.suspend = au1xpsc_i2s_drvsuspend,
.resume = au1xpsc_i2s_drvresume,
};
#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
#else
#define AU1XPSCI2S_PMOPS NULL
#endif
static struct platform_driver au1xpsc_i2s_driver = {
.driver = {
.name = "au1xpsc_i2s",
.owner = THIS_MODULE,
.pm = AU1XPSCI2S_PMOPS,
},
.probe = au1xpsc_i2s_drvprobe,
.remove = __devexit_p(au1xpsc_i2s_drvremove),
};
static int __init au1xpsc_i2s_load(void)
{
au1xpsc_i2s_workdata = NULL;
return snd_soc_register_dai(&au1xpsc_i2s_dai);
return platform_driver_register(&au1xpsc_i2s_driver);
}
static void __exit au1xpsc_i2s_exit(void)
static void __exit au1xpsc_i2s_unload(void)
{
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
platform_driver_unregister(&au1xpsc_i2s_driver);
}
module_init(au1xpsc_i2s_init);
module_exit(au1xpsc_i2s_exit);
module_init(au1xpsc_i2s_load);
module_exit(au1xpsc_i2s_unload);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
MODULE_AUTHOR("Manuel Lauss");

View file

@ -2,7 +2,7 @@
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <mano@roarinelk.homelinux.net>
* Manuel Lauss <manuel.lauss@gmail.com>
*
* 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
@ -21,6 +21,10 @@ extern struct snd_soc_dai au1xpsc_i2s_dai;
extern struct snd_soc_platform au1xpsc_soc_platform;
extern struct snd_ac97_bus_ops soc_ac97_ops;
/* DBDMA helpers */
extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
struct au1xpsc_audio_data {
void __iomem *mmio;
@ -30,6 +34,7 @@ struct au1xpsc_audio_data {
unsigned long pm[2];
struct resource *ioarea;
struct mutex lock;
struct platform_device *dmapd;
};
#define PCM_TX 0

View file

@ -52,6 +52,7 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@ -65,6 +66,12 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
/* set cpu DAI channel mapping */
ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
channel_map, ARRAY_SIZE(channel_map), channel_map);
if (ret < 0)
return ret;
return 0;
}

View file

@ -61,6 +61,7 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@ -75,7 +76,13 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
return ret;
/* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
return ret;
/* set cpu DAI channel mapping */
ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
channel_map, ARRAY_SIZE(channel_map), channel_map);
if (ret < 0)
return ret;

View file

@ -49,7 +49,6 @@ struct bf5xx_i2s_port {
u16 rcr1;
u16 tcr2;
u16 rcr2;
int counter;
int configured;
};
@ -133,16 +132,6 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return ret;
}
static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
/*this counter is used for counting how many pcm streams are opened*/
bf5xx_i2s.counter++;
return 0;
}
static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -201,9 +190,8 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
bf5xx_i2s.counter--;
/* No active stream, SPORT is allowed to be configured again. */
if (!bf5xx_i2s.counter)
if (!dai->active)
bf5xx_i2s.configured = 0;
}
@ -284,7 +272,6 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.startup = bf5xx_i2s_startup,
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,

View file

@ -43,7 +43,7 @@
#include "bf5xx-tdm.h"
#include "bf5xx-sport.h"
#define PCM_BUFFER_MAX 0x10000
#define PCM_BUFFER_MAX 0x8000
#define FRAGMENT_SIZE_MIN (4*1024)
#define FRAGMENTS_MIN 2
#define FRAGMENTS_MAX 32
@ -177,6 +177,9 @@ out:
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
struct bf5xx_tdm_port *tdm_port = sport->private_data;
unsigned int *src;
unsigned int *dst;
int i;
@ -188,7 +191,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
dst += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
*(dst + i) = *src++;
*(dst + tdm_port->tx_map[i]) = *src++;
dst += 8;
}
} else {
@ -198,7 +201,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
src += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
*dst++ = *(src+i);
*dst++ = *(src + tdm_port->rx_map[i]);
src += 8;
}
}

View file

@ -46,14 +46,6 @@
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
struct bf5xx_tdm_port {
u16 tcr1;
u16 rcr1;
u16 tcr2;
u16 rcr2;
int configured;
};
static struct bf5xx_tdm_port bf5xx_tdm;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
@ -181,6 +173,40 @@ static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
bf5xx_tdm.configured = 0;
}
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
int i;
unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0;
if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
(rx_num > BFIN_TDM_DAI_MAX_SLOTS))
return -EINVAL;
for (i = 0; i < tx_num; i++) {
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
bf5xx_tdm.tx_map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
}
for (i = 0; i < rx_num; i++) {
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
bf5xx_tdm.rx_map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_PM
static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
@ -235,6 +261,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
.hw_params = bf5xx_tdm_hw_params,
.set_fmt = bf5xx_tdm_set_dai_fmt,
.shutdown = bf5xx_tdm_shutdown,
.set_channel_map = bf5xx_tdm_set_channel_map,
};
struct snd_soc_dai bf5xx_tdm_dai = {
@ -300,6 +327,8 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:

View file

@ -9,6 +9,17 @@
#ifndef _BF5XX_TDM_H
#define _BF5XX_TDM_H
#define BFIN_TDM_DAI_MAX_SLOTS 8
struct bf5xx_tdm_port {
u16 tcr1;
u16 rcr1;
u16 tcr2;
u16 rcr2;
unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
int configured;
};
extern struct snd_soc_dai bf5xx_tdm_dai;
#endif

View file

@ -15,10 +15,12 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD1938 if SPI_MASTER
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_ADS117X
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
@ -28,6 +30,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
select SND_SOC_TLV320AIC3X if I2C
select SND_SOC_TPA6130A2 if I2C
select SND_SOC_TLV320DAC33 if I2C
select SND_SOC_TWL4030 if TWL4030_CORE
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
@ -36,6 +40,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8523 if I2C
select SND_SOC_WM8580 if I2C
select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8727
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
@ -86,6 +92,9 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
config SND_SOC_ADS117X
tristate
config SND_SOC_AK4104
tristate
@ -96,6 +105,9 @@ config SND_SOC_AK4535
config SND_SOC_AK4642
tristate
config SND_SOC_AK4671
tristate
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
@ -136,7 +148,11 @@ config SND_SOC_TLV320AIC26
config SND_SOC_TLV320AIC3X
tristate
config SND_SOC_TLV320DAC33
tristate
config SND_SOC_TWL4030
select TWL4030_CODEC
tristate
config SND_SOC_UDA134X
@ -160,6 +176,12 @@ config SND_SOC_WM8523
config SND_SOC_WM8580
tristate
config SND_SOC_WM8711
tristate
config SND_SOC_WM8727
tristate
config SND_SOC_WM8728
tristate
@ -220,3 +242,6 @@ config SND_SOC_WM9713
# Amp
config SND_SOC_MAX9877
tristate
config SND_SOC_TPA6130A2
tristate

View file

@ -3,9 +3,11 @@ snd-soc-ad1836-objs := ad1836.o
snd-soc-ad1938-objs := ad1938.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
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-l3-objs := l3.o
@ -16,6 +18,7 @@ snd-soc-stac9766-objs := stac9766.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-tlv320dac33-objs := tlv320dac33.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
@ -24,6 +27,8 @@ snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8523-objs := wm8523.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8711-objs := wm8711.o
snd-soc-wm8727-objs := wm8727.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
@ -47,15 +52,18 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
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_L3) += snd-soc-l3.o
@ -66,6 +74,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
@ -74,6 +83,8 @@ obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
@ -97,3 +108,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

View file

@ -117,9 +117,6 @@ static int ac97_soc_probe(struct platform_device *pdev)
if (ret < 0)
goto bus_err;
ret = snd_soc_init_card(socdev);
if (ret < 0)
goto bus_err;
return 0;
bus_err:

View file

@ -385,19 +385,7 @@ static int ad1836_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
ARRAY_SIZE(ad1836_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}

View file

@ -592,21 +592,9 @@ static int ad1938_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
ARRAY_SIZE(ad1938_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}

View file

@ -257,11 +257,6 @@ static int ad1980_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
ARRAY_SIZE(ad1980_snd_ac97_controls));
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register card\n");
goto reset_err;
}
return 0;

View file

@ -64,16 +64,8 @@ static int ad73311_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ad73311: failed to register card\n");
goto register_err;
}
return ret;
register_err:
snd_soc_free_pcms(socdev);
pcm_err:
kfree(socdev->card->codec);
socdev->card->codec = NULL;

123
sound/soc/codecs/ads117x.c Normal file
View file

@ -0,0 +1,123 @@
/*
* ads117x.c -- Driver for ads1174/8 ADC chips
*
* Copyright 2009 ShotSpotter Inc.
* Author: Graeme Gregory <gg@slimlogic.co.uk>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "ads117x.h"
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
struct snd_soc_dai ads117x_dai = {
/* ADC */
.name = "ADS117X ADC",
.id = 1,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 32,
.rates = ADS117X_RATES,
.formats = ADS117X_FORMATS,},
};
EXPORT_SYMBOL_GPL(ads117x_dai);
static int ads117x_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "ADS117X";
codec->owner = THIS_MODULE;
codec->dai = &ads117x_dai;
codec->num_dai = 1;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "ads117x: failed to create pcms\n");
kfree(codec);
return ret;
}
return 0;
}
static int ads117x_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
snd_soc_free_pcms(socdev);
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_ads117x = {
.probe = ads117x_probe,
.remove = ads117x_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ads117x);
static __devinit int ads117x_platform_probe(struct platform_device *pdev)
{
ads117x_dai.dev = &pdev->dev;
return snd_soc_register_dai(&ads117x_dai);
}
static int __devexit ads117x_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&ads117x_dai);
return 0;
}
static struct platform_driver ads117x_codec_driver = {
.driver = {
.name = "ads117x",
.owner = THIS_MODULE,
},
.probe = ads117x_platform_probe,
.remove = __devexit_p(ads117x_platform_remove),
};
static int __init ads117x_init(void)
{
return platform_driver_register(&ads117x_codec_driver);
}
module_init(ads117x_init);
static void __exit ads117x_exit(void)
{
platform_driver_unregister(&ads117x_codec_driver);
}
module_exit(ads117x_exit);
MODULE_DESCRIPTION("ASoC ads117x driver");
MODULE_AUTHOR("Graeme Gregory");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,13 @@
/*
* ads117x.h -- Driver for ads1174/8 ADC chips
*
* Copyright 2009 ShotSpotter Inc.
* Author: Graeme Gregory <gg@slimlogic.co.uk>
*
* 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.
*/
extern struct snd_soc_dai ads117x_dai;
extern struct snd_soc_codec_device soc_codec_dev_ads117x;

View file

@ -313,14 +313,6 @@ static int ak4104_probe(struct platform_device *pdev)
return ret;
}
/* Register the socdev */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card\n");
snd_soc_free_pcms(socdev);
return ret;
}
return 0;
}

View file

@ -294,7 +294,6 @@ static int ak4535_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -485,17 +484,9 @@ static int ak4535_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
ak4535_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ak4535: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);

View file

@ -442,18 +442,9 @@ static int ak4642_probe(struct platform_device *pdev)
goto pcm_err;
}
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ak4642: failed to register card\n");
goto card_err;
}
dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;

815
sound/soc/codecs/ak4671.c Normal file
View file

@ -0,0 +1,815 @@
/*
* ak4671.c -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ak4671.h"
static struct snd_soc_codec *ak4671_codec;
/* codec private data */
struct ak4671_priv {
struct snd_soc_codec codec;
u8 reg_cache[AK4671_CACHEREGNUM];
};
/* ak4671 register cache & default register settings */
static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
0x00, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */
0xf6, /* AK4671_PLL_MODE_SELECT0 (0x01) */
0x00, /* AK4671_PLL_MODE_SELECT1 (0x02) */
0x02, /* AK4671_FORMAT_SELECT (0x03) */
0x00, /* AK4671_MIC_SIGNAL_SELECT (0x04) */
0x55, /* AK4671_MIC_AMP_GAIN (0x05) */
0x00, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */
0x00, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */
0xb5, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */
0x00, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */
0x00, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */
0x00, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */
0x00, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */
0x00, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */
0x00, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */
0x00, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */
0x00, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */
0x80, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */
0x91, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */
0x91, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */
0xe1, /* AK4671_ALC_REFERENCE_SELECT (0x14) */
0x00, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */
0x00, /* AK4671_ALC_TIMER_SELECT (0x16) */
0x00, /* AK4671_ALC_MODE_CONTROL (0x17) */
0x02, /* AK4671_MODE_CONTROL1 (0x18) */
0x01, /* AK4671_MODE_CONTROL2 (0x19) */
0x18, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */
0x18, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */
0x00, /* AK4671_SIDETONE_A_CONTROL (0x1c) */
0x02, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */
0x00, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */
0x00, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */
0x00, /* AK4671_FIL3_COEFFICIENT2 (0x20) */
0x00, /* AK4671_FIL3_COEFFICIENT3 (0x21) */
0x00, /* AK4671_EQ_COEFFICIENT0 (0x22) */
0x00, /* AK4671_EQ_COEFFICIENT1 (0x23) */
0x00, /* AK4671_EQ_COEFFICIENT2 (0x24) */
0x00, /* AK4671_EQ_COEFFICIENT3 (0x25) */
0x00, /* AK4671_EQ_COEFFICIENT4 (0x26) */
0x00, /* AK4671_EQ_COEFFICIENT5 (0x27) */
0xa9, /* AK4671_FIL1_COEFFICIENT0 (0x28) */
0x1f, /* AK4671_FIL1_COEFFICIENT1 (0x29) */
0xad, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */
0x20, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */
0x00, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */
0x00, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */
0x00, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */
0x00, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */
0x00, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */
0x00, /* this register not used */
0x00, /* AK4671_E1_COEFFICIENT0 (0x32) */
0x00, /* AK4671_E1_COEFFICIENT1 (0x33) */
0x00, /* AK4671_E1_COEFFICIENT2 (0x34) */
0x00, /* AK4671_E1_COEFFICIENT3 (0x35) */
0x00, /* AK4671_E1_COEFFICIENT4 (0x36) */
0x00, /* AK4671_E1_COEFFICIENT5 (0x37) */
0x00, /* AK4671_E2_COEFFICIENT0 (0x38) */
0x00, /* AK4671_E2_COEFFICIENT1 (0x39) */
0x00, /* AK4671_E2_COEFFICIENT2 (0x3a) */
0x00, /* AK4671_E2_COEFFICIENT3 (0x3b) */
0x00, /* AK4671_E2_COEFFICIENT4 (0x3c) */
0x00, /* AK4671_E2_COEFFICIENT5 (0x3d) */
0x00, /* AK4671_E3_COEFFICIENT0 (0x3e) */
0x00, /* AK4671_E3_COEFFICIENT1 (0x3f) */
0x00, /* AK4671_E3_COEFFICIENT2 (0x40) */
0x00, /* AK4671_E3_COEFFICIENT3 (0x41) */
0x00, /* AK4671_E3_COEFFICIENT4 (0x42) */
0x00, /* AK4671_E3_COEFFICIENT5 (0x43) */
0x00, /* AK4671_E4_COEFFICIENT0 (0x44) */
0x00, /* AK4671_E4_COEFFICIENT1 (0x45) */
0x00, /* AK4671_E4_COEFFICIENT2 (0x46) */
0x00, /* AK4671_E4_COEFFICIENT3 (0x47) */
0x00, /* AK4671_E4_COEFFICIENT4 (0x48) */
0x00, /* AK4671_E4_COEFFICIENT5 (0x49) */
0x00, /* AK4671_E5_COEFFICIENT0 (0x4a) */
0x00, /* AK4671_E5_COEFFICIENT1 (0x4b) */
0x00, /* AK4671_E5_COEFFICIENT2 (0x4c) */
0x00, /* AK4671_E5_COEFFICIENT3 (0x4d) */
0x00, /* AK4671_E5_COEFFICIENT4 (0x4e) */
0x00, /* AK4671_E5_COEFFICIENT5 (0x4f) */
0x88, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */
0x88, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */
0x08, /* AK4671_EQ_CONTRO_10KHZ (0x52) */
0x00, /* AK4671_PCM_IF_CONTROL0 (0x53) */
0x00, /* AK4671_PCM_IF_CONTROL1 (0x54) */
0x00, /* AK4671_PCM_IF_CONTROL2 (0x55) */
0x18, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */
0x18, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */
0x00, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */
0x00, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */
0x00, /* AK4671_SAR_ADC_CONTROL (0x5a) */
};
/*
* LOUT1/ROUT1 output volume control:
* from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
*/
static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
/*
* LOUT2/ROUT2 output volume control:
* from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
*/
static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
/*
* LOUT3/ROUT3 output volume control:
* from -6 to 3 dB in 3 dB steps
*/
static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
/*
* Mic amp gain control:
* from -15 to 30 dB in 3 dB steps
* REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
* available
*/
static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
static const struct snd_kcontrol_new ak4671_snd_controls[] = {
/* Common playback gain controls */
SOC_SINGLE_TLV("Line Output1 Playback Volume",
AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
SOC_SINGLE_TLV("Line Output3 Playback Volume",
AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
/* Common capture gain controls */
SOC_DOUBLE_TLV("Mic Amp Capture Volume",
AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
};
/* event handlers */
static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
u8 reg;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
reg |= AK4671_MUTEN;
snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
break;
case SND_SOC_DAPM_PRE_PMD:
reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
reg &= ~AK4671_MUTEN;
snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
break;
}
return 0;
}
/* Output Mixers */
static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
};
static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
};
/* Input MUXs */
static const char *ak4671_lin_mux_texts[] =
{"LIN1", "LIN2", "LIN3", "LIN4"};
static const struct soc_enum ak4671_lin_mux_enum =
SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
ARRAY_SIZE(ak4671_lin_mux_texts),
ak4671_lin_mux_texts);
static const struct snd_kcontrol_new ak4671_lin_mux_control =
SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
static const char *ak4671_rin_mux_texts[] =
{"RIN1", "RIN2", "RIN3", "RIN4"};
static const struct soc_enum ak4671_rin_mux_enum =
SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
ARRAY_SIZE(ak4671_rin_mux_texts),
ak4671_rin_mux_texts);
static const struct snd_kcontrol_new ak4671_rin_mux_control =
SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
/* Inputs */
SND_SOC_DAPM_INPUT("LIN1"),
SND_SOC_DAPM_INPUT("RIN1"),
SND_SOC_DAPM_INPUT("LIN2"),
SND_SOC_DAPM_INPUT("RIN2"),
SND_SOC_DAPM_INPUT("LIN3"),
SND_SOC_DAPM_INPUT("RIN3"),
SND_SOC_DAPM_INPUT("LIN4"),
SND_SOC_DAPM_INPUT("RIN4"),
/* Outputs */
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
SND_SOC_DAPM_OUTPUT("LOUT2"),
SND_SOC_DAPM_OUTPUT("ROUT2"),
SND_SOC_DAPM_OUTPUT("LOUT3"),
SND_SOC_DAPM_OUTPUT("ROUT3"),
/* DAC */
SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
/* ADC */
SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
/* PGA */
SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
/* Output Mixers */
SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
&ak4671_lout1_mixer_controls[0],
ARRAY_SIZE(ak4671_lout1_mixer_controls)),
SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
&ak4671_rout1_mixer_controls[0],
ARRAY_SIZE(ak4671_rout1_mixer_controls)),
SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
0, 0, &ak4671_lout2_mixer_controls[0],
ARRAY_SIZE(ak4671_lout2_mixer_controls),
ak4671_out2_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
1, 0, &ak4671_rout2_mixer_controls[0],
ARRAY_SIZE(ak4671_rout2_mixer_controls),
ak4671_out2_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
&ak4671_lout3_mixer_controls[0],
ARRAY_SIZE(ak4671_lout3_mixer_controls)),
SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
&ak4671_rout3_mixer_controls[0],
ARRAY_SIZE(ak4671_rout3_mixer_controls)),
/* Input MUXs */
SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
&ak4671_lin_mux_control),
SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
&ak4671_rin_mux_control),
/* Mic Power */
SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
/* Supply */
SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
};
static const struct snd_soc_dapm_route intercon[] = {
{"DAC Left", "NULL", "PMPLL"},
{"DAC Right", "NULL", "PMPLL"},
{"ADC Left", "NULL", "PMPLL"},
{"ADC Right", "NULL", "PMPLL"},
/* Outputs */
{"LOUT1", "NULL", "LOUT1 Mixer"},
{"ROUT1", "NULL", "ROUT1 Mixer"},
{"LOUT2", "NULL", "LOUT2 Mix Amp"},
{"ROUT2", "NULL", "ROUT2 Mix Amp"},
{"LOUT3", "NULL", "LOUT3 Mixer"},
{"ROUT3", "NULL", "ROUT3 Mixer"},
{"LOUT1 Mixer", "DACL", "DAC Left"},
{"ROUT1 Mixer", "DACR", "DAC Right"},
{"LOUT2 Mixer", "DACHL", "DAC Left"},
{"ROUT2 Mixer", "DACHR", "DAC Right"},
{"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
{"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
{"LOUT3 Mixer", "DACSL", "DAC Left"},
{"ROUT3 Mixer", "DACSR", "DAC Right"},
/* Inputs */
{"LIN MUX", "LIN1", "LIN1"},
{"LIN MUX", "LIN2", "LIN2"},
{"LIN MUX", "LIN3", "LIN3"},
{"LIN MUX", "LIN4", "LIN4"},
{"RIN MUX", "RIN1", "RIN1"},
{"RIN MUX", "RIN2", "RIN2"},
{"RIN MUX", "RIN3", "RIN3"},
{"RIN MUX", "RIN4", "RIN4"},
{"LIN1", NULL, "Mic Bias"},
{"RIN1", NULL, "Mic Bias"},
{"LIN2", NULL, "Mic Bias"},
{"RIN2", NULL, "Mic Bias"},
{"ADC Left", "NULL", "LIN MUX"},
{"ADC Right", "NULL", "RIN MUX"},
/* Analog Loops */
{"LIN1 Mixing Circuit", "NULL", "LIN1"},
{"RIN1 Mixing Circuit", "NULL", "RIN1"},
{"LIN2 Mixing Circuit", "NULL", "LIN2"},
{"RIN2 Mixing Circuit", "NULL", "RIN2"},
{"LIN3 Mixing Circuit", "NULL", "LIN3"},
{"RIN3 Mixing Circuit", "NULL", "RIN3"},
{"LIN4 Mixing Circuit", "NULL", "LIN4"},
{"RIN4 Mixing Circuit", "NULL", "RIN4"},
{"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
{"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
{"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
{"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
{"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
{"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
{"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
{"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
{"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
{"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
{"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
{"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
{"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
{"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
{"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
{"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
{"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
{"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
{"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
{"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
{"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
{"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
{"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
};
static int ak4671_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
ARRAY_SIZE(ak4671_dapm_widgets));
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
return 0;
}
static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u8 fs;
fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
fs &= ~AK4671_FS;
switch (params_rate(params)) {
case 8000:
fs |= AK4671_FS_8KHZ;
break;
case 12000:
fs |= AK4671_FS_12KHZ;
break;
case 16000:
fs |= AK4671_FS_16KHZ;
break;
case 24000:
fs |= AK4671_FS_24KHZ;
break;
case 11025:
fs |= AK4671_FS_11_025KHZ;
break;
case 22050:
fs |= AK4671_FS_22_05KHZ;
break;
case 32000:
fs |= AK4671_FS_32KHZ;
break;
case 44100:
fs |= AK4671_FS_44_1KHZ;
break;
case 48000:
fs |= AK4671_FS_48KHZ;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
return 0;
}
static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
u8 pll;
pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
pll &= ~AK4671_PLL;
switch (freq) {
case 11289600:
pll |= AK4671_PLL_11_2896MHZ;
break;
case 12000000:
pll |= AK4671_PLL_12MHZ;
break;
case 12288000:
pll |= AK4671_PLL_12_288MHZ;
break;
case 13000000:
pll |= AK4671_PLL_13MHZ;
break;
case 13500000:
pll |= AK4671_PLL_13_5MHZ;
break;
case 19200000:
pll |= AK4671_PLL_19_2MHZ;
break;
case 24000000:
pll |= AK4671_PLL_24MHZ;
break;
case 26000000:
pll |= AK4671_PLL_26MHZ;
break;
case 27000000:
pll |= AK4671_PLL_27MHZ;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
return 0;
}
static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
u8 mode;
u8 format;
/* set master/slave audio interface */
mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
mode |= AK4671_M_S;
break;
case SND_SOC_DAIFMT_CBM_CFS:
mode &= ~(AK4671_M_S);
break;
default:
return -EINVAL;
}
/* interface format */
format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
format &= ~AK4671_DIF;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
format |= AK4671_DIF_I2S_MODE;
break;
case SND_SOC_DAIFMT_LEFT_J:
format |= AK4671_DIF_MSB_MODE;
break;
case SND_SOC_DAIFMT_DSP_A:
format |= AK4671_DIF_DSP_MODE;
format |= AK4671_BCKP;
format |= AK4671_MSBS;
break;
default:
return -EINVAL;
}
/* set mode and format */
snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
return 0;
}
static int ak4671_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u8 reg;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
reg | AK4671_PMVCM);
break;
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
break;
}
codec->bias_level = level;
return 0;
}
#define AK4671_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static struct snd_soc_dai_ops ak4671_dai_ops = {
.hw_params = ak4671_hw_params,
.set_sysclk = ak4671_set_dai_sysclk,
.set_fmt = ak4671_set_dai_fmt,
};
struct snd_soc_dai ak4671_dai = {
.name = "AK4671",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = AK4671_RATES,
.formats = AK4671_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = AK4671_RATES,
.formats = AK4671_FORMATS,},
.ops = &ak4671_dai_ops,
};
EXPORT_SYMBOL_GPL(ak4671_dai);
static int ak4671_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (ak4671_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = ak4671_codec;
codec = ak4671_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
snd_soc_add_controls(codec, ak4671_snd_controls,
ARRAY_SIZE(ak4671_snd_controls));
ak4671_add_widgets(codec);
ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return ret;
pcm_err:
return ret;
}
static int ak4671_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_ak4671 = {
.probe = ak4671_probe,
.remove = ak4671_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
static int ak4671_register(struct ak4671_priv *ak4671,
enum snd_soc_control_type control)
{
int ret;
struct snd_soc_codec *codec = &ak4671->codec;
if (ak4671_codec) {
dev_err(codec->dev, "Another AK4671 is registered\n");
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = ak4671;
codec->name = "AK4671";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = ak4671_set_bias_level;
codec->dai = &ak4671_dai;
codec->num_dai = 1;
codec->reg_cache_size = AK4671_CACHEREGNUM;
codec->reg_cache = &ak4671->reg_cache;
memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
goto err;
}
ak4671_dai.dev = codec->dev;
ak4671_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err;
}
ret = snd_soc_register_dai(&ak4671_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(ak4671);
return ret;
}
static void ak4671_unregister(struct ak4671_priv *ak4671)
{
ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&ak4671_dai);
snd_soc_unregister_codec(&ak4671->codec);
kfree(ak4671);
ak4671_codec = NULL;
}
static int __devinit ak4671_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ak4671_priv *ak4671;
struct snd_soc_codec *codec;
ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
if (ak4671 == NULL)
return -ENOMEM;
codec = &ak4671->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(client, ak4671);
codec->control_data = client;
codec->dev = &client->dev;
return ak4671_register(ak4671, SND_SOC_I2C);
}
static __devexit int ak4671_i2c_remove(struct i2c_client *client)
{
struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
ak4671_unregister(ak4671);
return 0;
}
static const struct i2c_device_id ak4671_i2c_id[] = {
{ "ak4671", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671",
.owner = THIS_MODULE,
},
.probe = ak4671_i2c_probe,
.remove = __devexit_p(ak4671_i2c_remove),
.id_table = ak4671_i2c_id,
};
static int __init ak4671_modinit(void)
{
return i2c_add_driver(&ak4671_i2c_driver);
}
module_init(ak4671_modinit);
static void __exit ak4671_exit(void)
{
i2c_del_driver(&ak4671_i2c_driver);
}
module_exit(ak4671_exit);
MODULE_DESCRIPTION("ASoC AK4671 codec driver");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_LICENSE("GPL");

156
sound/soc/codecs/ak4671.h Normal file
View file

@ -0,0 +1,156 @@
/*
* ak4671.h -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.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 _AK4671_H
#define _AK4671_H
#define AK4671_AD_DA_POWER_MANAGEMENT 0x00
#define AK4671_PLL_MODE_SELECT0 0x01
#define AK4671_PLL_MODE_SELECT1 0x02
#define AK4671_FORMAT_SELECT 0x03
#define AK4671_MIC_SIGNAL_SELECT 0x04
#define AK4671_MIC_AMP_GAIN 0x05
#define AK4671_MIXING_POWER_MANAGEMENT0 0x06
#define AK4671_MIXING_POWER_MANAGEMENT1 0x07
#define AK4671_OUTPUT_VOLUME_CONTROL 0x08
#define AK4671_LOUT1_SIGNAL_SELECT 0x09
#define AK4671_ROUT1_SIGNAL_SELECT 0x0a
#define AK4671_LOUT2_SIGNAL_SELECT 0x0b
#define AK4671_ROUT2_SIGNAL_SELECT 0x0c
#define AK4671_LOUT3_SIGNAL_SELECT 0x0d
#define AK4671_ROUT3_SIGNAL_SELECT 0x0e
#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f
#define AK4671_LOUT2_POWER_MANAGERMENT 0x10
#define AK4671_LOUT3_POWER_MANAGERMENT 0x11
#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12
#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13
#define AK4671_ALC_REFERENCE_SELECT 0x14
#define AK4671_DIGITAL_MIXING_CONTROL 0x15
#define AK4671_ALC_TIMER_SELECT 0x16
#define AK4671_ALC_MODE_CONTROL 0x17
#define AK4671_MODE_CONTROL1 0x18
#define AK4671_MODE_CONTROL2 0x19
#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a
#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b
#define AK4671_SIDETONE_A_CONTROL 0x1c
#define AK4671_DIGITAL_FILTER_SELECT 0x1d
#define AK4671_FIL3_COEFFICIENT0 0x1e
#define AK4671_FIL3_COEFFICIENT1 0x1f
#define AK4671_FIL3_COEFFICIENT2 0x20
#define AK4671_FIL3_COEFFICIENT3 0x21
#define AK4671_EQ_COEFFICIENT0 0x22
#define AK4671_EQ_COEFFICIENT1 0x23
#define AK4671_EQ_COEFFICIENT2 0x24
#define AK4671_EQ_COEFFICIENT3 0x25
#define AK4671_EQ_COEFFICIENT4 0x26
#define AK4671_EQ_COEFFICIENT5 0x27
#define AK4671_FIL1_COEFFICIENT0 0x28
#define AK4671_FIL1_COEFFICIENT1 0x29
#define AK4671_FIL1_COEFFICIENT2 0x2a
#define AK4671_FIL1_COEFFICIENT3 0x2b
#define AK4671_FIL2_COEFFICIENT0 0x2c
#define AK4671_FIL2_COEFFICIENT1 0x2d
#define AK4671_FIL2_COEFFICIENT2 0x2e
#define AK4671_FIL2_COEFFICIENT3 0x2f
#define AK4671_DIGITAL_FILTER_SELECT2 0x30
#define AK4671_E1_COEFFICIENT0 0x32
#define AK4671_E1_COEFFICIENT1 0x33
#define AK4671_E1_COEFFICIENT2 0x34
#define AK4671_E1_COEFFICIENT3 0x35
#define AK4671_E1_COEFFICIENT4 0x36
#define AK4671_E1_COEFFICIENT5 0x37
#define AK4671_E2_COEFFICIENT0 0x38
#define AK4671_E2_COEFFICIENT1 0x39
#define AK4671_E2_COEFFICIENT2 0x3a
#define AK4671_E2_COEFFICIENT3 0x3b
#define AK4671_E2_COEFFICIENT4 0x3c
#define AK4671_E2_COEFFICIENT5 0x3d
#define AK4671_E3_COEFFICIENT0 0x3e
#define AK4671_E3_COEFFICIENT1 0x3f
#define AK4671_E3_COEFFICIENT2 0x40
#define AK4671_E3_COEFFICIENT3 0x41
#define AK4671_E3_COEFFICIENT4 0x42
#define AK4671_E3_COEFFICIENT5 0x43
#define AK4671_E4_COEFFICIENT0 0x44
#define AK4671_E4_COEFFICIENT1 0x45
#define AK4671_E4_COEFFICIENT2 0x46
#define AK4671_E4_COEFFICIENT3 0x47
#define AK4671_E4_COEFFICIENT4 0x48
#define AK4671_E4_COEFFICIENT5 0x49
#define AK4671_E5_COEFFICIENT0 0x4a
#define AK4671_E5_COEFFICIENT1 0x4b
#define AK4671_E5_COEFFICIENT2 0x4c
#define AK4671_E5_COEFFICIENT3 0x4d
#define AK4671_E5_COEFFICIENT4 0x4e
#define AK4671_E5_COEFFICIENT5 0x4f
#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50
#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51
#define AK4671_EQ_CONTRO_10KHZ 0x52
#define AK4671_PCM_IF_CONTROL0 0x53
#define AK4671_PCM_IF_CONTROL1 0x54
#define AK4671_PCM_IF_CONTROL2 0x55
#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56
#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57
#define AK4671_SIDETONE_VOLUME_CONTROL 0x58
#define AK4671_DIGITAL_MIXING_CONTROL2 0x59
#define AK4671_SAR_ADC_CONTROL 0x5a
#define AK4671_CACHEREGNUM (AK4671_SAR_ADC_CONTROL + 1)
/* Bitfield Definitions */
/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
#define AK4671_PMVCM 0x01
/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
#define AK4671_PLL 0x0f
#define AK4671_PLL_11_2896MHZ (4 << 0)
#define AK4671_PLL_12_288MHZ (5 << 0)
#define AK4671_PLL_12MHZ (6 << 0)
#define AK4671_PLL_24MHZ (7 << 0)
#define AK4671_PLL_19_2MHZ (8 << 0)
#define AK4671_PLL_13_5MHZ (12 << 0)
#define AK4671_PLL_27MHZ (13 << 0)
#define AK4671_PLL_13MHZ (14 << 0)
#define AK4671_PLL_26MHZ (15 << 0)
#define AK4671_FS 0xf0
#define AK4671_FS_8KHZ (0 << 4)
#define AK4671_FS_12KHZ (1 << 4)
#define AK4671_FS_16KHZ (2 << 4)
#define AK4671_FS_24KHZ (3 << 4)
#define AK4671_FS_11_025KHZ (5 << 4)
#define AK4671_FS_22_05KHZ (7 << 4)
#define AK4671_FS_32KHZ (10 << 4)
#define AK4671_FS_48KHZ (11 << 4)
#define AK4671_FS_44_1KHZ (15 << 4)
/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
#define AK4671_PMPLL 0x01
#define AK4671_M_S 0x02
/* AK4671_FORMAT_SELECT (0x03) Fields */
#define AK4671_DIF 0x03
#define AK4671_DIF_DSP_MODE (0 << 0)
#define AK4671_DIF_MSB_MODE (2 << 0)
#define AK4671_DIF_I2S_MODE (3 << 0)
#define AK4671_BCKP 0x04
#define AK4671_MSBS 0x08
#define AK4671_SDOD 0x10
/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
#define AK4671_MUTEN 0x04
extern struct snd_soc_dai ak4671_dai;
extern struct snd_soc_codec_device soc_codec_dev_ak4671;
#endif

View file

@ -520,6 +520,7 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
@ -598,13 +599,6 @@ static int cs4270_probe(struct platform_device *pdev)
goto error_free_pcms;
}
/* And finally, register the socdev */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card\n");
goto error_free_pcms;
}
return 0;
error_free_pcms:
@ -802,22 +796,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
* and all registers are written back to the hardware when resuming.
*/
static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(client);
struct snd_soc_codec *codec = &cs4270->codec;
return snd_soc_suspend_device(codec->dev);
}
static int cs4270_i2c_resume(struct i2c_client *client)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(client);
struct snd_soc_codec *codec = &cs4270->codec;
return snd_soc_resume_device(codec->dev);
}
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct snd_soc_codec *codec = cs4270_codec;
@ -853,8 +831,6 @@ static int cs4270_soc_resume(struct platform_device *pdev)
return snd_soc_write(codec, CS4270_PWRCTL, reg);
}
#else
#define cs4270_i2c_suspend NULL
#define cs4270_i2c_resume NULL
#define cs4270_soc_suspend NULL
#define cs4270_soc_resume NULL
#endif /* CONFIG_PM */
@ -873,8 +849,6 @@ static struct i2c_driver cs4270_i2c_driver = {
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
.suspend = cs4270_i2c_suspend,
.resume = cs4270_i2c_resume,
};
/*

View file

@ -93,7 +93,6 @@ static int cx20442_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, cx20442_audio_map,
ARRAY_SIZE(cx20442_audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -355,17 +354,6 @@ static int cx20442_codec_probe(struct platform_device *pdev)
cx20442_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}

View file

@ -90,13 +90,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
/* Register Card. */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "pcm3008: failed to register card\n");
goto card_err;
}
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
* Low High De-emphasis OFF
@ -136,8 +129,6 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
gpio_err:
pcm3008_gpio_free(setup);
card_err:
snd_soc_free_pcms(socdev);
pcm_err:
kfree(socdev->card->codec);

View file

@ -210,7 +210,6 @@ static int ssm2602_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -613,17 +612,9 @@ static int ssm2602_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
ssm2602_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
pr_err("ssm2602: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;

View file

@ -418,9 +418,6 @@ static int stac9766_codec_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
ARRAY_SIZE(stac9766_snd_ac97_controls));
ret = snd_soc_init_card(socdev);
if (ret < 0)
goto reset_err;
return 0;
reset_err:

View file

@ -395,7 +395,6 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
/* set up audio path interconnects */
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -706,17 +705,9 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls));
tlv320aic23_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "tlv320aic23: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;

View file

@ -356,18 +356,7 @@ static int aic26_probe(struct platform_device *pdev)
ARRAY_SIZE(aic26_snd_controls));
WARN_ON(err < 0);
/* CODEC is setup, we can register the card now */
dev_dbg(&pdev->dev, "Registering card\n");
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "aic26: failed to register card\n");
goto card_err;
}
return 0;
card_err:
snd_soc_free_pcms(socdev);
return ret;
}
static int aic26_remove(struct platform_device *pdev)

View file

@ -753,7 +753,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
/* set up audio path interconnects */
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -1405,18 +1404,8 @@ static int aic3x_probe(struct platform_device *pdev)
aic3x_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "aic3x: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,267 @@
/*
* ALSA SoC Texas Instruments TLV320DAC33 codec driver
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __TLV320DAC33_H
#define __TLV320DAC33_H
#define DAC33_PAGE_SELECT 0x00
#define DAC33_PWR_CTRL 0x01
#define DAC33_PLL_CTRL_A 0x02
#define DAC33_PLL_CTRL_B 0x03
#define DAC33_PLL_CTRL_C 0x04
#define DAC33_PLL_CTRL_D 0x05
#define DAC33_PLL_CTRL_E 0x06
#define DAC33_INT_OSC_CTRL 0x07
#define DAC33_INT_OSC_FREQ_RAT_A 0x08
#define DAC33_INT_OSC_FREQ_RAT_B 0x09
#define DAC33_INT_OSC_DAC_RATIO_SET 0x0A
#define DAC33_CALIB_TIME 0x0B
#define DAC33_INT_OSC_CTRL_B 0x0C
#define DAC33_INT_OSC_CTRL_C 0x0D
#define DAC33_INT_OSC_STATUS 0x0E
#define DAC33_INT_OSC_DAC_RATIO_READ 0x0F
#define DAC33_INT_OSC_FREQ_RAT_READ_A 0x10
#define DAC33_INT_OSC_FREQ_RAT_READ_B 0x11
#define DAC33_SER_AUDIOIF_CTRL_A 0x12
#define DAC33_SER_AUDIOIF_CTRL_B 0x13
#define DAC33_SER_AUDIOIF_CTRL_C 0x14
#define DAC33_FIFO_CTRL_A 0x15
#define DAC33_UTHR_MSB 0x16
#define DAC33_UTHR_LSB 0x17
#define DAC33_ATHR_MSB 0x18
#define DAC33_ATHR_LSB 0x19
#define DAC33_LTHR_MSB 0x1A
#define DAC33_LTHR_LSB 0x1B
#define DAC33_PREFILL_MSB 0x1C
#define DAC33_PREFILL_LSB 0x1D
#define DAC33_NSAMPLE_MSB 0x1E
#define DAC33_NSAMPLE_LSB 0x1F
#define DAC33_FIFO_WPTR_MSB 0x20
#define DAC33_FIFO_WPTR_LSB 0x21
#define DAC33_FIFO_RPTR_MSB 0x22
#define DAC33_FIFO_RPTR_LSB 0x23
#define DAC33_FIFO_DEPTH_MSB 0x24
#define DAC33_FIFO_DEPTH_LSB 0x25
#define DAC33_SAMPLES_REMAINING_MSB 0x26
#define DAC33_SAMPLES_REMAINING_LSB 0x27
#define DAC33_FIFO_IRQ_FLAG 0x28
#define DAC33_FIFO_IRQ_MASK 0x29
#define DAC33_FIFO_IRQ_MODE_A 0x2A
#define DAC33_FIFO_IRQ_MODE_B 0x2B
#define DAC33_DAC_CTRL_A 0x2C
#define DAC33_DAC_CTRL_B 0x2D
#define DAC33_DAC_CTRL_C 0x2E
#define DAC33_LDAC_DIG_VOL_CTRL 0x2F
#define DAC33_RDAC_DIG_VOL_CTRL 0x30
#define DAC33_DAC_STATUS_FLAGS 0x31
#define DAC33_ASRC_CTRL_A 0x32
#define DAC33_ASRC_CTRL_B 0x33
#define DAC33_SRC_REF_CLK_RATIO_A 0x34
#define DAC33_SRC_REF_CLK_RATIO_B 0x35
#define DAC33_SRC_EST_REF_CLK_RATIO_A 0x36
#define DAC33_SRC_EST_REF_CLK_RATIO_B 0x37
#define DAC33_INTP_CTRL_A 0x38
#define DAC33_INTP_CTRL_B 0x39
/* Registers 0x3A - 0x3F Reserved */
#define DAC33_LDAC_PWR_CTRL 0x40
#define DAC33_RDAC_PWR_CTRL 0x41
#define DAC33_OUT_AMP_CM_CTRL 0x42
#define DAC33_OUT_AMP_PWR_CTRL 0x43
#define DAC33_OUT_AMP_CTRL 0x44
#define DAC33_LINEL_TO_LLO_VOL 0x45
/* Registers 0x45 - 0x47 Reserved */
#define DAC33_LINER_TO_RLO_VOL 0x48
#define DAC33_ANA_VOL_SOFT_STEP_CTRL 0x49
#define DAC33_OSC_TRIM 0x4A
/* Registers 0x4B - 0x7C Reserved */
#define DAC33_DEVICE_ID_MSB 0x7D
#define DAC33_DEVICE_ID_LSB 0x7E
#define DAC33_DEVICE_REV_ID 0x7F
#define DAC33_CACHEREGNUM 128
/* Bit definitions */
/* DAC33_PWR_CTRL (0x01) */
#define DAC33_DACRPDNB (0x01 << 0)
#define DAC33_DACLPDNB (0x01 << 1)
#define DAC33_OSCPDNB (0x01 << 2)
#define DAC33_PLLPDNB (0x01 << 3)
#define DAC33_PDNALLB (0x01 << 4)
#define DAC33_SOFT_RESET (0x01 << 7)
/* DAC33_INT_OSC_CTRL (0x07) */
#define DAC33_REFSEL (0x01 << 1)
/* DAC33_INT_OSC_CTRL_B (0x0C) */
#define DAC33_ADJSTEP(x) (x << 0)
#define DAC33_ADJTHRSHLD(x) (x << 4)
/* DAC33_INT_OSC_CTRL_C (0x0D) */
#define DAC33_REFDIV(x) (x << 4)
/* DAC33_INT_OSC_STATUS (0x0E) */
#define DAC33_OSCSTATUS_IDLE_CALIB (0x00)
#define DAC33_OSCSTATUS_NORMAL (0x01)
#define DAC33_OSCSTATUS_ADJUSTMENT (0x03)
#define DAC33_OSCSTATUS_NOT_USED (0x02)
/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */
#define DAC33_MSWCLK (0x01 << 0)
#define DAC33_MSBCLK (0x01 << 1)
#define DAC33_AFMT_MASK (0x03 << 2)
#define DAC33_AFMT_I2S (0x00 << 2)
#define DAC33_AFMT_DSP (0x01 << 2)
#define DAC33_AFMT_RIGHT_J (0x02 << 2)
#define DAC33_AFMT_LEFT_J (0x03 << 2)
#define DAC33_WLEN_MASK (0x03 << 4)
#define DAC33_WLEN_16 (0x00 << 4)
#define DAC33_WLEN_20 (0x01 << 4)
#define DAC33_WLEN_24 (0x02 << 4)
#define DAC33_WLEN_32 (0x03 << 4)
#define DAC33_NCYCL_MASK (0x03 << 6)
#define DAC33_NCYCL_16 (0x00 << 6)
#define DAC33_NCYCL_20 (0x01 << 6)
#define DAC33_NCYCL_24 (0x02 << 6)
#define DAC33_NCYCL_32 (0x03 << 6)
/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */
#define DAC33_DATA_DELAY_MASK (0x03 << 2)
#define DAC33_DATA_DELAY(x) (x << 2)
#define DAC33_BCLKON (0x01 << 5)
/* DAC33_FIFO_CTRL_A (0x15) */
#define DAC33_WIDTH (0x01 << 0)
#define DAC33_FBYPAS (0x01 << 1)
#define DAC33_FAUTO (0x01 << 2)
#define DAC33_FIFOFLUSH (0x01 << 3)
/*
* UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F)
* 13-bit values
*/
#define DAC33_THRREG(x) (((x) & 0x1FFF) << 3)
/* DAC33_FIFO_IRQ_MASK (0x29) */
#define DAC33_MNS (0x01 << 0)
#define DAC33_MPS (0x01 << 1)
#define DAC33_MAT (0x01 << 2)
#define DAC33_MLT (0x01 << 3)
#define DAC33_MUT (0x01 << 4)
#define DAC33_MUF (0x01 << 5)
#define DAC33_MOF (0x01 << 6)
#define DAC33_FIFO_IRQ_MODE_MASK (0x03)
#define DAC33_FIFO_IRQ_MODE_RISING (0x00)
#define DAC33_FIFO_IRQ_MODE_FALLING (0x01)
#define DAC33_FIFO_IRQ_MODE_LEVEL (0x02)
#define DAC33_FIFO_IRQ_MODE_EDGE (0x03)
/* DAC33_FIFO_IRQ_MODE_A (0x2A) */
#define DAC33_UTM(x) (x << 0)
#define DAC33_UFM(x) (x << 2)
#define DAC33_OFM(x) (x << 4)
/* DAC33_FIFO_IRQ_MODE_B (0x2B) */
#define DAC33_NSM(x) (x << 0)
#define DAC33_PSM(x) (x << 2)
#define DAC33_ATM(x) (x << 4)
#define DAC33_LTM(x) (x << 6)
/* DAC33_DAC_CTRL_A (0x2C) */
#define DAC33_DACRATE(x) (x << 0)
#define DAC33_DACDUAL (0x01 << 4)
#define DAC33_DACLKSEL_MASK (0x03 << 5)
#define DAC33_DACLKSEL_INTSOC (0x00 << 5)
#define DAC33_DACLKSEL_PLL (0x01 << 5)
#define DAC33_DACLKSEL_MCLK (0x02 << 5)
#define DAC33_DACLKSEL_BCLK (0x03 << 5)
/* DAC33_DAC_CTRL_B (0x2D) */
#define DAC33_DACSRCR_MASK (0x03 << 0)
#define DAC33_DACSRCR_MUTE (0x00 << 0)
#define DAC33_DACSRCR_RIGHT (0x01 << 0)
#define DAC33_DACSRCR_LEFT (0x02 << 0)
#define DAC33_DACSRCR_MONOMIX (0x03 << 0)
#define DAC33_DACSRCL_MASK (0x03 << 2)
#define DAC33_DACSRCL_MUTE (0x00 << 2)
#define DAC33_DACSRCL_LEFT (0x01 << 2)
#define DAC33_DACSRCL_RIGHT (0x02 << 2)
#define DAC33_DACSRCL_MONOMIX (0x03 << 2)
#define DAC33_DVOLSTEP_MASK (0x03 << 4)
#define DAC33_DVOLSTEP_SS_PERFS (0x00 << 4)
#define DAC33_DVOLSTEP_SS_PER2FS (0x01 << 4)
#define DAC33_DVOLSTEP_SS_DISABLED (0x02 << 4)
#define DAC33_DVOLCTRL_MASK (0x03 << 6)
#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6)
#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL (0x01 << 6)
#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6)
#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6)
/* DAC33_DAC_CTRL_C (0x2E) */
#define DAC33_DEEMENR (0x01 << 0)
#define DAC33_EFFENR (0x01 << 1)
#define DAC33_DEEMENL (0x01 << 2)
#define DAC33_EFFENL (0x01 << 3)
#define DAC33_EN3D (0x01 << 4)
#define DAC33_RESYNMUTE (0x01 << 5)
#define DAC33_RESYNEN (0x01 << 6)
/* DAC33_ASRC_CTRL_A (0x32) */
#define DAC33_SRCBYP (0x01 << 0)
#define DAC33_SRCLKSEL_MASK (0x03 << 1)
#define DAC33_SRCLKSEL_INTSOC (0x00 << 1)
#define DAC33_SRCLKSEL_PLL (0x01 << 1)
#define DAC33_SRCLKSEL_MCLK (0x02 << 1)
#define DAC33_SRCLKSEL_BCLK (0x03 << 1)
#define DAC33_SRCLKDIV(x) (x << 3)
/* DAC33_ASRC_CTRL_B (0x33) */
#define DAC33_SRCSETUP(x) (x << 0)
#define DAC33_SRCREFSEL (0x01 << 4)
#define DAC33_SRCREFDIV(x) (x << 5)
/* DAC33_INTP_CTRL_A (0x38) */
#define DAC33_INTPSEL (0x01 << 0)
#define DAC33_INTPM_MASK (0x03 << 1)
#define DAC33_INTPM_ALOW_OPENDRAIN (0x00 << 1)
#define DAC33_INTPM_ALOW (0x01 << 1)
#define DAC33_INTPM_AHIGH (0x02 << 1)
/* DAC33_LDAC_PWR_CTRL (0x40) */
/* DAC33_RDAC_PWR_CTRL (0x41) */
#define DAC33_DACLRNUM (0x01 << 2)
#define DAC33_LROUT_GAIN(x) (x << 0)
/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */
#define DAC33_VOLCLKSEL (0x01 << 0)
#define DAC33_VOLCLKEN (0x01 << 1)
#define DAC33_VOLBYPASS (0x01 << 2)
#define TLV320DAC33_MCLK 0
#define TLV320DAC33_SLEEPCLK 1
extern struct snd_soc_dai dac33_dai;
extern struct snd_soc_codec_device soc_codec_dev_tlv320dac33;
#endif /* __TLV320DAC33_H */

View file

@ -0,0 +1,463 @@
/*
* ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
*
* Copyright (C) Nokia Corporation
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <sound/tpa6130a2-plat.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "tpa6130a2.h"
static struct i2c_client *tpa6130a2_client;
/* This struct is used to save the context */
struct tpa6130a2_data {
struct mutex mutex;
unsigned char regs[TPA6130A2_CACHEREGNUM];
int power_gpio;
unsigned char power_state;
};
static int tpa6130a2_i2c_read(int reg)
{
struct tpa6130a2_data *data;
int val;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
/* If powered off, return the cached value */
if (data->power_state) {
val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
if (val < 0)
dev_err(&tpa6130a2_client->dev, "Read failed\n");
else
data->regs[reg] = val;
} else {
val = data->regs[reg];
}
return val;
}
static int tpa6130a2_i2c_write(int reg, u8 value)
{
struct tpa6130a2_data *data;
int val = 0;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
if (data->power_state) {
val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
if (val < 0)
dev_err(&tpa6130a2_client->dev, "Write failed\n");
}
/* Either powered on or off, we save the context */
data->regs[reg] = value;
return val;
}
static u8 tpa6130a2_read(int reg)
{
struct tpa6130a2_data *data;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
return data->regs[reg];
}
static void tpa6130a2_initialize(void)
{
struct tpa6130a2_data *data;
int i;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
for (i = 1; i < TPA6130A2_REG_VERSION; i++)
tpa6130a2_i2c_write(i, data->regs[i]);
}
static void tpa6130a2_power(int power)
{
struct tpa6130a2_data *data;
u8 val;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
mutex_lock(&data->mutex);
if (power) {
/* Power on */
if (data->power_gpio >= 0) {
gpio_set_value(data->power_gpio, 1);
data->power_state = 1;
tpa6130a2_initialize();
}
/* Clear SWS */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val &= ~TPA6130A2_SWS;
tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
} else {
/* set SWS */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val |= TPA6130A2_SWS;
tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
/* Power off */
if (data->power_gpio >= 0) {
gpio_set_value(data->power_gpio, 0);
data->power_state = 0;
}
}
mutex_unlock(&data->mutex);
}
static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tpa6130a2_data *data;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int mask = mc->max;
unsigned int invert = mc->invert;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
mutex_lock(&data->mutex);
ucontrol->value.integer.value[0] =
(tpa6130a2_read(reg) >> shift) & mask;
if (invert)
ucontrol->value.integer.value[0] =
mask - ucontrol->value.integer.value[0];
mutex_unlock(&data->mutex);
return 0;
}
static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tpa6130a2_data *data;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int mask = mc->max;
unsigned int invert = mc->invert;
unsigned int val = (ucontrol->value.integer.value[0] & mask);
unsigned int val_reg;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
if (invert)
val = mask - val;
mutex_lock(&data->mutex);
val_reg = tpa6130a2_read(reg);
if (((val_reg >> shift) & mask) == val) {
mutex_unlock(&data->mutex);
return 0;
}
val_reg &= ~(mask << shift);
val_reg |= val << shift;
tpa6130a2_i2c_write(reg, val_reg);
mutex_unlock(&data->mutex);
return 1;
}
/*
* TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
* down in gain.
*/
static const unsigned int tpa6130_tlv[] = {
TLV_DB_RANGE_HEAD(10),
0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
};
static const struct snd_kcontrol_new tpa6130a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
tpa6130a2_get_reg, tpa6130a2_set_reg,
tpa6130_tlv),
};
/*
* Enable or disable channel (left or right)
* The bit number for mute and amplifier are the same per channel:
* bit 6: Right channel
* bit 7: Left channel
* in both registers.
*/
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 */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val |= channel;
tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
/* Unmute channel */
val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
val &= ~channel;
tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
} else {
/* Disable channel */
/* Mute channel */
val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
val |= channel;
tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
/* Disable amplifier */
val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
val &= ~channel;
tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
}
}
static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1);
break;
case SND_SOC_DAPM_POST_PMD:
tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0);
break;
}
return 0;
}
static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1);
break;
case SND_SOC_DAPM_POST_PMD:
tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0);
break;
}
return 0;
}
static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_POST_PMU:
tpa6130a2_power(1);
break;
case SND_SOC_DAPM_POST_PMD:
tpa6130a2_power(0);
break;
}
return 0;
}
static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM,
0, 0, NULL, 0, tpa6130a2_left_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM,
0, 0, NULL, 0, tpa6130a2_right_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM,
0, 0, tpa6130a2_supply_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Outputs */
SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL),
SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
{"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"},
{"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"},
{"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"},
{"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"},
};
int tpa6130a2_add_controls(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
ARRAY_SIZE(tpa6130a2_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
return snd_soc_add_controls(codec, tpa6130a2_controls,
ARRAY_SIZE(tpa6130a2_controls));
}
EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
static int 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;
dev = &client->dev;
if (client->dev.platform_data == NULL) {
dev_err(dev, "Platform data not set\n");
dump_stack();
return -ENODEV;
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL) {
dev_err(dev, "Can not allocate memory\n");
return -ENOMEM;
}
tpa6130a2_client = client;
i2c_set_clientdata(tpa6130a2_client, data);
pdata = client->dev.platform_data;
data->power_gpio = pdata->power_gpio;
mutex_init(&data->mutex);
/* Set default register values */
data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS;
data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R |
TPA6130A2_MUTE_L;
if (data->power_gpio >= 0) {
ret = gpio_request(data->power_gpio, "tpa6130a2 enable");
if (ret < 0) {
dev_err(dev, "Failed to request power GPIO (%d)\n",
data->power_gpio);
goto fail;
}
gpio_direction_output(data->power_gpio, 0);
} else {
data->power_state = 1;
tpa6130a2_initialize();
}
tpa6130a2_power(1);
/* Read version */
ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
TPA6130A2_VERSION_MASK;
if ((ret != 1) && (ret != 2))
dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
/* Disable the chip */
tpa6130a2_power(0);
return 0;
fail:
kfree(data);
i2c_set_clientdata(tpa6130a2_client, NULL);
tpa6130a2_client = NULL;
return ret;
}
static int tpa6130a2_remove(struct i2c_client *client)
{
struct tpa6130a2_data *data = i2c_get_clientdata(client);
tpa6130a2_power(0);
if (data->power_gpio >= 0)
gpio_free(data->power_gpio);
kfree(data);
tpa6130a2_client = NULL;
return 0;
}
static const struct i2c_device_id tpa6130a2_id[] = {
{ "tpa6130a2", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
static struct i2c_driver tpa6130a2_i2c_driver = {
.driver = {
.name = "tpa6130a2",
.owner = THIS_MODULE,
},
.probe = tpa6130a2_probe,
.remove = __devexit_p(tpa6130a2_remove),
.id_table = tpa6130a2_id,
};
static int __init tpa6130a2_init(void)
{
return i2c_add_driver(&tpa6130a2_i2c_driver);
}
static void __exit tpa6130a2_exit(void)
{
i2c_del_driver(&tpa6130a2_i2c_driver);
}
MODULE_AUTHOR("Peter Ujfalusi");
MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
MODULE_LICENSE("GPL");
module_init(tpa6130a2_init);
module_exit(tpa6130a2_exit);

View file

@ -0,0 +1,61 @@
/*
* ALSA SoC TPA6130A2 amplifier driver
*
* Copyright (C) Nokia Corporation
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __TPA6130A2_H__
#define __TPA6130A2_H__
/* Register addresses */
#define TPA6130A2_REG_CONTROL 0x01
#define TPA6130A2_REG_VOL_MUTE 0x02
#define TPA6130A2_REG_OUT_IMPEDANCE 0x03
#define TPA6130A2_REG_VERSION 0x04
#define TPA6130A2_CACHEREGNUM (TPA6130A2_REG_VERSION + 1)
/* Register bits */
/* TPA6130A2_REG_CONTROL (0x01) */
#define TPA6130A2_SWS (0x01 << 0)
#define TPA6130A2_TERMAL (0x01 << 1)
#define TPA6130A2_MODE(x) (x << 4)
#define TPA6130A2_MODE_STEREO (0x00)
#define TPA6130A2_MODE_DUAL_MONO (0x01)
#define TPA6130A2_MODE_BRIDGE (0x02)
#define TPA6130A2_MODE_MASK (0x03)
#define TPA6130A2_HP_EN_R (0x01 << 6)
#define TPA6130A2_HP_EN_L (0x01 << 7)
/* TPA6130A2_REG_VOL_MUTE (0x02) */
#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0)
#define TPA6130A2_MUTE_R (0x01 << 6)
#define TPA6130A2_MUTE_L (0x01 << 7)
/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */
#define TPA6130A2_HIZ_R (0x01 << 0)
#define TPA6130A2_HIZ_L (0x01 << 1)
/* TPA6130A2_REG_VERSION (0x04) */
#define TPA6130A2_VERSION_MASK (0x0f)
extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
#endif /* __TPA6130A2_H__ */

View file

@ -120,9 +120,10 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
/* codec private data */
struct twl4030_priv {
unsigned int bypass_state;
struct snd_soc_codec codec;
unsigned int codec_powered;
unsigned int codec_muted;
unsigned int apll_enabled;
struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
@ -183,19 +184,20 @@ static int twl4030_write(struct snd_soc_codec *codec,
static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 mode;
int mode;
if (enable == twl4030->codec_powered)
return;
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
if (enable)
mode |= TWL4030_CODECPDZ;
mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
else
mode &= ~TWL4030_CODECPDZ;
mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030->codec_powered = enable;
if (mode >= 0) {
twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030->codec_powered = enable;
}
/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
@ -212,31 +214,30 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
/* set all audio section registers to reasonable defaults */
for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
twl4030_write(codec, i, cache[i]);
if (i != TWL4030_REG_APLL_CTL)
twl4030_write(codec, i, cache[i]);
}
static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 reg_val;
int status;
if (mute == twl4030->codec_muted)
if (enable == twl4030->apll_enabled)
return;
if (mute) {
/* Disable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val &= ~TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
} else {
if (enable)
/* Enable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val |= TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
}
status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
else
/* Disable PLL */
status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
twl4030->codec_muted = mute;
if (status >= 0)
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
twl4030->apll_enabled = enable;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
@ -613,6 +614,27 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
static int vibramux_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff);
return 0;
}
static int apll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
twl4030_apll_enable(w->codec, 1);
break;
case SND_SOC_DAPM_POST_PMD:
twl4030_apll_enable(w->codec, 0);
break;
}
return 0;
}
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
struct snd_soc_device *socdev = codec->socdev;
@ -724,67 +746,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
static int bypass_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct soc_mixer_control *m =
(struct soc_mixer_control *)w->kcontrols->private_value;
struct twl4030_priv *twl4030 = w->codec->private_data;
unsigned char reg, misc;
reg = twl4030_read_reg_cache(w->codec, m->reg);
/*
* bypass_state[0:3] - analog HiFi bypass
* bypass_state[4] - analog voice bypass
* bypass_state[5] - digital voice bypass
* bypass_state[6:7] - digital HiFi bypass
*/
if (m->reg == TWL4030_REG_VSTPGA) {
/* Voice digital bypass */
if (reg)
twl4030->bypass_state |= (1 << 5);
else
twl4030->bypass_state &= ~(1 << 5);
} else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
/* Analog bypass */
if (reg & (1 << m->shift))
twl4030->bypass_state |=
(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
else
twl4030->bypass_state &=
~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
} else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
/* Analog voice bypass */
if (reg & (1 << m->shift))
twl4030->bypass_state |= (1 << 4);
else
twl4030->bypass_state &= ~(1 << 4);
} else {
/* Digital bypass */
if (reg & (0x7 << m->shift))
twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
else
twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
}
/* Enable master analog loopback mode if any analog switch is enabled*/
misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
if (twl4030->bypass_state & 0x1F)
misc |= TWL4030_FMLOOP_EN;
else
misc &= ~TWL4030_FMLOOP_EN;
twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
if (twl4030->bypass_state)
twl4030_codec_mute(w->codec, 0);
else
twl4030_codec_mute(w->codec, 1);
}
return 0;
}
/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
@ -1192,32 +1153,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_NOPM, 0, 0),
/* Analog bypasses */
SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr1_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl1_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr2_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl2_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassv_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr1_control),
SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl1_control),
SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr2_control),
SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl2_control),
SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassv_control),
/* Master analog loopback switch */
SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
NULL, 0),
/* Digital bypasses */
SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassl_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassr_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassv_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassl_control),
SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassr_control),
SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassv_control),
/* Digital mixers, power control for the physical DACs */
SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
@ -1243,6 +1200,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
/* Output MIXER controls */
/* Earpiece */
SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@ -1308,8 +1268,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
0, 0, NULL, 0, handsfreerpga_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
/* Vibra */
SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
&twl4030_dapm_vibra_control),
SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
&twl4030_dapm_vibra_control, vibramux_event,
SND_SOC_DAPM_PRE_PMU),
SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_vibrapath_control),
@ -1369,6 +1330,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital R2 Playback Mixer", NULL, "DAC Right2"},
{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
/* Supply for the digital part (APLL) */
{"Digital R1 Playback Mixer", NULL, "APLL Enable"},
{"Digital L1 Playback Mixer", NULL, "APLL Enable"},
{"Digital R2 Playback Mixer", NULL, "APLL Enable"},
{"Digital L2 Playback Mixer", NULL, "APLL Enable"},
{"Digital Voice Playback Mixer", NULL, "APLL 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"},
@ -1482,6 +1450,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
{"ADC Virtual Left1", NULL, "APLL Enable"},
{"ADC Virtual Right1", NULL, "APLL Enable"},
{"ADC Virtual Left2", NULL, "APLL Enable"},
{"ADC Virtual Right2", NULL, "APLL Enable"},
/* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right"},
{"Left1 Analog Loopback", "Switch", "Analog Left"},
@ -1489,6 +1462,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left2 Analog Loopback", "Switch", "Analog Left"},
{"Voice Analog Loopback", "Switch", "Analog Left"},
/* Supply for the Analog loopbacks */
{"Right1 Analog Loopback", NULL, "FM Loop Enable"},
{"Left1 Analog Loopback", NULL, "FM Loop Enable"},
{"Right2 Analog Loopback", NULL, "FM Loop Enable"},
{"Left2 Analog Loopback", NULL, "FM Loop Enable"},
{"Voice Analog Loopback", NULL, "FM Loop Enable"},
{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
@ -1513,32 +1493,20 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
static int twl4030_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct twl4030_priv *twl4030 = codec->private_data;
switch (level) {
case SND_SOC_BIAS_ON:
twl4030_codec_mute(codec, 0);
break;
case SND_SOC_BIAS_PREPARE:
twl4030_power_up(codec);
if (twl4030->bypass_state)
twl4030_codec_mute(codec, 0);
else
twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_STANDBY:
twl4030_power_up(codec);
if (twl4030->bypass_state)
twl4030_codec_mute(codec, 0);
else
twl4030_codec_mute(codec, 1);
if (codec->bias_level == SND_SOC_BIAS_OFF)
twl4030_power_up(codec);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
@ -1785,29 +1753,23 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
{
struct snd_soc_codec *codec = codec_dai->codec;
struct twl4030_priv *twl4030 = codec->private_data;
u8 infreq;
switch (freq) {
case 19200000:
infreq = TWL4030_APLL_INFREQ_19200KHZ;
twl4030->sysclk = 19200;
break;
case 26000000:
infreq = TWL4030_APLL_INFREQ_26000KHZ;
twl4030->sysclk = 26000;
break;
case 38400000:
infreq = TWL4030_APLL_INFREQ_38400KHZ;
twl4030->sysclk = 38400;
break;
default:
printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
freq);
dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq);
return -EINVAL;
}
infreq |= TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
if ((freq / 1000) != twl4030->sysclk) {
dev_err(codec->dev,
"Mismatch in APLL mclk: %u (configured: %u)\n",
freq, twl4030->sysclk * 1000);
return -EINVAL;
}
return 0;
}
@ -1905,18 +1867,16 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
u8 infreq;
struct twl4030_priv *twl4030 = codec->private_data;
u8 mode;
/* If the system master clock is not 26MHz, the voice PCM interface is
* not avilable.
*/
infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
& TWL4030_APLL_INFREQ;
if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
printk(KERN_ERR "TWL4030 voice startup: "
"MCLK is not 26MHz, call set_sysclk() on init\n");
if (twl4030->sysclk != 26000) {
dev_err(codec->dev, "The board is configured for %u Hz, while"
"the Voice interface needs 26MHz APLL mclk\n",
twl4030->sysclk * 1000);
return -EINVAL;
}
@ -1989,21 +1949,19 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
u8 infreq;
struct twl4030_priv *twl4030 = codec->private_data;
switch (freq) {
case 26000000:
infreq = TWL4030_APLL_INFREQ_26000KHZ;
break;
default:
printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
freq);
if (freq != 26000000) {
dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice"
"interface needs 26MHz APLL mclk\n", freq);
return -EINVAL;
}
if ((freq / 1000) != twl4030->sysclk) {
dev_err(codec->dev,
"Mismatch in APLL mclk: %u (configured: %u)\n",
freq, twl4030->sysclk * 1000);
return -EINVAL;
}
infreq |= TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
return 0;
}
@ -2121,7 +2079,7 @@ struct snd_soc_dai twl4030_dai[] = {
};
EXPORT_SYMBOL_GPL(twl4030_dai);
static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
static int twl4030_soc_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;
@ -2131,7 +2089,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
static int twl4030_resume(struct platform_device *pdev)
static int twl4030_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
@ -2141,19 +2099,92 @@ static int twl4030_resume(struct platform_device *pdev)
return 0;
}
/*
* initialize the driver
* register the mixer and dsp interfaces with the kernel
*/
static struct snd_soc_codec *twl4030_codec;
static int twl4030_init(struct snd_soc_device *socdev)
static int twl4030_soc_probe(struct platform_device *pdev)
{
struct snd_soc_codec *codec = socdev->card->codec;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct twl4030_setup_data *setup = socdev->codec_data;
struct twl4030_priv *twl4030 = codec->private_data;
int ret = 0;
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
int ret;
printk(KERN_INFO "TWL4030 Audio Codec init \n");
BUG_ON(!twl4030_codec);
codec = twl4030_codec;
twl4030 = codec->private_data;
socdev->card->codec = codec;
/* Configuration for headset ramp delay from setup data */
if (setup) {
unsigned char hs_pop;
if (setup->sysclk != twl4030->sysclk)
dev_warn(&pdev->dev,
"Mismatch in APLL mclk: %u (configured: %u)\n",
setup->sysclk, twl4030->sysclk);
hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
hs_pop &= ~TWL4030_RAMP_DELAY;
hs_pop |= (setup->ramp_delay_value << 2);
twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
}
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create pcms\n");
return ret;
}
snd_soc_add_controls(codec, twl4030_snd_controls,
ARRAY_SIZE(twl4030_snd_controls));
twl4030_add_widgets(codec);
return 0;
}
static int twl4030_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
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;
}
static int __devinit twl4030_codec_probe(struct platform_device *pdev)
{
struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
int ret;
if (!pdata) {
dev_err(&pdev->dev, "platform_data is missing\n");
return -EINVAL;
}
twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
if (twl4030 == NULL) {
dev_err(&pdev->dev, "Can not allocate memroy\n");
return -ENOMEM;
}
codec = &twl4030->codec;
codec->private_data = twl4030;
codec->dev = &pdev->dev;
twl4030_dai[0].dev = &pdev->dev;
twl4030_dai[1].dev = &pdev->dev;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "twl4030";
codec->owner = THIS_MODULE;
@ -2165,123 +2196,84 @@ static int twl4030_init(struct snd_soc_device *socdev)
codec->reg_cache_size = sizeof(twl4030_reg);
codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
/* Configuration for headset ramp delay from setup data */
if (setup) {
unsigned char hs_pop;
if (setup->sysclk)
twl4030->sysclk = setup->sysclk;
else
twl4030->sysclk = 26000;
hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
hs_pop &= ~TWL4030_RAMP_DELAY;
hs_pop |= (setup->ramp_delay_value << 2);
twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
} else {
twl4030->sysclk = 26000;
if (codec->reg_cache == NULL) {
ret = -ENOMEM;
goto error_cache;
}
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "twl4030: failed to create pcms\n");
goto pcm_err;
}
platform_set_drvdata(pdev, twl4030);
twl4030_codec = codec;
/* Set the defaults, and power up the codec */
twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
twl4030_init_chip(codec);
/* power on device */
codec->bias_level = SND_SOC_BIAS_OFF;
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_controls(codec, twl4030_snd_controls,
ARRAY_SIZE(twl4030_snd_controls));
twl4030_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "twl4030: failed to register card\n");
goto card_err;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto error_codec;
}
return ret;
ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
snd_soc_unregister_codec(codec);
goto error_codec;
}
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return 0;
error_codec:
twl4030_power_down(codec);
kfree(codec->reg_cache);
error_cache:
kfree(twl4030);
return ret;
}
static struct snd_soc_device *twl4030_socdev;
static int twl4030_probe(struct platform_device *pdev)
static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
if (twl4030 == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = twl4030;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
twl4030_socdev = socdev;
twl4030_init(socdev);
kfree(twl4030);
twl4030_codec = NULL;
return 0;
}
static int twl4030_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
MODULE_ALIAS("platform:twl4030_codec_audio");
printk(KERN_INFO "TWL4030 Audio Codec remove\n");
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;
}
struct snd_soc_codec_device soc_codec_dev_twl4030 = {
.probe = twl4030_probe,
.remove = twl4030_remove,
.suspend = twl4030_suspend,
.resume = twl4030_resume,
static struct platform_driver twl4030_codec_driver = {
.probe = twl4030_codec_probe,
.remove = __devexit_p(twl4030_codec_remove),
.driver = {
.name = "twl4030_codec_audio",
.owner = THIS_MODULE,
},
};
EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
static int __init twl4030_modinit(void)
{
return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
return platform_driver_register(&twl4030_codec_driver);
}
module_init(twl4030_modinit);
static void __exit twl4030_exit(void)
{
snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
platform_driver_unregister(&twl4030_codec_driver);
}
module_exit(twl4030_exit);
struct snd_soc_codec_device soc_codec_dev_twl4030 = {
.probe = twl4030_soc_probe,
.remove = twl4030_soc_remove,
.suspend = twl4030_soc_suspend,
.resume = twl4030_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
MODULE_AUTHOR("Steve Sakoman");
MODULE_LICENSE("GPL");

View file

@ -22,245 +22,13 @@
#ifndef __TWL4030_AUDIO_H__
#define __TWL4030_AUDIO_H__
#define TWL4030_REG_CODEC_MODE 0x1
#define TWL4030_REG_OPTION 0x2
#define TWL4030_REG_UNKNOWN 0x3
#define TWL4030_REG_MICBIAS_CTL 0x4
#define TWL4030_REG_ANAMICL 0x5
#define TWL4030_REG_ANAMICR 0x6
#define TWL4030_REG_AVADC_CTL 0x7
#define TWL4030_REG_ADCMICSEL 0x8
#define TWL4030_REG_DIGMIXING 0x9
#define TWL4030_REG_ATXL1PGA 0xA
#define TWL4030_REG_ATXR1PGA 0xB
#define TWL4030_REG_AVTXL2PGA 0xC
#define TWL4030_REG_AVTXR2PGA 0xD
#define TWL4030_REG_AUDIO_IF 0xE
#define TWL4030_REG_VOICE_IF 0xF
#define TWL4030_REG_ARXR1PGA 0x10
#define TWL4030_REG_ARXL1PGA 0x11
#define TWL4030_REG_ARXR2PGA 0x12
#define TWL4030_REG_ARXL2PGA 0x13
#define TWL4030_REG_VRXPGA 0x14
#define TWL4030_REG_VSTPGA 0x15
#define TWL4030_REG_VRX2ARXPGA 0x16
#define TWL4030_REG_AVDAC_CTL 0x17
#define TWL4030_REG_ARX2VTXPGA 0x18
#define TWL4030_REG_ARXL1_APGA_CTL 0x19
#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
#define TWL4030_REG_ATX2ARXPGA 0x1D
#define TWL4030_REG_BT_IF 0x1E
#define TWL4030_REG_BTPGA 0x1F
#define TWL4030_REG_BTSTPGA 0x20
#define TWL4030_REG_EAR_CTL 0x21
#define TWL4030_REG_HS_SEL 0x22
#define TWL4030_REG_HS_GAIN_SET 0x23
#define TWL4030_REG_HS_POPN_SET 0x24
#define TWL4030_REG_PREDL_CTL 0x25
#define TWL4030_REG_PREDR_CTL 0x26
#define TWL4030_REG_PRECKL_CTL 0x27
#define TWL4030_REG_PRECKR_CTL 0x28
#define TWL4030_REG_HFL_CTL 0x29
#define TWL4030_REG_HFR_CTL 0x2A
#define TWL4030_REG_ALC_CTL 0x2B
#define TWL4030_REG_ALC_SET1 0x2C
#define TWL4030_REG_ALC_SET2 0x2D
#define TWL4030_REG_BOOST_CTL 0x2E
#define TWL4030_REG_SOFTVOL_CTL 0x2F
#define TWL4030_REG_DTMF_FREQSEL 0x30
#define TWL4030_REG_DTMF_TONEXT1H 0x31
#define TWL4030_REG_DTMF_TONEXT1L 0x32
#define TWL4030_REG_DTMF_TONEXT2H 0x33
#define TWL4030_REG_DTMF_TONEXT2L 0x34
#define TWL4030_REG_DTMF_TONOFF 0x35
#define TWL4030_REG_DTMF_WANONOFF 0x36
#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
#define TWL4030_REG_APLL_CTL 0x3A
#define TWL4030_REG_DTMF_CTL 0x3B
#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
#define TWL4030_REG_MISC_SET_1 0x3E
#define TWL4030_REG_PCMBTMUX 0x3F
#define TWL4030_REG_RX_PATH_SEL 0x43
#define TWL4030_REG_VDL_APGA_CTL 0x44
#define TWL4030_REG_VIBRA_CTL 0x45
#define TWL4030_REG_VIBRA_SET 0x46
#define TWL4030_REG_VIBRA_PWM_SET 0x47
#define TWL4030_REG_ANAMIC_GAIN 0x48
#define TWL4030_REG_MISC_SET_2 0x49
/* Register descriptions are here */
#include <linux/mfd/twl4030-codec.h>
/* Sgadow register used by the audio driver */
#define TWL4030_REG_SW_SHADOW 0x4A
#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
/* Bitfield Definitions */
/* TWL4030_CODEC_MODE (0x01) Fields */
#define TWL4030_APLL_RATE 0xF0
#define TWL4030_APLL_RATE_8000 0x00
#define TWL4030_APLL_RATE_11025 0x10
#define TWL4030_APLL_RATE_12000 0x20
#define TWL4030_APLL_RATE_16000 0x40
#define TWL4030_APLL_RATE_22050 0x50
#define TWL4030_APLL_RATE_24000 0x60
#define TWL4030_APLL_RATE_32000 0x80
#define TWL4030_APLL_RATE_44100 0x90
#define TWL4030_APLL_RATE_48000 0xA0
#define TWL4030_APLL_RATE_96000 0xE0
#define TWL4030_SEL_16K 0x08
#define TWL4030_CODECPDZ 0x02
#define TWL4030_OPT_MODE 0x01
#define TWL4030_OPTION_1 (1 << 0)
#define TWL4030_OPTION_2 (0 << 0)
/* TWL4030_OPTION (0x02) Fields */
#define TWL4030_ATXL1_EN (1 << 0)
#define TWL4030_ATXR1_EN (1 << 1)
#define TWL4030_ATXL2_VTXL_EN (1 << 2)
#define TWL4030_ATXR2_VTXR_EN (1 << 3)
#define TWL4030_ARXL1_VRX_EN (1 << 4)
#define TWL4030_ARXR1_EN (1 << 5)
#define TWL4030_ARXL2_EN (1 << 6)
#define TWL4030_ARXR2_EN (1 << 7)
/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
#define TWL4030_MICBIAS2_CTL 0x40
#define TWL4030_MICBIAS1_CTL 0x20
#define TWL4030_HSMICBIAS_EN 0x04
#define TWL4030_MICBIAS2_EN 0x02
#define TWL4030_MICBIAS1_EN 0x01
/* ANAMICL (0x05) Fields */
#define TWL4030_CNCL_OFFSET_START 0x80
#define TWL4030_OFFSET_CNCL_SEL 0x60
#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
#define TWL4030_MICAMPL_EN 0x10
#define TWL4030_CKMIC_EN 0x08
#define TWL4030_AUXL_EN 0x04
#define TWL4030_HSMIC_EN 0x02
#define TWL4030_MAINMIC_EN 0x01
/* ANAMICR (0x06) Fields */
#define TWL4030_MICAMPR_EN 0x10
#define TWL4030_AUXR_EN 0x04
#define TWL4030_SUBMIC_EN 0x01
/* AVADC_CTL (0x07) Fields */
#define TWL4030_ADCL_EN 0x08
#define TWL4030_AVADC_CLK_PRIORITY 0x04
#define TWL4030_ADCR_EN 0x02
/* TWL4030_REG_ADCMICSEL (0x08) Fields */
#define TWL4030_DIGMIC1_EN 0x08
#define TWL4030_TX2IN_SEL 0x04
#define TWL4030_DIGMIC0_EN 0x02
#define TWL4030_TX1IN_SEL 0x01
/* AUDIO_IF (0x0E) Fields */
#define TWL4030_AIF_SLAVE_EN 0x80
#define TWL4030_DATA_WIDTH 0x60
#define TWL4030_DATA_WIDTH_16S_16W 0x00
#define TWL4030_DATA_WIDTH_32S_16W 0x40
#define TWL4030_DATA_WIDTH_32S_24W 0x60
#define TWL4030_AIF_FORMAT 0x18
#define TWL4030_AIF_FORMAT_CODEC 0x00
#define TWL4030_AIF_FORMAT_LEFT 0x08
#define TWL4030_AIF_FORMAT_RIGHT 0x10
#define TWL4030_AIF_FORMAT_TDM 0x18
#define TWL4030_AIF_TRI_EN 0x04
#define TWL4030_CLK256FS_EN 0x02
#define TWL4030_AIF_EN 0x01
/* VOICE_IF (0x0F) Fields */
#define TWL4030_VIF_SLAVE_EN 0x80
#define TWL4030_VIF_DIN_EN 0x40
#define TWL4030_VIF_DOUT_EN 0x20
#define TWL4030_VIF_SWAP 0x10
#define TWL4030_VIF_FORMAT 0x08
#define TWL4030_VIF_TRI_EN 0x04
#define TWL4030_VIF_SUB_EN 0x02
#define TWL4030_VIF_EN 0x01
/* EAR_CTL (0x21) */
#define TWL4030_EAR_GAIN 0x30
/* HS_GAIN_SET (0x23) Fields */
#define TWL4030_HSR_GAIN 0x0C
#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
#define TWL4030_HSR_GAIN_0DB 0x08
#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
#define TWL4030_HSL_GAIN 0x03
#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
#define TWL4030_HSL_GAIN_0DB 0x02
#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
/* HS_POPN_SET (0x24) Fields */
#define TWL4030_VMID_EN 0x40
#define TWL4030_EXTMUTE 0x20
#define TWL4030_RAMP_DELAY 0x1C
#define TWL4030_RAMP_DELAY_20MS 0x00
#define TWL4030_RAMP_DELAY_40MS 0x04
#define TWL4030_RAMP_DELAY_81MS 0x08
#define TWL4030_RAMP_DELAY_161MS 0x0C
#define TWL4030_RAMP_DELAY_323MS 0x10
#define TWL4030_RAMP_DELAY_645MS 0x14
#define TWL4030_RAMP_DELAY_1291MS 0x18
#define TWL4030_RAMP_DELAY_2581MS 0x1C
#define TWL4030_RAMP_EN 0x02
/* PREDL_CTL (0x25) */
#define TWL4030_PREDL_GAIN 0x30
/* PREDR_CTL (0x26) */
#define TWL4030_PREDR_GAIN 0x30
/* PRECKL_CTL (0x27) */
#define TWL4030_PRECKL_GAIN 0x30
/* PRECKR_CTL (0x28) */
#define TWL4030_PRECKR_GAIN 0x30
/* HFL_CTL (0x29, 0x2A) Fields */
#define TWL4030_HF_CTL_HB_EN 0x04
#define TWL4030_HF_CTL_LOOP_EN 0x08
#define TWL4030_HF_CTL_RAMP_EN 0x10
#define TWL4030_HF_CTL_REF_EN 0x20
/* APLL_CTL (0x3A) Fields */
#define TWL4030_APLL_EN 0x10
#define TWL4030_APLL_INFREQ 0x0F
#define TWL4030_APLL_INFREQ_19200KHZ 0x05
#define TWL4030_APLL_INFREQ_26000KHZ 0x06
#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
/* REG_MISC_SET_1 (0x3E) Fields */
#define TWL4030_CLK64_EN 0x80
#define TWL4030_SCRAMBLE_EN 0x40
#define TWL4030_FMLOOP_EN 0x20
#define TWL4030_SMOOTH_ANAVOL_EN 0x02
#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
#define TWL4030_HFL_EN 0x01
#define TWL4030_HFR_EN 0x02
@ -279,3 +47,5 @@ struct twl4030_setup_data {
};
#endif /* End of __TWL4030_AUDIO_H__ */

View file

@ -562,17 +562,8 @@ static int uda134x_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "UDA134X: failed to register card\n");
goto card_err;
}
return 0;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
reg_err:

View file

@ -378,7 +378,6 @@ static int uda1380_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -713,17 +712,9 @@ static int uda1380_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}

View file

@ -800,7 +800,7 @@ static int wm8350_add_widgets(struct snd_soc_codec *codec)
return ret;
}
return snd_soc_dapm_new_widgets(codec);
return 0;
}
static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
@ -1101,7 +1101,7 @@ static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
}
static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in,
int pll_id, int source, unsigned int freq_in,
unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
@ -1501,18 +1501,7 @@ static int wm8350_probe(struct platform_device *pdev)
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register card\n");
goto card_err;
}
return 0;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return ret;
}
static int wm8350_remove(struct platform_device *pdev)
@ -1680,21 +1669,6 @@ static int __devexit wm8350_codec_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
{
return snd_soc_suspend_device(&pdev->dev);
}
static int wm8350_codec_resume(struct platform_device *pdev)
{
return snd_soc_resume_device(&pdev->dev);
}
#else
#define wm8350_codec_suspend NULL
#define wm8350_codec_resume NULL
#endif
static struct platform_driver wm8350_codec_driver = {
.driver = {
.name = "wm8350-codec",
@ -1702,8 +1676,6 @@ static struct platform_driver wm8350_codec_driver = {
},
.probe = wm8350_codec_probe,
.remove = __devexit_p(wm8350_codec_remove),
.suspend = wm8350_codec_suspend,
.resume = wm8350_codec_resume,
};
static __init int wm8350_init(void)

View file

@ -915,7 +915,6 @@ static int wm8400_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -1011,7 +1010,8 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
}
static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
unsigned int freq_in, unsigned int freq_out)
int source, unsigned int freq_in,
unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8400_priv *wm8400 = codec->private_data;
@ -1399,17 +1399,6 @@ static int wm8400_probe(struct platform_device *pdev)
wm8400_add_controls(codec);
wm8400_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -1558,21 +1547,6 @@ static int __exit wm8400_codec_remove(struct platform_device *dev)
return 0;
}
#ifdef CONFIG_PM
static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
{
return snd_soc_suspend_device(&pdev->dev);
}
static int wm8400_pdev_resume(struct platform_device *pdev)
{
return snd_soc_resume_device(&pdev->dev);
}
#else
#define wm8400_pdev_suspend NULL
#define wm8400_pdev_resume NULL
#endif
static struct platform_driver wm8400_codec_driver = {
.driver = {
.name = "wm8400-codec",
@ -1580,8 +1554,6 @@ static struct platform_driver wm8400_codec_driver = {
},
.probe = wm8400_codec_probe,
.remove = __exit_p(wm8400_codec_remove),
.suspend = wm8400_pdev_suspend,
.resume = wm8400_pdev_resume,
};
static int __init wm8400_codec_init(void)

View file

@ -219,7 +219,6 @@ static int wm8510_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -271,8 +270,8 @@ static void pll_factors(unsigned int target, unsigned int source)
pll_div.k = K;
}
static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@ -604,16 +603,9 @@ static int wm8510_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8510_snd_controls,
ARRAY_SIZE(wm8510_snd_controls));
wm8510_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8510: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;

View file

@ -117,7 +117,6 @@ static int wm8523_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -448,17 +447,9 @@ static int wm8523_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8523_snd_controls,
ARRAY_SIZE(wm8523_snd_controls));
wm8523_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -638,21 +629,6 @@ static __devexit int wm8523_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
{
return snd_soc_suspend_device(&i2c->dev);
}
static int wm8523_i2c_resume(struct i2c_client *i2c)
{
return snd_soc_resume_device(&i2c->dev);
}
#else
#define wm8523_i2c_suspend NULL
#define wm8523_i2c_resume NULL
#endif
static const struct i2c_device_id wm8523_i2c_id[] = {
{ "wm8523", 0 },
{ }
@ -666,8 +642,6 @@ static struct i2c_driver wm8523_i2c_driver = {
},
.probe = wm8523_i2c_probe,
.remove = __devexit_p(wm8523_i2c_remove),
.suspend = wm8523_i2c_suspend,
.resume = wm8523_i2c_resume,
.id_table = wm8523_i2c_id,
};
#endif

View file

@ -315,7 +315,6 @@ static int wm8580_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -407,8 +406,8 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
return 0;
}
static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
int offset;
struct snd_soc_codec *codec = codec_dai->codec;
@ -800,17 +799,9 @@ static int wm8580_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8580_snd_controls,
ARRAY_SIZE(wm8580_snd_controls));
wm8580_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -961,21 +952,6 @@ static int wm8580_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8580_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8580_i2c_suspend NULL
#define wm8580_i2c_resume NULL
#endif
static const struct i2c_device_id wm8580_i2c_id[] = {
{ "wm8580", 0 },
{ }
@ -989,8 +965,6 @@ static struct i2c_driver wm8580_i2c_driver = {
},
.probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove,
.suspend = wm8580_i2c_suspend,
.resume = wm8580_i2c_resume,
.id_table = wm8580_i2c_id,
};
#endif

633
sound/soc/codecs/wm8711.c Normal file
View file

@ -0,0 +1,633 @@
/*
* wm8711.c -- WM8711 ALSA SoC Audio driver
*
* Copyright 2006 Wolfson Microelectronics
*
* Author: Mike Arthur <linux@wolfsonmicro.com>
*
* Based on wm8731.c by Richard Purdie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.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 "wm8711.h"
static struct snd_soc_codec *wm8711_codec;
/* codec private data */
struct wm8711_priv {
struct snd_soc_codec codec;
u16 reg_cache[WM8711_CACHEREGNUM];
unsigned int sysclk;
};
/*
* wm8711 register cache
* We can't read the WM8711 register space when we are
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
0x0079, 0x0079, 0x000a, 0x0008,
0x009f, 0x000a, 0x0000, 0x0000
};
#define wm8711_reset(c) snd_soc_write(c, WM8711_RESET, 0)
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static const struct snd_kcontrol_new wm8711_snd_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
0, 127, 0, out_tlv),
SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
7, 1, 0),
};
/* Output Mixer */
static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
};
static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
&wm8711_output_mixer_controls[0],
ARRAY_SIZE(wm8711_output_mixer_controls)),
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
};
static const struct snd_soc_dapm_route intercon[] = {
/* output mixer */
{"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"},
/* outputs */
{"RHPOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"LOUT", NULL, "Output Mixer"},
};
static int wm8711_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets,
ARRAY_SIZE(wm8711_dapm_widgets));
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
return 0;
}
struct _coeff_div {
u32 mclk;
u32 rate;
u16 fs;
u8 sr:4;
u8 bosr:1;
u8 usb:1;
};
/* codec mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
/* 48k */
{12288000, 48000, 256, 0x0, 0x0, 0x0},
{18432000, 48000, 384, 0x0, 0x1, 0x0},
{12000000, 48000, 250, 0x0, 0x0, 0x1},
/* 32k */
{12288000, 32000, 384, 0x6, 0x0, 0x0},
{18432000, 32000, 576, 0x6, 0x1, 0x0},
{12000000, 32000, 375, 0x6, 0x0, 0x1},
/* 8k */
{12288000, 8000, 1536, 0x3, 0x0, 0x0},
{18432000, 8000, 2304, 0x3, 0x1, 0x0},
{11289600, 8000, 1408, 0xb, 0x0, 0x0},
{16934400, 8000, 2112, 0xb, 0x1, 0x0},
{12000000, 8000, 1500, 0x3, 0x0, 0x1},
/* 96k */
{12288000, 96000, 128, 0x7, 0x0, 0x0},
{18432000, 96000, 192, 0x7, 0x1, 0x0},
{12000000, 96000, 125, 0x7, 0x0, 0x1},
/* 44.1k */
{11289600, 44100, 256, 0x8, 0x0, 0x0},
{16934400, 44100, 384, 0x8, 0x1, 0x0},
{12000000, 44100, 272, 0x8, 0x1, 0x1},
/* 88.2k */
{11289600, 88200, 128, 0xf, 0x0, 0x0},
{16934400, 88200, 192, 0xf, 0x1, 0x0},
{12000000, 88200, 136, 0xf, 0x1, 0x1},
};
static inline int get_coeff(int mclk, int rate)
{
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
return i;
}
return 0;
}
static int wm8711_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8711_priv *wm8711 = codec->private_data;
u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
int i = get_coeff(wm8711->sysclk, params_rate(params));
u16 srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
snd_soc_write(codec, WM8711_SRATE, srate);
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
iface |= 0x0004;
break;
case SNDRV_PCM_FORMAT_S24_LE:
iface |= 0x0008;
break;
}
snd_soc_write(codec, WM8711_IFACE, iface);
return 0;
}
static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
/* set active */
snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
return 0;
}
static void wm8711_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
/* deactivate */
if (!codec->active) {
udelay(50);
snd_soc_write(codec, WM8711_ACTIVE, 0x0);
}
}
static int wm8711_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
if (mute)
snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
else
snd_soc_write(codec, WM8711_APDIGI, mute_reg);
return 0;
}
static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8711_priv *wm8711 = codec->private_data;
switch (freq) {
case 11289600:
case 12000000:
case 12288000:
case 16934400:
case 18432000:
wm8711->sysclk = freq;
return 0;
}
return -EINVAL;
}
static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 iface = 0;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
iface |= 0x0040;
break;
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return -EINVAL;
}
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= 0x0002;
break;
case SND_SOC_DAIFMT_RIGHT_J:
break;
case SND_SOC_DAIFMT_LEFT_J:
iface |= 0x0001;
break;
case SND_SOC_DAIFMT_DSP_A:
iface |= 0x0003;
break;
case SND_SOC_DAIFMT_DSP_B:
iface |= 0x0013;
break;
default:
return -EINVAL;
}
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
iface |= 0x0090;
break;
case SND_SOC_DAIFMT_IB_NF:
iface |= 0x0080;
break;
case SND_SOC_DAIFMT_NB_IF:
iface |= 0x0010;
break;
default:
return -EINVAL;
}
/* set iface */
snd_soc_write(codec, WM8711_IFACE, iface);
return 0;
}
static int wm8711_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
switch (level) {
case SND_SOC_BIAS_ON:
snd_soc_write(codec, WM8711_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
snd_soc_write(codec, WM8711_ACTIVE, 0x0);
snd_soc_write(codec, WM8711_PWR, 0xffff);
break;
}
codec->bias_level = level;
return 0;
}
#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8711_ops = {
.prepare = wm8711_pcm_prepare,
.hw_params = wm8711_hw_params,
.shutdown = wm8711_shutdown,
.digital_mute = wm8711_mute,
.set_sysclk = wm8711_set_dai_sysclk,
.set_fmt = wm8711_set_dai_fmt,
};
struct snd_soc_dai wm8711_dai = {
.name = "WM8711",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8711_RATES,
.formats = WM8711_FORMATS,
},
.ops = &wm8711_ops,
};
EXPORT_SYMBOL_GPL(wm8711_dai);
static int wm8711_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;
snd_soc_write(codec, WM8711_ACTIVE, 0x0);
wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int wm8711_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
}
wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8711_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
static int wm8711_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (wm8711_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = wm8711_codec;
codec = wm8711_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
snd_soc_add_controls(codec, wm8711_snd_controls,
ARRAY_SIZE(wm8711_snd_controls));
wm8711_add_widgets(codec);
return ret;
pcm_err:
return ret;
}
/* power down chip */
static int wm8711_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_wm8711 = {
.probe = wm8711_probe,
.remove = wm8711_remove,
.suspend = wm8711_suspend,
.resume = wm8711_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
static int wm8711_register(struct wm8711_priv *wm8711,
enum snd_soc_control_type control)
{
int ret;
struct snd_soc_codec *codec = &wm8711->codec;
u16 reg;
if (wm8711_codec) {
dev_err(codec->dev, "Another WM8711 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = wm8711;
codec->name = "WM8711";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8711_set_bias_level;
codec->dai = &wm8711_dai;
codec->num_dai = 1;
codec->reg_cache_size = WM8711_CACHEREGNUM;
codec->reg_cache = &wm8711->reg_cache;
memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg));
ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
goto err;
}
ret = wm8711_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
goto err;
}
wm8711_dai.dev = codec->dev;
wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
reg = snd_soc_read(codec, WM8711_LOUT1V);
snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100);
reg = snd_soc_read(codec, WM8711_ROUT1V);
snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100);
wm8711_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err;
}
ret = snd_soc_register_dai(&wm8711_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm8711);
return ret;
}
static void wm8711_unregister(struct wm8711_priv *wm8711)
{
wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8711_dai);
snd_soc_unregister_codec(&wm8711->codec);
kfree(wm8711);
wm8711_codec = NULL;
}
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8711_spi_probe(struct spi_device *spi)
{
struct snd_soc_codec *codec;
struct wm8711_priv *wm8711;
wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
if (wm8711 == NULL)
return -ENOMEM;
codec = &wm8711->codec;
codec->control_data = spi;
codec->dev = &spi->dev;
dev_set_drvdata(&spi->dev, wm8711);
return wm8711_register(wm8711, SND_SOC_SPI);
}
static int __devexit wm8711_spi_remove(struct spi_device *spi)
{
struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev);
wm8711_unregister(wm8711);
return 0;
}
static struct spi_driver wm8711_spi_driver = {
.driver = {
.name = "wm8711",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm8711_spi_probe,
.remove = __devexit_p(wm8711_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int wm8711_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8711_priv *wm8711;
struct snd_soc_codec *codec;
wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
if (wm8711 == NULL)
return -ENOMEM;
codec = &wm8711->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8711);
codec->control_data = i2c;
codec->dev = &i2c->dev;
return wm8711_register(wm8711, SND_SOC_I2C);
}
static __devexit int wm8711_i2c_remove(struct i2c_client *client)
{
struct wm8711_priv *wm8711 = i2c_get_clientdata(client);
wm8711_unregister(wm8711);
return 0;
}
static const struct i2c_device_id wm8711_i2c_id[] = {
{ "wm8711", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
static struct i2c_driver wm8711_i2c_driver = {
.driver = {
.name = "WM8711 I2C Codec",
.owner = THIS_MODULE,
},
.probe = wm8711_i2c_probe,
.remove = __devexit_p(wm8711_i2c_remove),
.id_table = wm8711_i2c_id,
};
#endif
static int __init wm8711_modinit(void)
{
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8711_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
ret);
}
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&wm8711_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
ret);
}
#endif
return 0;
}
module_init(wm8711_modinit);
static void __exit wm8711_exit(void)
{
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8711_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8711_spi_driver);
#endif
}
module_exit(wm8711_exit);
MODULE_DESCRIPTION("ASoC WM8711 driver");
MODULE_AUTHOR("Mike Arthur");
MODULE_LICENSE("GPL");

42
sound/soc/codecs/wm8711.h Normal file
View file

@ -0,0 +1,42 @@
/*
* wm8711.h -- WM8711 Soc Audio driver
*
* Copyright 2006 Wolfson Microelectronics
*
* Author: Mike Arthur <linux@wolfsonmicro.com>
*
* Based on wm8731.h
*
* 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 _WM8711_H
#define _WM8711_H
/* WM8711 register space */
#define WM8711_LOUT1V 0x02
#define WM8711_ROUT1V 0x03
#define WM8711_APANA 0x04
#define WM8711_APDIGI 0x05
#define WM8711_PWR 0x06
#define WM8711_IFACE 0x07
#define WM8711_SRATE 0x08
#define WM8711_ACTIVE 0x09
#define WM8711_RESET 0x0f
#define WM8711_CACHEREGNUM 8
#define WM8711_SYSCLK 0
#define WM8711_DAI 0
struct wm8711_setup_data {
unsigned short i2c_address;
};
extern struct snd_soc_dai wm8711_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8711;
#endif

135
sound/soc/codecs/wm8727.c Normal file
View file

@ -0,0 +1,135 @@
/*
* wm8727.c
*
* Created on: 15-Oct-2009
* Author: neil.jones@imgtec.com
*
* Copyright (C) 2009 Imagination Technologies Ltd.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include "wm8727.h"
/*
* Note this is a simple chip with no configuration interface, sample rate is
* determined automatically by examining the Master clock and Bit clock ratios
*/
#define WM8727_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
SNDRV_PCM_RATE_192000)
struct snd_soc_dai wm8727_dai = {
.name = "WM8727",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = WM8727_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
};
EXPORT_SYMBOL_GPL(wm8727_dai);
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);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8727: failed to create pcms\n");
goto pcm_err;
}
return ret;
pcm_err:
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
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;
}
struct snd_soc_codec_device soc_codec_dev_wm8727 = {
.probe = wm8727_soc_probe,
.remove = wm8727_soc_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
static __devinit int wm8727_platform_probe(struct platform_device *pdev)
{
wm8727_dai.dev = &pdev->dev;
return snd_soc_register_dai(&wm8727_dai);
}
static int __devexit wm8727_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&wm8727_dai);
return 0;
}
static struct platform_driver wm8727_codec_driver = {
.driver = {
.name = "wm8727-codec",
.owner = THIS_MODULE,
},
.probe = wm8727_platform_probe,
.remove = __devexit_p(wm8727_platform_remove),
};
static int __init wm8727_init(void)
{
return platform_driver_register(&wm8727_codec_driver);
}
module_init(wm8727_init);
static void __exit wm8727_exit(void)
{
platform_driver_unregister(&wm8727_codec_driver);
}
module_exit(wm8727_exit);
MODULE_DESCRIPTION("ASoC wm8727 driver");
MODULE_AUTHOR("Neil Jones");
MODULE_LICENSE("GPL");

21
sound/soc/codecs/wm8727.h Normal file
View file

@ -0,0 +1,21 @@
/*
* wm8727.h
*
* Created on: 15-Oct-2009
* Author: neil.jones@imgtec.com
*
* Copyright (C) 2009 Imagination Technologies Ltd.
*
* 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 WM8727_H_
#define WM8727_H_
extern struct snd_soc_dai wm8727_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8727;
#endif /* WM8727_H_ */

View file

@ -74,8 +74,6 @@ static int wm8728_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -287,17 +285,9 @@ static int wm8728_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls));
wm8728_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8728: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;

View file

@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
@ -33,9 +34,18 @@
static struct snd_soc_codec *wm8731_codec;
struct snd_soc_codec_device soc_codec_dev_wm8731;
#define WM8731_NUM_SUPPLIES 4
static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
"AVDD",
"HPVDD",
"DCVDD",
"DBVDD",
};
/* codec private data */
struct wm8731_priv {
struct snd_soc_codec codec;
struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
u16 reg_cache[WM8731_CACHEREGNUM];
unsigned int sysclk;
};
@ -149,7 +159,6 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -422,9 +431,12 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8731_priv *wm8731 = codec->private_data;
snd_soc_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
return 0;
}
@ -432,10 +444,16 @@ static int wm8731_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
int i;
struct wm8731_priv *wm8731 = codec->private_data;
int i, ret;
u8 data[2];
u16 *cache = codec->reg_cache;
ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0)
return ret;
/* Sync reg_cache with the hardware */
for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
@ -444,6 +462,7 @@ static int wm8731_resume(struct platform_device *pdev)
}
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8731_set_bias_level(codec, codec->suspend_bias_level);
return 0;
}
#else
@ -475,17 +494,9 @@ static int wm8731_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls));
wm8731_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -512,7 +523,7 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
static int wm8731_register(struct wm8731_priv *wm8731,
enum snd_soc_control_type control)
{
int ret;
int ret, i;
struct snd_soc_codec *codec = &wm8731->codec;
if (wm8731_codec) {
@ -543,10 +554,27 @@ static int wm8731_register(struct wm8731_priv *wm8731,
goto err;
}
for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
wm8731->supplies[i].supply = wm8731_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
wm8731->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_regulator_get;
}
ret = wm8731_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
goto err;
goto err_regulator_enable;
}
wm8731_dai.dev = codec->dev;
@ -567,7 +595,7 @@ static int wm8731_register(struct wm8731_priv *wm8731,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err;
goto err_regulator_enable;
}
ret = snd_soc_register_dai(&wm8731_dai);
@ -581,6 +609,10 @@ static int wm8731_register(struct wm8731_priv *wm8731,
err_codec:
snd_soc_unregister_codec(codec);
err_regulator_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
err_regulator_get:
regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
err:
kfree(wm8731);
return ret;
@ -591,6 +623,8 @@ static void wm8731_unregister(struct wm8731_priv *wm8731)
wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8731_dai);
snd_soc_unregister_codec(&wm8731->codec);
regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
kfree(wm8731);
wm8731_codec = NULL;
}
@ -623,21 +657,6 @@ static int __devexit wm8731_spi_remove(struct spi_device *spi)
return 0;
}
#ifdef CONFIG_PM
static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8731_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8731_spi_suspend NULL
#define wm8731_spi_resume NULL
#endif
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
@ -645,8 +664,6 @@ static struct spi_driver wm8731_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
.suspend = wm8731_spi_suspend,
.resume = wm8731_spi_resume,
.remove = __devexit_p(wm8731_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@ -679,21 +696,6 @@ static __devexit int wm8731_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
{
return snd_soc_suspend_device(&i2c->dev);
}
static int wm8731_i2c_resume(struct i2c_client *i2c)
{
return snd_soc_resume_device(&i2c->dev);
}
#else
#define wm8731_i2c_suspend NULL
#define wm8731_i2c_resume NULL
#endif
static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 },
{ }
@ -707,8 +709,6 @@ static struct i2c_driver wm8731_i2c_driver = {
},
.probe = wm8731_i2c_probe,
.remove = __devexit_p(wm8731_i2c_remove),
.suspend = wm8731_i2c_suspend,
.resume = wm8731_i2c_resume,
.id_table = wm8731_i2c_id,
};
#endif

View file

@ -403,7 +403,6 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -772,16 +771,8 @@ static int wm8750_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8750_snd_controls,
ARRAY_SIZE(wm8750_snd_controls));
wm8750_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8750: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;

View file

@ -673,7 +673,6 @@ static int wm8753_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -724,8 +723,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
u16 reg, enable;
int offset;
@ -1583,18 +1582,9 @@ static int wm8753_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8753_snd_controls,
ARRAY_SIZE(wm8753_snd_controls));
wm8753_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8753: failed to register card\n");
goto card_err;
}
return 0;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -1767,21 +1757,6 @@ static int wm8753_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8753_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8753_i2c_suspend NULL
#define wm8753_i2c_resume NULL
#endif
static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 },
{ }
@ -1795,8 +1770,6 @@ static struct i2c_driver wm8753_i2c_driver = {
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
.suspend = wm8753_i2c_suspend,
.resume = wm8753_i2c_resume,
.id_table = wm8753_i2c_id,
};
#endif
@ -1852,22 +1825,6 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
return 0;
}
#ifdef CONFIG_PM
static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8753_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8753_spi_suspend NULL
#define wm8753_spi_resume NULL
#endif
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
@ -1876,8 +1833,6 @@ static struct spi_driver wm8753_spi_driver = {
},
.probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove),
.suspend = wm8753_spi_suspend,
.resume = wm8753_spi_resume,
};
#endif

View file

@ -447,17 +447,8 @@ static int wm8776_probe(struct platform_device *pdev)
ARRAY_SIZE(wm8776_dapm_widgets));
snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -616,21 +607,6 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi)
return 0;
}
#ifdef CONFIG_PM
static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8776_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8776_spi_suspend NULL
#define wm8776_spi_resume NULL
#endif
static struct spi_driver wm8776_spi_driver = {
.driver = {
.name = "wm8776",
@ -638,8 +614,6 @@ static struct spi_driver wm8776_spi_driver = {
.owner = THIS_MODULE,
},
.probe = wm8776_spi_probe,
.suspend = wm8776_spi_suspend,
.resume = wm8776_spi_resume,
.remove = __devexit_p(wm8776_spi_remove),
};
#endif /* CONFIG_SPI_MASTER */
@ -673,21 +647,6 @@ static __devexit int wm8776_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
{
return snd_soc_suspend_device(&i2c->dev);
}
static int wm8776_i2c_resume(struct i2c_client *i2c)
{
return snd_soc_resume_device(&i2c->dev);
}
#else
#define wm8776_i2c_suspend NULL
#define wm8776_i2c_resume NULL
#endif
static const struct i2c_device_id wm8776_i2c_id[] = {
{ "wm8776", 0 },
{ }
@ -701,8 +660,6 @@ static struct i2c_driver wm8776_i2c_driver = {
},
.probe = wm8776_i2c_probe,
.remove = __devexit_p(wm8776_i2c_remove),
.suspend = wm8776_i2c_suspend,
.resume = wm8776_i2c_resume,
.id_table = wm8776_i2c_id,
};
#endif

View file

@ -618,8 +618,6 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -814,8 +812,8 @@ reenable:
return 0;
}
static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
}
@ -1312,21 +1310,6 @@ static __devexit int wm8900_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8900_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8900_i2c_suspend NULL
#define wm8900_i2c_resume NULL
#endif
static const struct i2c_device_id wm8900_i2c_id[] = {
{ "wm8900", 0 },
{ }
@ -1340,8 +1323,6 @@ static struct i2c_driver wm8900_i2c_driver = {
},
.probe = wm8900_i2c_probe,
.remove = __devexit_p(wm8900_i2c_remove),
.suspend = wm8900_i2c_suspend,
.resume = wm8900_i2c_resume,
.id_table = wm8900_i2c_id,
};
@ -1370,17 +1351,6 @@ static int wm8900_probe(struct platform_device *pdev)
ARRAY_SIZE(wm8900_snd_controls));
wm8900_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}

View file

@ -919,8 +919,6 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -1655,21 +1653,6 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8903_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8903_i2c_suspend NULL
#define wm8903_i2c_resume NULL
#endif
/* i2c codec control layer */
static const struct i2c_device_id wm8903_i2c_id[] = {
{ "wm8903", 0 },
@ -1684,8 +1667,6 @@ static struct i2c_driver wm8903_i2c_driver = {
},
.probe = wm8903_i2c_probe,
.remove = __devexit_p(wm8903_i2c_remove),
.suspend = wm8903_i2c_suspend,
.resume = wm8903_i2c_resume,
.id_table = wm8903_i2c_id,
};
@ -1712,17 +1693,8 @@ static int wm8903_probe(struct platform_device *pdev)
ARRAY_SIZE(wm8903_snd_controls));
wm8903_add_widgets(socdev->card->codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "wm8903: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
err:
return ret;
}

View file

@ -298,7 +298,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec)
ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
if (ret)
goto error_ret;
ret = snd_soc_dapm_new_widgets(codec);
error_ret:
return ret;
@ -536,8 +535,8 @@ static void pll_factors(unsigned int target, unsigned int source)
}
/* Untested at the moment */
static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@ -731,12 +730,6 @@ static int wm8940_probe(struct platform_device *pdev)
if (ret)
goto error_free_pcms;
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto error_free_pcms;
}
return ret;
error_free_pcms:
@ -877,21 +870,6 @@ static int __devexit wm8940_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8940_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8940_i2c_suspend NULL
#define wm8940_i2c_resume NULL
#endif
static const struct i2c_device_id wm8940_i2c_id[] = {
{ "wm8940", 0 },
{ }
@ -905,8 +883,6 @@ static struct i2c_driver wm8940_i2c_driver = {
},
.probe = wm8940_i2c_probe,
.remove = __devexit_p(wm8940_i2c_remove),
.suspend = wm8940_i2c_suspend,
.resume = wm8940_i2c_resume,
.id_table = wm8940_i2c_id,
};

View file

@ -307,7 +307,6 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -540,8 +539,8 @@ static int pll_factors(unsigned int source, unsigned int target,
return 0;
}
static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 reg;
@ -713,17 +712,9 @@ static int wm8960_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8960_snd_controls,
ARRAY_SIZE(wm8960_snd_controls));
wm8960_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -883,21 +874,6 @@ static __devexit int wm8960_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8960_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8960_i2c_suspend NULL
#define wm8960_i2c_resume NULL
#endif
static const struct i2c_device_id wm8960_i2c_id[] = {
{ "wm8960", 0 },
{ }
@ -911,8 +887,6 @@ static struct i2c_driver wm8960_i2c_driver = {
},
.probe = wm8960_i2c_probe,
.remove = __devexit_p(wm8960_i2c_remove),
.suspend = wm8960_i2c_suspend,
.resume = wm8960_i2c_resume,
.id_table = wm8960_i2c_id,
};

View file

@ -986,19 +986,9 @@ static int wm8961_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
ARRAY_SIZE(wm8961_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -1206,21 +1196,6 @@ static __devexit int wm8961_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8961_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8961_i2c_suspend NULL
#define wm8961_i2c_resume NULL
#endif
static const struct i2c_device_id wm8961_i2c_id[] = {
{ "wm8961", 0 },
{ }
@ -1234,8 +1209,6 @@ static struct i2c_driver wm8961_i2c_driver = {
},
.probe = wm8961_i2c_probe,
.remove = __devexit_p(wm8961_i2c_remove),
.suspend = wm8961_i2c_suspend,
.resume = wm8961_i2c_resume,
.id_table = wm8961_i2c_id,
};

View file

@ -338,8 +338,6 @@ static int wm8971_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -703,16 +701,9 @@ static int wm8971_init(struct snd_soc_device *socdev,
snd_soc_add_controls(codec, wm8971_snd_controls,
ARRAY_SIZE(wm8971_snd_controls));
wm8971_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8971: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
err:
kfree(codec->reg_cache);
return ret;

View file

@ -276,41 +276,42 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
struct pll_ {
unsigned int pre_div:4; /* prescale - 1 */
unsigned int pre_div:1;
unsigned int n:4;
unsigned int k;
};
static struct pll_ pll_div;
/* The size in bits of the pll divide multiplied by 10
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 24) * 10)
static void pll_factors(unsigned int target, unsigned int source)
static void pll_factors(struct pll_ *pll_div,
unsigned int target, unsigned int source)
{
unsigned long long Kpart;
unsigned int K, Ndiv, Nmod;
/* There is a fixed divide by 4 in the output path */
target *= 4;
Ndiv = target / source;
if (Ndiv < 6) {
source >>= 1;
pll_div.pre_div = 1;
source /= 2;
pll_div->pre_div = 1;
Ndiv = target / source;
} else
pll_div.pre_div = 0;
pll_div->pre_div = 0;
if ((Ndiv < 6) || (Ndiv > 12))
printk(KERN_WARNING
"WM8974 N value %u outwith recommended range!\n",
Ndiv);
pll_div.n = Ndiv;
pll_div->n = Ndiv;
Nmod = target % source;
Kpart = FIXED_PLL_SIZE * (long long)Nmod;
@ -325,13 +326,14 @@ static void pll_factors(unsigned int target, unsigned int source)
/* Move down to proper range now rounding is done */
K /= 10;
pll_div.k = K;
pll_div->k = K;
}
static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct pll_ pll_div;
u16 reg;
if (freq_in == 0 || freq_out == 0) {
@ -345,7 +347,7 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
return 0;
}
pll_factors(freq_out*4, freq_in);
pll_factors(&pll_div, freq_out, freq_in);
snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
@ -638,17 +640,9 @@ static int wm8974_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm8974_snd_controls,
ARRAY_SIZE(wm8974_snd_controls));
wm8974_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}

View file

@ -790,19 +790,9 @@ static int wm8988_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
ARRAY_SIZE(wm8988_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -944,21 +934,6 @@ static int wm8988_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm8988_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm8988_i2c_suspend NULL
#define wm8988_i2c_resume NULL
#endif
static const struct i2c_device_id wm8988_i2c_id[] = {
{ "wm8988", 0 },
{ }
@ -972,8 +947,6 @@ static struct i2c_driver wm8988_i2c_driver = {
},
.probe = wm8988_i2c_probe,
.remove = wm8988_i2c_remove,
.suspend = wm8988_i2c_suspend,
.resume = wm8988_i2c_resume,
.id_table = wm8988_i2c_id,
};
#endif
@ -1006,21 +979,6 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
return 0;
}
#ifdef CONFIG_PM
static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
{
return snd_soc_suspend_device(&spi->dev);
}
static int wm8988_spi_resume(struct spi_device *spi)
{
return snd_soc_resume_device(&spi->dev);
}
#else
#define wm8988_spi_suspend NULL
#define wm8988_spi_resume NULL
#endif
static struct spi_driver wm8988_spi_driver = {
.driver = {
.name = "wm8988",
@ -1029,8 +987,6 @@ static struct spi_driver wm8988_spi_driver = {
},
.probe = wm8988_spi_probe,
.remove = __devexit_p(wm8988_spi_remove),
.suspend = wm8988_spi_suspend,
.resume = wm8988_spi_resume,
};
#endif

View file

@ -920,7 +920,6 @@ static int wm8990_add_widgets(struct snd_soc_codec *codec)
/* set up the WM8990 audio map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -972,8 +971,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
pll_div->k = K;
}
static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
u16 reg;
struct snd_soc_codec *codec = codec_dai->codec;
@ -1409,16 +1408,9 @@ static int wm8990_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, wm8990_snd_controls,
ARRAY_SIZE(wm8990_snd_controls));
wm8990_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8990: failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;

View file

@ -422,7 +422,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
return 0;
}
static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
unsigned int Fref, unsigned int Fout)
{
struct snd_soc_codec *codec = dai->codec;
@ -1464,19 +1464,8 @@ static int wm8993_probe(struct platform_device *pdev)
wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
wm8993->pdata.lineout2_diff);
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card\n");
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
err:
return ret;
}
@ -1572,33 +1561,15 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
/* Use automatic clock configuration */
snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
if (!wm8993->pdata.lineout1_diff)
snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
WM8993_LINEOUT1_MODE,
WM8993_LINEOUT1_MODE);
if (!wm8993->pdata.lineout2_diff)
snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
WM8993_LINEOUT2_MODE,
WM8993_LINEOUT2_MODE);
if (wm8993->pdata.lineout1fb)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
if (wm8993->pdata.lineout2fb)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
/* Apply the microphone bias/detection configuration - the
* platform data is directly applicable to the register. */
snd_soc_update_bits(codec, WM8993_MICBIAS,
WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
WM8993_MICB1_LVL | WM8993_MICB2_LVL,
wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
wm8993->pdata.micbias1_lvl |
wm8993->pdata.micbias1_lvl << 1);
wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
wm8993->pdata.lineout2_diff,
wm8993->pdata.lineout1fb,
wm8993->pdata.lineout2fb,
wm8993->pdata.jd_scthr,
wm8993->pdata.jd_thr,
wm8993->pdata.micbias1_lvl,
wm8993->pdata.micbias2_lvl);
ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret != 0)
goto err;

View file

@ -1262,19 +1262,9 @@ static int wm9081_probe(struct platform_device *pdev)
snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets,
ARRAY_SIZE(wm9081_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
@ -1452,21 +1442,6 @@ static __devexit int wm9081_i2c_remove(struct i2c_client *client)
return 0;
}
#ifdef CONFIG_PM
static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
{
return snd_soc_suspend_device(&client->dev);
}
static int wm9081_i2c_resume(struct i2c_client *client)
{
return snd_soc_resume_device(&client->dev);
}
#else
#define wm9081_i2c_suspend NULL
#define wm9081_i2c_resume NULL
#endif
static const struct i2c_device_id wm9081_i2c_id[] = {
{ "wm9081", 0 },
{ }
@ -1480,8 +1455,6 @@ static struct i2c_driver wm9081_i2c_driver = {
},
.probe = wm9081_i2c_probe,
.remove = __devexit_p(wm9081_i2c_remove),
.suspend = wm9081_i2c_suspend,
.resume = wm9081_i2c_resume,
.id_table = wm9081_i2c_id,
};

View file

@ -205,7 +205,6 @@ static int wm9705_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets,
ARRAY_SIZE(wm9705_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -403,12 +402,6 @@ static int wm9705_soc_probe(struct platform_device *pdev)
ARRAY_SIZE(wm9705_snd_ac97_controls));
wm9705_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register card\n");
goto reset_err;
}
return 0;
reset_err:

View file

@ -436,7 +436,6 @@ static int wm9712_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -695,17 +694,11 @@ static int wm9712_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls));
wm9712_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register card\n");
goto reset_err;
}
return 0;
reset_err:
snd_soc_free_pcms(socdev);
pcm_err:
snd_soc_free_ac97_codec(codec);

View file

@ -625,7 +625,6 @@ static int wm9713_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
@ -800,8 +799,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = codec_dai->codec;
return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
@ -1247,14 +1246,11 @@ static int wm9713_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
wm9713_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0)
goto reset_err;
return 0;
reset_err:
snd_soc_free_pcms(socdev);
pcm_err:
snd_soc_free_ac97_codec(codec);

View file

@ -438,11 +438,11 @@ static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1LN"),
SND_SOC_DAPM_INPUT("IN1LP"),
SND_SOC_DAPM_INPUT("IN2LN"),
SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
SND_SOC_DAPM_INPUT("IN2LP:VXRN"),
SND_SOC_DAPM_INPUT("IN1RN"),
SND_SOC_DAPM_INPUT("IN1RP"),
SND_SOC_DAPM_INPUT("IN2RN"),
SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
@ -537,14 +537,14 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "IN1R PGA", "IN1RP Switch", "IN1RP" },
{ "IN1R PGA", "IN1RN Switch", "IN1RN" },
{ "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
{ "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" },
{ "IN2L PGA", "IN2LN Switch", "IN2LN" },
{ "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
{ "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" },
{ "IN2R PGA", "IN2RN Switch", "IN2RN" },
{ "Direct Voice", NULL, "IN2LP/VXRN" },
{ "Direct Voice", NULL, "IN2RP/VXRP" },
{ "Direct Voice", NULL, "IN2LP:VXRN" },
{ "Direct Voice", NULL, "IN2RP:VXRP" },
{ "MIXINL", "IN1L Switch", "IN1L PGA" },
{ "MIXINL", "IN2L Switch", "IN2L PGA" },
@ -565,7 +565,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Left Output Mixer", "Right Input Switch", "MIXINR" },
{ "Left Output Mixer", "IN2RN Switch", "IN2RN" },
{ "Left Output Mixer", "IN2LN Switch", "IN2LN" },
{ "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
{ "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" },
{ "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
{ "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
@ -573,7 +573,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
{ "Right Output Mixer", "Right Input Switch", "MIXINR" },
{ "Right Output Mixer", "IN2LN Switch", "IN2LN" },
{ "Right Output Mixer", "IN2RN Switch", "IN2RN" },
{ "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
{ "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" },
{ "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
{ "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
@ -738,6 +738,41 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
int jd_scthr, int jd_thr, int micbias1_lvl,
int micbias2_lvl)
{
if (!lineout1_diff)
snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
WM8993_LINEOUT1_MODE,
WM8993_LINEOUT1_MODE);
if (!lineout2_diff)
snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
WM8993_LINEOUT2_MODE,
WM8993_LINEOUT2_MODE);
if (lineout1fb)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
if (lineout2fb)
snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
snd_soc_update_bits(codec, WM8993_MICBIAS,
WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
WM8993_MICB1_LVL | WM8993_MICB2_LVL,
jd_scthr << WM8993_JD_SCTHR_SHIFT |
jd_thr << WM8993_JD_THR_SHIFT |
micbias1_lvl |
micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
return 0;
}
EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");

View file

@ -20,5 +20,10 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
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 *,
int lineout1_diff, int lineout2_diff,
int lineout1fb, int lineout2fb,
int jd_scthr, int jd_thr,
int micbias1_lvl, int micbias2_lvl);
#endif

View file

@ -13,9 +13,9 @@ config SND_DAVINCI_SOC_MCASP
tristate
config SND_DAVINCI_SOC_EVM
tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
depends on SND_DAVINCI_SOC
depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
select SND_DAVINCI_SOC_I2S
select SND_SOC_TLV320AIC3X
help

View file

@ -45,7 +45,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
unsigned sysclk;
/* ASP1 on DM355 EVM is clocked by an external oscillator */
if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
machine_is_davinci_dm365_evm())
sysclk = 27000000;
/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@ -176,7 +177,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = {
.ops = &evm_ops,
};
/* davinci-evm audio machine driver */
/* davinci dm6446, dm355 or dm365 evm audio machine driver */
static struct snd_soc_card snd_soc_card_evm = {
.name = "DaVinci EVM",
.platform = &davinci_soc_platform,
@ -243,7 +244,7 @@ static int __init evm_init(void)
int index;
int ret;
if (machine_is_davinci_evm()) {
if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
evm_snd_dev_data = &evm_snd_devdata;
index = 0;
} else if (machine_is_davinci_dm355_evm()) {

View file

@ -97,12 +97,24 @@ enum {
DAVINCI_MCBSP_WORD_32,
};
static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
[SNDRV_PCM_FORMAT_S8] = 1,
[SNDRV_PCM_FORMAT_S16_LE] = 2,
[SNDRV_PCM_FORMAT_S32_LE] = 4,
};
static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
[SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
[SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
[SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
};
static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
[SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE,
[SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE,
};
struct davinci_mcbsp_dev {
/*
* dma_params must be first because rtd->dai->cpu_dai->private_data
* is cast to a pointer of an array of struct davinci_pcm_dma_params in
* davinci_pcm_open.
*/
struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
#define MOD_DSP_A 0
@ -110,6 +122,27 @@ struct davinci_mcbsp_dev {
int mode;
u32 pcr;
struct clk *clk;
/*
* Combining both channels into 1 element will at least double the
* amount of time between servicing the dma channel, increase
* effiency, and reduce the chance of overrun/underrun. But,
* it will result in the left & right channels being swapped.
*
* If relabeling the left and right channels is not possible,
* you may want to let the codec know to swap them back.
*
* It may allow x10 the amount of time to service dma requests,
* if the codec is master and is using an unnecessarily fast bit clock
* (ie. tlvaic23b), independent of the sample rate. So, having an
* entire frame at once means it can be serviced at the sample rate
* instead of the bit clock rate.
*
* In the now unlikely case that an underrun still
* occurs, both the left and right samples will be repeated
* so that no pops are heard, and the left and right channels
* won't end up being swapped because of the underrun.
*/
unsigned enable_channel_combine:1;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@ -349,6 +382,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
int mcbsp_word_length;
unsigned int rcr, xcr, srgr;
u32 spcr;
snd_pcm_format_t fmt;
unsigned element_cnt = 1;
/* general line settings */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@ -378,27 +413,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
}
/* Determine xfer data type */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
dma_params->data_type = 1;
mcbsp_word_length = DAVINCI_MCBSP_WORD_8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
dma_params->data_type = 2;
mcbsp_word_length = DAVINCI_MCBSP_WORD_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
dma_params->data_type = 4;
mcbsp_word_length = DAVINCI_MCBSP_WORD_32;
break;
default:
fmt = params_format(params);
if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
return -EINVAL;
}
dma_params->acnt = dma_params->data_type;
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
if (params_channels(params) == 2) {
element_cnt = 2;
if (double_fmt[fmt] && dev->enable_channel_combine) {
element_cnt = 1;
fmt = double_fmt[fmt];
}
}
dma_params->acnt = dma_params->data_type = data_type[fmt];
dma_params->fifo_level = 0;
mcbsp_word_length = asp_word_length[fmt];
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@ -513,7 +545,13 @@ static int davinci_i2s_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_release_region;
}
if (pdata) {
dev->enable_channel_combine = pdata->enable_channel_combine;
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size =
pdata->sram_size_playback;
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
pdata->sram_size_capture;
}
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
@ -547,6 +585,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
davinci_i2s_dai.private_data = dev;
davinci_i2s_dai.dma_data = dev->dma_params;
ret = snd_soc_register_dai(&davinci_i2s_dai);
if (ret != 0)
goto err_free_mem;

Some files were not shown because too many files have changed in this diff Show more