mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 11:16:11 +00:00
Merge branch 'topic/asoc' into for-linus
This commit is contained in:
commit
baf9226667
154 changed files with 8956 additions and 2445 deletions
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
276
drivers/mfd/twl4030-codec.c
Normal 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");
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
272
include/linux/mfd/twl4030-codec.h
Normal file
272
include/linux/mfd/twl4030-codec.h
Normal 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__ */
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
20
include/sound/tlv320dac33-plat.h
Normal file
20
include/sound/tlv320dac33-plat.h
Normal 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 */
|
30
include/sound/tpa6130a2-plat.h
Normal file
30
include/sound/tpa6130a2-plat.h
Normal 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
|
|
@ -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/
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
123
sound/soc/codecs/ads117x.c
Normal 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");
|
13
sound/soc/codecs/ads117x.h
Normal file
13
sound/soc/codecs/ads117x.h
Normal 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;
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
815
sound/soc/codecs/ak4671.c
Normal 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
156
sound/soc/codecs/ak4671.h
Normal 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
|
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
1229
sound/soc/codecs/tlv320dac33.c
Normal file
1229
sound/soc/codecs/tlv320dac33.c
Normal file
File diff suppressed because it is too large
Load diff
267
sound/soc/codecs/tlv320dac33.h
Normal file
267
sound/soc/codecs/tlv320dac33.h
Normal 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 */
|
463
sound/soc/codecs/tpa6130a2.c
Normal file
463
sound/soc/codecs/tpa6130a2.c
Normal 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);
|
61
sound/soc/codecs/tpa6130a2.h
Normal file
61
sound/soc/codecs/tpa6130a2.h
Normal 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__ */
|
|
@ -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");
|
||||
|
|
|
@ -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__ */
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
633
sound/soc/codecs/wm8711.c
Normal 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
42
sound/soc/codecs/wm8711.h
Normal 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
135
sound/soc/codecs/wm8727.c
Normal 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
21
sound/soc/codecs/wm8727.h
Normal 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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue