Merge branch 'topic/asoc' into for-linus

This commit is contained in:
Takashi Iwai 2010-08-05 11:17:01 +02:00
commit e71981343a
127 changed files with 9119 additions and 668 deletions

View File

@ -208,7 +208,7 @@ static struct snd_platform_data da830_evm_snd_data = {
.num_serializer = ARRAY_SIZE(da830_iis_serializer_direction),
.tdm_slots = 2,
.serial_dir = da830_iis_serializer_direction,
.eventq_no = EVENTQ_0,
.asp_chan_q = EVENTQ_0,
.version = MCASP_VERSION_2,
.txnumevt = 1,
.rxnumevt = 1,

View File

@ -343,7 +343,7 @@ static struct snd_platform_data da850_evm_snd_data = {
.num_serializer = ARRAY_SIZE(da850_iis_serializer_direction),
.tdm_slots = 2,
.serial_dir = da850_iis_serializer_direction,
.eventq_no = EVENTQ_1,
.asp_chan_q = EVENTQ_1,
.version = MCASP_VERSION_2,
.txnumevt = 1,
.rxnumevt = 1,

View File

@ -323,7 +323,7 @@ static struct snd_platform_data dm646x_evm_snd_data[] = {
.num_serializer = ARRAY_SIZE(dm646x_iis_serializer_direction),
.tdm_slots = 2,
.serial_dir = dm646x_iis_serializer_direction,
.eventq_no = EVENTQ_0,
.asp_chan_q = EVENTQ_0,
},
{
.tx_dma_offset = 0x400,
@ -332,7 +332,7 @@ static struct snd_platform_data dm646x_evm_snd_data[] = {
.num_serializer = ARRAY_SIZE(dm646x_dit_serializer_direction),
.tdm_slots = 32,
.serial_dir = dm646x_dit_serializer_direction,
.eventq_no = EVENTQ_0,
.asp_chan_q = EVENTQ_0,
},
};

View File

@ -52,7 +52,8 @@
struct snd_platform_data {
u32 tx_dma_offset;
u32 rx_dma_offset;
enum dma_event_q eventq_no; /* event queue number */
enum dma_event_q asp_chan_q; /* event queue number for ASP channel */
enum dma_event_q ram_chan_q; /* event queue number for RAM channel */
unsigned int codec_fmt;
/*
* Allowing this is more efficient and eliminates left and right swaps
@ -63,6 +64,49 @@ struct snd_platform_data {
unsigned sram_size_playback;
unsigned sram_size_capture;
/*
* If McBSP peripheral gets the clock from an external pin,
* there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR
* and MCBSP_CLKS.
* Depending on different hardware connections it is possible
* to use this setting to change the behaviour of McBSP
* driver. The dm365_clk_input_pin enum is available for dm365
*/
int clk_input_pin;
/*
* This flag works when both clock and FS are outputs for the cpu
* and makes clock more accurate (FS is not symmetrical and the
* clock is very fast.
* The clock becoming faster is named
* i2s continuous serial clock (I2S_SCK) and it is an externally
* visible bit clock.
*
* first line : WordSelect
* second line : ContinuousSerialClock
* third line: SerialData
*
* SYMMETRICAL APPROACH:
* _______________________ LEFT
* _| RIGHT |______________________|
* _ _ _ _ _ _ _ _
* _| |_| |_ x16 _| |_| |_| |_| |_ x16 _| |_| |_
* _ _ _ _ _ _ _ _
* _/ \_/ \_ ... _/ \_/ \_/ \_/ \_ ... _/ \_/ \_
* \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
*
* ACCURATE CLOCK APPROACH:
* ______________ LEFT
* _| RIGHT |_______________________________|
* _ _ _ _ _ _ _ _ _
* _| |_ x16 _| |_| |_ x16 _| |_| |_| |_| |_| |_| |
* _ _ _ _ dummy cycles
* _/ \_ ... _/ \_/ \_ ... _/ \__________________
* \_/ \_/ \_/ \_/
*
*/
bool i2s_accurate_sck;
/* McASP specific fields */
int tdm_slots;
u8 op_mode;
@ -78,6 +122,11 @@ enum {
MCASP_VERSION_2, /* DA8xx/OMAPL1x */
};
enum dm365_clk_input_pin {
MCBSP_CLKR = 0, /* DM365 */
MCBSP_CLKS,
};
#define INACTIVE_MODE 0
#define TX_MODE 1
#define RX_MODE 2

View File

@ -43,7 +43,8 @@ static unsigned long get_uart_rate(struct clk *clk);
static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
static int set_div_rate(struct clk *clk, unsigned long rate);
static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate);
static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate);
static struct clk clk_xtali = {
.rate = EP93XX_EXT_CLK_RATE,
@ -112,6 +113,29 @@ static struct clk clk_video = {
.set_rate = set_div_rate,
};
static struct clk clk_i2s_mclk = {
.sw_locked = 1,
.enable_reg = EP93XX_SYSCON_I2SCLKDIV,
.enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE,
.set_rate = set_div_rate,
};
static struct clk clk_i2s_sclk = {
.sw_locked = 1,
.parent = &clk_i2s_mclk,
.enable_reg = EP93XX_SYSCON_I2SCLKDIV,
.enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA,
.set_rate = set_i2s_sclk_rate,
};
static struct clk clk_i2s_lrclk = {
.sw_locked = 1,
.parent = &clk_i2s_sclk,
.enable_reg = EP93XX_SYSCON_I2SCLKDIV,
.enable_mask = EP93XX_SYSCON_I2SCLKDIV_SENA,
.set_rate = set_i2s_lrclk_rate,
};
/* DMA Clocks */
static struct clk clk_m2p0 = {
.parent = &clk_h,
@ -191,6 +215,9 @@ static struct clk_lookup clocks[] = {
INIT_CK("ep93xx-keypad", NULL, &clk_keypad),
INIT_CK("ep93xx-fb", NULL, &clk_video),
INIT_CK("ep93xx-spi.0", NULL, &clk_spi),
INIT_CK("ep93xx-i2s", "mclk", &clk_i2s_mclk),
INIT_CK("ep93xx-i2s", "sclk", &clk_i2s_sclk),
INIT_CK("ep93xx-i2s", "lrclk", &clk_i2s_lrclk),
INIT_CK(NULL, "pwm_clk", &clk_pwm),
INIT_CK(NULL, "m2p0", &clk_m2p0),
INIT_CK(NULL, "m2p1", &clk_m2p1),
@ -401,6 +428,44 @@ static int set_div_rate(struct clk *clk, unsigned long rate)
return 0;
}
static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate)
{
unsigned val = __raw_readl(clk->enable_reg);
if (rate == clk_i2s_mclk.rate / 2)
ep93xx_syscon_swlocked_write(val & ~EP93XX_I2SCLKDIV_SDIV,
clk->enable_reg);
else if (rate == clk_i2s_mclk.rate / 4)
ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_SDIV,
clk->enable_reg);
else
return -EINVAL;
clk_i2s_sclk.rate = rate;
return 0;
}
static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate)
{
unsigned val = __raw_readl(clk->enable_reg) &
~EP93XX_I2SCLKDIV_LRDIV_MASK;
if (rate == clk_i2s_sclk.rate / 32)
ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV32,
clk->enable_reg);
else if (rate == clk_i2s_sclk.rate / 64)
ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV64,
clk->enable_reg);
else if (rate == clk_i2s_sclk.rate / 128)
ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV128,
clk->enable_reg);
else
return -EINVAL;
clk_i2s_lrclk.rate = rate;
return 0;
}
int clk_set_rate(struct clk *clk, unsigned long rate)
{
if (clk->set_rate)

View File

@ -714,6 +714,73 @@ void ep93xx_keypad_release_gpio(struct platform_device *pdev)
}
EXPORT_SYMBOL(ep93xx_keypad_release_gpio);
/*************************************************************************
* EP93xx I2S audio peripheral handling
*************************************************************************/
static struct resource ep93xx_i2s_resource[] = {
{
.start = EP93XX_I2S_PHYS_BASE,
.end = EP93XX_I2S_PHYS_BASE + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device ep93xx_i2s_device = {
.name = "ep93xx-i2s",
.id = -1,
.num_resources = ARRAY_SIZE(ep93xx_i2s_resource),
.resource = ep93xx_i2s_resource,
};
void __init ep93xx_register_i2s(void)
{
platform_device_register(&ep93xx_i2s_device);
}
#define EP93XX_SYSCON_DEVCFG_I2S_MASK (EP93XX_SYSCON_DEVCFG_I2SONSSP | \
EP93XX_SYSCON_DEVCFG_I2SONAC97)
#define EP93XX_I2SCLKDIV_MASK (EP93XX_SYSCON_I2SCLKDIV_ORIDE | \
EP93XX_SYSCON_I2SCLKDIV_SPOL)
int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config)
{
unsigned val;
/* Sanity check */
if (i2s_pins & ~EP93XX_SYSCON_DEVCFG_I2S_MASK)
return -EINVAL;
if (i2s_config & ~EP93XX_I2SCLKDIV_MASK)
return -EINVAL;
/* Must have only one of I2SONSSP/I2SONAC97 set */
if ((i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONSSP) ==
(i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONAC97))
return -EINVAL;
ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK);
ep93xx_devcfg_set_bits(i2s_pins);
/*
* This is potentially racy with the clock api for i2s_mclk, sclk and
* lrclk. Since the i2s driver is the only user of those clocks we
* rely on it to prevent parallel use of this function and the
* clock api for the i2s clocks.
*/
val = __raw_readl(EP93XX_SYSCON_I2SCLKDIV);
val &= ~EP93XX_I2SCLKDIV_MASK;
val |= i2s_config;
ep93xx_syscon_swlocked_write(val, EP93XX_SYSCON_I2SCLKDIV);
return 0;
}
EXPORT_SYMBOL(ep93xx_i2s_acquire);
void ep93xx_i2s_release(void)
{
ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK);
}
EXPORT_SYMBOL(ep93xx_i2s_release);
extern void ep93xx_gpio_init(void);

View File

@ -93,6 +93,7 @@
/* APB peripherals */
#define EP93XX_TIMER_BASE EP93XX_APB_IOMEM(0x00010000)
#define EP93XX_I2S_PHYS_BASE EP93XX_APB_PHYS(0x00020000)
#define EP93XX_I2S_BASE EP93XX_APB_IOMEM(0x00020000)
#define EP93XX_SECURITY_BASE EP93XX_APB_IOMEM(0x00030000)
@ -194,6 +195,15 @@
#define EP93XX_SYSCON_CLKDIV_ESEL (1<<14)
#define EP93XX_SYSCON_CLKDIV_PSEL (1<<13)
#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8
#define EP93XX_SYSCON_I2SCLKDIV EP93XX_SYSCON_REG(0x8c)
#define EP93XX_SYSCON_I2SCLKDIV_SENA (1<<31)
#define EP93XX_SYSCON_I2SCLKDIV_ORIDE (1<<29)
#define EP93XX_SYSCON_I2SCLKDIV_SPOL (1<<19)
#define EP93XX_I2SCLKDIV_SDIV (1 << 16)
#define EP93XX_I2SCLKDIV_LRDIV32 (0 << 17)
#define EP93XX_I2SCLKDIV_LRDIV64 (1 << 17)
#define EP93XX_I2SCLKDIV_LRDIV128 (2 << 17)
#define EP93XX_I2SCLKDIV_LRDIV_MASK (3 << 17)
#define EP93XX_SYSCON_KEYTCHCLKDIV EP93XX_SYSCON_REG(0x90)
#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN (1<<31)
#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV (1<<16)

View File

@ -55,6 +55,9 @@ void ep93xx_pwm_release_gpio(struct platform_device *pdev);
void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data);
int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
void ep93xx_keypad_release_gpio(struct platform_device *pdev);
void ep93xx_register_i2s(void);
int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config);
void ep93xx_i2s_release(void);
void ep93xx_init_devices(void);
extern struct sys_timer ep93xx_timer;

View File

@ -157,6 +157,7 @@ static void __init snappercl15_init_machine(void)
ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data,
ARRAY_SIZE(snappercl15_i2c_data));
ep93xx_register_fb(&snappercl15_fb_info);
ep93xx_register_i2s();
platform_device_register(&snappercl15_nand_device);
}

View File

@ -25,6 +25,7 @@
#include <asm/mach/time.h>
#include <mach/kirkwood.h>
#include <mach/bridge-regs.h>
#include <plat/audio.h>
#include <plat/cache-feroceon-l2.h>
#include <plat/ehci-orion.h>
#include <plat/mvsdio.h>
@ -864,6 +865,42 @@ struct sys_timer kirkwood_timer = {
.init = kirkwood_timer_init,
};
/*****************************************************************************
* Audio
****************************************************************************/
static struct resource kirkwood_i2s_resources[] = {
[0] = {
.start = AUDIO_PHYS_BASE,
.end = AUDIO_PHYS_BASE + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_KIRKWOOD_I2S,
.end = IRQ_KIRKWOOD_I2S,
.flags = IORESOURCE_IRQ,
},
};
static struct kirkwood_asoc_platform_data kirkwood_i2s_data = {
.dram = &kirkwood_mbus_dram_info,
.burst = 128,
};
static struct platform_device kirkwood_i2s_device = {
.name = "kirkwood-i2s",
.id = -1,
.num_resources = ARRAY_SIZE(kirkwood_i2s_resources),
.resource = kirkwood_i2s_resources,
.dev = {
.platform_data = &kirkwood_i2s_data,
},
};
void __init kirkwood_audio_init(void)
{
kirkwood_clk_ctrl |= CGC_AUDIO;
platform_device_register(&kirkwood_i2s_device);
}
/*****************************************************************************
* General
@ -923,6 +960,7 @@ void __init kirkwood_init(void)
kirkwood_spi_plat_data.tclk = kirkwood_tclk;
kirkwood_uart0_data[0].uartclk = kirkwood_tclk;
kirkwood_uart1_data[0].uartclk = kirkwood_tclk;
kirkwood_i2s_data.tclk = kirkwood_tclk;
/*
* Disable propagation of mbus errors to the CPU local bus,

View File

@ -17,6 +17,7 @@ struct mv_sata_platform_data;
struct mvsdio_platform_data;
struct mtd_partition;
struct mtd_info;
struct kirkwood_asoc_platform_data;
/*
* Basic Kirkwood init functions used early by machine-setup.
@ -43,6 +44,7 @@ void kirkwood_uart0_init(void);
void kirkwood_uart1_init(void);
void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay);
void kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts, int (*dev_ready)(struct mtd_info *));
void kirkwood_audio_init(void);
extern int kirkwood_tclk;
extern struct sys_timer kirkwood_timer;

View File

@ -96,6 +96,9 @@
#define SDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0x90000)
#define AUDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0xA0000)
#define AUDIO_VIRT_BASE (KIRKWOOD_REGS_VIRT_BASE | 0xA0000)
/*
* Supported devices and revisions.
*/

View File

@ -15,6 +15,7 @@
#include <linux/mtd/partitions.h>
#include <linux/ata_platform.h>
#include <linux/mv643xx_eth.h>
#include <linux/i2c.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/kirkwood.h>
@ -60,6 +61,12 @@ static unsigned int openrd_mpp_config[] __initdata = {
0
};
static struct i2c_board_info i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO("cs42l51", 0x4a),
},
};
static void __init openrd_init(void)
{
/*
@ -80,6 +87,12 @@ static void __init openrd_init(void)
kirkwood_sdio_init(&openrd_mvsdio_data);
kirkwood_i2c_init();
if (machine_is_openrd_client()) {
i2c_register_board_info(0, i2c_board_info,
ARRAY_SIZE(i2c_board_info));
kirkwood_audio_init();
}
}
static int __init openrd_pci_init(void)

View File

@ -133,7 +133,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP1_IRQ_RX,
.tx_irq = INT_24XX_MCBSP1_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
.buffer_size = 0x80, /* The FIFO has 128 locations */
},
{
.phys_base = OMAP34XX_MCBSP2_BASE,
@ -143,7 +143,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP2_IRQ_RX,
.tx_irq = INT_24XX_MCBSP2_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x3FF,
.buffer_size = 0x500, /* The FIFO has 1024 + 256 locations */
},
{
.phys_base = OMAP34XX_MCBSP3_BASE,
@ -153,7 +153,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP3_IRQ_RX,
.tx_irq = INT_24XX_MCBSP3_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
.buffer_size = 0x80, /* The FIFO has 128 locations */
},
{
.phys_base = OMAP34XX_MCBSP4_BASE,
@ -162,7 +162,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP4_IRQ_RX,
.tx_irq = INT_24XX_MCBSP4_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
.buffer_size = 0x80, /* The FIFO has 128 locations */
},
{
.phys_base = OMAP34XX_MCBSP5_BASE,
@ -171,7 +171,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP5_IRQ_RX,
.tx_irq = INT_24XX_MCBSP5_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
.buffer_size = 0x80, /* The FIFO has 128 locations */
},
};
#define OMAP34XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap34xx_mcbsp_pdata)

View File

@ -10,6 +10,9 @@ struct imx_ssi_platform_data {
unsigned int flags;
#define IMX_SSI_DMA (1 << 0)
#define IMX_SSI_USE_AC97 (1 << 1)
#define IMX_SSI_NET (1 << 2)
#define IMX_SSI_SYN (1 << 3)
#define IMX_SSI_USE_I2S_SLAVE (1 << 4)
void (*ac97_reset) (struct snd_ac97 *ac97);
void (*ac97_warm_reset)(struct snd_ac97 *ac97);
};

View File

@ -473,6 +473,7 @@ void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold);
void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
u16 omap_mcbsp_get_fifo_size(unsigned int id);
u16 omap_mcbsp_get_tx_delay(unsigned int id);
u16 omap_mcbsp_get_rx_delay(unsigned int id);
int omap_mcbsp_get_dma_op_mode(unsigned int id);
@ -483,6 +484,7 @@ static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
{ }
static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
static inline u16 omap_mcbsp_get_fifo_size(unsigned int id) { return 0; }
static inline u16 omap_mcbsp_get_tx_delay(unsigned int id) { return 0; }
static inline u16 omap_mcbsp_get_rx_delay(unsigned int id) { return 0; }
static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }

View File

@ -481,9 +481,9 @@ int omap_st_is_enabled(unsigned int id)
EXPORT_SYMBOL(omap_st_is_enabled);
/*
* omap_mcbsp_set_tx_threshold configures how to deal
* with transmit threshold. the threshold value and handler can be
* configure in here.
* omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
* The threshold parameter is 1 based, and it is converted (threshold - 1)
* for the THRSH2 register.
*/
void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
{
@ -498,14 +498,15 @@ void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
}
mcbsp = id_to_mcbsp_ptr(id);
MCBSP_WRITE(mcbsp, THRSH2, threshold);
if (threshold && threshold <= mcbsp->max_tx_thres)
MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
}
EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
/*
* omap_mcbsp_set_rx_threshold configures how to deal
* with receive threshold. the threshold value and handler can be
* configure in here.
* omap_mcbsp_set_rx_threshold configures the receive threshold in words.
* The threshold parameter is 1 based, and it is converted (threshold - 1)
* for the THRSH1 register.
*/
void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
{
@ -520,7 +521,8 @@ void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
}
mcbsp = id_to_mcbsp_ptr(id);
MCBSP_WRITE(mcbsp, THRSH1, threshold);
if (threshold && threshold <= mcbsp->max_rx_thres)
MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
}
EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
@ -560,8 +562,20 @@ u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
}
EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
#define MCBSP2_FIFO_SIZE 0x500 /* 1024 + 256 locations */
#define MCBSP1345_FIFO_SIZE 0x80 /* 128 locations */
u16 omap_mcbsp_get_fifo_size(unsigned int id)
{
struct omap_mcbsp *mcbsp;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
return -ENODEV;
}
mcbsp = id_to_mcbsp_ptr(id);
return mcbsp->pdata->buffer_size;
}
EXPORT_SYMBOL(omap_mcbsp_get_fifo_size);
/*
* omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
*/
@ -580,10 +594,7 @@ u16 omap_mcbsp_get_tx_delay(unsigned int id)
buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
/* Number of slots are different in McBSP ports */
if (mcbsp->id == 2)
return MCBSP2_FIFO_SIZE - buffstat;
else
return MCBSP1345_FIFO_SIZE - buffstat;
return mcbsp->pdata->buffer_size - buffstat;
}
EXPORT_SYMBOL(omap_mcbsp_get_tx_delay);
@ -1683,8 +1694,16 @@ static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
{
mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
if (cpu_is_omap34xx()) {
mcbsp->max_tx_thres = max_thres(mcbsp);
mcbsp->max_rx_thres = max_thres(mcbsp);
/*
* Initially configure the maximum thresholds to a safe value.
* The McBSP FIFO usage with these values should not go under
* 16 locations.
* If the whole FIFO without safety buffer is used, than there
* is a possibility that the DMA will be not able to push the
* new data on time, causing channel shifts in runtime.
*/
mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
/*
* REVISIT: Set dmap_op_mode to THRESHOLD as default
* for mcbsp2 instances.

View File

@ -0,0 +1,11 @@
#ifndef __PLAT_AUDIO_H
#define __PLAT_AUDIO_H
#include <linux/mbus.h>
struct kirkwood_asoc_platform_data {
u32 tclk;
struct mbus_dram_target_info *dram;
int burst;
};
#endif

View File

@ -12,6 +12,9 @@
* published by the Free Software Foundation.
*/
#define FSI_PORT_A 0
#define FSI_PORT_B 1
/* flags format
* 0xABCDEEFF
@ -55,12 +58,14 @@
#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK)
#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK)
#define SH_FSI_FMT_MONO (1 << 0)
#define SH_FSI_FMT_MONO_DELAY (1 << 1)
#define SH_FSI_FMT_PCM (1 << 2)
#define SH_FSI_FMT_I2S (1 << 3)
#define SH_FSI_FMT_TDM (1 << 4)
#define SH_FSI_FMT_TDM_DELAY (1 << 5)
#define SH_FSI_FMT_MONO 0
#define SH_FSI_FMT_MONO_DELAY 1
#define SH_FSI_FMT_PCM 2
#define SH_FSI_FMT_I2S 3
#define SH_FSI_FMT_TDM 4
#define SH_FSI_FMT_TDM_DELAY 5
#define SH_FSI_FMT_SPDIF 6
#define SH_FSI_IFMT_TDM_CH(x) \
(SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x))
@ -72,9 +77,41 @@
#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
(SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
/*
* set_rate return value
*
* see ACKMD/BPFMD on
* ACK_MD (FSI2)
* CKG1 (FSI)
*
* err: return value < 0
*
* 0x-00000AB
*
* A: ACKMD value
* B: BPFMD value
*/
#define SH_FSI_ACKMD_MASK (0xF << 0)
#define SH_FSI_ACKMD_512 (1 << 0)
#define SH_FSI_ACKMD_256 (2 << 0)
#define SH_FSI_ACKMD_128 (3 << 0)
#define SH_FSI_ACKMD_64 (4 << 0)
#define SH_FSI_ACKMD_32 (5 << 0)
#define SH_FSI_BPFMD_MASK (0xF << 4)
#define SH_FSI_BPFMD_512 (1 << 4)
#define SH_FSI_BPFMD_256 (2 << 4)
#define SH_FSI_BPFMD_128 (3 << 4)
#define SH_FSI_BPFMD_64 (4 << 4)
#define SH_FSI_BPFMD_32 (5 << 4)
#define SH_FSI_BPFMD_16 (6 << 4)
struct sh_fsi_platform_info {
unsigned long porta_flags;
unsigned long portb_flags;
int (*set_rate)(int is_porta, int rate); /* for master mode */
};
extern struct snd_soc_dai fsi_soc_dai[2];

View File

@ -273,6 +273,8 @@
#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
#define SND_SOC_DAPM_PRE_POST_PMD \
(SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)
/* convenience event type detection */
#define SND_SOC_DAPM_EVENT_ON(e) \

View File

@ -170,6 +170,21 @@
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&xenum }
#define SOC_DOUBLE_R_SX_TLV(xname, xreg_left, xreg_right, xshift,\
xmin, xmax, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw_2r_sx, \
.get = snd_soc_get_volsw_2r_sx, \
.put = snd_soc_put_volsw_2r_sx, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg_left, \
.rreg = xreg_right, .shift = xshift, \
.min = xmin, .max = xmax} }
/*
* Simplified versions of above macros, declaring a struct and calculating
* ARRAY_SIZE internally
@ -329,6 +344,12 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_limit_volume(struct snd_soc_codec *codec,
const char *name, int max);
int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
/**
* struct snd_soc_jack_pin - Describes a pin to update based on jack detection

View File

@ -15,6 +15,8 @@
struct tlv320dac33_platform_data {
int power_gpio;
int mode1_latency; /* latency caused by the i2c writes in us */
int auto_fifo_config; /* FIFO config based on the period size */
int keep_bclk; /* Keep the BCLK running in FIFO modes */
u8 burst_bclkdiv;
};

