Merge branch 'topic/asoc' into for-linus
This commit is contained in:
commit
e71981343a
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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];
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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");
|
|
@ -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 */
|
|
@ -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");
|
|
@ -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 */
|
|
@ -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");
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include <asm/mpc52xx_psc.h>
|
||||
|
||||
#include "mpc5200_psc_i2s.h"
|
||||
#include "mpc5200_dma.h"
|
||||
|
||||
/**
|
||||
|
|
|
@ -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__ */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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");
|
|
@ -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
|
|
@ -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");
|
|
@ -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
|
|
@ -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");
|
|
@ -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.
|
||||
|
|
@ -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
Loading…
Reference in New Issue