View File

@ -18,6 +18,18 @@ struct uda134x_platform_data {
struct l3_pins l3;
void (*power) (int);
int model;
/*
ALSA SOC usually puts the device in standby mode when it's not used
for sometime. If you unset is_powered_on_standby the driver will
turn off the ADC/DAC when this callback is invoked and turn it back
on when needed. Unfortunately this will result in a very light bump
(it can be audible only with good earphones). If this bothers you
set is_powered_on_standby, you will have slightly higher power
consumption. Please note that sending the L3 command for ADC is
enough to make the bump, so it doesn't make difference if you
completely take off power from the codec.
*/
int is_powered_on_standby;
#define UDA134X_UDA1340 1
#define UDA134X_UDA1341 2
#define UDA134X_UDA1344 3

View File

@ -28,9 +28,13 @@ source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
source "sound/soc/blackfin/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/ep93xx/Kconfig"
source "sound/soc/fsl/Kconfig"
source "sound/soc/imx/Kconfig"
source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/s6000/Kconfig"

View File

@ -6,9 +6,13 @@ obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += ep93xx/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += imx/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += s6000/

View File

@ -77,7 +77,6 @@ struct atmel_runtime_data {
size_t period_size;
dma_addr_t period_ptr; /* physical address of next period */
int periods; /* period index of period_ptr */
/* PDC register save */
u32 pdc_xpr_save;

View File

@ -549,7 +549,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
ssc_p->daifmt);
return -EINVAL;
break;
}
pr_debug("atmel_ssc_hw_params: "
"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",

View File

@ -375,12 +375,10 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
}
ret = -EBUSY;
wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_ac97");
if (!wd->ioarea)
if (!request_mem_region(r->start, resource_size(r), pdev->name))
goto out0;
wd->mmio = ioremap(r->start, 0xffff);
wd->mmio = ioremap(r->start, resource_size(r));
if (!wd->mmio)
goto out1;
@ -410,8 +408,7 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
out1:
release_resource(wd->ioarea);
kfree(wd->ioarea);
release_mem_region(r->start, resource_size(r));
out0:
kfree(wd);
return ret;
@ -420,6 +417,7 @@ out0:
static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (wd->dmapd)
au1xpsc_pcm_destroy(wd->dmapd);
@ -433,8 +431,7 @@ static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
au_sync();
iounmap(wd->mmio);
release_resource(wd->ioarea);
kfree(wd->ioarea);
release_mem_region(r->start, resource_size(r));
kfree(wd);
au1xpsc_ac97_workdata = NULL; /* MDEV */

View File

@ -321,12 +321,10 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
}
ret = -EBUSY;
wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_i2s");
if (!wd->ioarea)
if (!request_mem_region(r->start, resource_size(r), pdev->name))
goto out0;
wd->mmio = ioremap(r->start, 0xffff);
wd->mmio = ioremap(r->start, resource_size(r));
if (!wd->mmio)
goto out1;
@ -362,8 +360,7 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
out1:
release_resource(wd->ioarea);
kfree(wd->ioarea);
release_mem_region(r->start, resource_size(r));
out0:
kfree(wd);
return ret;
@ -372,6 +369,7 @@ out0:
static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
{
struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (wd->dmapd)
au1xpsc_pcm_destroy(wd->dmapd);
@ -384,8 +382,7 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
au_sync();
iounmap(wd->mmio);
release_resource(wd->ioarea);
kfree(wd->ioarea);
release_mem_region(r->start, resource_size(r));
kfree(wd);
au1xpsc_i2s_workdata = NULL; /* MDEV */

View File

@ -32,7 +32,6 @@ struct au1xpsc_audio_data {
unsigned long rate;
unsigned long pm[2];
struct resource *ioarea;
struct mutex lock;
struct platform_device *dmapd;
};

View File

@ -255,8 +255,7 @@ EXPORT_SYMBOL_GPL(soc_ac97_ops);
#ifdef CONFIG_PM
static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport =
(struct sport_device *)dai->private_data;
struct sport_device *sport = dai->private_data;
pr_debug("%s : sport %d\n", __func__, dai->id);
if (!dai->active)
@ -271,8 +270,7 @@ static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport =
(struct sport_device *)dai->private_data;
struct sport_device *sport = dai->private_data;
pr_debug("%s : sport %d\n", __func__, dai->id);
if (!dai->active)

View File

@ -210,8 +210,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
#ifdef CONFIG_PM
static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport =
(struct sport_device *)dai->private_data;
struct sport_device *sport = dai->private_data;
if (!dai->active)
return 0;
@ -225,8 +224,7 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport =
(struct sport_device *)dai->private_data;
struct sport_device *sport = dai->private_data;
if (!dai->active)
return 0;

View File

@ -22,9 +22,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS42L51 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_DA7210 if I2C
select SND_SOC_JZ4740 if SOC_JZ4740
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
@ -48,6 +50,7 @@ config SND_SOC_ALL_CODECS
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_WM8741 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
@ -120,13 +123,13 @@ config SND_SOC_AK4671
config SND_SOC_CQ0093VC
tristate
config SND_SOC_CS42L51
tristate
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
config SND_SOC_DA7210
tristate
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function
# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
@ -138,9 +141,15 @@ config SND_SOC_CS4270_VD33_ERRATA
config SND_SOC_CX20442
tristate
config SND_SOC_JZ4740_CODEC
tristate
config SND_SOC_L3
tristate
config SND_SOC_DA7210
tristate
config SND_SOC_PCM3008
tristate
@ -206,6 +215,9 @@ config SND_SOC_WM8728
config SND_SOC_WM8731
tristate
config SND_SOC_WM8741
tristate
config SND_SOC_WM8750
tristate

View File

@ -9,6 +9,7 @@ snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
@ -34,6 +35,7 @@ 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-wm8741-objs := wm8741.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8776-objs := wm8776.o
@ -56,6 +58,7 @@ snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-jz4740-codec-objs := jz4740.o
# Amp
snd-soc-max9877-objs := max9877.o
@ -74,10 +77,12 @@ 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_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
@ -99,6 +104,7 @@ 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_WM8741) += snd-soc-wm8741.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o

View File

@ -272,6 +272,7 @@ static int ad1836_register(struct ad1836_priv *ad1836)
if (ad1836_codec) {
dev_err(codec->dev, "Another ad1836 is registered\n");
kfree(ad1836);
return -EINVAL;
}

View File

@ -24,6 +24,7 @@
/* codec private data */
struct ad193x_priv {
unsigned int sysclk;
struct snd_soc_codec codec;
u8 reg_cache[AD193X_NUM_REGS];
};
@ -251,15 +252,32 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static int ad193x_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 ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
switch (freq) {
case 12288000:
case 18432000:
case 24576000:
case 36864000:
ad193x->sysclk = freq;
return 0;
}
return -EINVAL;
}
static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int word_len = 0, reg = 0;
int word_len = 0, reg = 0, master_rate = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
/* bit size */
switch (params_format(params)) {
@ -275,6 +293,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
break;
}
switch (ad193x->sysclk) {
case 12288000:
master_rate = AD193X_PLL_INPUT_256;
break;
case 18432000:
master_rate = AD193X_PLL_INPUT_384;
break;
case 24576000:
master_rate = AD193X_PLL_INPUT_512;
break;
case 36864000:
master_rate = AD193X_PLL_INPUT_768;
break;
}
reg = snd_soc_read(codec, AD193X_PLL_CLK_CTRL0);
reg = (reg & AD193X_PLL_INPUT_MASK) | master_rate;
snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, reg);
reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len;
snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
@ -348,6 +385,7 @@ static int ad193x_bus_probe(struct device *dev, void *ctrl_data, int bus_type)
/* pll input: mclki/xi */
snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04);
ad193x->sysclk = 12288000;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
@ -383,6 +421,7 @@ static struct snd_soc_dai_ops ad193x_dai_ops = {
.hw_params = ad193x_hw_params,
.digital_mute = ad193x_mute,
.set_tdm_slot = ad193x_set_tdm_slot,
.set_sysclk = ad193x_set_dai_sysclk,
.set_fmt = ad193x_set_dai_fmt,
};

View File

@ -11,6 +11,11 @@
#define AD193X_PLL_CLK_CTRL0 0x800
#define AD193X_PLL_POWERDOWN 0x01
#define AD193X_PLL_INPUT_MASK (~0x6)
#define AD193X_PLL_INPUT_256 (0 << 1)
#define AD193X_PLL_INPUT_384 (1 << 1)
#define AD193X_PLL_INPUT_512 (2 << 1)
#define AD193X_PLL_INPUT_768 (3 << 1)
#define AD193X_PLL_CLK_CTRL1 0x801
#define AD193X_DAC_CTRL0 0x802
#define AD193X_DAC_POWERDOWN 0x01

View File

@ -22,20 +22,13 @@
* AK4643 is tested.
*/
#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/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "ak4642.h"
@ -111,6 +104,23 @@
struct snd_soc_codec_device soc_codec_dev_ak4642;
/*
* Playback Volume (table 39)
*
* max : 0x00 : +12.0 dB
* ( 0.5 dB step )
* min : 0xFE : -115.0 dB
* mute: 0xFF
*/
static const DECLARE_TLV_DB_SCALE(out_tlv, -11500, 50, 1);
static const struct snd_kcontrol_new ak4642_snd_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC,
0, 0xFF, 1, out_tlv),
};
/* codec private data */
struct ak4642_priv {
struct snd_soc_codec codec;
@ -204,7 +214,6 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
*
* PLL, Master Mode
* Audio I/F Format :MSB justified (ADC & DAC)
* Digital Volume: -8dB
* Bass Boost Level : Middle
*
* This operation came from example code of
@ -214,8 +223,6 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
ak4642_write(codec, 0x0e, 0x19);
ak4642_write(codec, 0x09, 0x91);
ak4642_write(codec, 0x0c, 0x91);
ak4642_write(codec, 0x0a, 0x28);
ak4642_write(codec, 0x0d, 0x28);
ak4642_write(codec, 0x00, 0x64);
snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP);
snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN);
@ -491,8 +498,10 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
codec->control_data = i2c;
ret = ak4642_init(ak4642);
if (ret < 0)
if (ret < 0) {
printk(KERN_ERR "failed to initialise AK4642\n");
kfree(ak4642);
}
return ret;
}
@ -548,6 +557,9 @@ static int ak4642_probe(struct platform_device *pdev)
goto pcm_err;
}
snd_soc_add_controls(ak4642_codec, ak4642_snd_controls,
ARRAY_SIZE(ak4642_snd_controls));
dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
return ret;

763
sound/soc/codecs/cs42l51.c Normal file
View File

@ -0,0 +1,763 @@
/*
* cs42l51.c
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
*
* Based on cs4270.c - Copyright (c) Freescale Semiconductor
*
* 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.
*
* For now:
* - Only I2C is support. Not SPI
* - master mode *NOT* supported
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
#include <linux/i2c.h>
#include "cs42l51.h"
enum master_slave_mode {
MODE_SLAVE,
MODE_SLAVE_AUTO,
MODE_MASTER,
};
struct cs42l51_private {
unsigned int mclk;
unsigned int audio_mode; /* The mode (I2S or left-justified) */
enum master_slave_mode func;
struct snd_soc_codec codec;
u8 reg_cache[CS42L51_NUMREGS];
};
static struct snd_soc_codec *cs42l51_codec;
#define CS42L51_FORMATS ( \
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
static int cs42l51_fill_cache(struct snd_soc_codec *codec)
{
u8 *cache = codec->reg_cache + 1;
struct i2c_client *i2c_client = codec->control_data;
s32 length;
length = i2c_smbus_read_i2c_block_data(i2c_client,
CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache);
if (length != CS42L51_NUMREGS) {
dev_err(&i2c_client->dev,
"I2C read failure, addr=0x%x (ret=%d vs %d)\n",
i2c_client->addr, length, CS42L51_NUMREGS);
return -EIO;
}
return 0;
}
static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct snd_soc_codec *codec;
struct cs42l51_private *cs42l51;
int ret = 0;
int reg;
if (cs42l51_codec)
return -EBUSY;
/* Verify that we have a CS42L51 */
ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to read I2C\n");
goto error;
}
if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
(ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
dev_err(&i2c_client->dev, "Invalid chip id\n");
ret = -ENODEV;
goto error;
}
dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
ret & 7);
cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL);
if (!cs42l51) {
dev_err(&i2c_client->dev, "could not allocate codec\n");
return -ENOMEM;
}
codec = &cs42l51->codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->dev = &i2c_client->dev;
codec->name = "CS42L51";
codec->owner = THIS_MODULE;
codec->dai = &cs42l51_dai;
codec->num_dai = 1;
snd_soc_codec_set_drvdata(codec, cs42l51);
codec->control_data = i2c_client;
codec->reg_cache = cs42l51->reg_cache;
codec->reg_cache_size = CS42L51_NUMREGS;
i2c_set_clientdata(i2c_client, codec);
ret = cs42l51_fill_cache(codec);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to fill register cache\n");
goto error_alloc;
}
ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret < 0) {
dev_err(&i2c_client->dev, "Failed to set cache I/O: %d\n", ret);
goto error_alloc;
}
/*
* DAC configuration
* - Use signal processor
* - auto mute
* - vol changes immediate
* - no de-emphasize
*/
reg = CS42L51_DAC_CTL_DATA_SEL(1)
| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
if (ret < 0)
goto error_alloc;
cs42l51_dai.dev = codec->dev;
cs42l51_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto error_alloc;
}
ret = snd_soc_register_dai(&cs42l51_dai);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to register DAIe\n");
goto error_reg;
}
return 0;
error_reg:
snd_soc_unregister_codec(codec);
error_alloc:
kfree(cs42l51);
error:
return ret;
}
static int cs42l51_i2c_remove(struct i2c_client *client)
{
struct cs42l51_private *cs42l51 = i2c_get_clientdata(client);
snd_soc_unregister_dai(&cs42l51_dai);
snd_soc_unregister_codec(cs42l51_codec);
cs42l51_codec = NULL;
kfree(cs42l51);
return 0;
}
static const struct i2c_device_id cs42l51_id[] = {
{"cs42l51", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs42l51_id);
static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "CS42L51 I2C",
.owner = THIS_MODULE,
},
.id_table = cs42l51_id,
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
};
static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
switch (value) {
default:
case 0:
ucontrol->value.integer.value[0] = 0;
break;
/* same value : (L+R)/2 and (R+L)/2 */
case 1:
case 2:
ucontrol->value.integer.value[0] = 1;
break;
case 3:
ucontrol->value.integer.value[0] = 2;
break;
}
return 0;
}
#define CHAN_MIX_NORMAL 0x00
#define CHAN_MIX_BOTH 0x55
#define CHAN_MIX_SWAP 0xFF
static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned char val;
switch (ucontrol->value.integer.value[0]) {
default:
case 0:
val = CHAN_MIX_NORMAL;
break;
case 1:
val = CHAN_MIX_BOTH;
break;
case 2:
val = CHAN_MIX_SWAP;
break;
}
snd_soc_write(codec, CS42L51_PCM_MIXER, val);
return 1;
}
static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
/* This is a lie. after -102 db, it stays at -102 */
/* maybe a range would be better */
static const DECLARE_TLV_DB_SCALE(aout_tlv, -11550, 50, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
static const char *chan_mix[] = {
"L R",
"L+R",
"R L",
};
static const struct soc_enum cs42l51_chan_mix =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix);
static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
7, 0xffffff99, 0x18, adc_pcm_tlv),
SOC_DOUBLE_R("PCM Playback Switch",
CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
8, 0xffffff19, 0x18, aout_tlv),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
7, 0xffffff99, 0x18, adc_pcm_tlv),
SOC_DOUBLE_R("ADC Mixer Switch",
CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
SOC_DOUBLE_TLV("Mic Boost Volume",
CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
SOC_ENUM_EXT("PCM channel mixer",
cs42l51_chan_mix,
cs42l51_get_chan_mix, cs42l51_set_chan_mix),
};
/*
* to power down, one must:
* 1.) Enable the PDN bit
* 2.) enable power-down for the select channels
* 3.) disable the PDN bit.
*/
static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
unsigned long value;
value = snd_soc_read(w->codec, CS42L51_POWER_CTL1);
value &= ~CS42L51_POWER_CTL1_PDN;
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
value |= CS42L51_POWER_CTL1_PDN;
break;
default:
case SND_SOC_DAPM_POST_PMD:
break;
}
snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN, value);
return 0;
}
static const char *cs42l51_dac_names[] = {"Direct PCM",
"DSP PCM", "ADC"};
static const struct soc_enum cs42l51_dac_mux_enum =
SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names);
static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
"MIC Left", "MIC+preamp Left"};
static const struct soc_enum cs42l51_adcl_mux_enum =
SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names);
static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
"MIC Right", "MIC+preamp Right"};
static const struct soc_enum cs42l51_adcr_mux_enum =
SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names);
static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
CS42L51_POWER_CTL1, 1, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
CS42L51_POWER_CTL1, 2, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
CS42L51_POWER_CTL1, 5, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
CS42L51_POWER_CTL1, 6, 1,
cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
/* analog/mic */
SND_SOC_DAPM_INPUT("AIN1L"),
SND_SOC_DAPM_INPUT("AIN1R"),
SND_SOC_DAPM_INPUT("AIN2L"),
SND_SOC_DAPM_INPUT("AIN2R"),
SND_SOC_DAPM_INPUT("MICL"),
SND_SOC_DAPM_INPUT("MICR"),
SND_SOC_DAPM_MIXER("Mic Preamp Left",
CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
SND_SOC_DAPM_MIXER("Mic Preamp Right",
CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
/* HP */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
/* mux */
SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
&cs42l51_dac_mux_controls),
SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
&cs42l51_adcl_mux_controls),
SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
&cs42l51_adcr_mux_controls),
};
static const struct snd_soc_dapm_route cs42l51_routes[] = {
{"HPL", NULL, "Left DAC"},
{"HPR", NULL, "Right DAC"},
{"Left ADC", NULL, "Left PGA"},
{"Right ADC", NULL, "Right PGA"},
{"Mic Preamp Left", NULL, "MICL"},
{"Mic Preamp Right", NULL, "MICR"},
{"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" },
{"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" },
{"PGA-ADC Mux Left", "MIC Left", "MICL" },
{"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" },
{"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" },
{"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" },
{"PGA-ADC Mux Right", "MIC Right", "MICR" },
{"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
{"Left PGA", NULL, "PGA-ADC Mux Left"},
{"Right PGA", NULL, "PGA-ADC Mux Right"},
};
static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
dev_err(codec->dev, "invalid DAI format\n");
ret = -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
cs42l51->func = MODE_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
cs42l51->func = MODE_SLAVE_AUTO;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
struct cs42l51_ratios {
unsigned int ratio;
unsigned char speed_mode;
unsigned char mclk;
};
static struct cs42l51_ratios slave_ratios[] = {
{ 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 },
{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
{ 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
{ 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 },
{ 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
{ 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
{ 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 },
{ 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
{ 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 },
{ 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
{ 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 },
};
static struct cs42l51_ratios slave_auto_ratios[] = {
{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
{ 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
{ 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
{ 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
{ 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
{ 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 },
{ 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
{ 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
};
static int cs42l51_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 cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
unsigned int rates = 0;
unsigned int rate_min = -1;
unsigned int rate_max = 0;
int i;
cs42l51->mclk = freq;
switch (cs42l51->func) {
case MODE_MASTER:
return -EINVAL;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
break;
case MODE_SLAVE_AUTO:
ratios = slave_auto_ratios;
nr_ratios = ARRAY_SIZE(slave_auto_ratios);
break;
}
for (i = 0; i < nr_ratios; i++) {
unsigned int rate = freq / ratios[i].ratio;
rates |= snd_pcm_rate_to_rate_bit(rate);
if (rate < rate_min)
rate_min = rate;
if (rate > rate_max)
rate_max = rate;
}
rates &= ~SNDRV_PCM_RATE_KNOT;
if (!rates) {
dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL;
}
codec_dai->playback.rates = rates;
codec_dai->playback.rate_min = rate_min;
codec_dai->playback.rate_max = rate_max;
codec_dai->capture.rates = rates;
codec_dai->capture.rate_min = rate_min;
codec_dai->capture.rate_max = rate_max;
return 0;
}
static int cs42l51_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
int ret;
unsigned int i;
unsigned int rate;
unsigned int ratio;
struct cs42l51_ratios *ratios = NULL;
int nr_ratios = 0;
int intf_ctl, power_ctl, fmt;
switch (cs42l51->func) {
case MODE_MASTER:
return -EINVAL;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
break;
case MODE_SLAVE_AUTO:
ratios = slave_auto_ratios;
nr_ratios = ARRAY_SIZE(slave_auto_ratios);
break;
}
/* Figure out which MCLK/LRCK ratio to use */
rate = params_rate(params); /* Sampling rate, in Hz */
ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */
for (i = 0; i < nr_ratios; i++) {
if (ratios[i].ratio == ratio)
break;
}
if (i == nr_ratios) {
/* We did not find a matching ratio */
dev_err(codec->dev, "could not find matching ratio\n");
return -EINVAL;
}
intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
| CS42L51_INTF_CTL_DAC_FORMAT(7));
power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
| CS42L51_MIC_POWER_CTL_MCLK_DIV2);
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
break;
case MODE_SLAVE:
power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
break;
case MODE_SLAVE_AUTO:
power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
break;
}
switch (cs42l51->audio_mode) {
case SND_SOC_DAIFMT_I2S:
intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
break;
case SND_SOC_DAIFMT_LEFT_J:
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
break;
case SND_SOC_DAIFMT_RIGHT_J:
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
case SNDRV_PCM_FORMAT_S16_BE:
fmt = CS42L51_DAC_DIF_RJ16;
break;
case SNDRV_PCM_FORMAT_S18_3LE:
case SNDRV_PCM_FORMAT_S18_3BE:
fmt = CS42L51_DAC_DIF_RJ18;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
case SNDRV_PCM_FORMAT_S20_3BE:
fmt = CS42L51_DAC_DIF_RJ20;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S24_BE:
fmt = CS42L51_DAC_DIF_RJ24;
break;
default:
dev_err(codec->dev, "unknown format\n");
return -EINVAL;
}
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
break;
default:
dev_err(codec->dev, "unknown format\n");
return -EINVAL;
}
if (ratios[i].mclk)
power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
if (ret < 0)
return ret;
ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
if (ret < 0)
return ret;
return 0;
}
static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
int reg;
int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
if (mute)
reg |= mask;
else
reg &= ~mask;
return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
}
static struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
.set_fmt = cs42l51_set_dai_fmt,
.digital_mute = cs42l51_dai_mute,
};
struct snd_soc_dai cs42l51_dai = {
.name = "CS42L51 HiFi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L51_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = CS42L51_FORMATS,
},
.ops = &cs42l51_dai_ops,
};
EXPORT_SYMBOL_GPL(cs42l51_dai);
static int cs42l51_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (!cs42l51_codec) {
dev_err(&pdev->dev, "CS42L51 codec not yet registered\n");
return -EINVAL;
}
socdev->card->codec = cs42l51_codec;
codec = socdev->card->codec;
/* 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, cs42l51_snd_controls,
ARRAY_SIZE(cs42l51_snd_controls));
snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets,
ARRAY_SIZE(cs42l51_dapm_widgets));
snd_soc_dapm_add_routes(codec, cs42l51_routes,
ARRAY_SIZE(cs42l51_routes));
return 0;
}
static int cs42l51_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_device_cs42l51 = {
.probe = cs42l51_probe,
.remove = cs42l51_remove
};
EXPORT_SYMBOL_GPL(soc_codec_device_cs42l51);
static int __init cs42l51_init(void)
{
int ret;
ret = i2c_add_driver(&cs42l51_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "%s: can't add i2c driver\n", __func__);
return ret;
}
return 0;
}
module_init(cs42l51_init);
static void __exit cs42l51_exit(void)
{
i2c_del_driver(&cs42l51_i2c_driver);
}
module_exit(cs42l51_exit);
MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");

163
sound/soc/codecs/cs42l51.h Normal file
View File

@ -0,0 +1,163 @@
/*
* cs42l51.h
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.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.
*
* 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.
*/
#ifndef _CS42L51_H
#define _CS42L51_H
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
#define CS42L51_CHIP_REV_B 0x01
#define CS42L51_CHIP_REV_ID 0x01
#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b))
#define CS42L51_POWER_CTL1 0x02
#define CS42L51_POWER_CTL1_PDN_DACB (1<<6)
#define CS42L51_POWER_CTL1_PDN_DACA (1<<5)
#define CS42L51_POWER_CTL1_PDN_PGAB (1<<4)
#define CS42L51_POWER_CTL1_PDN_PGAA (1<<3)
#define CS42L51_POWER_CTL1_PDN_ADCB (1<<2)
#define CS42L51_POWER_CTL1_PDN_ADCA (1<<1)
#define CS42L51_POWER_CTL1_PDN (1<<0)
#define CS42L51_MIC_POWER_CTL 0x03
#define CS42L51_MIC_POWER_CTL_AUTO (1<<7)
#define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5)
#define CS42L51_QSM_MODE 3
#define CS42L51_HSM_MODE 2
#define CS42L51_SSM_MODE 1
#define CS42L51_DSM_MODE 0
#define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4)
#define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3)
#define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2)
#define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1)
#define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0)
#define CS42L51_INTF_CTL 0x04
#define CS42L51_INTF_CTL_LOOPBACK (1<<7)
#define CS42L51_INTF_CTL_MASTER (1<<6)
#define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3)
#define CS42L51_DAC_DIF_LJ24 0x00
#define CS42L51_DAC_DIF_I2S 0x01
#define CS42L51_DAC_DIF_RJ24 0x02
#define CS42L51_DAC_DIF_RJ20 0x03
#define CS42L51_DAC_DIF_RJ18 0x04
#define CS42L51_DAC_DIF_RJ16 0x05
#define CS42L51_INTF_CTL_ADC_I2S (1<<2)
#define CS42L51_INTF_CTL_DIGMIX (1<<1)
#define CS42L51_INTF_CTL_MICMIX (1<<0)
#define CS42L51_MIC_CTL 0x05
#define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7)
#define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6)
#define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5)
#define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4)
#define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2)
#define CS42L51_MIC_CTL_MICB_BOOST (1<<1)
#define CS42L51_MIC_CTL_MICA_BOOST (1<<0)
#define CS42L51_ADC_CTL 0x06
#define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7)
#define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6)
#define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5)
#define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4)
#define CS42L51_ADC_CTL_SOFTB (1<<3)
#define CS42L51_ADC_CTL_ZCROSSB (1<<2)
#define CS42L51_ADC_CTL_SOFTA (1<<1)
#define CS42L51_ADC_CTL_ZCROSSA (1<<0)
#define CS42L51_ADC_INPUT 0x07
#define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6)
#define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4)
#define CS42L51_ADC_INPUT_INV_ADCB (1<<3)
#define CS42L51_ADC_INPUT_INV_ADCA (1<<2)
#define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1)
#define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0)
#define CS42L51_DAC_OUT_CTL 0x08
#define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5)
#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4)
#define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3)
#define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2)
#define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1)
#define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0)
#define CS42L51_DAC_CTL 0x09
#define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6)
#define CS42L51_DAC_CTL_FREEZE (1<<5)
#define CS42L51_DAC_CTL_DEEMPH (1<<3)
#define CS42L51_DAC_CTL_AMUTE (1<<2)
#define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0)
#define CS42L51_ALC_PGA_CTL 0x0A
#define CS42L51_ALC_PGB_CTL 0x0B
#define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7)
#define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6)
#define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0)
#define CS42L51_ADCA_ATT 0x0C
#define CS42L51_ADCB_ATT 0x0D
#define CS42L51_ADCA_VOL 0x0E
#define CS42L51_ADCB_VOL 0x0F
#define CS42L51_PCMA_VOL 0x10
#define CS42L51_PCMB_VOL 0x11
#define CS42L51_MIX_MUTE_ADCMIX (1<<7)
#define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0)
#define CS42L51_BEEP_FREQ 0x12
#define CS42L51_BEEP_VOL 0x13
#define CS42L51_BEEP_CONF 0x14
#define CS42L51_TONE_CTL 0x15
#define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4)
#define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0)
#define CS42L51_AOUTA_VOL 0x16
#define CS42L51_AOUTB_VOL 0x17
#define CS42L51_PCM_MIXER 0x18
#define CS42L51_LIMIT_THRES_DIS 0x19
#define CS42L51_LIMIT_REL 0x1A
#define CS42L51_LIMIT_ATT 0x1B
#define CS42L51_ALC_EN 0x1C
#define CS42L51_ALC_REL 0x1D
#define CS42L51_ALC_THRES 0x1E
#define CS42L51_NOISE_CONF 0x1F
#define CS42L51_STATUS 0x20
#define CS42L51_STATUS_SP_CLKERR (1<<6)
#define CS42L51_STATUS_SPEA_OVFL (1<<5)
#define CS42L51_STATUS_SPEB_OVFL (1<<4)
#define CS42L51_STATUS_PCMA_OVFL (1<<3)
#define CS42L51_STATUS_PCMB_OVFL (1<<2)
#define CS42L51_STATUS_ADCA_OVFL (1<<1)
#define CS42L51_STATUS_ADCB_OVFL (1<<0)
#define CS42L51_CHARGE_FREQ 0x21
#define CS42L51_FIRSTREG 0x01
/*
* Hack: with register 0x21, it makes 33 registers. Looks like someone in the
* i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using
* 32 regs
*/
#define CS42L51_LASTREG 0x20
#define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1)
extern struct snd_soc_dai cs42l51_dai;
extern struct snd_soc_codec_device soc_codec_device_cs42l51;
#endif

View File

@ -15,23 +15,15 @@
* option) any later version.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <sound/initval.h>
#include <asm/div64.h>
#include <sound/tlv.h>
#include "da7210.h"
@ -145,6 +137,29 @@
#define DA7210_VERSION "0.0.1"
/*
* Playback Volume
*
* max : 0x3F (+15.0 dB)
* (1.5 dB step)
* min : 0x11 (-54.0 dB)
* mute : 0x10
* reserved : 0x00 - 0x0F
*
* ** FIXME **
*
* Reserved area are considered as "mute".
* -> min = -79.5 dB
*/
static const DECLARE_TLV_DB_SCALE(hp_out_tlv, -7950, 150, 1);
static const struct snd_kcontrol_new da7210_snd_controls[] = {
SOC_DOUBLE_R_TLV("HeadPhone Playback Volume",
DA7210_HP_L_VOL, DA7210_HP_R_VOL,
0, 0x3F, 0, hp_out_tlv),
};
/* Codec private data */
struct da7210_priv {
struct snd_soc_codec codec;
@ -227,10 +242,6 @@ static int da7210_startup(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
if (is_play) {
/* PlayBack Volume 40 */
snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40);
snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40);
/* Enable Out */
snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
@ -488,7 +499,7 @@ static int da7210_init(struct da7210_priv *da7210)
ret = snd_soc_register_dai(&da7210_dai);
if (ret) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
goto init_err;
goto codec_err;
}
/* FIXME
@ -574,6 +585,8 @@ static int da7210_init(struct da7210_priv *da7210)
return ret;
codec_err:
snd_soc_unregister_codec(codec);
init_err:
kfree(codec->reg_cache);
codec->reg_cache = NULL;
@ -601,8 +614,10 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
codec->control_data = i2c;
ret = da7210_init(da7210);
if (ret < 0)
if (ret < 0) {
pr_err("Failed to initialise da7210 audio codec\n");
kfree(da7210);
}
return ret;
}
@ -656,6 +671,9 @@ static int da7210_probe(struct platform_device *pdev)
if (ret < 0)
goto pcm_err;
snd_soc_add_controls(da7210_codec, da7210_snd_controls,
ARRAY_SIZE(da7210_snd_controls));
dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
pcm_err:

511
sound/soc/codecs/jz4740.c Normal file
View File

@ -0,0 +1,511 @@
/*
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc-dapm.h>
#include <sound/soc.h>
#define JZ4740_REG_CODEC_1 0x0
#define JZ4740_REG_CODEC_2 0x1
#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
#define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18)
#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
#define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12)
#define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10))
#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9)
#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8)
#define JZ4740_CODEC_1_SUSPEND BIT(1)
#define JZ4740_CODEC_1_RESET BIT(0)
#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8
#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000
#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00
#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030
#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003
#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16
#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8
#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
static const uint32_t jz4740_codec_regs[] = {
0x021b2302, 0x00170803,
};
struct jz4740_codec {
void __iomem *base;
struct resource *mem;
uint32_t reg_cache[2];
struct snd_soc_codec codec;
};
static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
{
return container_of(codec, struct jz4740_codec, codec);
}
static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
unsigned int reg)
{
struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
return readl(jz4740_codec->base + (reg << 2));
}
static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
jz4740_codec->reg_cache[reg] = val;
writel(val, jz4740_codec->base + (reg << 2));
return 0;
}
static const struct snd_kcontrol_new jz4740_codec_controls[] = {
SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2,
JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
};
static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
};
static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
};
static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1,
jz4740_codec_output_controls,
ARRAY_SIZE(jz4740_codec_output_controls)),
SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
jz4740_codec_input_controls,
ARRAY_SIZE(jz4740_codec_input_controls)),
SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_INPUT("MIC"),
SND_SOC_DAPM_INPUT("LIN"),
SND_SOC_DAPM_INPUT("RIN"),
};
static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
{"Line Input", NULL, "LIN"},
{"Line Input", NULL, "RIN"},
{"Input Mixer", "Line Capture Switch", "Line Input"},
{"Input Mixer", "Mic Capture Switch", "MIC"},
{"ADC", NULL, "Input Mixer"},
{"Output Mixer", "Bypass Switch", "Input Mixer"},
{"Output Mixer", "DAC Switch", "DAC"},
{"LOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"},
};
static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
uint32_t val;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
switch (params_rate(params)) {
case 8000:
val = 0;
break;
case 11025:
val = 1;
break;
case 12000:
val = 2;
break;
case 16000:
val = 3;
break;
case 22050:
val = 4;
break;
case 24000:
val = 5;
break;
case 32000:
val = 6;
break;
case 44100:
val = 7;
break;
case 48000:
val = 8;
break;
default:
return -EINVAL;
}
val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
return 0;
}
static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
.hw_params = jz4740_codec_hw_params,
};
struct snd_soc_dai jz4740_codec_dai = {
.name = "jz4740",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
},
.ops = &jz4740_codec_dai_ops,
.symmetric_rates = 1,
};
EXPORT_SYMBOL_GPL(jz4740_codec_dai);
static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
{
int i;
uint32_t *cache = codec->reg_cache;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
udelay(2);
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
jz4740_codec_write(codec, i, cache[i]);
}
static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
unsigned int mask;
unsigned int value;
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
value = 0;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
break;
case SND_SOC_BIAS_STANDBY:
/* The only way to clear the suspend flag is to reset the codec */
if (codec->bias_level == SND_SOC_BIAS_OFF)
jz4740_codec_wakeup(codec);
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
value = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
break;
case SND_SOC_BIAS_OFF:
mask = JZ4740_CODEC_1_SUSPEND;
value = JZ4740_CODEC_1_SUSPEND;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
break;
default:
break;
}
codec->bias_level = level;
return 0;
}
static struct snd_soc_codec *jz4740_codec_codec;
static int jz4740_codec_dev_probe(struct platform_device *pdev)
{
int ret;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = jz4740_codec_codec;
BUG_ON(!codec);
socdev->card->codec = codec;
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret) {
dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
return ret;
}
snd_soc_add_controls(codec, jz4740_codec_controls,
ARRAY_SIZE(jz4740_codec_controls));
snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
ARRAY_SIZE(jz4740_codec_dapm_widgets));
snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
ARRAY_SIZE(jz4740_codec_dapm_routes));
snd_soc_dapm_new_widgets(codec);
return 0;
}
static int jz4740_codec_dev_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;
}
#ifdef CONFIG_PM_SLEEP
static int jz4740_codec_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;
return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
}
static int jz4740_codec_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
}
#else
#define jz4740_codec_suspend NULL
#define jz4740_codec_resume NULL
#endif
struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
.probe = jz4740_codec_dev_probe,
.remove = jz4740_codec_dev_remove,
.suspend = jz4740_codec_suspend,
.resume = jz4740_codec_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
static int __devinit jz4740_codec_probe(struct platform_device *pdev)
{
int ret;
struct jz4740_codec *jz4740_codec;
struct snd_soc_codec *codec;
struct resource *mem;
jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
if (!jz4740_codec)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
ret = -ENOENT;
goto err_free_codec;
}
mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
if (!mem) {
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
ret = -EBUSY;
goto err_free_codec;
}
jz4740_codec->base = ioremap(mem->start, resource_size(mem));
if (!jz4740_codec->base) {
dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
ret = -EBUSY;
goto err_release_mem_region;
}
jz4740_codec->mem = mem;
jz4740_codec_dai.dev = &pdev->dev;
codec = &jz4740_codec->codec;
codec->dev = &pdev->dev;
codec->name = "jz4740";
codec->owner = THIS_MODULE;
codec->read = jz4740_codec_read;
codec->write = jz4740_codec_write;
codec->set_bias_level = jz4740_codec_set_bias_level;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->dai = &jz4740_codec_dai;
codec->num_dai = 1;
codec->reg_cache = jz4740_codec->reg_cache;
codec->reg_cache_size = 2;
memcpy(codec->reg_cache, jz4740_codec_regs, sizeof(jz4740_codec_regs));
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
jz4740_codec_codec = codec;
snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
platform_set_drvdata(pdev, jz4740_codec);
ret = snd_soc_register_codec(codec);
if (ret) {
dev_err(&pdev->dev, "Failed to register codec\n");
goto err_iounmap;
}
ret = snd_soc_register_dai(&jz4740_codec_dai);
if (ret) {
dev_err(&pdev->dev, "Failed to register codec dai\n");
goto err_unregister_codec;
}
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
err_unregister_codec:
snd_soc_unregister_codec(codec);
err_iounmap:
iounmap(jz4740_codec->base);
err_release_mem_region:
release_mem_region(mem->start, resource_size(mem));
err_free_codec:
kfree(jz4740_codec);
return ret;
}
static int __devexit jz4740_codec_remove(struct platform_device *pdev)
{
struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
struct resource *mem = jz4740_codec->mem;
snd_soc_unregister_dai(&jz4740_codec_dai);
snd_soc_unregister_codec(&jz4740_codec->codec);
iounmap(jz4740_codec->base);
release_mem_region(mem->start, resource_size(mem));
platform_set_drvdata(pdev, NULL);
kfree(jz4740_codec);
return 0;
}
static struct platform_driver jz4740_codec_driver = {
.probe = jz4740_codec_probe,
.remove = __devexit_p(jz4740_codec_remove),
.driver = {
.name = "jz4740-codec",
.owner = THIS_MODULE,
},
};
static int __init jz4740_codec_init(void)
{
return platform_driver_register(&jz4740_codec_driver);
}
module_init(jz4740_codec_init);
static void __exit jz4740_codec_exit(void)
{
platform_driver_unregister(&jz4740_codec_driver);
}
module_exit(jz4740_codec_exit);
MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:jz4740-codec");

20
sound/soc/codecs/jz4740.h Normal file
View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
#define __SND_SOC_CODECS_JZ4740_CODEC_H__
extern struct snd_soc_dai jz4740_codec_dai;
extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
#endif

View File

@ -16,8 +16,10 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include "spdif_transciever.h"
@ -26,6 +28,48 @@ MODULE_LICENSE("GPL");
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static struct snd_soc_codec *spdif_dit_codec;
static int spdif_dit_codec_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret;
if (spdif_dit_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = spdif_dit_codec;
codec = spdif_dit_codec;
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 err_create_pcms;
}
return 0;
err_create_pcms:
return ret;
}
static int spdif_dit_codec_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_spdif_dit = {
.probe = spdif_dit_codec_probe,
.remove = spdif_dit_codec_remove,
}; EXPORT_SYMBOL_GPL(soc_codec_dev_spdif_dit);
struct snd_soc_dai dit_stub_dai = {
.name = "DIT",
.playback = {
@ -40,13 +84,61 @@ EXPORT_SYMBOL_GPL(dit_stub_dai);
static int spdif_dit_probe(struct platform_device *pdev)
{
struct snd_soc_codec *codec;
int ret;
if (spdif_dit_codec) {
dev_err(&pdev->dev, "Another Codec is registered\n");
ret = -EINVAL;
goto err_reg_codec;
}
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
codec->dev = &pdev->dev;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "spdif-dit";
codec->owner = THIS_MODULE;
codec->dai = &dit_stub_dai;
codec->num_dai = 1;
spdif_dit_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err_reg_codec;
}
dit_stub_dai.dev = &pdev->dev;
return snd_soc_register_dai(&dit_stub_dai);
ret = snd_soc_register_dai(&dit_stub_dai);
if (ret < 0) {
dev_err(codec->dev, "Failed to register dai: %d\n", ret);
goto err_reg_dai;
}
return 0;
err_reg_dai:
snd_soc_unregister_codec(codec);
err_reg_codec:
kfree(spdif_dit_codec);
return ret;
}
static int spdif_dit_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&dit_stub_dai);
snd_soc_unregister_codec(spdif_dit_codec);
kfree(spdif_dit_codec);
spdif_dit_codec = NULL;
return 0;
}

View File

@ -12,6 +12,7 @@
#ifndef CODEC_STUBS_H
#define CODEC_STUBS_H
extern struct snd_soc_codec_device soc_codec_dev_spdif_dit;
extern struct snd_soc_dai dit_stub_dai;
#endif /* CODEC_STUBS_H */

View File

@ -560,13 +560,16 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \
TLV320AIC23_DAC_OFF);
tlv320aic23_write(codec, TLV320AIC23_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040);
tlv320aic23_write(codec, TLV320AIC23_PWR, reg | \
TLV320AIC23_CLK_OFF);
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
@ -615,7 +618,6 @@ static int tlv320aic23_suspend(struct platform_device *pdev,
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -632,7 +634,6 @@ static int tlv320aic23_resume(struct platform_device *pdev)
u16 val = tlv320aic23_read_reg_cache(codec, reg);
tlv320aic23_write(codec, reg, val);
}
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;

View File

@ -49,8 +49,6 @@
#define NSAMPLE_MAX 5700
#define LATENCY_TIME_MS 20
#define MODE7_LTHR 10
#define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10)
@ -62,6 +60,9 @@
#define US_TO_SAMPLES(rate, us) \
(rate / (1000000 / us))
#define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \
((samples * 5000) / ((burstrate * 5000) / (burstrate - playrate)))
static void dac33_calculate_times(struct snd_pcm_substream *substream);
static int dac33_prepare_chip(struct snd_pcm_substream *substream);
@ -107,6 +108,10 @@ struct tlv320dac33_priv {
* this */
enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */
unsigned int nsample; /* burst read amount from host */
int mode1_latency; /* latency caused by the i2c writes in
* us */
int auto_fifo_config; /* Configure the FIFO based on the
* period size */
u8 burst_bclkdiv; /* BCLK divider value in burst mode */
unsigned int burst_rate; /* Interface speed in Burst modes */
@ -120,6 +125,8 @@ struct tlv320dac33_priv {
* samples */
unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */
unsigned int uthr;
enum dac33_state state;
};
@ -442,6 +449,39 @@ static int dac33_set_nsample(struct snd_kcontrol *kcontrol,
return ret;
}
static int dac33_get_uthr(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = dac33->uthr;
return 0;
}
static int dac33_set_uthr(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
if (dac33->substream)
return -EBUSY;
if (dac33->uthr == ucontrol->value.integer.value[0])
return 0;
if (ucontrol->value.integer.value[0] < (MODE7_LTHR + 10) ||
ucontrol->value.integer.value[0] > MODE7_UTHR)
ret = -EINVAL;
else
dac33->uthr = ucontrol->value.integer.value[0];
return ret;
}
static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -503,13 +543,18 @@ static const struct snd_kcontrol_new dac33_snd_controls[] = {
DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
};
static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
dac33_get_nsample, dac33_set_nsample),
static const struct snd_kcontrol_new dac33_mode_snd_controls[] = {
SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
dac33_get_fifo_mode, dac33_set_fifo_mode),
};
static const struct snd_kcontrol_new dac33_fifo_snd_controls[] = {
SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
dac33_get_nsample, dac33_set_nsample),
SOC_SINGLE_EXT("UTHR", 0, 0, MODE7_UTHR, 0,
dac33_get_uthr, dac33_set_uthr),
};
/* Analog bypass */
static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
@ -612,7 +657,7 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
dac33_write16(codec, DAC33_NSAMPLE_MSB,
DAC33_THRREG(dac33->nsample + dac33->alarm_threshold));
DAC33_THRREG(dac33->nsample));
/* Take the timestamps */
spin_lock_irq(&dac33->lock);
@ -761,6 +806,10 @@ static void dac33_shutdown(struct snd_pcm_substream *substream,
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
dac33->substream = NULL;
/* Reset the nSample restrictions */
dac33->nsample_min = 0;
dac33->nsample_max = NSAMPLE_MAX;
}
static int dac33_hw_params(struct snd_pcm_substream *substream,
@ -985,7 +1034,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
* Configure the threshold levels, and leave 10 sample space
* at the bottom, and also at the top of the FIFO
*/
dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(MODE7_UTHR));
dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr));
dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR));
break;
default:
@ -1003,57 +1052,71 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream)
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
unsigned int period_size = substream->runtime->period_size;
unsigned int rate = substream->runtime->rate;
unsigned int nsample_limit;
/* In bypass mode we don't need to calculate */
if (!dac33->fifo_mode)
return;
/* Number of samples (16bit, stereo) in one period */
dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
/* Number of samples (16bit, stereo) in ALSA buffer */
dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4;
/* Subtract one period from the total */
dac33->nsample_max -= dac33->nsample_min;
/* Number of samples for LATENCY_TIME_MS / 2 */
dac33->alarm_threshold = substream->runtime->rate /
(1000 / (LATENCY_TIME_MS / 2));
/* Find and fix up the lowest nsmaple limit */
nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS);
if (dac33->nsample_min < nsample_limit)
dac33->nsample_min = nsample_limit;
if (dac33->nsample < dac33->nsample_min)
dac33->nsample = dac33->nsample_min;
/*
* Find and fix up the highest nsmaple limit
* In order to not overflow the DAC33 buffer substract the
* alarm_threshold value from the size of the DAC33 buffer
*/
nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold;
if (dac33->nsample_max > nsample_limit)
dac33->nsample_max = nsample_limit;
if (dac33->nsample > dac33->nsample_max)
dac33->nsample = dac33->nsample_max;
switch (dac33->fifo_mode) {
case DAC33_FIFO_MODE1:
/* Number of samples under i2c latency */
dac33->alarm_threshold = US_TO_SAMPLES(rate,
dac33->mode1_latency);
if (dac33->auto_fifo_config) {
if (period_size <= dac33->alarm_threshold)
/*
* Configure nSamaple to number of periods,
* which covers the latency requironment.
*/
dac33->nsample = period_size *
((dac33->alarm_threshold / period_size) +
(dac33->alarm_threshold % period_size ?
1 : 0));
else
dac33->nsample = period_size;
} else {
/* nSample time shall not be shorter than i2c latency */
dac33->nsample_min = dac33->alarm_threshold;
/*
* nSample should not be bigger than alsa buffer minus
* size of one period to avoid overruns
*/
dac33->nsample_max = substream->runtime->buffer_size -
period_size;
nsample_limit = DAC33_BUFFER_SIZE_SAMPLES -
dac33->alarm_threshold;
if (dac33->nsample_max > nsample_limit)
dac33->nsample_max = nsample_limit;
/* Correct the nSample if it is outside of the ranges */
if (dac33->nsample < dac33->nsample_min)
dac33->nsample = dac33->nsample_min;
if (dac33->nsample > dac33->nsample_max)
dac33->nsample = dac33->nsample_max;
}
dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate,
dac33->nsample);
dac33->t_stamp1 = 0;
dac33->t_stamp2 = 0;
break;
case DAC33_FIFO_MODE7:
if (dac33->auto_fifo_config) {
dac33->uthr = UTHR_FROM_PERIOD_SIZE(
period_size,
rate,
dac33->burst_rate) + 9;
if (dac33->uthr > MODE7_UTHR)
dac33->uthr = MODE7_UTHR;
if (dac33->uthr < (MODE7_LTHR + 10))
dac33->uthr = (MODE7_LTHR + 10);
}
dac33->mode7_us_to_lthr =
SAMPLES_TO_US(substream->runtime->rate,
MODE7_UTHR - MODE7_LTHR + 1);
SAMPLES_TO_US(substream->runtime->rate,
dac33->uthr - MODE7_LTHR + 1);
dac33->t_stamp1 = 0;
break;
default:
@ -1104,7 +1167,7 @@ static snd_pcm_sframes_t dac33_dai_delay(
struct snd_soc_codec *codec = socdev->card->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
unsigned long long t0, t1, t_now;
unsigned int time_delta;
unsigned int time_delta, uthr;
int samples_out, samples_in, samples;
snd_pcm_sframes_t delay = 0;
@ -1182,6 +1245,7 @@ static snd_pcm_sframes_t dac33_dai_delay(
case DAC33_FIFO_MODE7:
spin_lock(&dac33->lock);
t0 = dac33->t_stamp1;
uthr = dac33->uthr;
spin_unlock(&dac33->lock);
t_now = ktime_to_us(ktime_get());
@ -1194,7 +1258,7 @@ static snd_pcm_sframes_t dac33_dai_delay(
* Either the timestamps are messed or equal. Report
* maximum delay
*/
delay = MODE7_UTHR;
delay = uthr;
goto out;
}
@ -1208,8 +1272,8 @@ static snd_pcm_sframes_t dac33_dai_delay(
substream->runtime->rate,
time_delta);
if (likely(MODE7_UTHR > samples_out))
delay = MODE7_UTHR - samples_out;
if (likely(uthr > samples_out))
delay = uthr - samples_out;
else
delay = 0;
} else {
@ -1227,8 +1291,8 @@ static snd_pcm_sframes_t dac33_dai_delay(
time_delta);
delay = MODE7_LTHR + samples_in - samples_out;
if (unlikely(delay > MODE7_UTHR))
delay = MODE7_UTHR;
if (unlikely(delay > uthr))
delay = uthr;
}
break;
default:
@ -1347,10 +1411,15 @@ static int dac33_soc_probe(struct platform_device *pdev)
snd_soc_add_controls(codec, dac33_snd_controls,
ARRAY_SIZE(dac33_snd_controls));
/* Only add the nSample controls, if we have valid IRQ number */
if (dac33->irq >= 0)
snd_soc_add_controls(codec, dac33_nsample_snd_controls,
ARRAY_SIZE(dac33_nsample_snd_controls));
/* Only add the FIFO controls, if we have valid IRQ number */
if (dac33->irq >= 0) {
snd_soc_add_controls(codec, dac33_mode_snd_controls,
ARRAY_SIZE(dac33_mode_snd_controls));
/* FIFO usage controls only, if autoio config is not selected */
if (!dac33->auto_fifo_config)
snd_soc_add_controls(codec, dac33_fifo_snd_controls,
ARRAY_SIZE(dac33_fifo_snd_controls));
}
dac33_add_widgets(codec);
@ -1481,9 +1550,14 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client,
/* Pre calculate the burst rate */
dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32;
dac33->keep_bclk = pdata->keep_bclk;
dac33->auto_fifo_config = pdata->auto_fifo_config;
dac33->mode1_latency = pdata->mode1_latency;
if (!dac33->mode1_latency)
dac33->mode1_latency = 10000; /* 10ms */
dac33->irq = client->irq;
dac33->nsample = NSAMPLE_MAX;
dac33->nsample_max = NSAMPLE_MAX;
dac33->uthr = MODE7_UTHR;
/* Disable FIFO use by default */
dac33->fifo_mode = DAC33_FIFO_BYPASS;

View File

@ -43,37 +43,37 @@
*/
static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* this register not used */
0x91, /* REG_CODEC_MODE (0x1) */
0xc3, /* REG_OPTION (0x2) */
0x00, /* REG_CODEC_MODE (0x1) */
0x00, /* REG_OPTION (0x2) */
0x00, /* REG_UNKNOWN (0x3) */
0x00, /* REG_MICBIAS_CTL (0x4) */
0x20, /* REG_ANAMICL (0x5) */
0x00, /* REG_ANAMICL (0x5) */
0x00, /* REG_ANAMICR (0x6) */
0x00, /* REG_AVADC_CTL (0x7) */
0x00, /* REG_ADCMICSEL (0x8) */
0x00, /* REG_DIGMIXING (0x9) */
0x0c, /* REG_ATXL1PGA (0xA) */
0x0c, /* REG_ATXR1PGA (0xB) */
0x00, /* REG_AVTXL2PGA (0xC) */
0x00, /* REG_AVTXR2PGA (0xD) */
0x0f, /* REG_ATXL1PGA (0xA) */
0x0f, /* REG_ATXR1PGA (0xB) */
0x0f, /* REG_AVTXL2PGA (0xC) */
0x0f, /* REG_AVTXR2PGA (0xD) */
0x00, /* REG_AUDIO_IF (0xE) */
0x00, /* REG_VOICE_IF (0xF) */
0x00, /* REG_ARXR1PGA (0x10) */
0x00, /* REG_ARXL1PGA (0x11) */
0x6c, /* REG_ARXR2PGA (0x12) */
0x6c, /* REG_ARXL2PGA (0x13) */
0x00, /* REG_VRXPGA (0x14) */
0x3f, /* REG_ARXR1PGA (0x10) */
0x3f, /* REG_ARXL1PGA (0x11) */
0x3f, /* REG_ARXR2PGA (0x12) */
0x3f, /* REG_ARXL2PGA (0x13) */
0x25, /* REG_VRXPGA (0x14) */
0x00, /* REG_VSTPGA (0x15) */
0x00, /* REG_VRX2ARXPGA (0x16) */
0x00, /* REG_AVDAC_CTL (0x17) */
0x00, /* REG_ARX2VTXPGA (0x18) */
0x00, /* REG_ARXL1_APGA_CTL (0x19) */
0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */
0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */
0x32, /* REG_ARXL1_APGA_CTL (0x19) */
0x32, /* REG_ARXR1_APGA_CTL (0x1A) */
0x32, /* REG_ARXL2_APGA_CTL (0x1B) */
0x32, /* REG_ARXR2_APGA_CTL (0x1C) */
0x00, /* REG_ATX2ARXPGA (0x1D) */
0x00, /* REG_BT_IF (0x1E) */
0x00, /* REG_BTPGA (0x1F) */
0x55, /* REG_BTPGA (0x1F) */
0x00, /* REG_BTSTPGA (0x20) */
0x00, /* REG_EAR_CTL (0x21) */
0x00, /* REG_HS_SEL (0x22) */
@ -85,32 +85,32 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_PRECKR_CTL (0x28) */
0x00, /* REG_HFL_CTL (0x29) */
0x00, /* REG_HFR_CTL (0x2A) */
0x00, /* REG_ALC_CTL (0x2B) */
0x05, /* REG_ALC_CTL (0x2B) */
0x00, /* REG_ALC_SET1 (0x2C) */
0x00, /* REG_ALC_SET2 (0x2D) */
0x00, /* REG_BOOST_CTL (0x2E) */
0x00, /* REG_SOFTVOL_CTL (0x2F) */
0x00, /* REG_DTMF_FREQSEL (0x30) */
0x13, /* REG_DTMF_FREQSEL (0x30) */
0x00, /* REG_DTMF_TONEXT1H (0x31) */
0x00, /* REG_DTMF_TONEXT1L (0x32) */
0x00, /* REG_DTMF_TONEXT2H (0x33) */
0x00, /* REG_DTMF_TONEXT2L (0x34) */
0x00, /* REG_DTMF_TONOFF (0x35) */
0x00, /* REG_DTMF_WANONOFF (0x36) */
0x79, /* REG_DTMF_TONOFF (0x35) */
0x11, /* REG_DTMF_WANONOFF (0x36) */
0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
0x06, /* REG_APLL_CTL (0x3A) */
0x00, /* REG_DTMF_CTL (0x3B) */
0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */
0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
0x44, /* REG_DTMF_PGA_CTL2 (0x3C) */
0x69, /* REG_DTMF_PGA_CTL1 (0x3D) */
0x00, /* REG_MISC_SET_1 (0x3E) */
0x00, /* REG_PCMBTMUX (0x3F) */
0x00, /* not used (0x40) */
0x00, /* not used (0x41) */
0x00, /* not used (0x42) */
0x00, /* REG_RX_PATH_SEL (0x43) */
0x00, /* REG_VDL_APGA_CTL (0x44) */
0x32, /* REG_VDL_APGA_CTL (0x44) */
0x00, /* REG_VIBRA_CTL (0x45) */
0x00, /* REG_VIBRA_SET (0x46) */
0x00, /* REG_VIBRA_PWM_SET (0x47) */
@ -143,6 +143,9 @@ struct twl4030_priv {
u8 earpiece_enabled;
u8 predrivel_enabled, predriver_enabled;
u8 carkitl_enabled, carkitr_enabled;
/* Delay needed after enabling the digimic interface */
unsigned int digimic_delay;
};
/*
@ -244,21 +247,112 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
udelay(10);
}
static void twl4030_init_chip(struct snd_soc_codec *codec)
static inline void twl4030_check_defaults(struct snd_soc_codec *codec)
{
u8 *cache = codec->reg_cache;
int i;
int i, difference = 0;
u8 val;
/* clear CODECPDZ prior to setting register defaults */
twl4030_codec_enable(codec, 0);
dev_dbg(codec->dev, "Checking TWL audio default configuration\n");
for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) {
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i);
if (val != twl4030_reg[i]) {
difference++;
dev_dbg(codec->dev,
"Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n",
i, val, twl4030_reg[i]);
}
}
dev_dbg(codec->dev, "Found %d non maching registers. %s\n",
difference, difference ? "Not OK" : "OK");
}
static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
{
int i;
/* set all audio section registers to reasonable defaults */
for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
if (i != TWL4030_REG_APLL_CTL)
twl4030_write(codec, i, cache[i]);
twl4030_write(codec, i, twl4030_reg[i]);
}
static void twl4030_init_chip(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct twl4030_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->card->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 reg, byte;
int i = 0;
/* Check defaults, if instructed before anything else */
if (setup && setup->check_defaults)
twl4030_check_defaults(codec);
/* Reset registers, if no setup data or if instructed to do so */
if (!setup || (setup && setup->reset_registers))
twl4030_reset_registers(codec);
/* Refresh APLL_CTL register from HW */
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
TWL4030_REG_APLL_CTL);
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte);
/* anti-pop when changing analog gain */
reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
twl4030_write(codec, TWL4030_REG_MISC_SET_1,
reg | TWL4030_SMOOTH_ANAVOL_EN);
twl4030_write(codec, TWL4030_REG_OPTION,
TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
/* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
/* Machine dependent setup */
if (!setup)
return;
twl4030->digimic_delay = setup->digimic_delay;
/* Configuration for headset ramp delay from setup data */
if (setup->sysclk != twl4030->sysclk)
dev_warn(codec->dev,
"Mismatch in APLL mclk: %u (configured: %u)\n",
setup->sysclk, twl4030->sysclk);
reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
reg &= ~TWL4030_RAMP_DELAY;
reg |= (setup->ramp_delay_value << 2);
twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg);
/* initiate offset cancellation */
twl4030_codec_enable(codec, 1);
reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
reg &= ~TWL4030_OFFSET_CNCL_SEL;
reg |= setup->offset_cncl_path;
twl4030_write(codec, TWL4030_REG_ANAMICL,
reg | TWL4030_CNCL_OFFSET_START);
/* wait for offset cancellation to complete */
do {
/* this takes a little while, so don't slam i2c */
udelay(2000);
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
TWL4030_REG_ANAMICL);
} while ((i++ < 100) &&
((byte & TWL4030_CNCL_OFFSET_START) ==
TWL4030_CNCL_OFFSET_START));
/* Make sure that the reg_cache has the same value as the HW */
twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
twl4030_codec_enable(codec, 0);
}
static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
@ -280,55 +374,6 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
}
static void twl4030_power_up(struct snd_soc_codec *codec)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 anamicl, regmisc1, byte;
int i = 0;
if (twl4030->codec_powered)
return;
/* set CODECPDZ to turn on codec */
twl4030_codec_enable(codec, 1);
/* initiate offset cancellation */
anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
twl4030_write(codec, TWL4030_REG_ANAMICL,
anamicl | TWL4030_CNCL_OFFSET_START);
/* wait for offset cancellation to complete */
do {
/* this takes a little while, so don't slam i2c */
udelay(2000);
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
TWL4030_REG_ANAMICL);
} while ((i++ < 100) &&
((byte & TWL4030_CNCL_OFFSET_START) ==
TWL4030_CNCL_OFFSET_START));
/* Make sure that the reg_cache has the same value as the HW */
twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
/* anti-pop when changing analog gain */
regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
twl4030_write(codec, TWL4030_REG_MISC_SET_1,
regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
/* toggle CODECPDZ as per TRM */
twl4030_codec_enable(codec, 0);
twl4030_codec_enable(codec, 1);
}
/*
* Unconditional power down
*/
static void twl4030_power_down(struct snd_soc_codec *codec)
{
/* power down */
twl4030_codec_enable(codec, 0);
}
/* Earpiece */
static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = {
SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0),
@ -500,10 +545,11 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
/* Digital bypass gain, 0 mutes the bypass */
/* Digital bypass gain, mute instead of -30dB */
static const unsigned int twl4030_dapm_dbypass_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1),
TLV_DB_RANGE_HEAD(3),
0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1),
2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0),
4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0),
};
@ -531,36 +577,6 @@ static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
TWL4030_REG_VSTPGA, 0, 0x29, 0,
twl4030_dapm_dbypassv_tlv);
static int micpath_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
unsigned char adcmicsel, micbias_ctl;
adcmicsel = twl4030_read_reg_cache(w->codec, TWL4030_REG_ADCMICSEL);
micbias_ctl = twl4030_read_reg_cache(w->codec, TWL4030_REG_MICBIAS_CTL);
/* Prepare the bits for the given TX path:
* shift_l == 0: TX1 microphone path
* shift_l == 2: TX2 microphone path */
if (e->shift_l) {
/* TX2 microphone path */
if (adcmicsel & TWL4030_TX2IN_SEL)
micbias_ctl |= TWL4030_MICBIAS2_CTL; /* digimic */
else
micbias_ctl &= ~TWL4030_MICBIAS2_CTL;
} else {
/* TX1 microphone path */
if (adcmicsel & TWL4030_TX1IN_SEL)
micbias_ctl |= TWL4030_MICBIAS1_CTL; /* digimic */
else
micbias_ctl &= ~TWL4030_MICBIAS1_CTL;
}
twl4030_write(w->codec, TWL4030_REG_MICBIAS_CTL, micbias_ctl);
return 0;
}
/*
* Output PGA builder:
* Handle the muting and unmuting of the given output (turning off the
@ -814,6 +830,16 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w,
return 0;
}
static int digimic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec);
if (twl4030->digimic_delay)
mdelay(twl4030->digimic_delay);
return 0;
}
/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
@ -1374,14 +1400,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
/* Analog/Digital mic path selection.
TX1 Left/Right: either analog Left/Right or Digimic0
TX2 Left/Right: either analog Left/Right or Digimic1 */
SND_SOC_DAPM_MUX_E("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_micpathtx1_control, micpath_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_MUX_E("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_micpathtx2_control, micpath_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_micpathtx1_control),
SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_micpathtx2_control),
/* Analog input mixers for the capture amplifiers */
SND_SOC_DAPM_MIXER("Analog Left",
@ -1398,10 +1420,17 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_PGA("ADC Physical Right",
TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Digimic0 Enable",
TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Digimic1 Enable",
TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA_E("Digimic0 Enable",
TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0,
digimic_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("Digimic1 Enable",
TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0,
digimic_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0,
NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
@ -1419,8 +1448,11 @@ static const struct snd_soc_dapm_route intercon[] = {
/* Supply for the digital part (APLL) */
{"Digital Voice Playback Mixer", NULL, "APLL Enable"},
{"Digital R1 Playback Mixer", NULL, "AIF Enable"},
{"Digital L1 Playback Mixer", NULL, "AIF Enable"},
{"DAC Left1", NULL, "AIF Enable"},
{"DAC Right1", NULL, "AIF Enable"},
{"DAC Left2", NULL, "AIF Enable"},
{"DAC Right1", NULL, "AIF Enable"},
{"Digital R2 Playback Mixer", NULL, "AIF Enable"},
{"Digital L2 Playback Mixer", NULL, "AIF Enable"},
@ -1491,10 +1523,10 @@ static const struct snd_soc_dapm_route intercon[] = {
/* outputs */
/* Must be always connected (for AIF and APLL) */
{"Virtual HiFi OUT", NULL, "Digital L1 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "Digital R1 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "Digital L2 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "Digital R2 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "DAC Left1"},
{"Virtual HiFi OUT", NULL, "DAC Right1"},
{"Virtual HiFi OUT", NULL, "DAC Left2"},
{"Virtual HiFi OUT", NULL, "DAC Right2"},
/* Must be always connected (for APLL) */
{"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"},
/* Physical outputs */
@ -1531,6 +1563,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digimic0 Enable", NULL, "DIGIMIC0"},
{"Digimic1 Enable", NULL, "DIGIMIC1"},
{"DIGIMIC0", NULL, "micbias1 select"},
{"DIGIMIC1", NULL, "micbias2 select"},
/* TX1 Left capture path */
{"TX1 Capture Route", "Analog", "ADC Physical Left"},
{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
@ -1605,10 +1640,10 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF)
twl4030_power_up(codec);
twl4030_codec_enable(codec, 1);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
twl4030_codec_enable(codec, 0);
break;
}
codec->bias_level = level;
@ -1794,13 +1829,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
if (mode != old_mode) {
/* change rate and set CODECPDZ */
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_codec_enable(codec, 1);
}
/* sample size */
old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
format = old_format;
@ -1818,16 +1846,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
if (format != old_format) {
/* clear CODECPDZ before changing format (codec requirement) */
twl4030_codec_enable(codec, 0);
/* change format */
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
/* set CODECPDZ afterwards */
twl4030_codec_enable(codec, 1);
if (format != old_format || mode != old_mode) {
if (twl4030->codec_powered) {
/*
* If the codec is powered, than we need to toggle the
* codec power.
*/
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
twl4030_codec_enable(codec, 1);
} else {
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
}
}
/* Store the important parameters for the DAI configuration and set
@ -1877,6 +1909,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 old_format, format;
/* get format */
@ -1911,15 +1944,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
if (format != old_format) {
/* clear CODECPDZ before changing format (codec requirement) */
twl4030_codec_enable(codec, 0);
/* change format */
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
/* set CODECPDZ afterwards */
twl4030_codec_enable(codec, 1);
if (twl4030->codec_powered) {
/*
* If the codec is powered, than we need to toggle the
* codec power.
*/
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
twl4030_codec_enable(codec, 1);
} else {
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
}
}
return 0;
@ -2011,6 +2046,7 @@ static int twl4030_voice_hw_params(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;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 old_mode, mode;
/* Enable voice digital filters */
@ -2035,10 +2071,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
}
if (mode != old_mode) {
/* change rate and set CODECPDZ */
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_codec_enable(codec, 1);
if (twl4030->codec_powered) {
/*
* If the codec is powered, than we need to toggle the
* codec power.
*/
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_codec_enable(codec, 1);
} else {
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
}
}
return 0;
@ -2068,6 +2111,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
u8 old_format, format;
/* get format */
@ -2099,10 +2143,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
if (format != old_format) {
/* change format and set CODECPDZ */
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
twl4030_codec_enable(codec, 1);
if (twl4030->codec_powered) {
/*
* If the codec is powered, than we need to toggle the
* codec power.
*/
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
twl4030_codec_enable(codec, 1);
} else {
twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
}
}
return 0;
@ -2202,31 +2253,15 @@ static struct snd_soc_codec *twl4030_codec;
static int twl4030_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct twl4030_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
int ret;
BUG_ON(!twl4030_codec);
codec = twl4030_codec;
twl4030 = snd_soc_codec_get_drvdata(codec);
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);
}
twl4030_init_chip(pdev);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@ -2247,6 +2282,8 @@ 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;
/* Reset registers to their chip default before leaving */
twl4030_reset_registers(codec);
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
@ -2287,6 +2324,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
codec->read = twl4030_read_reg_cache;
codec->write = twl4030_write;
codec->set_bias_level = twl4030_set_bias_level;
codec->idle_bias_off = 1;
codec->dai = twl4030_dai;
codec->num_dai = ARRAY_SIZE(twl4030_dai);
codec->reg_cache_size = sizeof(twl4030_reg);
@ -2302,9 +2340,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
/* Set the defaults, and power up the codec */
twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
twl4030_init_chip(codec);
codec->bias_level = SND_SOC_BIAS_OFF;
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
@ -2322,7 +2358,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
return 0;
error_codec:
twl4030_power_down(codec);
twl4030_codec_enable(codec, 0);
kfree(codec->reg_cache);
error_cache:
kfree(twl4030);

View File

@ -41,7 +41,11 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030;
struct twl4030_setup_data {
unsigned int ramp_delay_value;
unsigned int digimic_delay; /* in ms */
unsigned int sysclk;
unsigned int offset_cncl_path;
unsigned int check_defaults:1;
unsigned int reset_registers:1;
unsigned int hs_extmute:1;
void (*set_hs_extmute)(int mute);
};

View File

@ -360,6 +360,13 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
return 0;
}
static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
msleep(1);
return 0;
}
static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@ -371,6 +378,8 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
else
priv->non_lp--;
msleep(1);
return 0;
}
@ -471,20 +480,6 @@ static const struct snd_kcontrol_new hfdacl_switch_controls =
static const struct snd_kcontrol_new hfdacr_switch_controls =
SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 2, 1, 0);
/* Headset driver switches */
static const struct snd_kcontrol_new hsl_driver_switch_controls =
SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSLCTL, 2, 1, 0);
static const struct snd_kcontrol_new hsr_driver_switch_controls =
SOC_DAPM_SINGLE("Switch", TWL6040_REG_HSRCTL, 2, 1, 0);
/* Handsfree driver switches */
static const struct snd_kcontrol_new hfl_driver_switch_controls =
SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 4, 1, 0);
static const struct snd_kcontrol_new hfr_driver_switch_controls =
SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 4, 1, 0);
static const struct snd_kcontrol_new ep_driver_switch_controls =
SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
@ -548,10 +543,14 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
TWL6040_REG_DMICBCTL, 4, 0),
/* DACs */
SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback",
TWL6040_REG_HSLCTL, 0, 0),
SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback",
TWL6040_REG_HSRCTL, 0, 0),
SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback",
TWL6040_REG_HSLCTL, 0, 0,
twl6040_hs_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback",
TWL6040_REG_HSRCTL, 0, 0,
twl6040_hs_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback",
TWL6040_REG_HFLCTL, 0, 0,
twl6040_power_mode_event,
@ -571,18 +570,19 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("HFDAC Right Playback",
SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls),
SND_SOC_DAPM_SWITCH("Headset Left Driver",
SND_SOC_NOPM, 0, 0, &hsl_driver_switch_controls),
SND_SOC_DAPM_SWITCH("Headset Right Driver",
SND_SOC_NOPM, 0, 0, &hsr_driver_switch_controls),
SND_SOC_DAPM_SWITCH_E("Handsfree Left Driver",
SND_SOC_NOPM, 0, 0, &hfl_driver_switch_controls,
/* Analog playback drivers */
SND_SOC_DAPM_PGA_E("Handsfree Left Driver",
TWL6040_REG_HFLCTL, 4, 0, NULL, 0,
twl6040_power_mode_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SWITCH_E("Handsfree Right Driver",
SND_SOC_NOPM, 0, 0, &hfr_driver_switch_controls,
SND_SOC_DAPM_PGA_E("Handsfree Right Driver",
TWL6040_REG_HFRCTL, 4, 0, NULL, 0,
twl6040_power_mode_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA("Headset Left Driver",
TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Headset Right Driver",
TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
SND_SOC_DAPM_SWITCH_E("Earphone Driver",
SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls,
twl6040_power_mode_event,
@ -616,8 +616,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"HSDAC Left Playback", "Switch", "HSDAC Left"},
{"HSDAC Right Playback", "Switch", "HSDAC Right"},
{"Headset Left Driver", "Switch", "HSDAC Left Playback"},
{"Headset Right Driver", "Switch", "HSDAC Right Playback"},
{"Headset Left Driver", NULL, "HSDAC Left Playback"},
{"Headset Right Driver", NULL, "HSDAC Right Playback"},
{"HSOL", NULL, "Headset Left Driver"},
{"HSOR", NULL, "Headset Right Driver"},
@ -928,7 +928,7 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
case 19200000:
/* mclk input, pll disabled */
hppllctl |= TWL6040_MCLK_19200KHZ |
TWL6040_HPLLSQRBP |
TWL6040_HPLLSQRENA |
TWL6040_HPLLBP;
break;
case 26000000:

View File

@ -28,19 +28,6 @@
#include "uda134x.h"
#define POWER_OFF_ON_STANDBY 1
/*
ALSA SOC usually puts the device in standby mode when it's not used
for sometime. If you define POWER_OFF_ON_STANDBY the driver will
turn off the ADC/DAC when this callback is invoked and turn it back
on when needed. Unfortunately this will result in a very light bump
(it can be audible only with good earphones). If this bothers you
just comment this line, you will have slightly higher power
consumption . Please note that sending the L3 command for ADC is
enough to make the bump, so it doesn't make difference if you
completely take off power from the codec.
*/
#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
@ -58,7 +45,7 @@ static const char uda134x_reg[UDA134X_REGS_NUM] = {
/* Extended address registers */
0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Status, data regs */
0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
0x00, 0x83, 0x00, 0x40, 0x80, 0xC0, 0x00,
};
/*
@ -117,6 +104,7 @@ static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
case UDA134X_DATA000:
case UDA134X_DATA001:
case UDA134X_DATA010:
case UDA134X_DATA011:
addr = UDA134X_DATA0_ADDR;
break;
case UDA134X_DATA1:
@ -353,8 +341,22 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
/* ADC, DAC on */
reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
case UDA134X_UDA1345:
reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
uda134x_write(codec, UDA134X_DATA011, reg | 0x03);
break;
case UDA134X_UDA1341:
reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
break;
default:
printk(KERN_ERR "UDA134X SoC codec: "
"unsupported model %d\n", pd->model);
return -EINVAL;
}
break;
case SND_SOC_BIAS_PREPARE:
/* power on */
@ -367,8 +369,22 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* ADC, DAC power off */
reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
case UDA134X_UDA1345:
reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03));
break;
case UDA134X_UDA1341:
reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
break;
default:
printk(KERN_ERR "UDA134X SoC codec: "
"unsupported model %d\n", pd->model);
return -EINVAL;
}
break;
case SND_SOC_BIAS_OFF:
/* power off */
@ -531,9 +547,7 @@ static int uda134x_soc_probe(struct platform_device *pdev)
codec->num_dai = 1;
codec->read = uda134x_read_reg_cache;
codec->write = uda134x_write;
#ifdef POWER_OFF_ON_STANDBY
codec->set_bias_level = uda134x_set_bias_level;
#endif
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -544,6 +558,14 @@ static int uda134x_soc_probe(struct platform_device *pdev)
uda134x_reset(codec);
if (pd->is_powered_on_standby) {
codec->set_bias_level = NULL;
uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
} else {
codec->set_bias_level = uda134x_set_bias_level;
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
}
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {

View File

@ -23,9 +23,10 @@
#define UDA134X_DATA000 10
#define UDA134X_DATA001 11
#define UDA134X_DATA010 12
#define UDA134X_DATA1 13
#define UDA134X_DATA011 13
#define UDA134X_DATA1 14
#define UDA134X_REGS_NUM 14
#define UDA134X_REGS_NUM 15
#define STATUS0_DAIFMT_MASK (~(7<<1))
#define STATUS0_SYSCLK_MASK (~(3<<4))

View File

@ -795,6 +795,8 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
dev_set_drvdata(&i2c->dev, wm2000);
wm2000->anc_eng_ena = 1;
wm2000->anc_active = 1;
wm2000->spk_ena = 1;
wm2000->i2c = i2c;
wm2000_reset(wm2000);

View File

@ -482,7 +482,8 @@ static int wm8523_register(struct wm8523_priv *wm8523,
if (wm8523_codec) {
dev_err(codec->dev, "Another WM8523 is registered\n");
return -EINVAL;
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
@ -570,18 +571,19 @@ static int wm8523_register(struct wm8523_priv *wm8523,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
goto err_enable;
}
ret = snd_soc_register_dai(&wm8523_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
err_get:

View File

@ -439,7 +439,8 @@ static int wm8711_register(struct wm8711_priv *wm8711,
if (wm8711_codec) {
dev_err(codec->dev, "Another WM8711 is registered\n");
return -EINVAL;
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);

579
sound/soc/codecs/wm8741.c Normal file
View File

@ -0,0 +1,579 @@
/*
* wm8741.c -- WM8741 ALSA SoC Audio driver
*
* Copyright 2010 Wolfson Microelectronics plc
*
* Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "wm8741.h"
static struct snd_soc_codec *wm8741_codec;
struct snd_soc_codec_device soc_codec_dev_wm8741;
#define WM8741_NUM_SUPPLIES 2
static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
"AVDD",
"DVDD",
};
#define WM8741_NUM_RATES 4
/* codec private data */
struct wm8741_priv {
struct snd_soc_codec codec;
u16 reg_cache[WM8741_REGISTER_COUNT];
struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
unsigned int sysclk;
unsigned int rate_constraint_list[WM8741_NUM_RATES];
struct snd_pcm_hw_constraint_list rate_constraint;
};
static const u16 wm8741_reg_defaults[WM8741_REGISTER_COUNT] = {
0x0000, /* R0 - DACLLSB Attenuation */
0x0000, /* R1 - DACLMSB Attenuation */
0x0000, /* R2 - DACRLSB Attenuation */
0x0000, /* R3 - DACRMSB Attenuation */
0x0000, /* R4 - Volume Control */
0x000A, /* R5 - Format Control */
0x0000, /* R6 - Filter Control */
0x0000, /* R7 - Mode Control 1 */
0x0002, /* R8 - Mode Control 2 */
0x0000, /* R9 - Reset */
0x0002, /* R32 - ADDITONAL_CONTROL_1 */
};
static int wm8741_reset(struct snd_soc_codec *codec)
{
return snd_soc_write(codec, WM8741_RESET, 0);
}
static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
static const struct snd_kcontrol_new wm8741_snd_controls[] = {
SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
};
static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_OUTPUT("VOUTLP"),
SND_SOC_DAPM_OUTPUT("VOUTLN"),
SND_SOC_DAPM_OUTPUT("VOUTRP"),
SND_SOC_DAPM_OUTPUT("VOUTRN"),
};
static const struct snd_soc_dapm_route intercon[] = {
{ "VOUTLP", NULL, "DACL" },
{ "VOUTLN", NULL, "DACL" },
{ "VOUTRP", NULL, "DACR" },
{ "VOUTRN", NULL, "DACR" },
};
static int wm8741_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, wm8741_dapm_widgets,
ARRAY_SIZE(wm8741_dapm_widgets));
snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
return 0;
}
static struct {
int value;
int ratio;
} lrclk_ratios[WM8741_NUM_RATES] = {
{ 1, 256 },
{ 2, 384 },
{ 3, 512 },
{ 4, 768 },
};
static int wm8741_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
if (!wm8741->sysclk) {
dev_err(codec->dev,
"No MCLK configured, call set_sysclk() on init\n");
return -EINVAL;
}
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&wm8741->rate_constraint);
return 0;
}
static int wm8741_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC;
int i;
/* Find a supported LRCLK ratio */
for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
if (wm8741->sysclk / params_rate(params) ==
lrclk_ratios[i].ratio)
break;
}
/* Should never happen, should be handled by constraints */
if (i == ARRAY_SIZE(lrclk_ratios)) {
dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
wm8741->sysclk / params_rate(params));
return -EINVAL;
}
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
iface |= 0x0001;
break;
case SNDRV_PCM_FORMAT_S24_LE:
iface |= 0x0002;
break;
case SNDRV_PCM_FORMAT_S32_LE:
iface |= 0x0003;
break;
default:
dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d",
params_format(params));
return -EINVAL;
}
dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d",
params_format(params));
snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
return 0;
}
static int wm8741_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 wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
int i;
dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
wm8741->sysclk = freq;
wm8741->rate_constraint.count = 0;
for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
dev_dbg(codec->dev, "index = %d, ratio = %d, freq = %d",
i, lrclk_ratios[i].ratio, freq);
val = freq / lrclk_ratios[i].ratio;
/* Check that it's a standard rate since core can't
* cope with others and having the odd rates confuses
* constraint matching.
*/
switch (val) {
case 32000:
case 44100:
case 48000:
case 64000:
case 88200:
case 96000:
dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
val);
wm8741->rate_constraint_list[i] = val;
wm8741->rate_constraint.count++;
break;
default:
dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
val);
}
}
/* Need at least one supported rate... */
if (wm8741->rate_constraint.count == 0)
return -EINVAL;
return 0;
}
static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3;
/* check master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
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 |= 0x0008;
break;
case SND_SOC_DAIFMT_RIGHT_J:
break;
case SND_SOC_DAIFMT_LEFT_J:
iface |= 0x0004;
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 |= 0x0010;
break;
case SND_SOC_DAIFMT_IB_NF:
iface |= 0x0020;
break;
case SND_SOC_DAIFMT_NB_IF:
iface |= 0x0030;
break;
default:
return -EINVAL;
}
dev_dbg(codec->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK,
((fmt & SND_SOC_DAIFMT_INV_MASK)));
snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface);
return 0;
}
#define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000)
#define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops wm8741_dai_ops = {
.startup = wm8741_startup,
.hw_params = wm8741_hw_params,
.set_sysclk = wm8741_set_dai_sysclk,
.set_fmt = wm8741_set_dai_fmt,
};
struct snd_soc_dai wm8741_dai = {
.name = "WM8741",
.playback = {
.stream_name = "Playback",
.channels_min = 2, /* Mono modes not yet supported */
.channels_max = 2,
.rates = WM8741_RATES,
.formats = WM8741_FORMATS,
},
.ops = &wm8741_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8741_dai);
#ifdef CONFIG_PM
static int wm8741_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
u16 *cache = codec->reg_cache;
int i;
/* RESTORE REG Cache */
for (i = 0; i < WM8741_REGISTER_COUNT; i++) {
if (cache[i] == wm8741_reg_defaults[i] || WM8741_RESET == i)
continue;
snd_soc_write(codec, i, cache[i]);
}
return 0;
}
#else
#define wm8741_suspend NULL
#define wm8741_resume NULL
#endif
static int wm8741_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (wm8741_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = wm8741_codec;
codec = wm8741_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, wm8741_snd_controls,
ARRAY_SIZE(wm8741_snd_controls));
wm8741_add_widgets(codec);
return ret;
pcm_err:
return ret;
}
static int wm8741_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_wm8741 = {
.probe = wm8741_probe,
.remove = wm8741_remove,
.resume = wm8741_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8741);
static int wm8741_register(struct wm8741_priv *wm8741,
enum snd_soc_control_type control)
{
int ret;
struct snd_soc_codec *codec = &wm8741->codec;
int i;
if (wm8741_codec) {
dev_err(codec->dev, "Another WM8741 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
snd_soc_codec_set_drvdata(codec, wm8741);
codec->name = "WM8741";
codec->owner = THIS_MODULE;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = NULL;
codec->dai = &wm8741_dai;
codec->num_dai = 1;
codec->reg_cache_size = WM8741_REGISTER_COUNT;
codec->reg_cache = &wm8741->reg_cache;
wm8741->rate_constraint.list = &wm8741->rate_constraint_list[0];
wm8741->rate_constraint.count =
ARRAY_SIZE(wm8741->rate_constraint_list);
memcpy(codec->reg_cache, wm8741_reg_defaults,
sizeof(wm8741->reg_cache));
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;
}
for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
wm8741->supplies[i].supply = wm8741_supply_names[i];
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8741->supplies),
wm8741->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
wm8741->supplies);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_get;
}
ret = wm8741_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
goto err_enable;
}
wm8741_dai.dev = codec->dev;
/* Change some default settings - latch VU */
wm8741->reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
wm8741->reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
wm8741->reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
wm8741_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
}
ret = snd_soc_register_dai(&wm8741_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
}
dev_dbg(codec->dev, "Successful registration\n");
return 0;
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
err_get:
regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
err:
kfree(wm8741);
return ret;
}
static void wm8741_unregister(struct wm8741_priv *wm8741)
{
regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
snd_soc_unregister_dai(&wm8741_dai);
snd_soc_unregister_codec(&wm8741->codec);
kfree(wm8741);
wm8741_codec = NULL;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int wm8741_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8741_priv *wm8741;
struct snd_soc_codec *codec;
wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
if (wm8741 == NULL)
return -ENOMEM;
codec = &wm8741->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8741);
codec->control_data = i2c;
codec->dev = &i2c->dev;
return wm8741_register(wm8741, SND_SOC_I2C);
}
static __devexit int wm8741_i2c_remove(struct i2c_client *client)
{
struct wm8741_priv *wm8741 = i2c_get_clientdata(client);
wm8741_unregister(wm8741);
return 0;
}
static const struct i2c_device_id wm8741_i2c_id[] = {
{ "wm8741", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
static struct i2c_driver wm8741_i2c_driver = {
.driver = {
.name = "WM8741",
.owner = THIS_MODULE,
},
.probe = wm8741_i2c_probe,
.remove = __devexit_p(wm8741_i2c_remove),
.id_table = wm8741_i2c_id,
};
#endif
static int __init wm8741_modinit(void)
{
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8741_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8741 I2C driver: %d\n",
ret);
}
#endif
return 0;
}
module_init(wm8741_modinit);
static void __exit wm8741_exit(void)
{
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8741_i2c_driver);
#endif
}
module_exit(wm8741_exit);
MODULE_DESCRIPTION("ASoC WM8741 driver");
MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");

214
sound/soc/codecs/wm8741.h Normal file
View File

@ -0,0 +1,214 @@
/*
* wm8741.h -- WM8423 ASoC driver
*
* Copyright 2010 Wolfson Microelectronics, plc
*
* Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
*
* Based on wm8753.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 _WM8741_H
#define _WM8741_H
/*
* Register values.
*/
#define WM8741_DACLLSB_ATTENUATION 0x00
#define WM8741_DACLMSB_ATTENUATION 0x01
#define WM8741_DACRLSB_ATTENUATION 0x02
#define WM8741_DACRMSB_ATTENUATION 0x03
#define WM8741_VOLUME_CONTROL 0x04
#define WM8741_FORMAT_CONTROL 0x05
#define WM8741_FILTER_CONTROL 0x06
#define WM8741_MODE_CONTROL_1 0x07
#define WM8741_MODE_CONTROL_2 0x08
#define WM8741_RESET 0x09
#define WM8741_ADDITIONAL_CONTROL_1 0x20
#define WM8741_REGISTER_COUNT 11
#define WM8741_MAX_REGISTER 0x20
/*
* Field Definitions.
*/
/*
* R0 (0x00) - DACLLSB_ATTENUATION
*/
#define WM8741_UPDATELL 0x0020 /* UPDATELL */
#define WM8741_UPDATELL_MASK 0x0020 /* UPDATELL */
#define WM8741_UPDATELL_SHIFT 5 /* UPDATELL */
#define WM8741_UPDATELL_WIDTH 1 /* UPDATELL */
#define WM8741_LAT_4_0_MASK 0x001F /* LAT[4:0] - [4:0] */
#define WM8741_LAT_4_0_SHIFT 0 /* LAT[4:0] - [4:0] */
#define WM8741_LAT_4_0_WIDTH 5 /* LAT[4:0] - [4:0] */
/*
* R1 (0x01) - DACLMSB_ATTENUATION
*/
#define WM8741_UPDATELM 0x0020 /* UPDATELM */
#define WM8741_UPDATELM_MASK 0x0020 /* UPDATELM */
#define WM8741_UPDATELM_SHIFT 5 /* UPDATELM */
#define WM8741_UPDATELM_WIDTH 1 /* UPDATELM */
#define WM8741_LAT_9_5_0_MASK 0x001F /* LAT[9:5] - [4:0] */
#define WM8741_LAT_9_5_0_SHIFT 0 /* LAT[9:5] - [4:0] */
#define WM8741_LAT_9_5_0_WIDTH 5 /* LAT[9:5] - [4:0] */
/*
* R2 (0x02) - DACRLSB_ATTENUATION
*/
#define WM8741_UPDATERL 0x0020 /* UPDATERL */
#define WM8741_UPDATERL_MASK 0x0020 /* UPDATERL */
#define WM8741_UPDATERL_SHIFT 5 /* UPDATERL */
#define WM8741_UPDATERL_WIDTH 1 /* UPDATERL */
#define WM8741_RAT_4_0_MASK 0x001F /* RAT[4:0] - [4:0] */
#define WM8741_RAT_4_0_SHIFT 0 /* RAT[4:0] - [4:0] */
#define WM8741_RAT_4_0_WIDTH 5 /* RAT[4:0] - [4:0] */
/*
* R3 (0x03) - DACRMSB_ATTENUATION
*/
#define WM8741_UPDATERM 0x0020 /* UPDATERM */
#define WM8741_UPDATERM_MASK 0x0020 /* UPDATERM */
#define WM8741_UPDATERM_SHIFT 5 /* UPDATERM */
#define WM8741_UPDATERM_WIDTH 1 /* UPDATERM */
#define WM8741_RAT_9_5_0_MASK 0x001F /* RAT[9:5] - [4:0] */
#define WM8741_RAT_9_5_0_SHIFT 0 /* RAT[9:5] - [4:0] */
#define WM8741_RAT_9_5_0_WIDTH 5 /* RAT[9:5] - [4:0] */
/*
* R4 (0x04) - VOLUME_CONTROL
*/
#define WM8741_AMUTE 0x0080 /* AMUTE */
#define WM8741_AMUTE_MASK 0x0080 /* AMUTE */
#define WM8741_AMUTE_SHIFT 7 /* AMUTE */
#define WM8741_AMUTE_WIDTH 1 /* AMUTE */
#define WM8741_ZFLAG_MASK 0x0060 /* ZFLAG - [6:5] */
#define WM8741_ZFLAG_SHIFT 5 /* ZFLAG - [6:5] */
#define WM8741_ZFLAG_WIDTH 2 /* ZFLAG - [6:5] */
#define WM8741_IZD 0x0010 /* IZD */
#define WM8741_IZD_MASK 0x0010 /* IZD */
#define WM8741_IZD_SHIFT 4 /* IZD */
#define WM8741_IZD_WIDTH 1 /* IZD */
#define WM8741_SOFT 0x0008 /* SOFT MUTE */
#define WM8741_SOFT_MASK 0x0008 /* SOFT MUTE */
#define WM8741_SOFT_SHIFT 3 /* SOFT MUTE */
#define WM8741_SOFT_WIDTH 1 /* SOFT MUTE */
#define WM8741_ATC 0x0004 /* ATC */
#define WM8741_ATC_MASK 0x0004 /* ATC */
#define WM8741_ATC_SHIFT 2 /* ATC */
#define WM8741_ATC_WIDTH 1 /* ATC */
#define WM8741_ATT2DB 0x0002 /* ATT2DB */
#define WM8741_ATT2DB_MASK 0x0002 /* ATT2DB */
#define WM8741_ATT2DB_SHIFT 1 /* ATT2DB */
#define WM8741_ATT2DB_WIDTH 1 /* ATT2DB */
#define WM8741_VOL_RAMP 0x0001 /* VOL_RAMP */
#define WM8741_VOL_RAMP_MASK 0x0001 /* VOL_RAMP */
#define WM8741_VOL_RAMP_SHIFT 0 /* VOL_RAMP */
#define WM8741_VOL_RAMP_WIDTH 1 /* VOL_RAMP */
/*
* R5 (0x05) - FORMAT_CONTROL
*/
#define WM8741_PWDN 0x0080 /* PWDN */
#define WM8741_PWDN_MASK 0x0080 /* PWDN */
#define WM8741_PWDN_SHIFT 7 /* PWDN */
#define WM8741_PWDN_WIDTH 1 /* PWDN */
#define WM8741_REV 0x0040 /* REV */
#define WM8741_REV_MASK 0x0040 /* REV */
#define WM8741_REV_SHIFT 6 /* REV */
#define WM8741_REV_WIDTH 1 /* REV */
#define WM8741_BCP 0x0020 /* BCP */
#define WM8741_BCP_MASK 0x0020 /* BCP */
#define WM8741_BCP_SHIFT 5 /* BCP */
#define WM8741_BCP_WIDTH 1 /* BCP */
#define WM8741_LRP 0x0010 /* LRP */
#define WM8741_LRP_MASK 0x0010 /* LRP */
#define WM8741_LRP_SHIFT 4 /* LRP */
#define WM8741_LRP_WIDTH 1 /* LRP */
#define WM8741_FMT_MASK 0x000C /* FMT - [3:2] */
#define WM8741_FMT_SHIFT 2 /* FMT - [3:2] */
#define WM8741_FMT_WIDTH 2 /* FMT - [3:2] */
#define WM8741_IWL_MASK 0x0003 /* IWL - [1:0] */
#define WM8741_IWL_SHIFT 0 /* IWL - [1:0] */
#define WM8741_IWL_WIDTH 2 /* IWL - [1:0] */
/*
* R6 (0x06) - FILTER_CONTROL
*/
#define WM8741_ZFLAG_HI 0x0080 /* ZFLAG_HI */
#define WM8741_ZFLAG_HI_MASK 0x0080 /* ZFLAG_HI */
#define WM8741_ZFLAG_HI_SHIFT 7 /* ZFLAG_HI */
#define WM8741_ZFLAG_HI_WIDTH 1 /* ZFLAG_HI */
#define WM8741_DEEMPH_MASK 0x0060 /* DEEMPH - [6:5] */
#define WM8741_DEEMPH_SHIFT 5 /* DEEMPH - [6:5] */
#define WM8741_DEEMPH_WIDTH 2 /* DEEMPH - [6:5] */
#define WM8741_DSDFILT_MASK 0x0018 /* DSDFILT - [4:3] */
#define WM8741_DSDFILT_SHIFT 3 /* DSDFILT - [4:3] */
#define WM8741_DSDFILT_WIDTH 2 /* DSDFILT - [4:3] */
#define WM8741_FIRSEL_MASK 0x0007 /* FIRSEL - [2:0] */
#define WM8741_FIRSEL_SHIFT 0 /* FIRSEL - [2:0] */
#define WM8741_FIRSEL_WIDTH 3 /* FIRSEL - [2:0] */
/*
* R7 (0x07) - MODE_CONTROL_1
*/
#define WM8741_MODE8X 0x0080 /* MODE8X */
#define WM8741_MODE8X_MASK 0x0080 /* MODE8X */
#define WM8741_MODE8X_SHIFT 7 /* MODE8X */
#define WM8741_MODE8X_WIDTH 1 /* MODE8X */
#define WM8741_OSR_MASK 0x0060 /* OSR - [6:5] */
#define WM8741_OSR_SHIFT 5 /* OSR - [6:5] */
#define WM8741_OSR_WIDTH 2 /* OSR - [6:5] */
#define WM8741_SR_MASK 0x001C /* SR - [4:2] */
#define WM8741_SR_SHIFT 2 /* SR - [4:2] */
#define WM8741_SR_WIDTH 3 /* SR - [4:2] */
#define WM8741_MODESEL_MASK 0x0003 /* MODESEL - [1:0] */
#define WM8741_MODESEL_SHIFT 0 /* MODESEL - [1:0] */
#define WM8741_MODESEL_WIDTH 2 /* MODESEL - [1:0] */
/*
* R8 (0x08) - MODE_CONTROL_2
*/
#define WM8741_DSD_GAIN 0x0040 /* DSD_GAIN */
#define WM8741_DSD_GAIN_MASK 0x0040 /* DSD_GAIN */
#define WM8741_DSD_GAIN_SHIFT 6 /* DSD_GAIN */
#define WM8741_DSD_GAIN_WIDTH 1 /* DSD_GAIN */
#define WM8741_SDOUT 0x0020 /* SDOUT */
#define WM8741_SDOUT_MASK 0x0020 /* SDOUT */
#define WM8741_SDOUT_SHIFT 5 /* SDOUT */
#define WM8741_SDOUT_WIDTH 1 /* SDOUT */
#define WM8741_DOUT 0x0010 /* DOUT */
#define WM8741_DOUT_MASK 0x0010 /* DOUT */
#define WM8741_DOUT_SHIFT 4 /* DOUT */
#define WM8741_DOUT_WIDTH 1 /* DOUT */
#define WM8741_DIFF_MASK 0x000C /* DIFF - [3:2] */
#define WM8741_DIFF_SHIFT 2 /* DIFF - [3:2] */
#define WM8741_DIFF_WIDTH 2 /* DIFF - [3:2] */
#define WM8741_DITHER_MASK 0x0003 /* DITHER - [1:0] */
#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */
#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */
/*
* R32 (0x20) - ADDITONAL_CONTROL_1
*/
#define WM8741_DSD_LEVEL 0x0002 /* DSD_LEVEL */
#define WM8741_DSD_LEVEL_MASK 0x0002 /* DSD_LEVEL */
#define WM8741_DSD_LEVEL_SHIFT 1 /* DSD_LEVEL */
#define WM8741_DSD_LEVEL_WIDTH 1 /* DSD_LEVEL */
#define WM8741_DSD_NO_NOTCH 0x0001 /* DSD_NO_NOTCH */
#define WM8741_DSD_NO_NOTCH_MASK 0x0001 /* DSD_NO_NOTCH */
#define WM8741_DSD_NO_NOTCH_SHIFT 0 /* DSD_NO_NOTCH */
#define WM8741_DSD_NO_NOTCH_WIDTH 1 /* DSD_NO_NOTCH */
#define WM8741_SYSCLK 0
extern struct snd_soc_dai wm8741_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8741;
#endif

View File

@ -884,6 +884,7 @@ static int wm8750_i2c_remove(struct i2c_client *client)
static const struct i2c_device_id wm8750_i2c_id[] = {
{ "wm8750", 0 },
{ "wm8987", 0 }, /* WM8987 is register compatible with WM8750 */
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
@ -925,14 +926,22 @@ static int __devexit wm8750_spi_remove(struct spi_device *spi)
return 0;
}
static const struct spi_device_id wm8750_spi_id[] = {
{ "wm8750", 0 },
{ "wm8987", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, wm8750_spi_id);
static struct spi_driver wm8750_spi_driver = {
.driver = {
.name = "wm8750",
.name = "WM8750 SPI Codec",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm8750_spi_probe,
.remove = __devexit_p(wm8750_spi_remove),
.id_table = wm8750_spi_id,
};
#endif

View File

@ -2433,7 +2433,8 @@ static int wm8904_register(struct wm8904_priv *wm8904,
if (wm8904_codec) {
dev_err(codec->dev, "Another WM8904 is registered\n");
return -EINVAL;
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
@ -2462,7 +2463,8 @@ static int wm8904_register(struct wm8904_priv *wm8904,
default:
dev_err(codec->dev, "Unknown device type %d\n",
wm8904->devtype);
return -EINVAL;
ret = -EINVAL;
goto err;
}
memcpy(codec->reg_cache, wm8904_reg, sizeof(wm8904_reg));
@ -2566,18 +2568,19 @@ static int wm8904_register(struct wm8904_priv *wm8904,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
goto err_enable;
}
ret = snd_soc_register_dai(&wm8904_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
err_get:

View File

@ -845,6 +845,7 @@ static void wm8940_unregister(struct wm8940_priv *wm8940)
static int wm8940_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct wm8940_priv *wm8940;
struct snd_soc_codec *codec;
@ -858,7 +859,11 @@ static int wm8940_i2c_probe(struct i2c_client *i2c,
codec->control_data = i2c;
codec->dev = &i2c->dev;
return wm8940_register(wm8940, SND_SOC_I2C);
ret = wm8940_register(wm8940, SND_SOC_I2C);
if (ret < 0)
kfree(wm8940);
return ret;
}
static int __devexit wm8940_i2c_remove(struct i2c_client *client)

View File

@ -964,7 +964,8 @@ static int wm8955_register(struct wm8955_priv *wm8955,
if (wm8955_codec) {
dev_err(codec->dev, "Another WM8955 is registered\n");
return -EINVAL;
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
@ -1047,18 +1048,19 @@ static int wm8955_register(struct wm8955_priv *wm8955,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
goto err_enable;
}
ret = snd_soc_register_dai(&wm8955_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
err_get:

View File

@ -79,12 +79,13 @@ struct wm8960_priv {
struct snd_soc_dapm_widget *lout1;
struct snd_soc_dapm_widget *rout1;
struct snd_soc_dapm_widget *out3;
bool deemph;
int playback_fs;
};
#define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0)
/* enumerated controls */
static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
"Right Inverted", "Stereo Inversion"};
static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
@ -93,7 +94,6 @@ static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
@ -102,6 +102,59 @@ static const struct soc_enum wm8960_enum[] = {
SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
};
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
static int wm8960_set_deemph(struct snd_soc_codec *codec)
{
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
int val, i, best;
/* If we're using deemphasis select the nearest available sample
* rate.
*/
if (wm8960->deemph) {
best = 1;
for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
if (abs(deemph_settings[i] - wm8960->playback_fs) <
abs(deemph_settings[best] - wm8960->playback_fs))
best = i;
}
val = best << 1;
} else {
val = 0;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
return snd_soc_update_bits(codec, WM8960_DACCTL1,
0x6, val);
}
static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
return wm8960->deemph;
}
static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
int deemph = ucontrol->value.enumerated.item[0];
if (deemph > 1)
return -EINVAL;
wm8960->deemph = deemph;
return wm8960_set_deemph(codec);
}
static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
@ -131,23 +184,24 @@ SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
SOC_ENUM("ADC Polarity", wm8960_enum[1]),
SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
SOC_ENUM("ADC Polarity", wm8960_enum[0]),
SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
SOC_ENUM("DAC Polarity", wm8960_enum[2]),
SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
wm8960_get_deemph, wm8960_put_deemph),
SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]),
SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]),
SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
SOC_ENUM("ALC Function", wm8960_enum[5]),
SOC_ENUM("ALC Function", wm8960_enum[4]),
SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
SOC_ENUM("ALC Mode", wm8960_enum[6]),
SOC_ENUM("ALC Mode", wm8960_enum[5]),
SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
@ -433,6 +487,21 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static struct {
int rate;
unsigned int val;
} alc_rates[] = {
{ 48000, 0 },
{ 44100, 0 },
{ 32000, 1 },
{ 22050, 2 },
{ 24000, 2 },
{ 16000, 3 },
{ 11250, 4 },
{ 12000, 4 },
{ 8000, 5 },
};
static int wm8960_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -440,7 +509,9 @@ static int wm8960_hw_params(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;
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
int i;
/* bit size */
switch (params_format(params)) {
@ -454,6 +525,18 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
break;
}
/* Update filters for the new rate */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
wm8960->playback_fs = params_rate(params);
wm8960_set_deemph(codec);
} else {
for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
if (alc_rates[i].rate == params_rate(params))
snd_soc_update_bits(codec,
WM8960_ADDCTL3, 0x7,
alc_rates[i].val);
}
/* set iface */
snd_soc_write(codec, WM8960_IFACE1, iface);
return 0;

View File

@ -1102,7 +1102,7 @@ static int wm8961_register(struct wm8961_priv *wm8961)
ret = wm8961_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
return ret;
goto err;
}
/* Enable class W */
@ -1147,18 +1147,19 @@ static int wm8961_register(struct wm8961_priv *wm8961)
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
goto err;
}
ret = snd_soc_register_dai(&wm8961_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm8961);
return ret;

View File

@ -670,7 +670,8 @@ static __devinit int wm8974_register(struct wm8974_priv *wm8974)
if (wm8974_codec) {
dev_err(codec->dev, "Another WM8974 is registered\n");
return -EINVAL;
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);

View File

@ -1076,7 +1076,6 @@ static __devinit int wm8978_register(struct wm8978_priv *wm8978)
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm8978);
return ret;
}
@ -1085,13 +1084,13 @@ static __devexit void wm8978_unregister(struct wm8978_priv *wm8978)
wm8978_set_bias_level(&wm8978->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8978_dai);
snd_soc_unregister_codec(&wm8978->codec);
kfree(wm8978);
wm8978_codec = NULL;
}
static __devinit int wm8978_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct wm8978_priv *wm8978;
struct snd_soc_codec *codec;
@ -1107,13 +1106,18 @@ static __devinit int wm8978_i2c_probe(struct i2c_client *i2c,
codec->dev = &i2c->dev;
return wm8978_register(wm8978);
ret = wm8978_register(wm8978);
if (ret < 0)
kfree(wm8978);
return ret;
}
static __devexit int wm8978_i2c_remove(struct i2c_client *client)
{
struct wm8978_priv *wm8978 = i2c_get_clientdata(client);
wm8978_unregister(wm8978);
kfree(wm8978);
return 0;
}

View File

@ -30,8 +30,6 @@
#include "wm8990.h"
#define WM8990_VERSION "0.2"
/* codec private data */
struct wm8990_priv {
unsigned int sysclk;
@ -1511,8 +1509,6 @@ static int wm8990_probe(struct platform_device *pdev)
struct wm8990_priv *wm8990;
int ret;
pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)

View File

@ -1677,6 +1677,26 @@ static struct {
static int wm8994_readable(unsigned int reg)
{
switch (reg) {
case WM8994_GPIO_1:
case WM8994_GPIO_2:
case WM8994_GPIO_3:
case WM8994_GPIO_4:
case WM8994_GPIO_5:
case WM8994_GPIO_6:
case WM8994_GPIO_7:
case WM8994_GPIO_8:
case WM8994_GPIO_9:
case WM8994_GPIO_10:
case WM8994_GPIO_11:
case WM8994_INTERRUPT_STATUS_1:
case WM8994_INTERRUPT_STATUS_2:
case WM8994_INTERRUPT_RAW_STATUS_2:
return 1;
default:
break;
}
if (reg >= ARRAY_SIZE(access_masks))
return 0;
return access_masks[reg].readable != 0;
@ -2341,6 +2361,20 @@ SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
0, 1, 0),
};
static const struct snd_kcontrol_new aif1adc2l_mix[] = {
SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING,
1, 1, 0),
SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING,
0, 1, 0),
};
static const struct snd_kcontrol_new aif1adc2r_mix[] = {
SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING,
1, 1, 0),
SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING,
0, 1, 0),
};
static const struct snd_kcontrol_new aif2dac2l_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
5, 1, 0),
@ -2472,6 +2506,7 @@ static const struct snd_kcontrol_new aif3adc_mux =
static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),
SND_SOC_DAPM_INPUT("Clock"),
SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@ -2506,6 +2541,11 @@ SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0,
aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)),
SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0,
aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)),
SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0,
aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)),
SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0,
aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)),
SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0,
@ -2668,6 +2708,14 @@ static const struct snd_soc_dapm_route intercon[] = {
{ "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" },
{ "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" },
{ "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" },
{ "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" },
{ "AIF1ADC2L Mixer", "AIF2 Switch", "AIF2DACL" },
{ "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" },
{ "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" },
{ "AIF1ADC2R Mixer", "AIF2 Switch", "AIF2DACR" },
/* Pin level routing for AIF3 */
{ "AIF1DAC1L", NULL, "AIF1DAC Mux" },
{ "AIF1DAC1R", NULL, "AIF1DAC Mux" },
@ -2946,11 +2994,14 @@ static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
return 0;
}
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
int i;
switch (dai->id) {
case 1:
@ -2988,6 +3039,25 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id);
break;
case WM8994_SYSCLK_OPCLK:
/* Special case - a division (times 10) is given and
* no effect on main clocking.
*/
if (freq) {
for (i = 0; i < ARRAY_SIZE(opclk_divs); i++)
if (opclk_divs[i] == freq)
break;
if (i == ARRAY_SIZE(opclk_divs))
return -EINVAL;
snd_soc_update_bits(codec, WM8994_CLOCKING_2,
WM8994_OPCLK_DIV_MASK, i);
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
WM8994_OPCLK_ENA, WM8994_OPCLK_ENA);
} else {
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_2,
WM8994_OPCLK_ENA, 0);
}
default:
return -EINVAL;
}
@ -4004,6 +4074,11 @@ static int wm8994_codec_probe(struct platform_device *pdev)
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
/* Unconditionally enable AIF1 ADC TDM mode; it only affects
* behaviour on idle TDM clock cycles. */
snd_soc_update_bits(codec, WM8994_AIF1_CONTROL_1,
WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM);
wm8994_update_class_w(codec);
ret = snd_soc_register_codec(codec);

View File

@ -20,6 +20,9 @@ extern struct snd_soc_dai wm8994_dai[];
#define WM8994_SYSCLK_FLL1 3
#define WM8994_SYSCLK_FLL2 4
/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */
#define WM8994_SYSCLK_OPCLK 5
#define WM8994_FLL1 1
#define WM8994_FLL2 2

View File

@ -1356,7 +1356,7 @@ static int wm9081_register(struct wm9081_priv *wm9081,
ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
if (ret != 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
goto err;
}
reg = snd_soc_read(codec, WM9081_SOFTWARE_RESET);
@ -1369,7 +1369,7 @@ static int wm9081_register(struct wm9081_priv *wm9081,
ret = wm9081_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
return ret;
goto err;
}
wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@ -1388,18 +1388,19 @@ static int wm9081_register(struct wm9081_priv *wm9081,
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
goto err;
}
ret = snd_soc_register_dai(&wm9081_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm9081);
return ret;

View File

@ -410,6 +410,8 @@ static int hp_event(struct snd_soc_dapm_widget *w,
WM8993_HPOUT1L_DLY |
WM8993_HPOUT1R_DLY, 0);
snd_soc_write(codec, WM8993_DC_SERVO_0, 0);
snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
0);

View File

@ -26,6 +26,7 @@
#include <mach/asp.h>
#include "davinci-pcm.h"
#include "davinci-i2s.h"
/*
@ -68,16 +69,21 @@
#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
#define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24)
#define DAVINCI_MCBSP_RCR_RPHASE BIT(31)
#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
#define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24)
#define DAVINCI_MCBSP_XCR_XPHASE BIT(31)
#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29)
#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
@ -116,6 +122,7 @@ static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
};
struct davinci_mcbsp_dev {
struct device *dev;
struct davinci_pcm_dma_params dma_params[2];
void __iomem *base;
#define MOD_DSP_A 0
@ -144,6 +151,11 @@ struct davinci_mcbsp_dev {
* won't end up being swapped because of the underrun.
*/
unsigned enable_channel_combine:1;
unsigned int fmt;
int clk_div;
int clk_input_pin;
bool i2s_accurate_sck;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@ -254,10 +266,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
unsigned int pcr;
unsigned int srgr;
/* Attention srgr is updated by hw_params! */
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
dev->fmt = fmt;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
@ -268,11 +282,26 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
DAVINCI_MCBSP_PCR_CLKRM;
break;
case SND_SOC_DAIFMT_CBM_CFS:
/* McBSP CLKR pin is the input for the Sample Rate Generator.
* McBSP FSR and FSX are driven by the Sample Rate Generator. */
pcr = DAVINCI_MCBSP_PCR_SCLKME |
DAVINCI_MCBSP_PCR_FSXM |
DAVINCI_MCBSP_PCR_FSRM;
pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
/*
* Selection of the clock input pin that is the
* input for the Sample Rate Generator.
* McBSP FSR and FSX are driven by the Sample Rate
* Generator.
*/
switch (dev->clk_input_pin) {
case MCBSP_CLKS:
pcr |= DAVINCI_MCBSP_PCR_CLKXM |
DAVINCI_MCBSP_PCR_CLKRM;
break;
case MCBSP_CLKR:
pcr |= DAVINCI_MCBSP_PCR_SCLKME;
break;
default:
dev_err(dev->dev, "bad clk_input_pin\n");
return -EINVAL;
}
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* codec is master */
@ -372,6 +401,18 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return 0;
}
static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
if (div_id != DAVINCI_MCBSP_CLKGDV)
return -ENODEV;
dev->clk_div = div;
return 0;
}
static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -380,8 +421,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct davinci_pcm_dma_params *dma_params =
&dev->dma_params[substream->stream];
struct snd_interval *i = NULL;
int mcbsp_word_length;
unsigned int rcr, xcr, srgr;
int mcbsp_word_length, master;
unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
u32 spcr;
snd_pcm_format_t fmt;
unsigned element_cnt = 1;
@ -396,12 +437,59 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
srgr = DAVINCI_MCBSP_SRGR_FSGM;
srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
fmt = params_format(params);
mcbsp_word_length = asp_word_length[fmt];
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
freq = clk_get_rate(dev->clk);
srgr = DAVINCI_MCBSP_SRGR_FSGM |
DAVINCI_MCBSP_SRGR_CLKSM;
srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
8 - 1);
if (dev->i2s_accurate_sck) {
clk_div = 256;
do {
framesize = (freq / (--clk_div)) /
params->rate_num *
params->rate_den;
} while (((framesize < 33) || (framesize > 4095)) &&
(clk_div));
clk_div--;
srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
} else {
/* symmetric waveforms */
clk_div = freq / (mcbsp_word_length * 16) /
params->rate_num * params->rate_den;
srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
16 - 1);
}
clk_div &= 0xFF;
srgr |= clk_div;
break;
case SND_SOC_DAIFMT_CBM_CFS:
srgr = DAVINCI_MCBSP_SRGR_FSGM;
clk_div = dev->clk_div - 1;
srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
clk_div &= 0xFF;
srgr |= clk_div;
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* Clock and frame sync given from external sources */
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
srgr = DAVINCI_MCBSP_SRGR_FSGM;
srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
pr_debug("%s - %d FWID set: re-read srgr = %X\n",
__func__, __LINE__, snd_interval_value(i) - 1);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
break;
default:
return -EINVAL;
}
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
rcr = DAVINCI_MCBSP_RCR_RFIG;
@ -426,12 +514,41 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
element_cnt = 1;
fmt = double_fmt[fmt];
}
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
rcr |= DAVINCI_MCBSP_RCR_RPHASE;
xcr |= DAVINCI_MCBSP_XCR_XPHASE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
break;
default:
return -EINVAL;
}
}
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);
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
break;
case SND_SOC_DAIFMT_CBM_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
break;
default:
return -EINVAL;
}
rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
@ -442,6 +559,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
else
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr);
pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr);
pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr);
return 0;
}
@ -500,6 +621,7 @@ static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.trigger = davinci_i2s_trigger,
.hw_params = davinci_i2s_hw_params,
.set_fmt = davinci_i2s_set_dai_fmt,
.set_clkdiv = davinci_i2s_dai_set_clkdiv,
};
@ -526,6 +648,8 @@ static int davinci_i2s_probe(struct platform_device *pdev)
struct snd_platform_data *pdata = pdev->dev.platform_data;
struct davinci_mcbsp_dev *dev;
struct resource *mem, *ioarea, *res;
enum dma_event_q asp_chan_q = EVENTQ_0;
enum dma_event_q ram_chan_q = EVENTQ_1;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -552,7 +676,17 @@ static int davinci_i2s_probe(struct platform_device *pdev)
pdata->sram_size_playback;
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
pdata->sram_size_capture;
dev->clk_input_pin = pdata->clk_input_pin;
dev->i2s_accurate_sck = pdata->i2s_accurate_sck;
asp_chan_q = pdata->asp_chan_q;
ram_chan_q = pdata->ram_chan_q;
}
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q = asp_chan_q;
dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q = ram_chan_q;
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q = asp_chan_q;
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q = ram_chan_q;
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
ret = -ENODEV;
@ -584,6 +718,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
goto err_free_mem;
}
dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
dev->dev = &pdev->dev;
davinci_i2s_dai.private_data = dev;
davinci_i2s_dai.capture.dma_data = dev->dma_params;

View File

@ -12,6 +12,11 @@
#ifndef _DAVINCI_I2S_H
#define _DAVINCI_I2S_H
/* McBSP dividers */
enum davinci_mcbsp_div {
DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */
};
extern struct snd_soc_dai davinci_i2s_dai;
#endif

View File

@ -890,7 +890,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dev->rxnumevt = pdata->rxnumevt;
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->eventq_no = pdata->eventq_no;
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
io_v2p(dev->base));
@ -904,7 +905,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
dma_data->channel = res->start;
dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data->eventq_no = pdata->eventq_no;
dma_data->asp_chan_q = pdata->asp_chan_q;
dma_data->ram_chan_q = pdata->ram_chan_q;
dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
io_v2p(dev->base));

View File

@ -381,7 +381,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
/* Request ram master channel */
link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
davinci_pcm_dma_irq, substream,
EVENTQ_1);
prtd->params->ram_chan_q);
if (link < 0)
goto exit1;
@ -477,7 +477,8 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
/* Request asp master DMA channel */
link = prtd->asp_channel = edma_alloc_channel(params->channel,
davinci_pcm_dma_irq, substream, EVENTQ_0);
davinci_pcm_dma_irq, substream,
prtd->params->asp_chan_q);
if (link < 0)
goto exit1;
@ -800,7 +801,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
dma_free_writecombine(pcm->card->dev, buf->bytes,
buf->area, buf->addr);
buf->area = NULL;
iram_dma = (struct snd_dma_buffer *)buf->private_data;
iram_dma = buf->private_data;
if (iram_dma) {
sram_free(iram_dma->area, iram_dma->bytes);
kfree(iram_dma);

View File

@ -21,7 +21,8 @@ struct davinci_pcm_dma_params {
unsigned short acnt;
dma_addr_t dma_addr; /* device physical address for DMA */
unsigned sram_size;
enum dma_event_q eventq_no; /* event queue number */
enum dma_event_q asp_chan_q; /* event queue number for ASP channel */
enum dma_event_q ram_chan_q; /* event queue number for RAM channel */
unsigned char data_type; /* xfer data type */
unsigned char convert_mono_stereo;
unsigned int fifo_level;

View File

@ -203,7 +203,7 @@ static int davinci_vcif_probe(struct platform_device *pdev)
int ret;
davinci_vcif_dev = kzalloc(sizeof(struct davinci_vcif_dev), GFP_KERNEL);
if (!davinci_vc) {
if (!davinci_vcif_dev) {
dev_dbg(&pdev->dev,
"could not allocate memory for private data\n");
return -ENOMEM;

18
sound/soc/ep93xx/Kconfig Normal file
View File

@ -0,0 +1,18 @@
config SND_EP93XX_SOC
tristate "SoC Audio support for the Cirrus Logic EP93xx series"
depends on ARCH_EP93XX && SND_SOC
help
Say Y or M if you want to add support for codecs attached to
the EP93xx I2S interface.
config SND_EP93XX_SOC_I2S
tristate
config SND_EP93XX_SOC_SNAPPERCL15
tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
select SND_EP93XX_SOC_I2S
select SND_SOC_TLV320AIC23
help
Say Y or M here if you want to add support for I2S audio on the
Bluewater Systems Snapper CL15 module.

11
sound/soc/ep93xx/Makefile Normal file
View File

@ -0,0 +1,11 @@
# EP93xx Platform Support
snd-soc-ep93xx-objs := ep93xx-pcm.o
snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o
obj-$(CONFIG_SND_EP93XX_SOC) += snd-soc-ep93xx.o
obj-$(CONFIG_SND_EP93XX_SOC_I2S) += snd-soc-ep93xx-i2s.o
# EP93XX Machine Support
snd-soc-snappercl15-objs := snappercl15.o
obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o

View File

@ -0,0 +1,487 @@
/*
* linux/sound/soc/ep93xx-i2s.c
* EP93xx I2S driver
*
* Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.com>
*
* Based on the original driver by:
* Copyright (C) 2007 Chase Douglas <chasedouglas@gmail>
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
*
* 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/init.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <mach/hardware.h>
#include <mach/ep93xx-regs.h>
#include <mach/dma.h>
#include "ep93xx-pcm.h"
#include "ep93xx-i2s.h"
#define EP93XX_I2S_TXCLKCFG 0x00
#define EP93XX_I2S_RXCLKCFG 0x04
#define EP93XX_I2S_GLCTRL 0x0C
#define EP93XX_I2S_TXLINCTRLDATA 0x28
#define EP93XX_I2S_TXCTRL 0x2C
#define EP93XX_I2S_TXWRDLEN 0x30
#define EP93XX_I2S_TX0EN 0x34
#define EP93XX_I2S_RXLINCTRLDATA 0x58
#define EP93XX_I2S_RXCTRL 0x5C
#define EP93XX_I2S_RXWRDLEN 0x60
#define EP93XX_I2S_RX0EN 0x64
#define EP93XX_I2S_WRDLEN_16 (0 << 0)
#define EP93XX_I2S_WRDLEN_24 (1 << 0)
#define EP93XX_I2S_WRDLEN_32 (2 << 0)
#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */
#define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */
#define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */
#define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */
#define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */
#define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */
struct ep93xx_i2s_info {
struct clk *mclk;
struct clk *sclk;
struct clk *lrclk;
struct ep93xx_pcm_dma_params *dma_params;
struct resource *mem;
void __iomem *regs;
};
struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
[SNDRV_PCM_STREAM_PLAYBACK] = {
.name = "i2s-pcm-out",
.dma_port = EP93XX_DMA_M2P_PORT_I2S1,
},
[SNDRV_PCM_STREAM_CAPTURE] = {
.name = "i2s-pcm-in",
.dma_port = EP93XX_DMA_M2P_PORT_I2S1,
},
};
static inline void ep93xx_i2s_write_reg(struct ep93xx_i2s_info *info,
unsigned reg, unsigned val)
{
__raw_writel(val, info->regs + reg);
}
static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
unsigned reg)
{
return __raw_readl(info->regs + reg);
}
static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
int i;
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
/* Enable clocks */
clk_enable(info->mclk);
clk_enable(info->sclk);
clk_enable(info->lrclk);
/* Enable i2s */
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
}
/* Enable fifos */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
base_reg = EP93XX_I2S_TX0EN;
else
base_reg = EP93XX_I2S_RX0EN;
for (i = 0; i < 3; i++)
ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1);
}
static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
int i;
/* Disable fifos */
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
base_reg = EP93XX_I2S_TX0EN;
else
base_reg = EP93XX_I2S_RX0EN;
for (i = 0; i < 3; i++)
ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0);
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
/* Disable i2s */
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0);
/* Disable clocks */
clk_disable(info->lrclk);
clk_disable(info->sclk);
clk_disable(info->mclk);
}
}
static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct ep93xx_i2s_info *info = rtd->dai->cpu_dai->private_data;
snd_soc_dai_set_dma_data(cpu_dai, substream,
&info->dma_params[substream->stream]);
return 0;
}
static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ep93xx_i2s_info *info = rtd->dai->cpu_dai->private_data;
ep93xx_i2s_disable(info, substream->stream);
}
static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct ep93xx_i2s_info *info = cpu_dai->private_data;
unsigned int clk_cfg, lin_ctrl;
clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG);
lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
clk_cfg |= EP93XX_I2S_CLKCFG_REL;
lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
case SND_SOC_DAIFMT_RIGHT_J:
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* CPU is master */
clk_cfg |= EP93XX_I2S_CLKCFG_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* Codec is master */
clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
/* Negative bit clock, lrclk low on left word */
clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL);
break;
case SND_SOC_DAIFMT_NB_IF:
/* Negative bit clock, lrclk low on right word */
clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP;
clk_cfg |= EP93XX_I2S_CLKCFG_REL;
break;
case SND_SOC_DAIFMT_IB_NF:
/* Positive bit clock, lrclk low on left word */
clk_cfg |= EP93XX_I2S_CLKCFG_CKP;
clk_cfg &= ~EP93XX_I2S_CLKCFG_REL;
break;
case SND_SOC_DAIFMT_IB_IF:
/* Positive bit clock, lrclk low on right word */
clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL;
break;
}
/* Write new register values */
ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg);
ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg);
ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl);
ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl);
return 0;
}
static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct ep93xx_i2s_info *info = cpu_dai->private_data;
unsigned word_len, div, sdiv, lrdiv;
int found = 0, err;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
word_len = EP93XX_I2S_WRDLEN_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
word_len = EP93XX_I2S_WRDLEN_24;
break;
case SNDRV_PCM_FORMAT_S32_LE:
word_len = EP93XX_I2S_WRDLEN_32;
break;
default:
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ep93xx_i2s_write_reg(info, EP93XX_I2S_TXWRDLEN, word_len);
else
ep93xx_i2s_write_reg(info, EP93XX_I2S_RXWRDLEN, word_len);
/*
* Calculate the sdiv (bit clock) and lrdiv (left/right clock) values.
* If the lrclk is pulse length is larger than the word size, then the
* bit clock will be gated for the unused bits.
*/
div = (clk_get_rate(info->mclk) / params_rate(params)) *
params_channels(params);
for (sdiv = 2; sdiv <= 4; sdiv += 2)
for (lrdiv = 32; lrdiv <= 128; lrdiv <<= 1)
if (sdiv * lrdiv == div) {
found = 1;
goto out;
}
out:
if (!found)
return -EINVAL;
err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
if (err)
return err;
err = clk_set_rate(info->lrclk, clk_get_rate(info->sclk) / lrdiv);
if (err)
return err;
ep93xx_i2s_enable(info, substream->stream);
return 0;
}
static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
unsigned int freq, int dir)
{
struct ep93xx_i2s_info *info = cpu_dai->private_data;
if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
return -EINVAL;
return clk_set_rate(info->mclk, freq);
}
#ifdef CONFIG_PM
static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
{
struct ep93xx_i2s_info *info = dai->private_data;
if (!dai->active)
return;
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
}
static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
{
struct ep93xx_i2s_info *info = dai->private_data;
if (!dai->active)
return;
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
}
#else
#define ep93xx_i2s_suspend NULL
#define ep93xx_i2s_resume NULL
#endif
static struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
.startup = ep93xx_i2s_startup,
.shutdown = ep93xx_i2s_shutdown,
.hw_params = ep93xx_i2s_hw_params,
.set_sysclk = ep93xx_i2s_set_sysclk,
.set_fmt = ep93xx_i2s_set_dai_fmt,
};
#define EP93XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
struct snd_soc_dai ep93xx_i2s_dai = {
.name = "ep93xx-i2s",
.id = 0,
.symmetric_rates= 1,
.suspend = ep93xx_i2s_suspend,
.resume = ep93xx_i2s_resume,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = EP93XX_I2S_FORMATS,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = EP93XX_I2S_FORMATS,
},
.ops = &ep93xx_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(ep93xx_i2s_dai);
static int ep93xx_i2s_probe(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info;
struct resource *res;
int err;
info = kzalloc(sizeof(struct ep93xx_i2s_info), GFP_KERNEL);
if (!info) {
err = -ENOMEM;
goto fail;
}
ep93xx_i2s_dai.dev = &pdev->dev;
ep93xx_i2s_dai.private_data = info;
info->dma_params = ep93xx_i2s_dma_params;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
err = -ENODEV;
goto fail;
}
info->mem = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!info->mem) {
err = -EBUSY;
goto fail;
}
info->regs = ioremap(info->mem->start, resource_size(info->mem));
if (!info->regs) {
err = -ENXIO;
goto fail_release_mem;
}
info->mclk = clk_get(&pdev->dev, "mclk");
if (IS_ERR(info->mclk)) {
err = PTR_ERR(info->mclk);
goto fail_unmap_mem;
}
info->sclk = clk_get(&pdev->dev, "sclk");
if (IS_ERR(info->sclk)) {
err = PTR_ERR(info->sclk);
goto fail_put_mclk;
}
info->lrclk = clk_get(&pdev->dev, "lrclk");
if (IS_ERR(info->lrclk)) {
err = PTR_ERR(info->lrclk);
goto fail_put_sclk;
}
err = snd_soc_register_dai(&ep93xx_i2s_dai);
if (err)
goto fail_put_lrclk;
return 0;
fail_put_lrclk:
clk_put(info->lrclk);
fail_put_sclk:
clk_put(info->sclk);
fail_put_mclk:
clk_put(info->mclk);
fail_unmap_mem:
iounmap(info->regs);
fail_release_mem:
release_mem_region(info->mem->start, resource_size(info->mem));
kfree(info);
fail:
return err;
}
static int __devexit ep93xx_i2s_remove(struct platform_device *pdev)
{
struct ep93xx_i2s_info *info = ep93xx_i2s_dai.private_data;
snd_soc_unregister_dai(&ep93xx_i2s_dai);
clk_put(info->lrclk);
clk_put(info->sclk);
clk_put(info->mclk);
iounmap(info->regs);
release_mem_region(info->mem->start, resource_size(info->mem));
kfree(info);
return 0;
}
static struct platform_driver ep93xx_i2s_driver = {
.probe = ep93xx_i2s_probe,
.remove = __devexit_p(ep93xx_i2s_remove),
.driver = {
.name = "ep93xx-i2s",
.owner = THIS_MODULE,
},
};
static int __init ep93xx_i2s_init(void)
{
return platform_driver_register(&ep93xx_i2s_driver);
}
static void __exit ep93xx_i2s_exit(void)
{
platform_driver_unregister(&ep93xx_i2s_driver);
}
module_init(ep93xx_i2s_init);
module_exit(ep93xx_i2s_exit);
MODULE_ALIAS("platform:ep93xx-i2s");
MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
MODULE_DESCRIPTION("EP93XX I2S driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,18 @@
/*
* linux/sound/soc/ep93xx-i2s.h
* EP93xx I2S driver
*
* Copyright (C) 2010 Ryan Mallon <ryan@bluewatersys.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 _EP93XX_SND_SOC_I2S_H
#define _EP93XX_SND_SOC_I2S_H
extern struct snd_soc_dai ep93xx_i2s_dai;
#endif /* _EP93XX_SND_SOC_I2S_H */

View File

@ -0,0 +1,319 @@
/*
* linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
*
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
* Copyright (C) 2006 Applied Data Systems
*
* Rewritten for the SoC audio subsystem (Based on PXA2xx code):
* Copyright (c) 2008 Ryan Mallon <ryan@bluewatersys.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <mach/dma.h>
#include <mach/hardware.h>
#include <mach/ep93xx-regs.h>
#include "ep93xx-pcm.h"
static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = SNDRV_PCM_RATE_8000,
.rate_max = SNDRV_PCM_RATE_48000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE),
.buffer_bytes_max = 131072,
.period_bytes_min = 32,
.period_bytes_max = 32768,
.periods_min = 1,
.periods_max = 32,
.fifo_size = 32,
};
struct ep93xx_runtime_data
{
struct ep93xx_dma_m2p_client cl;
struct ep93xx_pcm_dma_params *params;
int pointer_bytes;
struct tasklet_struct period_tasklet;
int periods;
struct ep93xx_dma_buffer buf[32];
};
static void ep93xx_pcm_period_elapsed(unsigned long data)
{
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
snd_pcm_period_elapsed(substream);
}
static void ep93xx_pcm_buffer_started(void *cookie,
struct ep93xx_dma_buffer *buf)
{
}
static void ep93xx_pcm_buffer_finished(void *cookie,
struct ep93xx_dma_buffer *buf,
int bytes, int error)
{
struct snd_pcm_substream *substream = cookie;
struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
if (buf == rtd->buf + rtd->periods - 1)
rtd->pointer_bytes = 0;
else
rtd->pointer_bytes += buf->size;
if (!error) {
ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf);
tasklet_schedule(&rtd->period_tasklet);
} else {
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
}
}
static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = soc_rtd->dai->cpu_dai;
struct ep93xx_pcm_dma_params *dma_params;
struct ep93xx_runtime_data *rtd;
int ret;
dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
if (!rtd)
return -ENOMEM;
memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet));
rtd->period_tasklet.func = ep93xx_pcm_period_elapsed;
rtd->period_tasklet.data = (unsigned long)substream;
rtd->cl.name = dma_params->name;
rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR |
((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX);
rtd->cl.cookie = substream;
rtd->cl.buffer_started = ep93xx_pcm_buffer_started;
rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished;
ret = ep93xx_dma_m2p_client_register(&rtd->cl);
if (ret < 0) {
kfree(rtd);
return ret;
}
substream->runtime->private_data = rtd;
return 0;
}
static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
{
struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
ep93xx_dma_m2p_client_unregister(&rtd->cl);
kfree(rtd);
return 0;
}
static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct ep93xx_runtime_data *rtd = runtime->private_data;
size_t totsize = params_buffer_bytes(params);
size_t period = params_period_bytes(params);
int i;
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totsize;
rtd->periods = (totsize + period - 1) / period;
for (i = 0; i < rtd->periods; i++) {
rtd->buf[i].bus_addr = runtime->dma_addr + (i * period);
rtd->buf[i].size = period;
if ((i + 1) * period > totsize)
rtd->buf[i].size = totsize - (i * period);
}
return 0;
}
static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
int ret;
int i;
ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
rtd->pointer_bytes = 0;
for (i = 0; i < rtd->periods; i++)
ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ep93xx_dma_m2p_flush(&rtd->cl);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static snd_pcm_uframes_t ep93xx_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
/* FIXME: implement this with sub-period granularity */
return bytes_to_frames(runtime, rtd->pointer_bytes);
}
static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
}
static struct snd_pcm_ops ep93xx_pcm_ops = {
.open = ep93xx_pcm_open,
.close = ep93xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = ep93xx_pcm_hw_params,
.hw_free = ep93xx_pcm_hw_free,
.trigger = ep93xx_pcm_trigger,
.pointer = ep93xx_pcm_pointer,
.mmap = ep93xx_pcm_mmap,
};
static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
buf->bytes = size;
return (buf->area == NULL) ? -ENOMEM : 0;
}
static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
buf->addr);
buf->area = NULL;
}
}
static u64 ep93xx_pcm_dmamask = 0xffffffff;
static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &ep93xx_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = 0xffffffff;
if (dai->playback.channels_min) {
ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
return ret;
}
if (dai->capture.channels_min) {
ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
return ret;
}
return 0;
}
struct snd_soc_platform ep93xx_soc_platform = {
.name = "ep93xx-audio",
.pcm_ops = &ep93xx_pcm_ops,
.pcm_new = &ep93xx_pcm_new,
.pcm_free = &ep93xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(ep93xx_soc_platform);
static int __init ep93xx_soc_platform_init(void)
{
return snd_soc_register_platform(&ep93xx_soc_platform);
}
static void __exit ep93xx_soc_platform_exit(void)
{
snd_soc_unregister_platform(&ep93xx_soc_platform);
}
module_init(ep93xx_soc_platform_init);
module_exit(ep93xx_soc_platform_exit);
MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,22 @@
/*
* sound/soc/ep93xx/ep93xx-pcm.h - EP93xx ALSA PCM interface
*
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
* Copyright (C) 2006 Applied Data Systems
*
* 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 _EP93XX_SND_SOC_PCM_H
#define _EP93XX_SND_SOC_PCM_H
struct ep93xx_pcm_dma_params {
char *name;
int dma_port;
};
extern struct snd_soc_platform ep93xx_soc_platform;
#endif /* _EP93XX_SND_SOC_PCM_H */

View File

@ -0,0 +1,150 @@
/*
* snappercl15.c -- SoC audio for Bluewater Systems Snapper CL15 module
*
* Copyright (C) 2008 Bluewater Systems Ltd
* Author: Ryan Mallon <ryan@bluewatersys.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/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include "../codecs/tlv320aic23.h"
#include "ep93xx-pcm.h"
#include "ep93xx-i2s.h"
#define CODEC_CLOCK 5644800
static int snappercl15_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int err;
err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBS_CFS);
err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBS_CFS);
if (err)
return err;
err = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK,
SND_SOC_CLOCK_IN);
if (err)
return err;
err = snd_soc_dai_set_sysclk(cpu_dai, 0, CODEC_CLOCK,
SND_SOC_CLOCK_OUT);
if (err)
return err;
return 0;
}
static struct snd_soc_ops snappercl15_ops = {
.hw_params = snappercl15_hw_params,
};
static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
{"Headphone Jack", NULL, "LHPOUT"},
{"Headphone Jack", NULL, "RHPOUT"},
{"LLINEIN", NULL, "Line In"},
{"RLINEIN", NULL, "Line In"},
{"MICIN", NULL, "Mic Jack"},
};
static int snappercl15_tlv320aic23_init(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
ARRAY_SIZE(tlv320aic23_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
return 0;
}
static struct snd_soc_dai_link snappercl15_dai = {
.name = "tlv320aic23",
.stream_name = "AIC23",
.cpu_dai = &ep93xx_i2s_dai,
.codec_dai = &tlv320aic23_dai,
.init = snappercl15_tlv320aic23_init,
.ops = &snappercl15_ops,
};
static struct snd_soc_card snd_soc_snappercl15 = {
.name = "Snapper CL15",
.platform = &ep93xx_soc_platform,
.dai_link = &snappercl15_dai,
.num_links = 1,
};
static struct snd_soc_device snappercl15_snd_devdata = {
.card = &snd_soc_snappercl15,
.codec_dev = &soc_codec_dev_tlv320aic23,
};
static struct platform_device *snappercl15_snd_device;
static int __init snappercl15_init(void)
{
int ret;
if (!machine_is_snapper_cl15())
return -ENODEV;
ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
EP93XX_SYSCON_I2SCLKDIV_ORIDE |
EP93XX_SYSCON_I2SCLKDIV_SPOL);
if (ret)
return ret;
snappercl15_snd_device = platform_device_alloc("soc-audio", -1);
if (!snappercl15_snd_device)
return -ENOMEM;
platform_set_drvdata(snappercl15_snd_device, &snappercl15_snd_devdata);
snappercl15_snd_devdata.dev = &snappercl15_snd_device->dev;
ret = platform_device_add(snappercl15_snd_device);
if (ret)
platform_device_put(snappercl15_snd_device);
return ret;
}
static void __exit snappercl15_exit(void)
{
platform_device_unregister(snappercl15_snd_device);
ep93xx_i2s_release();
}
module_init(snappercl15_init);
module_exit(snappercl15_exit);
MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
MODULE_LICENSE("GPL");

View File

@ -16,7 +16,6 @@
#include <asm/mpc52xx_psc.h>
#include "mpc5200_psc_i2s.h"
#include "mpc5200_dma.h"
/**

View File

@ -1,12 +0,0 @@
/*
* Freescale MPC5200 PSC in I2S mode
* ALSA SoC Digital Audio Interface (DAI) driver
*
*/
#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
#define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
extern struct snd_soc_dai psc_i2s_dai[];
#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */

View File

@ -1,4 +1,4 @@
config SND_IMX_SOC
menuconfig SND_IMX_SOC
tristate "SoC Audio for Freescale i.MX CPUs"
depends on ARCH_MXC
select SND_PCM
@ -8,14 +8,12 @@ config SND_IMX_SOC
Say Y or M if you want to add support for codecs attached to
the i.MX SSI interface.
config SND_MXC_SOC_SSI
tristate
if SND_IMX_SOC
config SND_MXC_SOC_WM1133_EV1
tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted"
depends on SND_IMX_SOC && MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
select SND_SOC_WM8350
select SND_MXC_SOC_SSI
help
Enable support for audio on the i.MX31ADS with the WM1133-EV1
PMIC board with WM8835x fitted.
@ -23,8 +21,17 @@ config SND_MXC_SOC_WM1133_EV1
config SND_SOC_PHYCORE_AC97
tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
depends on MACH_PCM043 || MACH_PCA100
select SND_MXC_SOC_SSI
select SND_SOC_WM9712
help
Say Y if you want to add support for SoC audio on Phytec phyCORE
and phyCARD boards in AC97 mode
config SND_SOC_EUKREA_TLV320
tristate "Eukrea TLV320"
depends on MACH_EUKREA_MBIMX27_BASEBOARD || MACH_EUKREA_MBIMXSD_BASEBOARD
select SND_SOC_TLV320AIC23
help
Enable I2S based access to the TLV320AIC23B codec attached
to the SSI interface
endif # SND_IMX_SOC

View File

@ -8,8 +8,10 @@ endif
obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o

View File

@ -0,0 +1,137 @@
/*
* eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
*
* Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
*
* based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
* which is Copyright 2009 Simtec Electronics
* and on sound/soc/imx/phycore-ac97.c which is
* Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include "../codecs/tlv320aic23.h"
#include "imx-ssi.h"
#define CODEC_CLOCK 12000000
static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int ret;
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret) {
pr_err("%s: failed set cpu dai format\n", __func__);
return ret;
}
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret) {
pr_err("%s: failed set codec dai format\n", __func__);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
CODEC_CLOCK, SND_SOC_CLOCK_OUT);
if (ret) {
pr_err("%s: failed setting codec sysclk\n", __func__);
return ret;
}
snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
SND_SOC_CLOCK_IN);
if (ret) {
pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
return ret;
}
return 0;
}
static struct snd_soc_ops eukrea_tlv320_snd_ops = {
.hw_params = eukrea_tlv320_hw_params,
};
static struct snd_soc_dai_link eukrea_tlv320_dai = {
.name = "tlv320aic23",
.stream_name = "TLV320AIC23",
.codec_dai = &tlv320aic23_dai,
.ops = &eukrea_tlv320_snd_ops,
};
static struct snd_soc_card eukrea_tlv320 = {
.name = "cpuimx-audio",
.platform = &imx_soc_platform,
.dai_link = &eukrea_tlv320_dai,
.num_links = 1,
};
static struct snd_soc_device eukrea_tlv320_snd_devdata = {
.card = &eukrea_tlv320,
.codec_dev = &soc_codec_dev_tlv320aic23,
};
static struct platform_device *eukrea_tlv320_snd_device;
static int __init eukrea_tlv320_init(void)
{
int ret;
if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd()
&& !machine_is_eukrea_cpuimx35sd())
/* return happy. We might run on a totally different machine */
return 0;
eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1);
if (!eukrea_tlv320_snd_device)
return -ENOMEM;
eukrea_tlv320_dai.cpu_dai = &imx_ssi_pcm_dai[0];
platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320_snd_devdata);
eukrea_tlv320_snd_devdata.dev = &eukrea_tlv320_snd_device->dev;
ret = platform_device_add(eukrea_tlv320_snd_device);
if (ret) {
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
platform_device_put(eukrea_tlv320_snd_device);
}
return ret;
}
static void __exit eukrea_tlv320_exit(void)
{
platform_device_unregister(eukrea_tlv320_snd_device);
}
module_init(eukrea_tlv320_init);
module_exit(eukrea_tlv320_exit);
MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>");
MODULE_DESCRIPTION("CPUIMX ALSA SoC driver");
MODULE_LICENSE("GPL");

View File

@ -292,12 +292,16 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
int ret;
iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
if (iprtd == NULL)
return -ENOMEM;
runtime->private_data = iprtd;
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
if (ret < 0) {
kfree(iprtd);
return ret;
}
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
return 0;

View File

@ -192,6 +192,8 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
int ret;
iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
if (iprtd == NULL)
return -ENOMEM;
runtime->private_data = iprtd;
iprtd->substream = substream;
@ -202,8 +204,10 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
if (ret < 0) {
kfree(iprtd);
return ret;
}
snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
return 0;

View File

@ -83,8 +83,6 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
/*
* SSI DAI format configuration.
* Should only be called when port is inactive (i.e. SSIEN = 0).
* Note: We don't use the I2S modes but instead manually configure the
* SSI for I2S because the I2S mode is only a register preset.
*/
static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
@ -99,6 +97,10 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
/* data on rising edge of bclk, frame low 1clk before data */
strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
scr |= SSI_SCR_NET;
if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) {
scr &= ~SSI_I2S_MODE_MASK;
scr |= SSI_SCR_I2S_MODE_SLAVE;
}
break;
case SND_SOC_DAIFMT_LEFT_J:
/* data on rising edge of bclk, frame high with data */
@ -143,6 +145,11 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
strcr |= SSI_STCR_TFEN0;
if (ssi->flags & IMX_SSI_NET)
scr |= SSI_SCR_NET;
if (ssi->flags & IMX_SSI_SYN)
scr |= SSI_SCR_SYN;
writel(strcr, ssi->base + SSI_STCR);
writel(strcr, ssi->base + SSI_SRCR);
writel(scr, ssi->base + SSI_SCR);

23
sound/soc/jz4740/Kconfig Normal file
View File

@ -0,0 +1,23 @@
config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic JZ4740 SoC"
depends on MACH_JZ4740 && SND_SOC
help
Say Y or M if you want to add support for codecs attached to
the JZ4740 I2S interface. You will also need to select the audio
interfaces to support below.
config SND_JZ4740_SOC_I2S
depends on SND_JZ4740_SOC
tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
based boards.
config SND_JZ4740_SOC_QI_LB60
tristate "SoC Audio support for Qi LB60"
depends on SND_JZ4740_SOC && JZ4740_QI_LB60
select SND_JZ4740_SOC_I2S
select SND_SOC_JZ4740_CODEC
help
Say Y if you want to add support for ASoC audio on the Qi LB60 board
a.k.a Qi Ben NanoNote.

13
sound/soc/jz4740/Makefile Normal file
View File

@ -0,0 +1,13 @@
#
# Jz4740 Platform Support
#
snd-soc-jz4740-objs := jz4740-pcm.o
snd-soc-jz4740-i2s-objs := jz4740-i2s.o
obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
# Jz4740 Machine Support
snd-soc-qi-lb60-objs := qi_lb60.o
obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o

View File

@ -0,0 +1,540 @@
/*
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
*
* 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.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include "jz4740-i2s.h"
#include "jz4740-pcm.h"
#define JZ_REG_AIC_CONF 0x00
#define JZ_REG_AIC_CTRL 0x04
#define JZ_REG_AIC_I2S_FMT 0x10
#define JZ_REG_AIC_FIFO_STATUS 0x14
#define JZ_REG_AIC_I2S_STATUS 0x1c
#define JZ_REG_AIC_CLK_DIV 0x30
#define JZ_REG_AIC_FIFO 0x34
#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
#define JZ_AIC_CONF_I2S BIT(4)
#define JZ_AIC_CONF_RESET BIT(3)
#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
#define JZ_AIC_CONF_ENABLE BIT(0)
#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
#define JZ_AIC_CTRL_FLUSH BIT(8)
#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
#define JZ_AIC_I2S_FMT_MSB BIT(0)
#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
#define JZ_AIC_CLK_DIV_MASK 0xf
struct jz4740_i2s {
struct resource *mem;
void __iomem *base;
dma_addr_t phys_base;
struct clk *clk_aic;
struct clk *clk_i2s;
struct jz4740_pcm_config pcm_config_playback;
struct jz4740_pcm_config pcm_config_capture;
};
static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
unsigned int reg)
{
return readl(i2s->base + reg);
}
static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
unsigned int reg, uint32_t value)
{
writel(value, i2s->base + reg);
}
static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
{
return dai->private_data;
}
static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t conf, ctrl;
if (dai->active)
return 0;
ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
ctrl |= JZ_AIC_CTRL_FLUSH;
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
clk_enable(i2s->clk_i2s);
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf |= JZ_AIC_CONF_ENABLE;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
return 0;
}
static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t conf;
if (!dai->active)
return;
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf &= ~JZ_AIC_CONF_ENABLE;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
clk_disable(i2s->clk_i2s);
}
static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t ctrl;
uint32_t mask;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
else
mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ctrl |= mask;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ctrl &= ~mask;
break;
default:
return -EINVAL;
}
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
return 0;
}
static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t format = 0;
uint32_t conf;
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
break;
case SND_SOC_DAIFMT_CBM_CFS:
conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFM:
conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFM:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_MSB:
format |= JZ_AIC_I2S_FMT_MSB;
break;
case SND_SOC_DAIFMT_I2S:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
default:
return -EINVAL;
}
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
return 0;
}
static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
enum jz4740_dma_width dma_width;
struct jz4740_pcm_config *pcm_config;
unsigned int sample_size;
uint32_t ctrl;
ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
sample_size = 0;
dma_width = JZ4740_DMA_WIDTH_8BIT;
break;
case SNDRV_PCM_FORMAT_S16:
sample_size = 1;
dma_width = JZ4740_DMA_WIDTH_16BIT;
break;
default:
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
if (params_channels(params) == 1)
ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
else
ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
pcm_config = &i2s->pcm_config_playback;
pcm_config->dma_config.dst_width = dma_width;
} else {
ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
pcm_config = &i2s->pcm_config_capture;
pcm_config->dma_config.src_width = dma_width;
}
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
snd_soc_dai_set_dma_data(dai, substream, pcm_config);
return 0;
}
static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
struct clk *parent;
int ret = 0;
switch (clk_id) {
case JZ4740_I2S_CLKSRC_EXT:
parent = clk_get(NULL, "ext");
clk_set_parent(i2s->clk_i2s, parent);
break;
case JZ4740_I2S_CLKSRC_PLL:
parent = clk_get(NULL, "pll half");
clk_set_parent(i2s->clk_i2s, parent);
ret = clk_set_rate(i2s->clk_i2s, freq);
break;
default:
return -EINVAL;
}
clk_put(parent);
return ret;
}
static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t conf;
if (dai->active) {
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf &= ~JZ_AIC_CONF_ENABLE;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
clk_disable(i2s->clk_i2s);
}
clk_disable(i2s->clk_aic);
return 0;
}
static int jz4740_i2s_resume(struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t conf;
clk_enable(i2s->clk_aic);
if (dai->active) {
clk_enable(i2s->clk_i2s);
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
conf |= JZ_AIC_CONF_ENABLE;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
}
return 0;
}
static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
uint32_t conf;
conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
(8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
JZ_AIC_CONF_I2S |
JZ_AIC_CONF_INTERNAL_CODEC;
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
return 0;
}
static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
.startup = jz4740_i2s_startup,
.shutdown = jz4740_i2s_shutdown,
.trigger = jz4740_i2s_trigger,
.hw_params = jz4740_i2s_hw_params,
.set_fmt = jz4740_i2s_set_fmt,
.set_sysclk = jz4740_i2s_set_sysclk,
};
#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE)
struct snd_soc_dai jz4740_i2s_dai = {
.name = "jz4740-i2s",
.probe = jz4740_i2s_probe,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = JZ4740_I2S_FMTS,
},
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = JZ4740_I2S_FMTS,
},
.symmetric_rates = 1,
.ops = &jz4740_i2s_dai_ops,
.suspend = jz4740_i2s_suspend,
.resume = jz4740_i2s_resume,
};
EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
static void __devinit jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
{
struct jz4740_dma_config *dma_config;
/* Playback */
dma_config = &i2s->pcm_config_playback.dma_config;
dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
dma_config->mode = JZ4740_DMA_MODE_SINGLE;
i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
/* Capture */
dma_config = &i2s->pcm_config_capture.dma_config;
dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
dma_config->flags = JZ4740_DMA_DST_AUTOINC;
dma_config->mode = JZ4740_DMA_MODE_SINGLE;
i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
}
static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
{
struct jz4740_i2s *i2s;
int ret;
i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!i2s->mem) {
ret = -ENOENT;
goto err_free;
}
i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
pdev->name);
if (!i2s->mem) {
ret = -EBUSY;
goto err_free;
}
i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
if (!i2s->base) {
ret = -EBUSY;
goto err_release_mem_region;
}
i2s->phys_base = i2s->mem->start;
i2s->clk_aic = clk_get(&pdev->dev, "aic");
if (IS_ERR(i2s->clk_aic)) {
ret = PTR_ERR(i2s->clk_aic);
goto err_iounmap;
}
i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
if (IS_ERR(i2s->clk_i2s)) {
ret = PTR_ERR(i2s->clk_i2s);
goto err_clk_put_aic;
}
clk_enable(i2s->clk_aic);
jz4740_i2c_init_pcm_config(i2s);
jz4740_i2s_dai.private_data = i2s;
ret = snd_soc_register_dai(&jz4740_i2s_dai);
if (ret) {
dev_err(&pdev->dev, "Failed to register DAI\n");
goto err_clk_put_i2s;
}
platform_set_drvdata(pdev, i2s);
return 0;
err_clk_put_i2s:
clk_disable(i2s->clk_aic);
clk_put(i2s->clk_i2s);
err_clk_put_aic:
clk_put(i2s->clk_aic);
err_iounmap:
iounmap(i2s->base);
err_release_mem_region:
release_mem_region(i2s->mem->start, resource_size(i2s->mem));
err_free:
kfree(i2s);
return ret;
}
static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
{
struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
snd_soc_unregister_dai(&jz4740_i2s_dai);
clk_disable(i2s->clk_aic);
clk_put(i2s->clk_i2s);
clk_put(i2s->clk_aic);
iounmap(i2s->base);
release_mem_region(i2s->mem->start, resource_size(i2s->mem));
platform_set_drvdata(pdev, NULL);
kfree(i2s);
return 0;
}
static struct platform_driver jz4740_i2s_driver = {
.probe = jz4740_i2s_dev_probe,
.remove = __devexit_p(jz4740_i2s_dev_remove),
.driver = {
.name = "jz4740-i2s",
.owner = THIS_MODULE,
},
};
static int __init jz4740_i2s_init(void)
{
return platform_driver_register(&jz4740_i2s_driver);
}
module_init(jz4740_i2s_init);
static void __exit jz4740_i2s_exit(void)
{
platform_driver_unregister(&jz4740_i2s_driver);
}
module_exit(jz4740_i2s_exit);
MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:jz4740-i2s");

View File

@ -0,0 +1,18 @@
/*
* 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 _JZ4740_I2S_H
#define _JZ4740_I2S_H
/* I2S clock source */
#define JZ4740_I2S_CLKSRC_EXT 0
#define JZ4740_I2S_CLKSRC_PLL 1
#define JZ4740_I2S_BIT_CLK 0
extern struct snd_soc_dai jz4740_i2s_dai;
#endif

View File

@ -0,0 +1,373 @@
/*
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
*
* 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.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/mach-jz4740/dma.h>
#include "jz4740-pcm.h"
struct jz4740_runtime_data {
unsigned long dma_period;
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
struct jz4740_dma_chan *dma;
dma_addr_t fifo_addr;
};
/* identify hardware playback capabilities */
static const struct snd_pcm_hardware jz4740_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
.rates = SNDRV_PCM_RATE_8000_48000,
.channels_min = 1,
.channels_max = 2,
.period_bytes_min = 16,
.period_bytes_max = 2 * PAGE_SIZE,
.periods_min = 2,
.periods_max = 128,
.buffer_bytes_max = 128 * 2 * PAGE_SIZE,
.fifo_size = 32,
};
static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
struct snd_pcm_substream *substream)
{
unsigned long count;
if (prtd->dma_pos == prtd->dma_end)
prtd->dma_pos = prtd->dma_start;
if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
count = prtd->dma_end - prtd->dma_pos;
else
count = prtd->dma_period;
jz4740_dma_disable(prtd->dma);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
} else {
jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
}
jz4740_dma_set_transfer_count(prtd->dma, count);
prtd->dma_pos += count;
jz4740_dma_enable(prtd->dma);
}
static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
void *dev_id)
{
struct snd_pcm_substream *substream = dev_id;
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
snd_pcm_period_elapsed(substream);
jz4740_pcm_start_transfer(prtd, substream);
}
static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct jz4740_pcm_config *config;
config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
if (!config)
return 0;
if (!prtd->dma) {
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
prtd->dma = jz4740_dma_request(substream, "PCM Capture");
else
prtd->dma = jz4740_dma_request(substream, "PCM Playback");
}
if (!prtd->dma)
return -EBUSY;
jz4740_dma_configure(prtd->dma, &config->dma_config);
prtd->fifo_addr = config->fifo_addr;
jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = params_buffer_bytes(params);
prtd->dma_period = params_period_bytes(params);
prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start;
prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
return 0;
}
static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct jz4740_runtime_data *prtd = substream->runtime->private_data;
snd_pcm_set_runtime_buffer(substream, NULL);
if (prtd->dma) {
jz4740_dma_free(prtd->dma);
prtd->dma = NULL;
}
return 0;
}
static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
{
struct jz4740_runtime_data *prtd = substream->runtime->private_data;
if (!prtd->dma)
return -EBUSY;
prtd->dma_pos = prtd->dma_start;
return 0;
}
static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
jz4740_pcm_start_transfer(prtd, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
jz4740_dma_disable(prtd->dma);
break;
default:
break;
}
return 0;
}
static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
unsigned long byte_offset;
snd_pcm_uframes_t offset;
struct jz4740_dma_chan *dma = prtd->dma;
/* prtd->dma_pos points to the end of the current transfer. So by
* subtracting prdt->dma_start we get the offset to the end of the
* current period in bytes. By subtracting the residue of the transfer
* we get the current offset in bytes. */
byte_offset = prtd->dma_pos - prtd->dma_start;
byte_offset -= jz4740_dma_get_residue(dma);
offset = bytes_to_frames(runtime, byte_offset);
if (offset >= runtime->buffer_size)
offset = 0;
return offset;
}
static int jz4740_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd;
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
if (prtd == NULL)
return -ENOMEM;
snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
runtime->private_data = prtd;
return 0;
}
static int jz4740_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
kfree(prtd);
return 0;
}
static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
static struct snd_pcm_ops jz4740_pcm_ops = {
.open = jz4740_pcm_open,
.close = jz4740_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = jz4740_pcm_hw_params,
.hw_free = jz4740_pcm_hw_free,
.prepare = jz4740_pcm_prepare,
.trigger = jz4740_pcm_trigger,
.pointer = jz4740_pcm_pointer,
.mmap = jz4740_pcm_mmap,
};
static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = jz4740_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (!buf->area)
return -ENOMEM;
buf->bytes = size;
return 0;
}
static void jz4740_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
buf->addr);
buf->area = NULL;
}
}
static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &jz4740_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (dai->playback.channels_min) {
ret = jz4740_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto err;
}
if (dai->capture.channels_min) {
ret = jz4740_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto err;
}
err:
return ret;
}
struct snd_soc_platform jz4740_soc_platform = {
.name = "jz4740-pcm",
.pcm_ops = &jz4740_pcm_ops,
.pcm_new = jz4740_pcm_new,
.pcm_free = jz4740_pcm_free,
};
EXPORT_SYMBOL_GPL(jz4740_soc_platform);
static int __devinit jz4740_pcm_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&jz4740_soc_platform);
}
static int __devexit jz4740_pcm_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&jz4740_soc_platform);
return 0;
}
static struct platform_driver jz4740_pcm_driver = {
.probe = jz4740_pcm_probe,
.remove = __devexit_p(jz4740_pcm_remove),
.driver = {
.name = "jz4740-pcm",
.owner = THIS_MODULE,
},
};
static int __init jz4740_soc_platform_init(void)
{
return platform_driver_register(&jz4740_pcm_driver);
}
module_init(jz4740_soc_platform_init);
static void __exit jz4740_soc_platform_exit(void)
{
return platform_driver_unregister(&jz4740_pcm_driver);
}
module_exit(jz4740_soc_platform_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,22 @@
/*
*
* 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 _JZ4740_PCM_H
#define _JZ4740_PCM_H
#include <linux/dma-mapping.h>
#include <asm/mach-jz4740/dma.h>
/* platform data */
extern struct snd_soc_platform jz4740_soc_platform;
struct jz4740_pcm_config {
struct jz4740_dma_config dma_config;
phys_addr_t fifo_addr;
};
#endif

166
sound/soc/jz4740/qi_lb60.c Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <linux/gpio.h>
#include "../codecs/jz4740.h"
#include "jz4740-pcm.h"
#include "jz4740-i2s.h"
#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29)
#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4)
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *ctrl, int event)
{
int on = 0;
if (event & SND_SOC_DAPM_POST_PMU)
on = 1;
else if (event & SND_SOC_DAPM_PRE_PMD)
on = 0;
gpio_set_value(QI_LB60_SND_GPIO, on);
gpio_set_value(QI_LB60_AMP_GPIO, on);
return 0;
}
static const struct snd_soc_dapm_widget qi_lb60_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", qi_lb60_spk_event),
SND_SOC_DAPM_MIC("Mic", NULL),
};
static const struct snd_soc_dapm_route qi_lb60_routes[] = {
{"Mic", NULL, "MIC"},
{"Speaker", NULL, "LOUT"},
{"Speaker", NULL, "ROUT"},
};
#define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \
SND_SOC_DAIFMT_NB_NF | \
SND_SOC_DAIFMT_CBM_CFM)
static int qi_lb60_codec_init(struct snd_soc_codec *codec)
{
int ret;
struct snd_soc_dai *cpu_dai = codec->socdev->card->dai_link->cpu_dai;
snd_soc_dapm_nc_pin(codec, "LIN");
snd_soc_dapm_nc_pin(codec, "RIN");
ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
return ret;
}
snd_soc_dapm_new_controls(codec, qi_lb60_widgets, ARRAY_SIZE(qi_lb60_widgets));
snd_soc_dapm_add_routes(codec, qi_lb60_routes, ARRAY_SIZE(qi_lb60_routes));
snd_soc_dapm_sync(codec);
return 0;
}
static struct snd_soc_dai_link qi_lb60_dai = {
.name = "jz4740",
.stream_name = "jz4740",
.cpu_dai = &jz4740_i2s_dai,
.codec_dai = &jz4740_codec_dai,
.init = qi_lb60_codec_init,
};
static struct snd_soc_card qi_lb60 = {
.name = "QI LB60",
.dai_link = &qi_lb60_dai,
.num_links = 1,
.platform = &jz4740_soc_platform,
};
static struct snd_soc_device qi_lb60_snd_devdata = {
.card = &qi_lb60,
.codec_dev = &soc_codec_dev_jz4740_codec,
};
static struct platform_device *qi_lb60_snd_device;
static int __init qi_lb60_init(void)
{
int ret;
qi_lb60_snd_device = platform_device_alloc("soc-audio", -1);
if (!qi_lb60_snd_device)
return -ENOMEM;
ret = gpio_request(QI_LB60_SND_GPIO, "SND");
if (ret) {
pr_err("qi_lb60 snd: Failed to request SND GPIO(%d): %d\n",
QI_LB60_SND_GPIO, ret);
goto err_device_put;
}
ret = gpio_request(QI_LB60_AMP_GPIO, "AMP");
if (ret) {
pr_err("qi_lb60 snd: Failed to request AMP GPIO(%d): %d\n",
QI_LB60_AMP_GPIO, ret);
goto err_gpio_free_snd;
}
gpio_direction_output(QI_LB60_SND_GPIO, 0);
gpio_direction_output(QI_LB60_AMP_GPIO, 0);
platform_set_drvdata(qi_lb60_snd_device, &qi_lb60_snd_devdata);
qi_lb60_snd_devdata.dev = &qi_lb60_snd_device->dev;
ret = platform_device_add(qi_lb60_snd_device);
if (ret) {
pr_err("qi_lb60 snd: Failed to add snd soc device: %d\n", ret);
goto err_unset_pdata;
}
return 0;
err_unset_pdata:
platform_set_drvdata(qi_lb60_snd_device, NULL);
/*err_gpio_free_amp:*/
gpio_free(QI_LB60_AMP_GPIO);
err_gpio_free_snd:
gpio_free(QI_LB60_SND_GPIO);
err_device_put:
platform_device_put(qi_lb60_snd_device);
return ret;
}
module_init(qi_lb60_init);
static void __exit qi_lb60_exit(void)
{
gpio_free(QI_LB60_AMP_GPIO);
gpio_free(QI_LB60_SND_GPIO);
platform_device_unregister(qi_lb60_snd_device);
}
module_exit(qi_lb60_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,20 @@
config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood chip"
depends on ARCH_KIRKWOOD
help
Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the
audio interfaces to support below.
config SND_KIRKWOOD_SOC_I2S
tristate
config SND_KIRKWOOD_SOC_OPENRD
tristate "SoC Audio support for Kirkwood Openrd Client"
depends on SND_KIRKWOOD_SOC && MACH_OPENRD_CLIENT
select SND_KIRKWOOD_SOC_I2S
select SND_SOC_CS42L51
help
Say Y if you want to add support for SoC audio on
Openrd Client.

View File

@ -0,0 +1,9 @@
snd-soc-kirkwood-objs := kirkwood-dma.o
snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o
obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
snd-soc-openrd-objs := kirkwood-openrd.o
obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o

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