forked from Minki/linux
Merge branch 'topic/asoc' into topic/remove-irqf_disable
This commit is contained in:
commit
af1910a817
@ -0,0 +1,11 @@
|
||||
* Freescale SGTL5000 Stereo Codec
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,sgtl5000".
|
||||
|
||||
Example:
|
||||
|
||||
codec: sgtl5000@0a {
|
||||
compatible = "fsl,sgtl5000";
|
||||
reg = <0x0a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8510.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8510.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8510 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8510"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8510@1a {
|
||||
compatible = "wlf,wm8510";
|
||||
reg = <0x1a>;
|
||||
};
|
16
Documentation/devicetree/bindings/sound/wm8523.txt
Normal file
16
Documentation/devicetree/bindings/sound/wm8523.txt
Normal file
@ -0,0 +1,16 @@
|
||||
WM8523 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8523"
|
||||
|
||||
- reg : the I2C address of the device.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8523@1a {
|
||||
compatible = "wlf,wm8523";
|
||||
reg = <0x1a>;
|
||||
};
|
16
Documentation/devicetree/bindings/sound/wm8580.txt
Normal file
16
Documentation/devicetree/bindings/sound/wm8580.txt
Normal file
@ -0,0 +1,16 @@
|
||||
WM8580 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8580"
|
||||
|
||||
- reg : the I2C address of the device.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8580@1a {
|
||||
compatible = "wlf,wm8580";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8711.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8711.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8711 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8711"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8711@1a {
|
||||
compatible = "wlf,wm8711";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8728.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8728.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8728 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8728"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8728@1a {
|
||||
compatible = "wlf,wm8728";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8731.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8731.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8731 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8731"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8731@1a {
|
||||
compatible = "wlf,wm8731";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8737.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8737.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8737 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8737"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8737@1a {
|
||||
compatible = "wlf,wm8737";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8741.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8741.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8741 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8741"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8741@1a {
|
||||
compatible = "wlf,wm8741";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8750.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8750.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8750 and WM8987 audio CODECs
|
||||
|
||||
These devices support both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8750" or "wlf,wm8987"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8750@1a {
|
||||
compatible = "wlf,wm8750";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8753.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8753.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8753 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8753"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8737@1a {
|
||||
compatible = "wlf,wm8753";
|
||||
reg = <0x1a>;
|
||||
};
|
16
Documentation/devicetree/bindings/sound/wm8770.txt
Normal file
16
Documentation/devicetree/bindings/sound/wm8770.txt
Normal file
@ -0,0 +1,16 @@
|
||||
WM8770 audio CODEC
|
||||
|
||||
This device supports SPI.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8770"
|
||||
|
||||
- reg : the chip select number.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8770@1 {
|
||||
compatible = "wlf,wm8770";
|
||||
reg = <1>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8776.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8776.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8776 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8776"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8776@1a {
|
||||
compatible = "wlf,wm8776";
|
||||
reg = <0x1a>;
|
||||
};
|
18
Documentation/devicetree/bindings/sound/wm8804.txt
Normal file
18
Documentation/devicetree/bindings/sound/wm8804.txt
Normal file
@ -0,0 +1,18 @@
|
||||
WM8804 audio CODEC
|
||||
|
||||
This device supports both I2C and SPI (configured with pin strapping
|
||||
on the board).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "wlf,wm8804"
|
||||
|
||||
- reg : the I2C address of the device for I2C, the chip select
|
||||
number for SPI.
|
||||
|
||||
Example:
|
||||
|
||||
codec: wm8804@1a {
|
||||
compatible = "wlf,wm8804";
|
||||
reg = <0x1a>;
|
||||
};
|
@ -159,6 +159,11 @@ static void __init edb93xx_register_spi(void)
|
||||
/*************************************************************************
|
||||
* EDB93xx I2S
|
||||
*************************************************************************/
|
||||
static struct platform_device edb93xx_audio_device = {
|
||||
.name = "edb93xx-audio",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static int __init edb93xx_has_audio(void)
|
||||
{
|
||||
return (machine_is_edb9301() || machine_is_edb9302() ||
|
||||
@ -170,6 +175,7 @@ static void __init edb93xx_register_i2s(void)
|
||||
{
|
||||
if (edb93xx_has_audio()) {
|
||||
ep93xx_register_i2s();
|
||||
platform_device_register(&edb93xx_audio_device);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,17 @@ static struct i2c_board_info __initdata simone_i2c_board_info[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device simone_audio_device = {
|
||||
.name = "simone-audio",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static void __init simone_register_audio(void)
|
||||
{
|
||||
ep93xx_register_ac97();
|
||||
platform_device_register(&simone_audio_device);
|
||||
}
|
||||
|
||||
static void __init simone_init_machine(void)
|
||||
{
|
||||
ep93xx_init_devices();
|
||||
@ -61,7 +72,7 @@ static void __init simone_init_machine(void)
|
||||
ep93xx_register_fb(&simone_fb_info);
|
||||
ep93xx_register_i2c(&simone_i2c_gpio_data, simone_i2c_board_info,
|
||||
ARRAY_SIZE(simone_i2c_board_info));
|
||||
ep93xx_register_ac97();
|
||||
simone_register_audio();
|
||||
}
|
||||
|
||||
MACHINE_START(SIM_ONE, "Simplemachines Sim.One Board")
|
||||
|
@ -150,6 +150,17 @@ static struct ep93xxfb_mach_info __initdata snappercl15_fb_info = {
|
||||
.bpp = 16,
|
||||
};
|
||||
|
||||
static struct platform_device snappercl15_audio_device = {
|
||||
.name = "snappercl15-audio",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static void __init snappercl15_register_audio(void)
|
||||
{
|
||||
ep93xx_register_i2s();
|
||||
platform_device_register(&snappercl15_audio_device);
|
||||
}
|
||||
|
||||
static void __init snappercl15_init_machine(void)
|
||||
{
|
||||
ep93xx_init_devices();
|
||||
@ -157,7 +168,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();
|
||||
snappercl15_register_audio();
|
||||
platform_device_register(&snappercl15_nand_device);
|
||||
}
|
||||
|
||||
|
@ -422,6 +422,7 @@ static struct resource au1200_psc1_res[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/* AC97 or I2S device */
|
||||
static struct platform_device db1200_audio_dev = {
|
||||
/* name assigned later based on switch setting */
|
||||
.id = 1, /* PSC ID */
|
||||
@ -429,19 +430,32 @@ static struct platform_device db1200_audio_dev = {
|
||||
.resource = au1200_psc1_res,
|
||||
};
|
||||
|
||||
/* DB1200 ASoC card device */
|
||||
static struct platform_device db1200_sound_dev = {
|
||||
/* name assigned later based on switch setting */
|
||||
.id = 1, /* PSC ID */
|
||||
};
|
||||
|
||||
static struct platform_device db1200_stac_dev = {
|
||||
.name = "ac97-codec",
|
||||
.id = 1, /* on PSC1 */
|
||||
};
|
||||
|
||||
static struct platform_device db1200_audiodma_dev = {
|
||||
.name = "au1xpsc-pcm",
|
||||
.id = 1, /* PSC ID */
|
||||
};
|
||||
|
||||
static struct platform_device *db1200_devs[] __initdata = {
|
||||
NULL, /* PSC0, selected by S6.8 */
|
||||
&db1200_ide_dev,
|
||||
&db1200_eth_dev,
|
||||
&db1200_rtc_dev,
|
||||
&db1200_nand_dev,
|
||||
&db1200_audiodma_dev,
|
||||
&db1200_audio_dev,
|
||||
&db1200_stac_dev,
|
||||
&db1200_sound_dev,
|
||||
};
|
||||
|
||||
static int __init db1200_dev_init(void)
|
||||
@ -501,10 +515,12 @@ static int __init db1200_dev_init(void)
|
||||
if (sw == BCSR_SWITCHES_DIP_8) {
|
||||
bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_PSC1MUX);
|
||||
db1200_audio_dev.name = "au1xpsc_i2s";
|
||||
db1200_sound_dev.name = "db1200-i2s";
|
||||
printk(KERN_INFO " S6.7 ON : PSC1 mode I2S\n");
|
||||
} else {
|
||||
bcsr_mod(BCSR_RESETS, BCSR_RESETS_PSC1MUX, 0);
|
||||
db1200_audio_dev.name = "au1xpsc_ac97";
|
||||
db1200_sound_dev.name = "db1200-ac97";
|
||||
printk(KERN_INFO " S6.7 OFF: PSC1 mode AC97\n");
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1000_dma.h>
|
||||
#include <asm/mach-au1x00/au1xxx.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
#include "../platform.h"
|
||||
@ -85,6 +88,45 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static struct resource alchemy_ac97c_res[] = {
|
||||
[0] = {
|
||||
.start = AU1000_AC97_PHYS_ADDR,
|
||||
.end = AU1000_AC97_PHYS_ADDR + 0xfff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = DMA_ID_AC97C_TX,
|
||||
.end = DMA_ID_AC97C_TX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
[2] = {
|
||||
.start = DMA_ID_AC97C_RX,
|
||||
.end = DMA_ID_AC97C_RX,
|
||||
.flags = IORESOURCE_DMA,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device alchemy_ac97c_dev = {
|
||||
.name = "alchemy-ac97c",
|
||||
.id = -1,
|
||||
.resource = alchemy_ac97c_res,
|
||||
.num_resources = ARRAY_SIZE(alchemy_ac97c_res),
|
||||
};
|
||||
|
||||
static struct platform_device alchemy_ac97c_dma_dev = {
|
||||
.name = "alchemy-pcm-dma",
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
static struct platform_device db1x00_codec_dev = {
|
||||
.name = "ac97-codec",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static struct platform_device db1x00_audio_dev = {
|
||||
.name = "db1000-audio",
|
||||
};
|
||||
|
||||
static int __init db1xxx_dev_init(void)
|
||||
{
|
||||
#ifdef DB1XXX_HAS_PCMCIA
|
||||
@ -113,6 +155,12 @@ static int __init db1xxx_dev_init(void)
|
||||
1);
|
||||
#endif
|
||||
db1x_register_norflash(BOARD_FLASH_SIZE, BOARD_FLASH_WIDTH, F_SWAPPED);
|
||||
|
||||
platform_device_register(&db1x00_codec_dev);
|
||||
platform_device_register(&alchemy_ac97c_dma_dev);
|
||||
platform_device_register(&alchemy_ac97c_dev);
|
||||
platform_device_register(&db1x00_audio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(db1xxx_dev_init);
|
||||
|
@ -37,6 +37,11 @@ struct regmap {
|
||||
void *work_buf; /* Scratch buffer used to format I/O */
|
||||
struct regmap_format format; /* Buffer format */
|
||||
const struct regmap_bus *bus;
|
||||
|
||||
unsigned int max_register;
|
||||
bool (*writeable_reg)(struct device *dev, unsigned int reg);
|
||||
bool (*readable_reg)(struct device *dev, unsigned int reg);
|
||||
bool (*volatile_reg)(struct device *dev, unsigned int reg);
|
||||
};
|
||||
|
||||
static void regmap_format_4_12_write(struct regmap *map,
|
||||
@ -116,6 +121,10 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->format.val_bytes = config->val_bits / 8;
|
||||
map->dev = dev;
|
||||
map->bus = bus;
|
||||
map->max_register = config->max_register;
|
||||
map->writeable_reg = config->writeable_reg;
|
||||
map->readable_reg = config->readable_reg;
|
||||
map->volatile_reg = config->volatile_reg;
|
||||
|
||||
switch (config->reg_bits) {
|
||||
case 4:
|
||||
|
@ -97,7 +97,7 @@ static void twl6040_vibra_enable(struct vibra_info *info)
|
||||
}
|
||||
|
||||
twl6040_power(info->twl6040, 1);
|
||||
if (twl6040->rev <= TWL6040_REV_ES1_1) {
|
||||
if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) {
|
||||
/*
|
||||
* ERRATA: Disable overcurrent protection for at least
|
||||
* 3ms when enabling vibrator drivers to avoid false
|
||||
|
@ -34,8 +34,6 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/twl6040.h>
|
||||
|
||||
static struct platform_device *twl6040_dev;
|
||||
|
||||
int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
|
||||
{
|
||||
int ret;
|
||||
@ -203,11 +201,11 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data)
|
||||
if (intid & TWL6040_THINT) {
|
||||
status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
|
||||
if (status & TWL6040_TSHUTDET) {
|
||||
dev_warn(&twl6040_dev->dev,
|
||||
dev_warn(twl6040->dev,
|
||||
"Thermal shutdown, powering-off");
|
||||
twl6040_power(twl6040, 0);
|
||||
} else {
|
||||
dev_warn(&twl6040_dev->dev,
|
||||
dev_warn(twl6040->dev,
|
||||
"Leaving thermal shutdown, powering-on");
|
||||
twl6040_power(twl6040, 1);
|
||||
}
|
||||
@ -227,7 +225,7 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040,
|
||||
if (!time_left) {
|
||||
intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
|
||||
if (!(intid & TWL6040_READYINT)) {
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"timeout waiting for READYINT\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@ -255,7 +253,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)
|
||||
/* wait for power-up completion */
|
||||
ret = twl6040_power_up_completion(twl6040, naudint);
|
||||
if (ret) {
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"automatic power-down failed\n");
|
||||
twl6040->power_count = 0;
|
||||
goto out;
|
||||
@ -264,7 +262,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)
|
||||
/* use manual power-up sequence */
|
||||
ret = twl6040_power_up(twl6040);
|
||||
if (ret) {
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"manual power-up failed\n");
|
||||
twl6040->power_count = 0;
|
||||
goto out;
|
||||
@ -276,7 +274,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)
|
||||
} else {
|
||||
/* already powered-down */
|
||||
if (!twl6040->power_count) {
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"device is already powered-off\n");
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
@ -326,7 +324,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
lppllctl &= ~TWL6040_LPLLFIN;
|
||||
break;
|
||||
default:
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"freq_out %d not supported\n", freq_out);
|
||||
ret = -EINVAL;
|
||||
goto pll_out;
|
||||
@ -347,7 +345,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
hppllctl);
|
||||
break;
|
||||
default:
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"freq_in %d not supported\n", freq_in);
|
||||
ret = -EINVAL;
|
||||
goto pll_out;
|
||||
@ -356,7 +354,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
case TWL6040_SYSCLK_SEL_HPPLL:
|
||||
/* high-performance PLL can provide only 19.2 MHz */
|
||||
if (freq_out != 19200000) {
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"freq_out %d not supported\n", freq_out);
|
||||
ret = -EINVAL;
|
||||
goto pll_out;
|
||||
@ -389,7 +387,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
TWL6040_HPLLENA;
|
||||
break;
|
||||
default:
|
||||
dev_err(&twl6040_dev->dev,
|
||||
dev_err(twl6040->dev,
|
||||
"freq_in %d not supported\n", freq_in);
|
||||
ret = -EINVAL;
|
||||
goto pll_out;
|
||||
@ -406,7 +404,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
|
||||
break;
|
||||
default:
|
||||
dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id);
|
||||
dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
|
||||
ret = -EINVAL;
|
||||
goto pll_out;
|
||||
}
|
||||
@ -471,9 +469,7 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, twl6040);
|
||||
|
||||
twl6040_dev = pdev;
|
||||
twl6040->dev = &pdev->dev;
|
||||
twl6040->audpwron = pdata->audpwron_gpio;
|
||||
twl6040->irq = pdata->naudint_irq;
|
||||
twl6040->irq_base = pdata->irq_base;
|
||||
|
||||
@ -483,6 +479,12 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
|
||||
|
||||
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
|
||||
|
||||
/* ERRATA: Automatic power-up is not possible in ES1.0 */
|
||||
if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
|
||||
twl6040->audpwron = pdata->audpwron_gpio;
|
||||
else
|
||||
twl6040->audpwron = -EINVAL;
|
||||
|
||||
if (gpio_is_valid(twl6040->audpwron)) {
|
||||
ret = gpio_request(twl6040->audpwron, "audpwron");
|
||||
if (ret)
|
||||
@ -493,10 +495,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
|
||||
goto gpio2_err;
|
||||
}
|
||||
|
||||
/* ERRATA: Automatic power-up is not possible in ES1.0 */
|
||||
if (twl6040->rev == TWL6040_REV_ES1_0)
|
||||
twl6040->audpwron = -EINVAL;
|
||||
|
||||
/* codec interrupt */
|
||||
ret = twl6040_irq_init(twl6040);
|
||||
if (ret)
|
||||
@ -566,7 +564,6 @@ gpio2_err:
|
||||
gpio1_err:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(twl6040);
|
||||
twl6040_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -586,7 +583,6 @@ static int __devexit twl6040_remove(struct platform_device *pdev)
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(twl6040);
|
||||
twl6040_dev = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1552,6 +1552,63 @@ int regulator_force_disable(struct regulator *regulator)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regulator_force_disable);
|
||||
|
||||
static void regulator_disable_work(struct work_struct *work)
|
||||
{
|
||||
struct regulator_dev *rdev = container_of(work, struct regulator_dev,
|
||||
disable_work.work);
|
||||
int count, i, ret;
|
||||
|
||||
mutex_lock(&rdev->mutex);
|
||||
|
||||
BUG_ON(!rdev->deferred_disables);
|
||||
|
||||
count = rdev->deferred_disables;
|
||||
rdev->deferred_disables = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = _regulator_disable(rdev);
|
||||
if (ret != 0)
|
||||
rdev_err(rdev, "Deferred disable failed: %d\n", ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&rdev->mutex);
|
||||
|
||||
if (rdev->supply) {
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = regulator_disable(rdev->supply);
|
||||
if (ret != 0) {
|
||||
rdev_err(rdev,
|
||||
"Supply disable failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* regulator_disable_deferred - disable regulator output with delay
|
||||
* @regulator: regulator source
|
||||
* @ms: miliseconds until the regulator is disabled
|
||||
*
|
||||
* Execute regulator_disable() on the regulator after a delay. This
|
||||
* is intended for use with devices that require some time to quiesce.
|
||||
*
|
||||
* NOTE: this will only disable the regulator output if no other consumer
|
||||
* devices have it enabled, the regulator device supports disabling and
|
||||
* machine constraints permit this operation.
|
||||
*/
|
||||
int regulator_disable_deferred(struct regulator *regulator, int ms)
|
||||
{
|
||||
struct regulator_dev *rdev = regulator->rdev;
|
||||
|
||||
mutex_lock(&rdev->mutex);
|
||||
rdev->deferred_disables++;
|
||||
mutex_unlock(&rdev->mutex);
|
||||
|
||||
return schedule_delayed_work(&rdev->disable_work,
|
||||
msecs_to_jiffies(ms));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regulator_disable_deferred);
|
||||
|
||||
static int _regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
/* If we don't know then assume that the regulator is always on */
|
||||
@ -2622,6 +2679,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
|
||||
INIT_LIST_HEAD(&rdev->consumer_list);
|
||||
INIT_LIST_HEAD(&rdev->list);
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
|
||||
INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
|
||||
|
||||
/* preform any regulator specific init */
|
||||
if (init_data->regulator_init) {
|
||||
@ -2729,6 +2787,7 @@ void regulator_unregister(struct regulator_dev *rdev)
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
debugfs_remove_recursive(rdev->debugfs);
|
||||
#endif
|
||||
flush_work_sync(&rdev->disable_work.work);
|
||||
WARN_ON(rdev->open_count);
|
||||
unset_regulator_supplies(rdev);
|
||||
list_del(&rdev->list);
|
||||
|
@ -70,9 +70,6 @@
|
||||
|
||||
#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
|
||||
|
||||
#define TWL6040_VIOREGNUM 18
|
||||
#define TWL6040_VDDREGNUM 21
|
||||
|
||||
/* INTID (0x03) fields */
|
||||
|
||||
#define TWL6040_THINT 0x01
|
||||
@ -225,4 +222,9 @@ unsigned int twl6040_get_sysclk(struct twl6040 *twl6040);
|
||||
int twl6040_irq_init(struct twl6040 *twl6040);
|
||||
void twl6040_irq_exit(struct twl6040 *twl6040);
|
||||
|
||||
static inline int twl6040_get_revid(struct twl6040 *twl6040)
|
||||
{
|
||||
return twl6040->rev;
|
||||
}
|
||||
|
||||
#endif /* End of __TWL6040_CODEC_H__ */
|
||||
|
@ -72,6 +72,7 @@
|
||||
#define WM8994_DC_SERVO_2 0x55
|
||||
#define WM8994_DC_SERVO_4 0x57
|
||||
#define WM8994_DC_SERVO_READBACK 0x58
|
||||
#define WM8994_DC_SERVO_4E 0x59
|
||||
#define WM8994_ANALOGUE_HP_1 0x60
|
||||
#define WM8958_MIC_DETECT_1 0xD0
|
||||
#define WM8958_MIC_DETECT_2 0xD1
|
||||
@ -133,6 +134,8 @@
|
||||
#define WM8994_AIF1_DAC1_FILTERS_2 0x421
|
||||
#define WM8994_AIF1_DAC2_FILTERS_1 0x422
|
||||
#define WM8994_AIF1_DAC2_FILTERS_2 0x423
|
||||
#define WM8958_AIF1_DAC1_NOISE_GATE 0x430
|
||||
#define WM8958_AIF1_DAC2_NOISE_GATE 0x431
|
||||
#define WM8994_AIF1_DRC1_1 0x440
|
||||
#define WM8994_AIF1_DRC1_2 0x441
|
||||
#define WM8994_AIF1_DRC1_3 0x442
|
||||
@ -190,6 +193,7 @@
|
||||
#define WM8994_AIF2_ADC_FILTERS 0x510
|
||||
#define WM8994_AIF2_DAC_FILTERS_1 0x520
|
||||
#define WM8994_AIF2_DAC_FILTERS_2 0x521
|
||||
#define WM8958_AIF2_DAC_NOISE_GATE 0x530
|
||||
#define WM8994_AIF2_DRC_1 0x540
|
||||
#define WM8994_AIF2_DRC_2 0x541
|
||||
#define WM8994_AIF2_DRC_3 0x542
|
||||
@ -1920,6 +1924,44 @@
|
||||
#define WM8994_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */
|
||||
#define WM8994_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */
|
||||
|
||||
/*
|
||||
* R61 (0x3D) - MICBIAS1
|
||||
*/
|
||||
#define WM8958_MICB1_RATE 0x0020 /* MICB1_RATE */
|
||||
#define WM8958_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */
|
||||
#define WM8958_MICB1_RATE_SHIFT 5 /* MICB1_RATE */
|
||||
#define WM8958_MICB1_RATE_WIDTH 1 /* MICB1_RATE */
|
||||
#define WM8958_MICB1_MODE 0x0010 /* MICB1_MODE */
|
||||
#define WM8958_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */
|
||||
#define WM8958_MICB1_MODE_SHIFT 4 /* MICB1_MODE */
|
||||
#define WM8958_MICB1_MODE_WIDTH 1 /* MICB1_MODE */
|
||||
#define WM8958_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */
|
||||
#define WM8958_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */
|
||||
#define WM8958_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */
|
||||
#define WM8958_MICB1_DISCH 0x0001 /* MICB1_DISCH */
|
||||
#define WM8958_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */
|
||||
#define WM8958_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */
|
||||
#define WM8958_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */
|
||||
|
||||
/*
|
||||
* R62 (0x3E) - MICBIAS2
|
||||
*/
|
||||
#define WM8958_MICB2_RATE 0x0020 /* MICB2_RATE */
|
||||
#define WM8958_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */
|
||||
#define WM8958_MICB2_RATE_SHIFT 5 /* MICB2_RATE */
|
||||
#define WM8958_MICB2_RATE_WIDTH 1 /* MICB2_RATE */
|
||||
#define WM8958_MICB2_MODE 0x0010 /* MICB2_MODE */
|
||||
#define WM8958_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */
|
||||
#define WM8958_MICB2_MODE_SHIFT 4 /* MICB2_MODE */
|
||||
#define WM8958_MICB2_MODE_WIDTH 1 /* MICB2_MODE */
|
||||
#define WM8958_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */
|
||||
#define WM8958_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */
|
||||
#define WM8958_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */
|
||||
#define WM8958_MICB2_DISCH 0x0001 /* MICB2_DISCH */
|
||||
#define WM8958_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */
|
||||
#define WM8958_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */
|
||||
#define WM8958_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */
|
||||
|
||||
/*
|
||||
* R76 (0x4C) - Charge Pump (1)
|
||||
*/
|
||||
@ -2948,6 +2990,34 @@
|
||||
#define WM8994_AIF1DAC2_3D_ENA_SHIFT 8 /* AIF1DAC2_3D_ENA */
|
||||
#define WM8994_AIF1DAC2_3D_ENA_WIDTH 1 /* AIF1DAC2_3D_ENA */
|
||||
|
||||
/*
|
||||
* R1072 (0x430) - AIF1 DAC1 Noise Gate
|
||||
*/
|
||||
#define WM8958_AIF1DAC1_NG_HLD_MASK 0x0060 /* AIF1DAC1_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF1DAC1_NG_HLD_SHIFT 5 /* AIF1DAC1_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF1DAC1_NG_HLD_WIDTH 2 /* AIF1DAC1_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF1DAC1_NG_THR_MASK 0x000E /* AIF1DAC1_NG_THR - [3:1] */
|
||||
#define WM8958_AIF1DAC1_NG_THR_SHIFT 1 /* AIF1DAC1_NG_THR - [3:1] */
|
||||
#define WM8958_AIF1DAC1_NG_THR_WIDTH 3 /* AIF1DAC1_NG_THR - [3:1] */
|
||||
#define WM8958_AIF1DAC1_NG_ENA 0x0001 /* AIF1DAC1_NG_ENA */
|
||||
#define WM8958_AIF1DAC1_NG_ENA_MASK 0x0001 /* AIF1DAC1_NG_ENA */
|
||||
#define WM8958_AIF1DAC1_NG_ENA_SHIFT 0 /* AIF1DAC1_NG_ENA */
|
||||
#define WM8958_AIF1DAC1_NG_ENA_WIDTH 1 /* AIF1DAC1_NG_ENA */
|
||||
|
||||
/*
|
||||
* R1073 (0x431) - AIF1 DAC2 Noise Gate
|
||||
*/
|
||||
#define WM8958_AIF1DAC2_NG_HLD_MASK 0x0060 /* AIF1DAC2_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF1DAC2_NG_HLD_SHIFT 5 /* AIF1DAC2_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF1DAC2_NG_HLD_WIDTH 2 /* AIF1DAC2_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF1DAC2_NG_THR_MASK 0x000E /* AIF1DAC2_NG_THR - [3:1] */
|
||||
#define WM8958_AIF1DAC2_NG_THR_SHIFT 1 /* AIF1DAC2_NG_THR - [3:1] */
|
||||
#define WM8958_AIF1DAC2_NG_THR_WIDTH 3 /* AIF1DAC2_NG_THR - [3:1] */
|
||||
#define WM8958_AIF1DAC2_NG_ENA 0x0001 /* AIF1DAC2_NG_ENA */
|
||||
#define WM8958_AIF1DAC2_NG_ENA_MASK 0x0001 /* AIF1DAC2_NG_ENA */
|
||||
#define WM8958_AIF1DAC2_NG_ENA_SHIFT 0 /* AIF1DAC2_NG_ENA */
|
||||
#define WM8958_AIF1DAC2_NG_ENA_WIDTH 1 /* AIF1DAC2_NG_ENA */
|
||||
|
||||
/*
|
||||
* R1088 (0x440) - AIF1 DRC1 (1)
|
||||
*/
|
||||
@ -3559,6 +3629,20 @@
|
||||
#define WM8994_AIF2DAC_3D_ENA_SHIFT 8 /* AIF2DAC_3D_ENA */
|
||||
#define WM8994_AIF2DAC_3D_ENA_WIDTH 1 /* AIF2DAC_3D_ENA */
|
||||
|
||||
/*
|
||||
* R1328 (0x530) - AIF2 DAC Noise Gate
|
||||
*/
|
||||
#define WM8958_AIF2DAC_NG_HLD_MASK 0x0060 /* AIF2DAC_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF2DAC_NG_HLD_SHIFT 5 /* AIF2DAC_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF2DAC_NG_HLD_WIDTH 2 /* AIF2DAC_NG_HLD - [6:5] */
|
||||
#define WM8958_AIF2DAC_NG_THR_MASK 0x000E /* AIF2DAC_NG_THR - [3:1] */
|
||||
#define WM8958_AIF2DAC_NG_THR_SHIFT 1 /* AIF2DAC_NG_THR - [3:1] */
|
||||
#define WM8958_AIF2DAC_NG_THR_WIDTH 3 /* AIF2DAC_NG_THR - [3:1] */
|
||||
#define WM8958_AIF2DAC_NG_ENA 0x0001 /* AIF2DAC_NG_ENA */
|
||||
#define WM8958_AIF2DAC_NG_ENA_MASK 0x0001 /* AIF2DAC_NG_ENA */
|
||||
#define WM8958_AIF2DAC_NG_ENA_SHIFT 0 /* AIF2DAC_NG_ENA */
|
||||
#define WM8958_AIF2DAC_NG_ENA_WIDTH 1 /* AIF2DAC_NG_ENA */
|
||||
|
||||
/*
|
||||
* R1344 (0x540) - AIF2 DRC (1)
|
||||
*/
|
||||
|
@ -20,9 +20,61 @@
|
||||
struct i2c_client;
|
||||
struct spi_device;
|
||||
|
||||
/**
|
||||
* Default value for a register. We use an array of structs rather
|
||||
* than a simple array as many modern devices have very sparse
|
||||
* register maps.
|
||||
*
|
||||
* @reg: Register address.
|
||||
* @def: Register default value.
|
||||
*/
|
||||
struct reg_default {
|
||||
unsigned int reg;
|
||||
unsigned int def;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration for the register map of a device.
|
||||
*
|
||||
* @reg_bits: Number of bits in a register address, mandatory.
|
||||
* @val_bits: Number of bits in a register value, mandatory.
|
||||
*
|
||||
* @writeable_reg: Optional callback returning true if the register
|
||||
* can be written to.
|
||||
* @readable_reg: Optional callback returning true if the register
|
||||
* can be read from.
|
||||
* @volatile_reg: Optional callback returning true if the register
|
||||
* value can't be cached.
|
||||
* @precious_reg: Optional callback returning true if the rgister
|
||||
* should not be read outside of a call from the driver
|
||||
* (eg, a clear on read interrupt status register).
|
||||
*
|
||||
* @max_register: Optional, specifies the maximum valid register index.
|
||||
* @reg_defaults: Power on reset values for registers (for use with
|
||||
* register cache support).
|
||||
* @num_reg_defaults: Number of elements in reg_defaults.
|
||||
*
|
||||
* @read_flag_mask: Mask to be set in the top byte of the register when doing
|
||||
* a read.
|
||||
* @write_flag_mask: Mask to be set in the top byte of the register when doing
|
||||
* a write. If both read_flag_mask and write_flag_mask are
|
||||
* empty the regmap_bus default masks are used.
|
||||
*/
|
||||
struct regmap_config {
|
||||
int reg_bits;
|
||||
int val_bits;
|
||||
|
||||
bool (*writeable_reg)(struct device *dev, unsigned int reg);
|
||||
bool (*readable_reg)(struct device *dev, unsigned int reg);
|
||||
bool (*volatile_reg)(struct device *dev, unsigned int reg);
|
||||
bool (*precious_reg)(struct device *dev, unsigned int reg);
|
||||
|
||||
unsigned int max_register;
|
||||
struct reg_default *reg_defaults;
|
||||
int num_reg_defaults;
|
||||
|
||||
u8 read_flag_mask;
|
||||
u8 write_flag_mask;
|
||||
};
|
||||
|
||||
typedef int (*regmap_hw_write)(struct device *dev, const void *data,
|
||||
|
@ -141,6 +141,7 @@ int regulator_enable(struct regulator *regulator);
|
||||
int regulator_disable(struct regulator *regulator);
|
||||
int regulator_force_disable(struct regulator *regulator);
|
||||
int regulator_is_enabled(struct regulator *regulator);
|
||||
int regulator_disable_deferred(struct regulator *regulator, int ms);
|
||||
|
||||
int regulator_bulk_get(struct device *dev, int num_consumers,
|
||||
struct regulator_bulk_data *consumers);
|
||||
@ -211,6 +212,12 @@ static inline int regulator_disable(struct regulator *regulator)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int regulator_disable_deferred(struct regulator *regulator,
|
||||
int ms)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int regulator_is_enabled(struct regulator *regulator)
|
||||
{
|
||||
return 1;
|
||||
|
@ -199,6 +199,9 @@ struct regulator_dev {
|
||||
struct regulation_constraints *constraints;
|
||||
struct regulator *supply; /* for tree */
|
||||
|
||||
struct delayed_work disable_work;
|
||||
int deferred_disables;
|
||||
|
||||
void *reg_data; /* regulator_dev data */
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
34
include/sound/adau1373.h
Normal file
34
include/sound/adau1373.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Analog Devices ADAU1373 Audio Codec drive
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef __SOUND_ADAU1373_H__
|
||||
#define __SOUND_ADAU1373_H__
|
||||
|
||||
enum adau1373_micbias_voltage {
|
||||
ADAU1373_MICBIAS_2_9V = 0,
|
||||
ADAU1373_MICBIAS_2_2V = 1,
|
||||
ADAU1373_MICBIAS_2_6V = 2,
|
||||
ADAU1373_MICBIAS_1_8V = 3,
|
||||
};
|
||||
|
||||
#define ADAU1373_DRC_SIZE 13
|
||||
|
||||
struct adau1373_platform_data {
|
||||
bool input_differential[4];
|
||||
bool lineout_differential;
|
||||
bool lineout_ground_sense;
|
||||
|
||||
unsigned int num_drc;
|
||||
uint8_t drc_setting[3][ADAU1373_DRC_SIZE];
|
||||
|
||||
enum adau1373_micbias_voltage micbias1;
|
||||
enum adau1373_micbias_voltage micbias2;
|
||||
};
|
||||
|
||||
#endif
|
16
include/sound/saif.h
Normal file
16
include/sound/saif.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* 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 __SOUND_SAIF_H__
|
||||
#define __SOUND_SAIF_H__
|
||||
|
||||
struct mxs_saif_platform_data {
|
||||
int (*init) (void);
|
||||
int (*get_master_id) (unsigned int saif_id);
|
||||
};
|
||||
#endif
|
@ -524,6 +524,8 @@ struct snd_soc_dapm_context {
|
||||
enum snd_soc_bias_level target_bias_level;
|
||||
struct list_head list;
|
||||
|
||||
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_dapm;
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/control.h>
|
||||
@ -260,6 +261,7 @@ extern struct snd_ac97_bus_ops soc_ac97_ops;
|
||||
enum snd_soc_control_type {
|
||||
SND_SOC_I2C = 1,
|
||||
SND_SOC_SPI,
|
||||
SND_SOC_REGMAP,
|
||||
};
|
||||
|
||||
enum snd_soc_compress_type {
|
||||
@ -274,7 +276,7 @@ enum snd_soc_pcm_subclass {
|
||||
};
|
||||
|
||||
int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
||||
unsigned int freq, int dir);
|
||||
int source, unsigned int freq, int dir);
|
||||
int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
|
||||
unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
@ -576,6 +578,7 @@ struct snd_soc_codec {
|
||||
const void *reg_def_copy;
|
||||
const struct snd_soc_cache_ops *cache_ops;
|
||||
struct mutex cache_rw_mutex;
|
||||
int val_bytes;
|
||||
|
||||
/* dapm */
|
||||
struct snd_soc_dapm_context dapm;
|
||||
@ -607,7 +610,7 @@ struct snd_soc_codec_driver {
|
||||
|
||||
/* codec wide operations */
|
||||
int (*set_sysclk)(struct snd_soc_codec *codec,
|
||||
int clk_id, unsigned int freq, int dir);
|
||||
int clk_id, int source, unsigned int freq, int dir);
|
||||
int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
|
||||
unsigned int freq_in, unsigned int freq_out);
|
||||
|
||||
@ -619,7 +622,7 @@ struct snd_soc_codec_driver {
|
||||
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
|
||||
int (*readable_register)(struct snd_soc_codec *, unsigned int);
|
||||
int (*writable_register)(struct snd_soc_codec *, unsigned int);
|
||||
short reg_cache_size;
|
||||
unsigned int reg_cache_size;
|
||||
short reg_cache_step;
|
||||
short reg_word_size;
|
||||
const void *reg_cache_default;
|
||||
@ -630,10 +633,14 @@ struct snd_soc_codec_driver {
|
||||
/* codec bias level */
|
||||
int (*set_bias_level)(struct snd_soc_codec *,
|
||||
enum snd_soc_bias_level level);
|
||||
bool idle_bias_off;
|
||||
|
||||
void (*seq_notifier)(struct snd_soc_dapm_context *,
|
||||
enum snd_soc_dapm_type, int);
|
||||
|
||||
/* codec stream completion event */
|
||||
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
@ -669,6 +676,9 @@ struct snd_soc_platform_driver {
|
||||
/* platform stream ops */
|
||||
struct snd_pcm_ops *ops;
|
||||
|
||||
/* platform stream completion event */
|
||||
int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
|
||||
|
||||
/* probe ordering - for components with runtime dependencies */
|
||||
int probe_order;
|
||||
int remove_order;
|
||||
|
@ -23,12 +23,15 @@ config SND_SGI_HAL2
|
||||
|
||||
|
||||
config SND_AU1X00
|
||||
tristate "Au1x00 AC97 Port Driver"
|
||||
tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
|
||||
depends on SOC_AU1000 || SOC_AU1100 || SOC_AU1500
|
||||
select SND_PCM
|
||||
select SND_AC97_CODEC
|
||||
help
|
||||
ALSA Sound driver for the Au1x00's AC97 port.
|
||||
|
||||
Newer drivers for ASoC are available, please do not use
|
||||
this driver as it will be removed in the future.
|
||||
|
||||
endif # SND_MIPS
|
||||
|
||||
|
@ -7,6 +7,8 @@ menuconfig SND_SOC
|
||||
select SND_PCM
|
||||
select AC97_BUS if SND_SOC_AC97_BUS
|
||||
select SND_JACK if INPUT=y || INPUT=SND
|
||||
select REGMAP_I2C if I2C
|
||||
select REGMAP_SPI if SPI_MASTER
|
||||
---help---
|
||||
|
||||
If you want ASoC support, you should say Y here and also to the
|
||||
@ -51,6 +53,7 @@ source "sound/soc/nuc900/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/kirkwood/Kconfig"
|
||||
source "sound/soc/mid-x86/Kconfig"
|
||||
source "sound/soc/mxs/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/samsung/Kconfig"
|
||||
source "sound/soc/s6000/Kconfig"
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += imx/
|
||||
obj-$(CONFIG_SND_SOC) += jz4740/
|
||||
obj-$(CONFIG_SND_SOC) += mid-x86/
|
||||
obj-$(CONFIG_SND_SOC) += mxs/
|
||||
obj-$(CONFIG_SND_SOC) += nuc900/
|
||||
obj-$(CONFIG_SND_SOC) += omap/
|
||||
obj-$(CONFIG_SND_SOC) += kirkwood/
|
||||
|
@ -383,14 +383,17 @@ static int __init playpaq_asoc_init(void)
|
||||
_gclk0 = clk_get(NULL, "gclk0");
|
||||
if (IS_ERR(_gclk0)) {
|
||||
_gclk0 = NULL;
|
||||
ret = PTR_ERR(_gclk0);
|
||||
goto err_gclk0;
|
||||
}
|
||||
_pll0 = clk_get(NULL, "pll0");
|
||||
if (IS_ERR(_pll0)) {
|
||||
_pll0 = NULL;
|
||||
ret = PTR_ERR(_pll0);
|
||||
goto err_pll0;
|
||||
}
|
||||
if (clk_set_parent(_gclk0, _pll0)) {
|
||||
ret = clk_set_parent(_gclk0, _pll0);
|
||||
if (ret) {
|
||||
pr_warning("snd-soc-playpaq: "
|
||||
"Failed to set PLL0 as parent for DAC clock\n");
|
||||
goto err_set_clk;
|
||||
|
@ -18,10 +18,38 @@ config SND_SOC_AU1XPSC_AC97
|
||||
select SND_AC97_CODEC
|
||||
select SND_SOC_AC97_BUS
|
||||
|
||||
##
|
||||
## Au1000/1500/1100 DMA + AC97C/I2SC
|
||||
##
|
||||
config SND_SOC_AU1XAUDIO
|
||||
tristate "SoC Audio for Au1000/Au1500/Au1100"
|
||||
depends on MIPS_ALCHEMY
|
||||
help
|
||||
This is a driver set for the AC97 unit and the
|
||||
old DMA controller as found on the Au1000/Au1500/Au1100 chips.
|
||||
|
||||
config SND_SOC_AU1XAC97C
|
||||
tristate
|
||||
select AC97_BUS
|
||||
select SND_AC97_CODEC
|
||||
select SND_SOC_AC97_BUS
|
||||
|
||||
config SND_SOC_AU1XI2SC
|
||||
tristate
|
||||
|
||||
|
||||
##
|
||||
## Boards
|
||||
##
|
||||
config SND_SOC_DB1000
|
||||
tristate "DB1000 Audio support"
|
||||
depends on SND_SOC_AU1XAUDIO
|
||||
select SND_SOC_AU1XAC97C
|
||||
select SND_SOC_AC97_CODEC
|
||||
help
|
||||
Select this option to enable AC97 audio on the early DB1x00 series
|
||||
of boards (DB1000/DB1500/DB1100).
|
||||
|
||||
config SND_SOC_DB1200
|
||||
tristate "DB1200 AC97+I2S audio support"
|
||||
depends on SND_SOC_AU1XPSC
|
||||
|
@ -3,11 +3,21 @@ snd-soc-au1xpsc-dbdma-objs := dbdma2.o
|
||||
snd-soc-au1xpsc-i2s-objs := psc-i2s.o
|
||||
snd-soc-au1xpsc-ac97-objs := psc-ac97.o
|
||||
|
||||
# Au1000/1500/1100 Audio units
|
||||
snd-soc-au1x-dma-objs := dma.o
|
||||
snd-soc-au1x-ac97c-objs := ac97c.o
|
||||
snd-soc-au1x-i2sc-objs := i2sc.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
|
||||
obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
|
||||
obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
|
||||
obj-$(CONFIG_SND_SOC_AU1XAUDIO) += snd-soc-au1x-dma.o
|
||||
obj-$(CONFIG_SND_SOC_AU1XAC97C) += snd-soc-au1x-ac97c.o
|
||||
obj-$(CONFIG_SND_SOC_AU1XI2SC) += snd-soc-au1x-i2sc.o
|
||||
|
||||
# Boards
|
||||
snd-soc-db1000-objs := db1000.o
|
||||
snd-soc-db1200-objs := db1200.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_DB1000) += snd-soc-db1000.o
|
||||
obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o
|
||||
|
363
sound/soc/au1x/ac97c.c
Normal file
363
sound/soc/au1x/ac97c.c
Normal file
@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Au1000/Au1500/Au1100 AC97C controller driver for ASoC
|
||||
*
|
||||
* (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*
|
||||
* based on the old ALSA driver originally written by
|
||||
* Charles Eidsness <charles@cooper-street.com>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
|
||||
#include "psc.h"
|
||||
|
||||
/* register offsets and bits */
|
||||
#define AC97_CONFIG 0x00
|
||||
#define AC97_STATUS 0x04
|
||||
#define AC97_DATA 0x08
|
||||
#define AC97_CMDRESP 0x0c
|
||||
#define AC97_ENABLE 0x10
|
||||
|
||||
#define CFG_RC(x) (((x) & 0x3ff) << 13) /* valid rx slots mask */
|
||||
#define CFG_XS(x) (((x) & 0x3ff) << 3) /* valid tx slots mask */
|
||||
#define CFG_SG (1 << 2) /* sync gate */
|
||||
#define CFG_SN (1 << 1) /* sync control */
|
||||
#define CFG_RS (1 << 0) /* acrst# control */
|
||||
#define STAT_XU (1 << 11) /* tx underflow */
|
||||
#define STAT_XO (1 << 10) /* tx overflow */
|
||||
#define STAT_RU (1 << 9) /* rx underflow */
|
||||
#define STAT_RO (1 << 8) /* rx overflow */
|
||||
#define STAT_RD (1 << 7) /* codec ready */
|
||||
#define STAT_CP (1 << 6) /* command pending */
|
||||
#define STAT_TE (1 << 4) /* tx fifo empty */
|
||||
#define STAT_TF (1 << 3) /* tx fifo full */
|
||||
#define STAT_RE (1 << 1) /* rx fifo empty */
|
||||
#define STAT_RF (1 << 0) /* rx fifo full */
|
||||
#define CMD_SET_DATA(x) (((x) & 0xffff) << 16)
|
||||
#define CMD_GET_DATA(x) ((x) & 0xffff)
|
||||
#define CMD_READ (1 << 7)
|
||||
#define CMD_WRITE (0 << 7)
|
||||
#define CMD_IDX(x) ((x) & 0x7f)
|
||||
#define EN_D (1 << 1) /* DISable bit */
|
||||
#define EN_CE (1 << 0) /* clock enable bit */
|
||||
|
||||
/* how often to retry failed codec register reads/writes */
|
||||
#define AC97_RW_RETRIES 5
|
||||
|
||||
#define AC97_RATES \
|
||||
SNDRV_PCM_RATE_CONTINUOUS
|
||||
|
||||
#define AC97_FMTS \
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE)
|
||||
|
||||
/* instance data. There can be only one, MacLeod!!!!, fortunately there IS only
|
||||
* once AC97C on early Alchemy chips. The newer ones aren't so lucky.
|
||||
*/
|
||||
static struct au1xpsc_audio_data *ac97c_workdata;
|
||||
#define ac97_to_ctx(x) ac97c_workdata
|
||||
|
||||
static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
|
||||
{
|
||||
return __raw_readl(ctx->mmio + reg);
|
||||
}
|
||||
|
||||
static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
|
||||
{
|
||||
__raw_writel(v, ctx->mmio + reg);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
|
||||
unsigned short r)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
|
||||
unsigned int tmo, retry;
|
||||
unsigned long data;
|
||||
|
||||
data = ~0;
|
||||
retry = AC97_RW_RETRIES;
|
||||
do {
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
tmo = 5;
|
||||
while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
|
||||
udelay(21); /* wait an ac97 frame time */
|
||||
if (!tmo) {
|
||||
pr_debug("ac97rd timeout #1\n");
|
||||
goto next;
|
||||
}
|
||||
|
||||
WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ);
|
||||
|
||||
/* stupid errata: data is only valid for 21us, so
|
||||
* poll, Forrest, poll...
|
||||
*/
|
||||
tmo = 0x10000;
|
||||
while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
|
||||
asm volatile ("nop");
|
||||
data = RD(ctx, AC97_CMDRESP);
|
||||
|
||||
if (!tmo)
|
||||
pr_debug("ac97rd timeout #2\n");
|
||||
|
||||
next:
|
||||
mutex_unlock(&ctx->lock);
|
||||
} while (--retry && !tmo);
|
||||
|
||||
pr_debug("AC97RD %04x %04lx %d\n", r, data, retry);
|
||||
|
||||
return retry ? data & 0xffff : 0xffff;
|
||||
}
|
||||
|
||||
static void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r,
|
||||
unsigned short v)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
|
||||
unsigned int tmo, retry;
|
||||
|
||||
retry = AC97_RW_RETRIES;
|
||||
do {
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
|
||||
udelay(21);
|
||||
if (!tmo) {
|
||||
pr_debug("ac97wr timeout #1\n");
|
||||
goto next;
|
||||
}
|
||||
|
||||
WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v));
|
||||
|
||||
for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
|
||||
udelay(21);
|
||||
if (!tmo)
|
||||
pr_debug("ac97wr timeout #2\n");
|
||||
next:
|
||||
mutex_unlock(&ctx->lock);
|
||||
} while (--retry && !tmo);
|
||||
|
||||
pr_debug("AC97WR %04x %04x %d\n", r, v, retry);
|
||||
}
|
||||
|
||||
static void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
|
||||
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN);
|
||||
msleep(20);
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG);
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg);
|
||||
}
|
||||
|
||||
static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
|
||||
int i;
|
||||
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS);
|
||||
msleep(500);
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg);
|
||||
|
||||
/* wait for codec ready */
|
||||
i = 50;
|
||||
while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i)
|
||||
msleep(20);
|
||||
if (!i)
|
||||
printk(KERN_ERR "ac97c: codec not ready after cold reset\n");
|
||||
}
|
||||
|
||||
/* AC97 controller operations */
|
||||
struct snd_ac97_bus_ops soc_ac97_ops = {
|
||||
.read = au1xac97c_ac97_read,
|
||||
.write = au1xac97c_ac97_write,
|
||||
.reset = au1xac97c_ac97_cold_reset,
|
||||
.warm_reset = au1xac97c_ac97_warm_reset,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(soc_ac97_ops); /* globals be gone! */
|
||||
|
||||
static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
|
||||
snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops alchemy_ac97c_ops = {
|
||||
.startup = alchemy_ac97c_startup,
|
||||
};
|
||||
|
||||
static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
return ac97c_workdata ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
|
||||
.name = "alchemy-ac97c",
|
||||
.ac97_control = 1,
|
||||
.probe = au1xac97c_dai_probe,
|
||||
.playback = {
|
||||
.rates = AC97_RATES,
|
||||
.formats = AC97_FMTS,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.capture = {
|
||||
.rates = AC97_RATES,
|
||||
.formats = AC97_FMTS,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &alchemy_ac97c_ops,
|
||||
};
|
||||
|
||||
static int __devinit au1xac97c_drvprobe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *r;
|
||||
struct au1xpsc_audio_data *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ctx->lock);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
goto out0;
|
||||
}
|
||||
|
||||
ret = -EBUSY;
|
||||
if (!request_mem_region(r->start, resource_size(r), pdev->name))
|
||||
goto out0;
|
||||
|
||||
ctx->mmio = ioremap_nocache(r->start, resource_size(r));
|
||||
if (!ctx->mmio)
|
||||
goto out1;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r)
|
||||
goto out1;
|
||||
ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r)
|
||||
goto out1;
|
||||
ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
|
||||
|
||||
/* switch it on */
|
||||
WR(ctx, AC97_ENABLE, EN_D | EN_CE);
|
||||
WR(ctx, AC97_ENABLE, EN_CE);
|
||||
|
||||
ctx->cfg = CFG_RC(3) | CFG_XS(3);
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg);
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
ret = snd_soc_register_dai(&pdev->dev, &au1xac97c_dai_driver);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
ac97c_workdata = ctx;
|
||||
return 0;
|
||||
|
||||
out1:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
out0:
|
||||
kfree(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit au1xac97c_drvremove(struct platform_device *pdev)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
|
||||
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
|
||||
WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
|
||||
|
||||
iounmap(ctx->mmio);
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
kfree(ctx);
|
||||
|
||||
ac97c_workdata = NULL; /* MDEV */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int au1xac97c_drvsuspend(struct device *dev)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
|
||||
|
||||
WR(ctx, AC97_ENABLE, EN_D); /* clock off, disable */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xac97c_drvresume(struct device *dev)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
|
||||
|
||||
WR(ctx, AC97_ENABLE, EN_D | EN_CE);
|
||||
WR(ctx, AC97_ENABLE, EN_CE);
|
||||
WR(ctx, AC97_CONFIG, ctx->cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops au1xpscac97_pmops = {
|
||||
.suspend = au1xac97c_drvsuspend,
|
||||
.resume = au1xac97c_drvresume,
|
||||
};
|
||||
|
||||
#define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops)
|
||||
|
||||
#else
|
||||
|
||||
#define AU1XPSCAC97_PMOPS NULL
|
||||
|
||||
#endif
|
||||
|
||||
static struct platform_driver au1xac97c_driver = {
|
||||
.driver = {
|
||||
.name = "alchemy-ac97c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = AU1XPSCAC97_PMOPS,
|
||||
},
|
||||
.probe = au1xac97c_drvprobe,
|
||||
.remove = __devexit_p(au1xac97c_drvremove),
|
||||
};
|
||||
|
||||
static int __init au1xac97c_load(void)
|
||||
{
|
||||
ac97c_workdata = NULL;
|
||||
return platform_driver_register(&au1xac97c_driver);
|
||||
}
|
||||
|
||||
static void __exit au1xac97c_unload(void)
|
||||
{
|
||||
platform_driver_unregister(&au1xac97c_driver);
|
||||
}
|
||||
|
||||
module_init(au1xac97c_load);
|
||||
module_exit(au1xac97c_unload);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver");
|
||||
MODULE_AUTHOR("Manuel Lauss");
|
75
sound/soc/au1x/db1000.c
Normal file
75
sound/soc/au1x/db1000.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* DB1000/DB1500/DB1100 ASoC audio fabric support code.
|
||||
*
|
||||
* (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <asm/mach-au1x00/au1000.h>
|
||||
#include <asm/mach-db1x00/bcsr.h>
|
||||
|
||||
#include "psc.h"
|
||||
|
||||
static struct snd_soc_dai_link db1000_ac97_dai = {
|
||||
.name = "AC97",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.codec_dai_name = "ac97-hifi",
|
||||
.cpu_dai_name = "alchemy-ac97c",
|
||||
.platform_name = "alchemy-pcm-dma.0",
|
||||
.codec_name = "ac97-codec",
|
||||
};
|
||||
|
||||
static struct snd_soc_card db1000_ac97 = {
|
||||
.name = "DB1000_AC97",
|
||||
.dai_link = &db1000_ac97_dai,
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static int __devinit db1000_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &db1000_ac97;
|
||||
card->dev = &pdev->dev;
|
||||
return snd_soc_register_card(card);
|
||||
}
|
||||
|
||||
static int __devexit db1000_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
snd_soc_unregister_card(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver db1000_audio_driver = {
|
||||
.driver = {
|
||||
.name = "db1000-audio",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = db1000_audio_probe,
|
||||
.remove = __devexit_p(db1000_audio_remove),
|
||||
};
|
||||
|
||||
static int __init db1000_audio_load(void)
|
||||
{
|
||||
return platform_driver_register(&db1000_audio_driver);
|
||||
}
|
||||
|
||||
static void __exit db1000_audio_unload(void)
|
||||
{
|
||||
platform_driver_unregister(&db1000_audio_driver);
|
||||
}
|
||||
|
||||
module_init(db1000_audio_load);
|
||||
module_exit(db1000_audio_unload);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("DB1000/DB1500/DB1100 ASoC audio");
|
||||
MODULE_AUTHOR("Manuel Lauss");
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* DB1200 ASoC audio fabric support code.
|
||||
*
|
||||
* (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com>
|
||||
* (c) 2008-2011 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
@ -21,6 +21,17 @@
|
||||
#include "../codecs/wm8731.h"
|
||||
#include "psc.h"
|
||||
|
||||
static struct platform_device_id db1200_pids[] = {
|
||||
{
|
||||
.name = "db1200-ac97",
|
||||
.driver_data = 0,
|
||||
}, {
|
||||
.name = "db1200-i2s",
|
||||
.driver_data = 1,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
/*------------------------- AC97 PART ---------------------------*/
|
||||
|
||||
static struct snd_soc_dai_link db1200_ac97_dai = {
|
||||
@ -89,36 +100,47 @@ static struct snd_soc_card db1200_i2s_machine = {
|
||||
|
||||
/*------------------------- COMMON PART ---------------------------*/
|
||||
|
||||
static struct platform_device *db1200_asoc_dev;
|
||||
static struct snd_soc_card *db1200_cards[] __devinitdata = {
|
||||
&db1200_ac97_machine,
|
||||
&db1200_i2s_machine,
|
||||
};
|
||||
|
||||
static int __devinit db1200_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *pid = platform_get_device_id(pdev);
|
||||
struct snd_soc_card *card;
|
||||
|
||||
card = db1200_cards[pid->driver_data];
|
||||
card->dev = &pdev->dev;
|
||||
return snd_soc_register_card(card);
|
||||
}
|
||||
|
||||
static int __devexit db1200_audio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
snd_soc_unregister_card(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver db1200_audio_driver = {
|
||||
.driver = {
|
||||
.name = "db1200-ac97",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.id_table = db1200_pids,
|
||||
.probe = db1200_audio_probe,
|
||||
.remove = __devexit_p(db1200_audio_remove),
|
||||
};
|
||||
|
||||
static int __init db1200_audio_load(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
db1200_asoc_dev = platform_device_alloc("soc-audio", 1); /* PSC1 */
|
||||
if (!db1200_asoc_dev)
|
||||
goto out;
|
||||
|
||||
/* DB1200 board setup set PSC1MUX to preferred audio device */
|
||||
if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX)
|
||||
platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine);
|
||||
else
|
||||
platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine);
|
||||
|
||||
ret = platform_device_add(db1200_asoc_dev);
|
||||
|
||||
if (ret) {
|
||||
platform_device_put(db1200_asoc_dev);
|
||||
db1200_asoc_dev = NULL;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
return platform_driver_register(&db1200_audio_driver);
|
||||
}
|
||||
|
||||
static void __exit db1200_audio_unload(void)
|
||||
{
|
||||
platform_device_unregister(db1200_asoc_dev);
|
||||
platform_driver_unregister(&db1200_audio_driver);
|
||||
}
|
||||
|
||||
module_init(db1200_audio_load);
|
||||
|
@ -169,7 +169,7 @@ static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
|
||||
|
||||
au1x_pcm_dbdma_free(pcd);
|
||||
|
||||
if (stype == PCM_RX)
|
||||
if (stype == SNDRV_PCM_STREAM_CAPTURE)
|
||||
pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
|
||||
DSCR_CMD0_ALWAYS,
|
||||
au1x_pcm_dmarx_cb, (void *)pcd);
|
||||
@ -198,7 +198,7 @@ static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream
|
||||
struct snd_soc_pcm_runtime *rtd = ss->private_data;
|
||||
struct au1xpsc_audio_dmadata *pcd =
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
return &pcd[SUBSTREAM_TYPE(ss)];
|
||||
return &pcd[ss->stream];
|
||||
}
|
||||
|
||||
static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
@ -212,7 +212,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
stype = SUBSTREAM_TYPE(substream);
|
||||
stype = substream->stream;
|
||||
pcd = to_dmadata(substream);
|
||||
|
||||
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
|
||||
@ -255,7 +255,7 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
|
||||
au1xxx_dbdma_reset(pcd->ddma_chan);
|
||||
|
||||
if (SUBSTREAM_TYPE(substream) == PCM_RX) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
au1x_pcm_queue_rx(pcd);
|
||||
au1x_pcm_queue_rx(pcd);
|
||||
} else {
|
||||
@ -293,6 +293,16 @@ au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
|
||||
static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
int stype = substream->stream, *dmaids;
|
||||
|
||||
dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
if (!dmaids)
|
||||
return -ENODEV; /* whoa, has ordering changed? */
|
||||
|
||||
pcd->ddma_id = dmaids[stype];
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
|
||||
return 0;
|
||||
}
|
||||
@ -340,36 +350,18 @@ struct snd_soc_platform_driver au1xpsc_soc_platform = {
|
||||
static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
|
||||
{
|
||||
struct au1xpsc_audio_dmadata *dmadata;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
|
||||
dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
|
||||
if (!dmadata)
|
||||
return -ENOMEM;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
goto out1;
|
||||
}
|
||||
dmadata[PCM_TX].ddma_id = r->start;
|
||||
|
||||
/* RX DMA */
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
goto out1;
|
||||
}
|
||||
dmadata[PCM_RX].ddma_id = r->start;
|
||||
|
||||
platform_set_drvdata(pdev, dmadata);
|
||||
|
||||
ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
|
||||
if (!ret)
|
||||
return ret;
|
||||
if (ret)
|
||||
kfree(dmadata);
|
||||
|
||||
out1:
|
||||
kfree(dmadata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -405,57 +397,6 @@ static void __exit au1xpsc_audio_dbdma_unload(void)
|
||||
module_init(au1xpsc_audio_dbdma_load);
|
||||
module_exit(au1xpsc_audio_dbdma_unload);
|
||||
|
||||
|
||||
struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res, *r;
|
||||
struct platform_device *pd;
|
||||
int id[2];
|
||||
int ret;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r)
|
||||
return NULL;
|
||||
id[0] = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
id[1] = r->start;
|
||||
|
||||
res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
res[0].start = res[0].end = id[0];
|
||||
res[1].start = res[1].end = id[1];
|
||||
res[0].flags = res[1].flags = IORESOURCE_DMA;
|
||||
|
||||
pd = platform_device_alloc("au1xpsc-pcm", pdev->id);
|
||||
if (!pd)
|
||||
goto out;
|
||||
|
||||
pd->resource = res;
|
||||
pd->num_resources = 2;
|
||||
|
||||
ret = platform_device_add(pd);
|
||||
if (!ret)
|
||||
return pd;
|
||||
|
||||
platform_device_put(pd);
|
||||
out:
|
||||
kfree(res);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
|
||||
|
||||
void au1xpsc_pcm_destroy(struct platform_device *dmapd)
|
||||
{
|
||||
if (dmapd)
|
||||
platform_device_unregister(dmapd);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
|
||||
MODULE_AUTHOR("Manuel Lauss");
|
||||
|
377
sound/soc/au1x/dma.c
Normal file
377
sound/soc/au1x/dma.c
Normal file
@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Au1000/Au1500/Au1100 Audio DMA support.
|
||||
*
|
||||
* (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*
|
||||
* copied almost verbatim from the old ALSA driver, written by
|
||||
* Charles Eidsness <charles@cooper-street.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.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-au1x00/au1000.h>
|
||||
#include <asm/mach-au1x00/au1000_dma.h>
|
||||
|
||||
#include "psc.h"
|
||||
|
||||
#define ALCHEMY_PCM_FMTS \
|
||||
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
|
||||
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \
|
||||
SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \
|
||||
0)
|
||||
|
||||
struct pcm_period {
|
||||
u32 start;
|
||||
u32 relative_end; /* relative to start of buffer */
|
||||
struct pcm_period *next;
|
||||
};
|
||||
|
||||
struct audio_stream {
|
||||
struct snd_pcm_substream *substream;
|
||||
int dma;
|
||||
struct pcm_period *buffer;
|
||||
unsigned int period_size;
|
||||
unsigned int periods;
|
||||
};
|
||||
|
||||
struct alchemy_pcm_ctx {
|
||||
struct audio_stream stream[2]; /* playback & capture */
|
||||
};
|
||||
|
||||
static void au1000_release_dma_link(struct audio_stream *stream)
|
||||
{
|
||||
struct pcm_period *pointer;
|
||||
struct pcm_period *pointer_next;
|
||||
|
||||
stream->period_size = 0;
|
||||
stream->periods = 0;
|
||||
pointer = stream->buffer;
|
||||
if (!pointer)
|
||||
return;
|
||||
do {
|
||||
pointer_next = pointer->next;
|
||||
kfree(pointer);
|
||||
pointer = pointer_next;
|
||||
} while (pointer != stream->buffer);
|
||||
stream->buffer = NULL;
|
||||
}
|
||||
|
||||
static int au1000_setup_dma_link(struct audio_stream *stream,
|
||||
unsigned int period_bytes,
|
||||
unsigned int periods)
|
||||
{
|
||||
struct snd_pcm_substream *substream = stream->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct pcm_period *pointer;
|
||||
unsigned long dma_start;
|
||||
int i;
|
||||
|
||||
dma_start = virt_to_phys(runtime->dma_area);
|
||||
|
||||
if (stream->period_size == period_bytes &&
|
||||
stream->periods == periods)
|
||||
return 0; /* not changed */
|
||||
|
||||
au1000_release_dma_link(stream);
|
||||
|
||||
stream->period_size = period_bytes;
|
||||
stream->periods = periods;
|
||||
|
||||
stream->buffer = kmalloc(sizeof(struct pcm_period), GFP_KERNEL);
|
||||
if (!stream->buffer)
|
||||
return -ENOMEM;
|
||||
pointer = stream->buffer;
|
||||
for (i = 0; i < periods; i++) {
|
||||
pointer->start = (u32)(dma_start + (i * period_bytes));
|
||||
pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
|
||||
if (i < periods - 1) {
|
||||
pointer->next = kmalloc(sizeof(struct pcm_period),
|
||||
GFP_KERNEL);
|
||||
if (!pointer->next) {
|
||||
au1000_release_dma_link(stream);
|
||||
return -ENOMEM;
|
||||
}
|
||||
pointer = pointer->next;
|
||||
}
|
||||
}
|
||||
pointer->next = stream->buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void au1000_dma_stop(struct audio_stream *stream)
|
||||
{
|
||||
if (stream->buffer)
|
||||
disable_dma(stream->dma);
|
||||
}
|
||||
|
||||
static void au1000_dma_start(struct audio_stream *stream)
|
||||
{
|
||||
if (!stream->buffer)
|
||||
return;
|
||||
|
||||
init_dma(stream->dma);
|
||||
if (get_dma_active_buffer(stream->dma) == 0) {
|
||||
clear_dma_done0(stream->dma);
|
||||
set_dma_addr0(stream->dma, stream->buffer->start);
|
||||
set_dma_count0(stream->dma, stream->period_size >> 1);
|
||||
set_dma_addr1(stream->dma, stream->buffer->next->start);
|
||||
set_dma_count1(stream->dma, stream->period_size >> 1);
|
||||
} else {
|
||||
clear_dma_done1(stream->dma);
|
||||
set_dma_addr1(stream->dma, stream->buffer->start);
|
||||
set_dma_count1(stream->dma, stream->period_size >> 1);
|
||||
set_dma_addr0(stream->dma, stream->buffer->next->start);
|
||||
set_dma_count0(stream->dma, stream->period_size >> 1);
|
||||
}
|
||||
enable_dma_buffers(stream->dma);
|
||||
start_dma(stream->dma);
|
||||
}
|
||||
|
||||
static irqreturn_t au1000_dma_interrupt(int irq, void *ptr)
|
||||
{
|
||||
struct audio_stream *stream = (struct audio_stream *)ptr;
|
||||
struct snd_pcm_substream *substream = stream->substream;
|
||||
|
||||
switch (get_dma_buffer_done(stream->dma)) {
|
||||
case DMA_D0:
|
||||
stream->buffer = stream->buffer->next;
|
||||
clear_dma_done0(stream->dma);
|
||||
set_dma_addr0(stream->dma, stream->buffer->next->start);
|
||||
set_dma_count0(stream->dma, stream->period_size >> 1);
|
||||
enable_dma_buffer0(stream->dma);
|
||||
break;
|
||||
case DMA_D1:
|
||||
stream->buffer = stream->buffer->next;
|
||||
clear_dma_done1(stream->dma);
|
||||
set_dma_addr1(stream->dma, stream->buffer->next->start);
|
||||
set_dma_count1(stream->dma, stream->period_size >> 1);
|
||||
enable_dma_buffer1(stream->dma);
|
||||
break;
|
||||
case (DMA_D0 | DMA_D1):
|
||||
pr_debug("DMA %d missed interrupt.\n", stream->dma);
|
||||
au1000_dma_stop(stream);
|
||||
au1000_dma_start(stream);
|
||||
break;
|
||||
case (~DMA_D0 & ~DMA_D1):
|
||||
pr_debug("DMA %d empty irq.\n", stream->dma);
|
||||
}
|
||||
snd_pcm_period_elapsed(substream);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct snd_pcm_hardware alchemy_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
|
||||
.formats = ALCHEMY_PCM_FMTS,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.rate_min = SNDRV_PCM_RATE_8000,
|
||||
.rate_max = SNDRV_PCM_RATE_192000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.period_bytes_min = 1024,
|
||||
.period_bytes_max = 16 * 1024 - 1,
|
||||
.periods_min = 4,
|
||||
.periods_max = 255,
|
||||
.buffer_bytes_max = 128 * 1024,
|
||||
.fifo_size = 16,
|
||||
};
|
||||
|
||||
static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = ss->private_data;
|
||||
return snd_soc_platform_get_drvdata(rtd->platform);
|
||||
}
|
||||
|
||||
static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
|
||||
return &(ctx->stream[ss->stream]);
|
||||
}
|
||||
|
||||
static int alchemy_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
int *dmaids, s = substream->stream;
|
||||
char *name;
|
||||
|
||||
dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||
if (!dmaids)
|
||||
return -ENODEV; /* whoa, has ordering changed? */
|
||||
|
||||
/* DMA setup */
|
||||
name = (s == SNDRV_PCM_STREAM_PLAYBACK) ? "audio-tx" : "audio-rx";
|
||||
ctx->stream[s].dma = request_au1000_dma(dmaids[s], name,
|
||||
au1000_dma_interrupt, IRQF_DISABLED,
|
||||
&ctx->stream[s]);
|
||||
set_dma_mode(ctx->stream[s].dma,
|
||||
get_dma_mode(ctx->stream[s].dma) & ~DMA_NC);
|
||||
|
||||
ctx->stream[s].substream = substream;
|
||||
ctx->stream[s].buffer = NULL;
|
||||
snd_soc_set_runtime_hwparams(substream, &alchemy_pcm_hardware);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
|
||||
int stype = substream->stream;
|
||||
|
||||
ctx->stream[stype].substream = NULL;
|
||||
free_au1000_dma(ctx->stream[stype].dma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream);
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = au1000_setup_dma_link(stream,
|
||||
params_period_bytes(hw_params),
|
||||
params_periods(hw_params));
|
||||
if (err)
|
||||
snd_pcm_lib_free_pages(substream);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream);
|
||||
au1000_release_dma_link(stream);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(substream);
|
||||
int err = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
au1000_dma_start(stream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
au1000_dma_stop(stream);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
|
||||
{
|
||||
struct audio_stream *stream = ss_to_as(ss);
|
||||
long location;
|
||||
|
||||
location = get_dma_residue(stream->dma);
|
||||
location = stream->buffer->relative_end - location;
|
||||
if (location == -1)
|
||||
location = 0;
|
||||
return bytes_to_frames(ss->runtime, location);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops alchemy_pcm_ops = {
|
||||
.open = alchemy_pcm_open,
|
||||
.close = alchemy_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = alchemy_pcm_hw_params,
|
||||
.hw_free = alchemy_pcm_hw_free,
|
||||
.trigger = alchemy_pcm_trigger,
|
||||
.pointer = alchemy_pcm_pointer,
|
||||
};
|
||||
|
||||
static void alchemy_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_soc_platform_driver alchemy_pcm_soc_platform = {
|
||||
.ops = &alchemy_pcm_ops,
|
||||
.pcm_new = alchemy_pcm_new,
|
||||
.pcm_free = alchemy_pcm_free_dma_buffers,
|
||||
};
|
||||
|
||||
static int __devinit alchemy_pcm_drvprobe(struct platform_device *pdev)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
ret = snd_soc_register_platform(&pdev->dev, &alchemy_pcm_soc_platform);
|
||||
if (ret)
|
||||
kfree(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit alchemy_pcm_drvremove(struct platform_device *pdev)
|
||||
{
|
||||
struct alchemy_pcm_ctx *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
kfree(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver alchemy_pcmdma_driver = {
|
||||
.driver = {
|
||||
.name = "alchemy-pcm-dma",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = alchemy_pcm_drvprobe,
|
||||
.remove = __devexit_p(alchemy_pcm_drvremove),
|
||||
};
|
||||
|
||||
static int __init alchemy_pcmdma_load(void)
|
||||
{
|
||||
return platform_driver_register(&alchemy_pcmdma_driver);
|
||||
}
|
||||
|
||||
static void __exit alchemy_pcmdma_unload(void)
|
||||
{
|
||||
platform_driver_unregister(&alchemy_pcmdma_driver);
|
||||
}
|
||||
|
||||
module_init(alchemy_pcmdma_load);
|
||||
module_exit(alchemy_pcmdma_unload);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
|
||||
MODULE_AUTHOR("Manuel Lauss");
|
346
sound/soc/au1x/i2sc.c
Normal file
346
sound/soc/au1x/i2sc.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Au1000/Au1500/Au1100 I2S controller driver for ASoC
|
||||
*
|
||||
* (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
|
||||
*
|
||||
* Note: clock supplied to the I2S controller must be 256x samplerate.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
#include <asm/mach-au1x00/au1000.h>
|
||||
|
||||
#include "psc.h"
|
||||
|
||||
#define I2S_RXTX 0x00
|
||||
#define I2S_CFG 0x04
|
||||
#define I2S_ENABLE 0x08
|
||||
|
||||
#define CFG_XU (1 << 25) /* tx underflow */
|
||||
#define CFG_XO (1 << 24)
|
||||
#define CFG_RU (1 << 23)
|
||||
#define CFG_RO (1 << 22)
|
||||
#define CFG_TR (1 << 21)
|
||||
#define CFG_TE (1 << 20)
|
||||
#define CFG_TF (1 << 19)
|
||||
#define CFG_RR (1 << 18)
|
||||
#define CFG_RF (1 << 17)
|
||||
#define CFG_ICK (1 << 12) /* clock invert */
|
||||
#define CFG_PD (1 << 11) /* set to make I2SDIO INPUT */
|
||||
#define CFG_LB (1 << 10) /* loopback */
|
||||
#define CFG_IC (1 << 9) /* word select invert */
|
||||
#define CFG_FM_I2S (0 << 7) /* I2S format */
|
||||
#define CFG_FM_LJ (1 << 7) /* left-justified */
|
||||
#define CFG_FM_RJ (2 << 7) /* right-justified */
|
||||
#define CFG_FM_MASK (3 << 7)
|
||||
#define CFG_TN (1 << 6) /* tx fifo en */
|
||||
#define CFG_RN (1 << 5) /* rx fifo en */
|
||||
#define CFG_SZ_8 (0x08)
|
||||
#define CFG_SZ_16 (0x10)
|
||||
#define CFG_SZ_18 (0x12)
|
||||
#define CFG_SZ_20 (0x14)
|
||||
#define CFG_SZ_24 (0x18)
|
||||
#define CFG_SZ_MASK (0x1f)
|
||||
#define EN_D (1 << 1) /* DISable */
|
||||
#define EN_CE (1 << 0) /* clock enable */
|
||||
|
||||
/* only limited by clock generator and board design */
|
||||
#define AU1XI2SC_RATES \
|
||||
SNDRV_PCM_RATE_CONTINUOUS
|
||||
|
||||
#define AU1XI2SC_FMTS \
|
||||
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
|
||||
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
|
||||
SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE | \
|
||||
0)
|
||||
|
||||
static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
|
||||
{
|
||||
return __raw_readl(ctx->mmio + reg);
|
||||
}
|
||||
|
||||
static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
|
||||
{
|
||||
__raw_writel(v, ctx->mmio + reg);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
unsigned long c;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
c = ctx->cfg;
|
||||
|
||||
c &= ~CFG_FM_MASK;
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
c |= CFG_FM_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_MSB:
|
||||
c |= CFG_FM_RJ;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LSB:
|
||||
c |= CFG_FM_LJ;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
c &= ~(CFG_IC | CFG_ICK); /* IB-IF */
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
c |= CFG_IC | CFG_ICK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
c |= CFG_IC;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
c |= CFG_ICK;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* I2S controller only supports master */
|
||||
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
ctx->cfg = c;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int au1xi2s_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
|
||||
int stype = SUBSTREAM_TYPE(substream);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
/* power up */
|
||||
WR(ctx, I2S_ENABLE, EN_D | EN_CE);
|
||||
WR(ctx, I2S_ENABLE, EN_CE);
|
||||
ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN;
|
||||
WR(ctx, I2S_CFG, ctx->cfg);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN);
|
||||
WR(ctx, I2S_CFG, ctx->cfg);
|
||||
WR(ctx, I2S_ENABLE, EN_D); /* power off */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long msbits_to_reg(int msbits)
|
||||
{
|
||||
switch (msbits) {
|
||||
case 8:
|
||||
return CFG_SZ_8;
|
||||
case 16:
|
||||
return CFG_SZ_16;
|
||||
case 18:
|
||||
return CFG_SZ_18;
|
||||
case 20:
|
||||
return CFG_SZ_20;
|
||||
case 24:
|
||||
return CFG_SZ_24;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned long v;
|
||||
|
||||
v = msbits_to_reg(params->msbits);
|
||||
if (!v)
|
||||
return -EINVAL;
|
||||
|
||||
ctx->cfg &= ~CFG_SZ_MASK;
|
||||
ctx->cfg |= v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xi2s_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
|
||||
snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
|
||||
.startup = au1xi2s_startup,
|
||||
.trigger = au1xi2s_trigger,
|
||||
.hw_params = au1xi2s_hw_params,
|
||||
.set_fmt = au1xi2s_set_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver au1xi2s_dai_driver = {
|
||||
.symmetric_rates = 1,
|
||||
.playback = {
|
||||
.rates = AU1XI2SC_RATES,
|
||||
.formats = AU1XI2SC_FMTS,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.capture = {
|
||||
.rates = AU1XI2SC_RATES,
|
||||
.formats = AU1XI2SC_FMTS,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &au1xi2s_dai_ops,
|
||||
};
|
||||
|
||||
static int __devinit au1xi2s_drvprobe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *r;
|
||||
struct au1xpsc_audio_data *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
goto out0;
|
||||
}
|
||||
|
||||
ret = -EBUSY;
|
||||
if (!request_mem_region(r->start, resource_size(r), pdev->name))
|
||||
goto out0;
|
||||
|
||||
ctx->mmio = ioremap_nocache(r->start, resource_size(r));
|
||||
if (!ctx->mmio)
|
||||
goto out1;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r)
|
||||
goto out1;
|
||||
ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r)
|
||||
goto out1;
|
||||
ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
ret = snd_soc_register_dai(&pdev->dev, &au1xi2s_dai_driver);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
return 0;
|
||||
|
||||
out1:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
out0:
|
||||
kfree(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit au1xi2s_drvremove(struct platform_device *pdev)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
|
||||
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
|
||||
WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
|
||||
|
||||
iounmap(ctx->mmio);
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
kfree(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int au1xi2s_drvsuspend(struct device *dev)
|
||||
{
|
||||
struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
|
||||
|
||||
WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xi2s_drvresume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops au1xi2sc_pmops = {
|
||||
.suspend = au1xi2s_drvsuspend,
|
||||
.resume = au1xi2s_drvresume,
|
||||
};
|
||||
|
||||
#define AU1XI2SC_PMOPS (&au1xi2sc_pmops)
|
||||
|
||||
#else
|
||||
|
||||
#define AU1XI2SC_PMOPS NULL
|
||||
|
||||
#endif
|
||||
|
||||
static struct platform_driver au1xi2s_driver = {
|
||||
.driver = {
|
||||
.name = "alchemy-i2sc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = AU1XI2SC_PMOPS,
|
||||
},
|
||||
.probe = au1xi2s_drvprobe,
|
||||
.remove = __devexit_p(au1xi2s_drvremove),
|
||||
};
|
||||
|
||||
static int __init au1xi2s_load(void)
|
||||
{
|
||||
return platform_driver_register(&au1xi2s_driver);
|
||||
}
|
||||
|
||||
static void __exit au1xi2s_unload(void)
|
||||
{
|
||||
platform_driver_unregister(&au1xi2s_driver);
|
||||
}
|
||||
|
||||
module_init(au1xi2s_load);
|
||||
module_exit(au1xi2s_unload);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver");
|
||||
MODULE_AUTHOR("Manuel Lauss");
|
@ -41,14 +41,14 @@
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
|
||||
|
||||
#define AC97PCR_START(stype) \
|
||||
((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
|
||||
#define AC97PCR_STOP(stype) \
|
||||
((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
|
||||
#define AC97PCR_CLRFIFO(stype) \
|
||||
((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
|
||||
|
||||
#define AC97STAT_BUSY(stype) \
|
||||
((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
|
||||
|
||||
/* instance data. There can be only one, MacLeod!!!! */
|
||||
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
|
||||
@ -215,7 +215,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned long r, ro, stat;
|
||||
int chans, t, stype = SUBSTREAM_TYPE(substream);
|
||||
int chans, t, stype = substream->stream;
|
||||
|
||||
chans = params_channels(params);
|
||||
|
||||
@ -235,7 +235,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
r |= PSC_AC97CFG_SET_LEN(params->msbits);
|
||||
|
||||
/* channels: enable slots for front L/R channel */
|
||||
if (stype == PCM_TX) {
|
||||
if (stype == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
r &= ~PSC_AC97CFG_TXSLOT_MASK;
|
||||
r |= PSC_AC97CFG_TXSLOT_ENA(3);
|
||||
r |= PSC_AC97CFG_TXSLOT_ENA(4);
|
||||
@ -294,7 +294,7 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
|
||||
int ret, stype = SUBSTREAM_TYPE(substream);
|
||||
int ret, stype = substream->stream;
|
||||
|
||||
ret = 0;
|
||||
|
||||
@ -324,12 +324,21 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int au1xpsc_ac97_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
|
||||
snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
return au1xpsc_ac97_workdata ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
|
||||
.startup = au1xpsc_ac97_startup,
|
||||
.trigger = au1xpsc_ac97_trigger,
|
||||
.hw_params = au1xpsc_ac97_hw_params,
|
||||
};
|
||||
@ -379,6 +388,16 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
|
||||
if (!wd->mmio)
|
||||
goto out1;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r)
|
||||
goto out2;
|
||||
wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r)
|
||||
goto out2;
|
||||
wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
|
||||
|
||||
/* configuration: max dma trigger threshold, enable ac97 */
|
||||
wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
|
||||
PSC_AC97CFG_DE_ENABLE;
|
||||
@ -401,15 +420,13 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
|
||||
|
||||
ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
|
||||
if (ret)
|
||||
goto out1;
|
||||
goto out2;
|
||||
|
||||
wd->dmapd = au1xpsc_pcm_add(pdev);
|
||||
if (wd->dmapd) {
|
||||
au1xpsc_ac97_workdata = wd;
|
||||
return 0;
|
||||
}
|
||||
au1xpsc_ac97_workdata = wd;
|
||||
return 0;
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
out2:
|
||||
iounmap(wd->mmio);
|
||||
out1:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
out0:
|
||||
@ -422,9 +439,6 @@ 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);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
|
||||
/* disable PSC completely */
|
||||
|
@ -42,13 +42,13 @@
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
|
||||
|
||||
#define I2SSTAT_BUSY(stype) \
|
||||
((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
|
||||
#define I2SPCR_START(stype) \
|
||||
((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
|
||||
#define I2SPCR_STOP(stype) \
|
||||
((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
|
||||
#define I2SPCR_CLRFIFO(stype) \
|
||||
((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
|
||||
((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
|
||||
|
||||
|
||||
static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
|
||||
@ -240,7 +240,7 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
|
||||
int ret, stype = SUBSTREAM_TYPE(substream);
|
||||
int ret, stype = substream->stream;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@ -257,7 +257,16 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
|
||||
snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
|
||||
.startup = au1xpsc_i2s_startup,
|
||||
.trigger = au1xpsc_i2s_trigger,
|
||||
.hw_params = au1xpsc_i2s_hw_params,
|
||||
.set_fmt = au1xpsc_i2s_set_fmt,
|
||||
@ -304,6 +313,16 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
|
||||
if (!wd->mmio)
|
||||
goto out1;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!r)
|
||||
goto out2;
|
||||
wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!r)
|
||||
goto out2;
|
||||
wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
|
||||
|
||||
/* preserve PSC clock source set up by platform (dev.platform_data
|
||||
* is already occupied by soc layer)
|
||||
*/
|
||||
@ -330,15 +349,11 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, wd);
|
||||
|
||||
ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
/* finally add the DMA device for this PSC */
|
||||
wd->dmapd = au1xpsc_pcm_add(pdev);
|
||||
if (wd->dmapd)
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
out2:
|
||||
iounmap(wd->mmio);
|
||||
out1:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
out0:
|
||||
@ -351,9 +366,6 @@ 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);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
|
||||
au_writel(0, I2S_CFG(wd));
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Au12x0/Au1550 PSC ALSA ASoC audio support.
|
||||
* Alchemy ALSA ASoC audio support.
|
||||
*
|
||||
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
|
||||
* (c) 2007-2011 MSC Vertriebsges.m.b.H.,
|
||||
* Manuel Lauss <manuel.lauss@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -13,10 +13,6 @@
|
||||
#ifndef _AU1X_PCM_H
|
||||
#define _AU1X_PCM_H
|
||||
|
||||
/* DBDMA helpers */
|
||||
extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
|
||||
extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
|
||||
|
||||
struct au1xpsc_audio_data {
|
||||
void __iomem *mmio;
|
||||
|
||||
@ -27,15 +23,9 @@ struct au1xpsc_audio_data {
|
||||
|
||||
unsigned long pm[2];
|
||||
struct mutex lock;
|
||||
struct platform_device *dmapd;
|
||||
int dmaids[2];
|
||||
};
|
||||
|
||||
#define PCM_TX 0
|
||||
#define PCM_RX 1
|
||||
|
||||
#define SUBSTREAM_TYPE(substream) \
|
||||
((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
|
||||
|
||||
/* easy access macros */
|
||||
#define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
|
||||
#define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
|
||||
|
@ -27,6 +27,19 @@ config SND_SOC_BFIN_EVAL_ADAU1701
|
||||
board connected to one of the Blackfin evaluation boards like the
|
||||
BF5XX-STAMP or BF5XX-EZKIT.
|
||||
|
||||
config SND_SOC_BFIN_EVAL_ADAU1373
|
||||
tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
|
||||
depends on SND_BF5XX_I2S && I2C
|
||||
select SND_BF5XX_SOC_I2S
|
||||
select SND_SOC_ADAU1373
|
||||
help
|
||||
Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
|
||||
board connected to one of the Blackfin evaluation boards like the
|
||||
BF5XX-STAMP or BF5XX-EZKIT.
|
||||
|
||||
Note: This driver assumes that first ADAU1373 DAI is connected to the
|
||||
first SPORT port on the BF5XX board.
|
||||
|
||||
config SND_SOC_BFIN_EVAL_ADAV80X
|
||||
tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
|
||||
depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
|
||||
|
@ -21,6 +21,7 @@ snd-ad1980-objs := bf5xx-ad1980.o
|
||||
snd-ssm2602-objs := bf5xx-ssm2602.o
|
||||
snd-ad73311-objs := bf5xx-ad73311.o
|
||||
snd-ad193x-objs := bf5xx-ad193x.o
|
||||
snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
|
||||
snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
|
||||
snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
|
||||
|
||||
@ -29,5 +30,6 @@ obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
|
||||
obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
|
||||
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
|
||||
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
|
||||
obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
|
||||
|
@ -128,7 +128,7 @@ static int snd_ad73311_configure(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_probe(struct platform_device *pdev)
|
||||
static int bf5xx_probe(struct snd_soc_card *card)
|
||||
{
|
||||
int err;
|
||||
if (gpio_request(GPIO_SE, "AD73311_SE")) {
|
||||
|
202
sound/soc/blackfin/bfin-eval-adau1373.c
Normal file
202
sound/soc/blackfin/bfin-eval-adau1373.c
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Machine driver for EVAL-ADAU1373 on Analog Devices bfin
|
||||
* evaluation boards.
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "../codecs/adau1373.h"
|
||||
|
||||
static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_LINE("Line In1", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In2", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In3", NULL),
|
||||
SND_SOC_DAPM_LINE("Line In4", NULL),
|
||||
|
||||
SND_SOC_DAPM_LINE("Line Out1", NULL),
|
||||
SND_SOC_DAPM_LINE("Line Out2", NULL),
|
||||
SND_SOC_DAPM_LINE("Stereo Out", NULL),
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_HP("Earpiece", NULL),
|
||||
SND_SOC_DAPM_SPK("Speaker", NULL),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = {
|
||||
{ "AIN1L", NULL, "Line In1" },
|
||||
{ "AIN1R", NULL, "Line In1" },
|
||||
{ "AIN2L", NULL, "Line In2" },
|
||||
{ "AIN2R", NULL, "Line In2" },
|
||||
{ "AIN3L", NULL, "Line In3" },
|
||||
{ "AIN3R", NULL, "Line In3" },
|
||||
{ "AIN4L", NULL, "Line In4" },
|
||||
{ "AIN4R", NULL, "Line In4" },
|
||||
|
||||
/* MICBIAS can be connected via a jumper to the line-in jack, since w
|
||||
don't know which one is going to be used, just power both. */
|
||||
{ "Line In1", NULL, "MICBIAS1" },
|
||||
{ "Line In2", NULL, "MICBIAS1" },
|
||||
{ "Line In3", NULL, "MICBIAS1" },
|
||||
{ "Line In4", NULL, "MICBIAS1" },
|
||||
{ "Line In1", NULL, "MICBIAS2" },
|
||||
{ "Line In2", NULL, "MICBIAS2" },
|
||||
{ "Line In3", NULL, "MICBIAS2" },
|
||||
{ "Line In4", NULL, "MICBIAS2" },
|
||||
|
||||
{ "Line Out1", NULL, "LOUT1L" },
|
||||
{ "Line Out1", NULL, "LOUT1R" },
|
||||
{ "Line Out2", NULL, "LOUT2L" },
|
||||
{ "Line Out2", NULL, "LOUT2R" },
|
||||
{ "Headphone", NULL, "HPL" },
|
||||
{ "Headphone", NULL, "HPR" },
|
||||
{ "Earpiece", NULL, "EP" },
|
||||
{ "Speaker", NULL, "SPKL" },
|
||||
{ "Stereo Out", NULL, "SPKR" },
|
||||
};
|
||||
|
||||
static int bfin_eval_adau1373_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 *cpu_dai = rtd->cpu_dai;
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
int pll_rate;
|
||||
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 48000:
|
||||
case 8000:
|
||||
case 12000:
|
||||
case 16000:
|
||||
case 24000:
|
||||
case 32000:
|
||||
pll_rate = 48000 * 1024;
|
||||
break;
|
||||
case 44100:
|
||||
case 7350:
|
||||
case 11025:
|
||||
case 14700:
|
||||
case 22050:
|
||||
case 29400:
|
||||
pll_rate = 44100 * 1024;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
|
||||
ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
|
||||
SND_SOC_CLOCK_IN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
unsigned int pll_rate = 48000 * 1024;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
|
||||
ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
|
||||
SND_SOC_CLOCK_IN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static struct snd_soc_ops bfin_eval_adau1373_ops = {
|
||||
.hw_params = bfin_eval_adau1373_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bfin_eval_adau1373_dai = {
|
||||
.name = "adau1373",
|
||||
.stream_name = "adau1373",
|
||||
.cpu_dai_name = "bfin-i2s.0",
|
||||
.codec_dai_name = "adau1373-aif1",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "adau1373.0-001a",
|
||||
.ops = &bfin_eval_adau1373_ops,
|
||||
.init = bfin_eval_adau1373_codec_init,
|
||||
};
|
||||
|
||||
static struct snd_soc_card bfin_eval_adau1373 = {
|
||||
.name = "bfin-eval-adau1373",
|
||||
.dai_link = &bfin_eval_adau1373_dai,
|
||||
.num_links = 1,
|
||||
|
||||
.dapm_widgets = bfin_eval_adau1373_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets),
|
||||
.dapm_routes = bfin_eval_adau1373_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1373_dapm_routes),
|
||||
};
|
||||
|
||||
static int bfin_eval_adau1373_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &bfin_eval_adau1373;
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
return snd_soc_register_card(&bfin_eval_adau1373);
|
||||
}
|
||||
|
||||
static int __devexit bfin_eval_adau1373_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bfin_eval_adau1373_driver = {
|
||||
.driver = {
|
||||
.name = "bfin-eval-adau1373",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = bfin_eval_adau1373_probe,
|
||||
.remove = __devexit_p(bfin_eval_adau1373_remove),
|
||||
};
|
||||
|
||||
static int __init bfin_eval_adau1373_init(void)
|
||||
{
|
||||
return platform_driver_register(&bfin_eval_adau1373_driver);
|
||||
}
|
||||
module_init(bfin_eval_adau1373_init);
|
||||
|
||||
static void __exit bfin_eval_adau1373_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bfin_eval_adau1373_driver);
|
||||
}
|
||||
module_exit(bfin_eval_adau1373_exit);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:bfin-eval-adau1373");
|
@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
|
||||
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
|
||||
select SND_SOC_AD73311
|
||||
select SND_SOC_ADAU1373 if I2C
|
||||
select SND_SOC_ADAV80X
|
||||
select SND_SOC_ADS117X
|
||||
select SND_SOC_AK4104 if SPI_MASTER
|
||||
@ -139,6 +140,9 @@ config SND_SOC_ADAU1701
|
||||
select SIGMA
|
||||
tristate
|
||||
|
||||
config SND_SOC_ADAU1373
|
||||
tristate
|
||||
|
||||
config SND_SOC_ADAV80X
|
||||
tristate
|
||||
|
||||
|
@ -5,6 +5,7 @@ snd-soc-ad193x-objs := ad193x.o
|
||||
snd-soc-ad1980-objs := ad1980.o
|
||||
snd-soc-ad73311-objs := ad73311.o
|
||||
snd-soc-adau1701-objs := adau1701.o
|
||||
snd-soc-adau1373-objs := adau1373.o
|
||||
snd-soc-adav80x-objs := adav80x.o
|
||||
snd-soc-ads117x-objs := ads117x.o
|
||||
snd-soc-ak4104-objs := ak4104.o
|
||||
@ -100,6 +101,7 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
|
||||
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
|
||||
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
|
||||
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
|
||||
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
|
||||
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
|
||||
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/* codec private data */
|
||||
struct ad193x_priv {
|
||||
enum snd_soc_control_type control_type;
|
||||
struct regmap *regmap;
|
||||
int sysclk;
|
||||
};
|
||||
|
||||
@ -349,10 +349,8 @@ static int ad193x_probe(struct snd_soc_codec *codec)
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
int ret;
|
||||
|
||||
if (ad193x->control_type == SND_SOC_I2C)
|
||||
ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
|
||||
else
|
||||
ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
|
||||
codec->control_data = ad193x->regmap;
|
||||
ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
|
||||
return ret;
|
||||
@ -388,6 +386,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
|
||||
};
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
|
||||
static const struct regmap_config ad193x_spi_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.reg_bits = 16,
|
||||
.read_flag_mask = 0x09,
|
||||
.write_flag_mask = 0x08,
|
||||
};
|
||||
|
||||
static int __devinit ad193x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad193x_priv *ad193x;
|
||||
@ -397,20 +403,36 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
|
||||
if (ad193x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ad193x->regmap = regmap_init_spi(spi, &ad193x_spi_regmap_config);
|
||||
if (IS_ERR(ad193x->regmap)) {
|
||||
ret = PTR_ERR(ad193x->regmap);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, ad193x);
|
||||
ad193x->control_type = SND_SOC_SPI;
|
||||
|
||||
ret = snd_soc_register_codec(&spi->dev,
|
||||
&soc_codec_dev_ad193x, &ad193x_dai, 1);
|
||||
if (ret < 0)
|
||||
kfree(ad193x);
|
||||
goto err_regmap_exit;
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap_exit:
|
||||
regmap_exit(ad193x->regmap);
|
||||
err_free:
|
||||
kfree(ad193x);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ad193x_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ad193x_priv *ad193x = spi_get_drvdata(spi);
|
||||
|
||||
snd_soc_unregister_codec(&spi->dev);
|
||||
kfree(spi_get_drvdata(spi));
|
||||
regmap_exit(ad193x->regmap);
|
||||
kfree(ad193x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -425,6 +447,12 @@ static struct spi_driver ad193x_spi_driver = {
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
|
||||
static const struct regmap_config ad193x_i2c_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.reg_bits = 8,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id ad193x_id[] = {
|
||||
{ "ad1936", 0 },
|
||||
{ "ad1937", 0 },
|
||||
@ -442,20 +470,35 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
|
||||
if (ad193x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ad193x->regmap = regmap_init_i2c(client, &ad193x_i2c_regmap_config);
|
||||
if (IS_ERR(ad193x->regmap)) {
|
||||
ret = PTR_ERR(ad193x->regmap);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, ad193x);
|
||||
ad193x->control_type = SND_SOC_I2C;
|
||||
|
||||
ret = snd_soc_register_codec(&client->dev,
|
||||
&soc_codec_dev_ad193x, &ad193x_dai, 1);
|
||||
if (ret < 0)
|
||||
kfree(ad193x);
|
||||
goto err_regmap_exit;
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap_exit:
|
||||
regmap_exit(ad193x->regmap);
|
||||
err_free:
|
||||
kfree(ad193x);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ad193x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ad193x_priv *ad193x = i2c_get_clientdata(client);
|
||||
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
kfree(i2c_get_clientdata(client));
|
||||
regmap_exit(ad193x->regmap);
|
||||
kfree(ad193x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -9,20 +9,20 @@
|
||||
#ifndef __AD193X_H__
|
||||
#define __AD193X_H__
|
||||
|
||||
#define AD193X_PLL_CLK_CTRL0 0x800
|
||||
#define AD193X_PLL_CLK_CTRL0 0x00
|
||||
#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_PLL_CLK_CTRL1 0x01
|
||||
#define AD193X_DAC_CTRL0 0x02
|
||||
#define AD193X_DAC_POWERDOWN 0x01
|
||||
#define AD193X_DAC_SERFMT_MASK 0xC0
|
||||
#define AD193X_DAC_SERFMT_STEREO (0 << 6)
|
||||
#define AD193X_DAC_SERFMT_TDM (1 << 6)
|
||||
#define AD193X_DAC_CTRL1 0x803
|
||||
#define AD193X_DAC_CTRL1 0x03
|
||||
#define AD193X_DAC_2_CHANNELS 0
|
||||
#define AD193X_DAC_4_CHANNELS 1
|
||||
#define AD193X_DAC_8_CHANNELS 2
|
||||
@ -33,11 +33,11 @@
|
||||
#define AD193X_DAC_BCLK_MASTER (1 << 5)
|
||||
#define AD193X_DAC_LEFT_HIGH (1 << 3)
|
||||
#define AD193X_DAC_BCLK_INV (1 << 7)
|
||||
#define AD193X_DAC_CTRL2 0x804
|
||||
#define AD193X_DAC_CTRL2 0x04
|
||||
#define AD193X_DAC_WORD_LEN_SHFT 3
|
||||
#define AD193X_DAC_WORD_LEN_MASK 0x18
|
||||
#define AD193X_DAC_MASTER_MUTE 1
|
||||
#define AD193X_DAC_CHNL_MUTE 0x805
|
||||
#define AD193X_DAC_CHNL_MUTE 0x05
|
||||
#define AD193X_DACL1_MUTE 0
|
||||
#define AD193X_DACR1_MUTE 1
|
||||
#define AD193X_DACL2_MUTE 2
|
||||
@ -46,28 +46,28 @@
|
||||
#define AD193X_DACR3_MUTE 5
|
||||
#define AD193X_DACL4_MUTE 6
|
||||
#define AD193X_DACR4_MUTE 7
|
||||
#define AD193X_DAC_L1_VOL 0x806
|
||||
#define AD193X_DAC_R1_VOL 0x807
|
||||
#define AD193X_DAC_L2_VOL 0x808
|
||||
#define AD193X_DAC_R2_VOL 0x809
|
||||
#define AD193X_DAC_L3_VOL 0x80a
|
||||
#define AD193X_DAC_R3_VOL 0x80b
|
||||
#define AD193X_DAC_L4_VOL 0x80c
|
||||
#define AD193X_DAC_R4_VOL 0x80d
|
||||
#define AD193X_ADC_CTRL0 0x80e
|
||||
#define AD193X_DAC_L1_VOL 0x06
|
||||
#define AD193X_DAC_R1_VOL 0x07
|
||||
#define AD193X_DAC_L2_VOL 0x08
|
||||
#define AD193X_DAC_R2_VOL 0x09
|
||||
#define AD193X_DAC_L3_VOL 0x0a
|
||||
#define AD193X_DAC_R3_VOL 0x0b
|
||||
#define AD193X_DAC_L4_VOL 0x0c
|
||||
#define AD193X_DAC_R4_VOL 0x0d
|
||||
#define AD193X_ADC_CTRL0 0x0e
|
||||
#define AD193X_ADC_POWERDOWN 0x01
|
||||
#define AD193X_ADC_HIGHPASS_FILTER 1
|
||||
#define AD193X_ADCL1_MUTE 2
|
||||
#define AD193X_ADCR1_MUTE 3
|
||||
#define AD193X_ADCL2_MUTE 4
|
||||
#define AD193X_ADCR2_MUTE 5
|
||||
#define AD193X_ADC_CTRL1 0x80f
|
||||
#define AD193X_ADC_CTRL1 0x0f
|
||||
#define AD193X_ADC_SERFMT_MASK 0x60
|
||||
#define AD193X_ADC_SERFMT_STEREO (0 << 5)
|
||||
#define AD193X_ADC_SERFMT_TDM (1 << 5)
|
||||
#define AD193X_ADC_SERFMT_AUX (2 << 5)
|
||||
#define AD193X_ADC_WORD_LEN_MASK 0x3
|
||||
#define AD193X_ADC_CTRL2 0x810
|
||||
#define AD193X_ADC_CTRL2 0x10
|
||||
#define AD193X_ADC_2_CHANNELS 0
|
||||
#define AD193X_ADC_4_CHANNELS 1
|
||||
#define AD193X_ADC_8_CHANNELS 2
|
||||
|
@ -200,18 +200,22 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
|
||||
}
|
||||
|
||||
/* Read out vendor ID to make sure it is ad1980 */
|
||||
if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
|
||||
if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
|
||||
ret = -ENODEV;
|
||||
goto reset_err;
|
||||
}
|
||||
|
||||
vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
|
||||
|
||||
if (vendor_id2 != 0x5370) {
|
||||
if (vendor_id2 != 0x5374)
|
||||
if (vendor_id2 != 0x5374) {
|
||||
ret = -ENODEV;
|
||||
goto reset_err;
|
||||
else
|
||||
} else {
|
||||
printk(KERN_WARNING "ad1980: "
|
||||
"Found AD1981 - only 2/2 IN/OUT Channels "
|
||||
"supported\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* unmute captures and playbacks volume */
|
||||
|
1414
sound/soc/codecs/adau1373.c
Normal file
1414
sound/soc/codecs/adau1373.c
Normal file
File diff suppressed because it is too large
Load Diff
29
sound/soc/codecs/adau1373.h
Normal file
29
sound/soc/codecs/adau1373.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __ADAU1373_H__
|
||||
#define __ADAU1373_H__
|
||||
|
||||
enum adau1373_pll_src {
|
||||
ADAU1373_PLL_SRC_MCLK1 = 0,
|
||||
ADAU1373_PLL_SRC_BCLK1 = 1,
|
||||
ADAU1373_PLL_SRC_BCLK2 = 2,
|
||||
ADAU1373_PLL_SRC_BCLK3 = 3,
|
||||
ADAU1373_PLL_SRC_LRCLK1 = 4,
|
||||
ADAU1373_PLL_SRC_LRCLK2 = 5,
|
||||
ADAU1373_PLL_SRC_LRCLK3 = 6,
|
||||
ADAU1373_PLL_SRC_GPIO1 = 7,
|
||||
ADAU1373_PLL_SRC_GPIO2 = 8,
|
||||
ADAU1373_PLL_SRC_GPIO3 = 9,
|
||||
ADAU1373_PLL_SRC_GPIO4 = 10,
|
||||
ADAU1373_PLL_SRC_MCLK2 = 11,
|
||||
};
|
||||
|
||||
enum adau1373_pll {
|
||||
ADAU1373_PLL1 = 0,
|
||||
ADAU1373_PLL2 = 1,
|
||||
};
|
||||
|
||||
enum adau1373_clk_src {
|
||||
ADAU1373_CLK_SRC_PLL1 = 0,
|
||||
ADAU1373_CLK_SRC_PLL2 = 1,
|
||||
};
|
||||
|
||||
#endif
|
@ -523,7 +523,8 @@ static int adav80x_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
static int adav80x_set_sysclk(struct snd_soc_codec *codec,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
int clk_id, int source,
|
||||
unsigned int freq, int dir)
|
||||
{
|
||||
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
|
@ -41,7 +41,6 @@ MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
|
||||
struct alc5623_priv {
|
||||
enum snd_soc_control_type control_type;
|
||||
void *control_data;
|
||||
struct mutex mutex;
|
||||
u8 id;
|
||||
unsigned int sysclk;
|
||||
u16 reg_cache[ALC5623_VENDOR_ID2+2];
|
||||
@ -1052,7 +1051,6 @@ static int alc5623_i2c_probe(struct i2c_client *client,
|
||||
i2c_set_clientdata(client, alc5623);
|
||||
alc5623->control_data = client;
|
||||
alc5623->control_type = SND_SOC_I2C;
|
||||
mutex_init(&alc5623->mutex);
|
||||
|
||||
ret = snd_soc_register_codec(&client->dev,
|
||||
&soc_codec_device_alc5623, &alc5623_dai, 1);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -1436,10 +1437,17 @@ static const struct i2c_device_id sgtl5000_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
|
||||
|
||||
static const struct of_device_id sgtl5000_dt_ids[] = {
|
||||
{ .compatible = "fsl,sgtl5000", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
|
||||
|
||||
static struct i2c_driver sgtl5000_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "sgtl5000",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sgtl5000_dt_ids,
|
||||
},
|
||||
.probe = sgtl5000_i2c_probe,
|
||||
.remove = __devexit_p(sgtl5000_i2c_remove),
|
||||
|
@ -79,7 +79,7 @@ static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
|
||||
*/
|
||||
static int find_free_channel(struct snd_soc_codec *sn95031_codec)
|
||||
{
|
||||
int ret = 0, i, value;
|
||||
int i, value;
|
||||
|
||||
/* check whether ADC is enabled */
|
||||
value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
|
||||
@ -91,12 +91,10 @@ static int find_free_channel(struct snd_soc_codec *sn95031_codec)
|
||||
for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
|
||||
value = snd_soc_read(sn95031_codec,
|
||||
SN95031_ADC_CHNL_START_ADDR + i);
|
||||
if (value & SN95031_STOPBIT_MASK) {
|
||||
ret = i;
|
||||
if (value & SN95031_STOPBIT_MASK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
|
||||
return (i == SN95031_ADC_CHANLS_MAX) ? (-EINVAL) : i;
|
||||
}
|
||||
|
||||
/* Initialize the ADC for reading micbias values. Can sleep. */
|
||||
@ -660,7 +658,7 @@ static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
static int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned int format, rate;
|
||||
|
@ -294,7 +294,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
|
||||
struct i2c_client *i2c = codec->control_data;
|
||||
struct snd_pcm_runtime *master_runtime;
|
||||
|
||||
/* The DAI has shared clocks so if we already have a playback or
|
||||
@ -303,7 +302,7 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
|
||||
*/
|
||||
if (ssm2602->master_substream) {
|
||||
master_runtime = ssm2602->master_substream->runtime;
|
||||
dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
|
||||
dev_dbg(codec->dev, "Constraining to %d bits at %dHz\n",
|
||||
master_runtime->sample_bits,
|
||||
master_runtime->rate);
|
||||
|
||||
|
@ -524,13 +524,17 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
|
||||
rate = params_rate(params);
|
||||
pr_debug("rate: %u\n", rate);
|
||||
for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++)
|
||||
if (interpolation_ratios[i].fs == rate)
|
||||
if (interpolation_ratios[i].fs == rate) {
|
||||
ir = interpolation_ratios[i].ir;
|
||||
break;
|
||||
}
|
||||
if (ir < 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; mclk_ratios[ir][i].ratio; i++)
|
||||
if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk)
|
||||
if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk) {
|
||||
mcs = mclk_ratios[ir][i].mcs;
|
||||
break;
|
||||
}
|
||||
if (mcs < 0)
|
||||
return -EINVAL;
|
||||
|
||||
@ -808,6 +812,7 @@ static int sta32x_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
|
||||
regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
|
||||
|
||||
@ -867,18 +872,8 @@ static __devinit int sta32x_i2c_probe(struct i2c_client *i2c,
|
||||
static __devexit int sta32x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sta32x_priv *sta32x = i2c_get_clientdata(client);
|
||||
struct snd_soc_codec *codec = sta32x->codec;
|
||||
|
||||
if (codec)
|
||||
sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
|
||||
|
||||
if (codec) {
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
snd_soc_codec_set_drvdata(codec, NULL);
|
||||
}
|
||||
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
kfree(sta32x);
|
||||
return 0;
|
||||
}
|
||||
|
@ -446,7 +446,6 @@ err_regulator:
|
||||
gpio_free(data->power_gpio);
|
||||
err_gpio:
|
||||
kfree(data);
|
||||
i2c_set_clientdata(tpa6130a2_client, NULL);
|
||||
tpa6130a2_client = NULL;
|
||||
|
||||
return ret;
|
||||
|
@ -118,8 +118,8 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
|
||||
0x4A, /* TWL6040_LPPLLDIV 0x09 */
|
||||
0x00, /* TWL6040_AMICBCTL 0x0A */
|
||||
0x00, /* TWL6040_DMICBCTL 0x0B */
|
||||
0x18, /* TWL6040_MICLCTL 0x0C - No input selected on Left Mic */
|
||||
0x18, /* TWL6040_MICRCTL 0x0D - No input selected on Right Mic */
|
||||
0x00, /* TWL6040_MICLCTL 0x0C */
|
||||
0x00, /* TWL6040_MICRCTL 0x0D */
|
||||
0x00, /* TWL6040_MICGAIN 0x0E */
|
||||
0x1B, /* TWL6040_LINEGAIN 0x0F */
|
||||
0x00, /* TWL6040_HSLCTL 0x10 */
|
||||
@ -155,41 +155,8 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
|
||||
0x00, /* TWL6040_STATUS (ro) 0x2E */
|
||||
};
|
||||
|
||||
/*
|
||||
* twl6040 vio/gnd registers:
|
||||
* registers under vio/gnd supply can be accessed
|
||||
* before the power-up sequence, after NRESPWRON goes high
|
||||
*/
|
||||
static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = {
|
||||
TWL6040_REG_ASICID,
|
||||
TWL6040_REG_ASICREV,
|
||||
TWL6040_REG_INTID,
|
||||
TWL6040_REG_INTMR,
|
||||
TWL6040_REG_NCPCTL,
|
||||
TWL6040_REG_LDOCTL,
|
||||
TWL6040_REG_AMICBCTL,
|
||||
TWL6040_REG_DMICBCTL,
|
||||
TWL6040_REG_HKCTL1,
|
||||
TWL6040_REG_HKCTL2,
|
||||
TWL6040_REG_GPOCTL,
|
||||
TWL6040_REG_TRIM1,
|
||||
TWL6040_REG_TRIM2,
|
||||
TWL6040_REG_TRIM3,
|
||||
TWL6040_REG_HSOTRIM,
|
||||
TWL6040_REG_HFOTRIM,
|
||||
TWL6040_REG_ACCCTL,
|
||||
TWL6040_REG_STATUS,
|
||||
};
|
||||
|
||||
/*
|
||||
* twl6040 vdd/vss registers:
|
||||
* registers under vdd/vss supplies can only be accessed
|
||||
* after the power-up sequence
|
||||
*/
|
||||
static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
|
||||
TWL6040_REG_HPPLLCTL,
|
||||
TWL6040_REG_LPPLLCTL,
|
||||
TWL6040_REG_LPPLLDIV,
|
||||
/* List of registers to be restored after power up */
|
||||
static const int twl6040_restore_list[] = {
|
||||
TWL6040_REG_MICLCTL,
|
||||
TWL6040_REG_MICRCTL,
|
||||
TWL6040_REG_MICGAIN,
|
||||
@ -202,12 +169,6 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
|
||||
TWL6040_REG_HFLGAIN,
|
||||
TWL6040_REG_HFRCTL,
|
||||
TWL6040_REG_HFRGAIN,
|
||||
TWL6040_REG_VIBCTLL,
|
||||
TWL6040_REG_VIBDATL,
|
||||
TWL6040_REG_VIBCTLR,
|
||||
TWL6040_REG_VIBDATR,
|
||||
TWL6040_REG_ALB,
|
||||
TWL6040_REG_DLB,
|
||||
};
|
||||
|
||||
/* set of rates for each pll: low-power and high-performance */
|
||||
@ -296,56 +257,27 @@ static int twl6040_write(struct snd_soc_codec *codec,
|
||||
return twl6040_reg_write(twl6040, reg, value);
|
||||
}
|
||||
|
||||
static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
|
||||
static void twl6040_init_chip(struct snd_soc_codec *codec)
|
||||
{
|
||||
u8 *cache = codec->reg_cache;
|
||||
int reg, i;
|
||||
struct twl6040 *twl6040 = codec->control_data;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < TWL6040_VIOREGNUM; i++) {
|
||||
reg = twl6040_vio_reg[i];
|
||||
/*
|
||||
* skip read-only registers (ASICID, ASICREV, STATUS)
|
||||
* and registers shared among MFD children
|
||||
*/
|
||||
switch (reg) {
|
||||
case TWL6040_REG_ASICID:
|
||||
case TWL6040_REG_ASICREV:
|
||||
case TWL6040_REG_INTID:
|
||||
case TWL6040_REG_INTMR:
|
||||
case TWL6040_REG_NCPCTL:
|
||||
case TWL6040_REG_LDOCTL:
|
||||
case TWL6040_REG_GPOCTL:
|
||||
case TWL6040_REG_ACCCTL:
|
||||
case TWL6040_REG_STATUS:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
twl6040_write(codec, reg, cache[reg]);
|
||||
}
|
||||
val = twl6040_get_revid(twl6040);
|
||||
twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val);
|
||||
|
||||
/* Change chip defaults */
|
||||
/* No imput selected for microphone amplifiers */
|
||||
twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18);
|
||||
twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18);
|
||||
}
|
||||
|
||||
static void twl6040_init_vdd_regs(struct snd_soc_codec *codec)
|
||||
static void twl6040_restore_regs(struct snd_soc_codec *codec)
|
||||
{
|
||||
u8 *cache = codec->reg_cache;
|
||||
int reg, i;
|
||||
|
||||
for (i = 0; i < TWL6040_VDDREGNUM; i++) {
|
||||
reg = twl6040_vdd_reg[i];
|
||||
/* skip vibra and PLL registers */
|
||||
switch (reg) {
|
||||
case TWL6040_REG_VIBCTLL:
|
||||
case TWL6040_REG_VIBDATL:
|
||||
case TWL6040_REG_VIBCTLR:
|
||||
case TWL6040_REG_VIBDATR:
|
||||
case TWL6040_REG_HPPLLCTL:
|
||||
case TWL6040_REG_LPPLLCTL:
|
||||
case TWL6040_REG_LPPLLDIV:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(twl6040_restore_list); i++) {
|
||||
reg = twl6040_restore_list[i];
|
||||
twl6040_write(codec, reg, cache[reg]);
|
||||
}
|
||||
}
|
||||
@ -1325,8 +1257,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
|
||||
|
||||
priv->codec_powered = 1;
|
||||
|
||||
/* initialize vdd/vss registers with reg_cache */
|
||||
twl6040_init_vdd_regs(codec);
|
||||
twl6040_restore_regs(codec);
|
||||
|
||||
/* Set external boost GPO */
|
||||
twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
|
||||
@ -1468,7 +1399,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
.playback = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.channels_max = 5,
|
||||
.rates = TWL6040_RATES,
|
||||
.formats = TWL6040_FORMATS,
|
||||
},
|
||||
@ -1518,8 +1449,8 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
|
||||
.name = "twl6040-vib",
|
||||
.playback = {
|
||||
.stream_name = "Vibra Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
||||
.formats = TWL6040_FORMATS,
|
||||
},
|
||||
@ -1620,8 +1551,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
|
||||
goto plugirq_err;
|
||||
}
|
||||
|
||||
/* init vio registers */
|
||||
twl6040_init_vio_regs(codec);
|
||||
twl6040_init_chip(codec);
|
||||
|
||||
/* power on device */
|
||||
ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
@ -56,8 +56,26 @@ static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
|
||||
};
|
||||
|
||||
static int __devinit wm1250_ev1_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *i2c_id)
|
||||
{
|
||||
int id, board, rev;
|
||||
|
||||
board = i2c_smbus_read_byte_data(i2c, 0);
|
||||
if (board < 0) {
|
||||
dev_err(&i2c->dev, "Failed to read ID: %d\n", board);
|
||||
return board;
|
||||
}
|
||||
|
||||
id = (board & 0xfe) >> 2;
|
||||
rev = board & 0x3;
|
||||
|
||||
if (id != 1) {
|
||||
dev_err(&i2c->dev, "Unknown board ID %d\n", id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&i2c->dev, "revision %d\n", rev + 1);
|
||||
|
||||
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
|
||||
&wm1250_ev1_dai, 1);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -598,6 +599,11 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8510 = {
|
||||
.reg_cache_default =wm8510_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8510_of_match[] = {
|
||||
{ .compatible = "wlf,wm8510" },
|
||||
{ },
|
||||
};
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8510_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -628,6 +634,7 @@ static struct spi_driver wm8510_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8510",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8510_of_match,
|
||||
},
|
||||
.probe = wm8510_spi_probe,
|
||||
.remove = __devexit_p(wm8510_spi_remove),
|
||||
@ -671,6 +678,7 @@ static struct i2c_driver wm8510_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8510-codec",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8510_of_match,
|
||||
},
|
||||
.probe = wm8510_i2c_probe,
|
||||
.remove = __devexit_p(wm8510_i2c_remove),
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -84,7 +85,7 @@ static const char *wm8523_zd_count_text[] = {
|
||||
static const struct soc_enum wm8523_zc_count =
|
||||
SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text);
|
||||
|
||||
static const struct snd_kcontrol_new wm8523_snd_controls[] = {
|
||||
static const struct snd_kcontrol_new wm8523_controls[] = {
|
||||
SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
|
||||
0, 448, 0, dac_tlv),
|
||||
SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0),
|
||||
@ -101,22 +102,11 @@ SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
|
||||
SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route intercon[] = {
|
||||
static const struct snd_soc_dapm_route wm8523_dapm_routes[] = {
|
||||
{ "LINEVOUTL", NULL, "DAC" },
|
||||
{ "LINEVOUTR", NULL, "DAC" },
|
||||
};
|
||||
|
||||
static int wm8523_add_widgets(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
|
||||
snd_soc_dapm_new_controls(dapm, wm8523_dapm_widgets,
|
||||
ARRAY_SIZE(wm8523_dapm_widgets));
|
||||
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct {
|
||||
int value;
|
||||
int ratio;
|
||||
@ -479,10 +469,6 @@ static int wm8523_probe(struct snd_soc_codec *codec)
|
||||
/* Bias level configuration will have done an extra enable */
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
|
||||
|
||||
snd_soc_add_controls(codec, wm8523_snd_controls,
|
||||
ARRAY_SIZE(wm8523_snd_controls));
|
||||
wm8523_add_widgets(codec);
|
||||
|
||||
return 0;
|
||||
|
||||
err_enable:
|
||||
@ -512,6 +498,18 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8523 = {
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_default = wm8523_reg,
|
||||
.volatile_register = wm8523_volatile_register,
|
||||
|
||||
.controls = wm8523_controls,
|
||||
.num_controls = ARRAY_SIZE(wm8523_controls),
|
||||
.dapm_widgets = wm8523_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets),
|
||||
.dapm_routes = wm8523_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes),
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8523_of_match[] = {
|
||||
{ .compatible = "wlf,wm8523" },
|
||||
{ },
|
||||
};
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
@ -551,8 +549,9 @@ MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8523_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8523-codec",
|
||||
.name = "wm8523",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8523_of_match,
|
||||
},
|
||||
.probe = wm8523_i2c_probe,
|
||||
.remove = __devexit_p(wm8523_i2c_remove),
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -907,6 +908,11 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8580 = {
|
||||
.reg_cache_default = wm8580_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8580_of_match[] = {
|
||||
{ .compatible = "wlf,wm8580" },
|
||||
{ },
|
||||
};
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
static int wm8580_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
@ -943,8 +949,9 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8580_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8580-codec",
|
||||
.name = "wm8580",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8580_of_match,
|
||||
},
|
||||
.probe = wm8580_i2c_probe,
|
||||
.remove = wm8580_i2c_remove,
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -414,6 +415,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
|
||||
.num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8711_of_match[] = {
|
||||
{ .compatible = "wlf,wm8711", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8711_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8711_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -443,8 +450,9 @@ static int __devexit wm8711_spi_remove(struct spi_device *spi)
|
||||
|
||||
static struct spi_driver wm8711_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8711-codec",
|
||||
.name = "wm8711",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8711_of_match,
|
||||
},
|
||||
.probe = wm8711_spi_probe,
|
||||
.remove = __devexit_p(wm8711_spi_remove),
|
||||
@ -487,8 +495,9 @@ MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8711_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8711-codec",
|
||||
.name = "wm8711",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8711_of_match,
|
||||
},
|
||||
.probe = wm8711_i2c_probe,
|
||||
.remove = __devexit_p(wm8711_i2c_remove),
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -269,6 +270,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
|
||||
.num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8728_of_match[] = {
|
||||
{ .compatible = "wlf,wm8728", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8728_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8728_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -298,8 +305,9 @@ static int __devexit wm8728_spi_remove(struct spi_device *spi)
|
||||
|
||||
static struct spi_driver wm8728_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8728-codec",
|
||||
.name = "wm8728",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8728_of_match,
|
||||
},
|
||||
.probe = wm8728_spi_probe,
|
||||
.remove = __devexit_p(wm8728_spi_remove),
|
||||
@ -342,8 +350,9 @@ MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8728_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8728-codec",
|
||||
.name = "wm8728",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8728_of_match,
|
||||
},
|
||||
.probe = wm8728_i2c_probe,
|
||||
.remove = __devexit_p(wm8728_i2c_remove),
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -607,6 +608,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
|
||||
.num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8731_of_match[] = {
|
||||
{ .compatible = "wlf,wm8731", },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, wm8731_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8731_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -638,6 +646,7 @@ static struct spi_driver wm8731_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8731",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8731_of_match,
|
||||
},
|
||||
.probe = wm8731_spi_probe,
|
||||
.remove = __devexit_p(wm8731_spi_remove),
|
||||
@ -682,6 +691,7 @@ static struct i2c_driver wm8731_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8731",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8731_of_match,
|
||||
},
|
||||
.probe = wm8731_i2c_probe,
|
||||
.remove = __devexit_p(wm8731_i2c_remove),
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -634,6 +635,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8737 = {
|
||||
.reg_cache_default = wm8737_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8737_of_match[] = {
|
||||
{ .compatible = "wlf,wm8737", },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, wm8737_of_match);
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
static __devinit int wm8737_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
@ -673,6 +681,7 @@ static struct i2c_driver wm8737_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8737",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8737_of_match,
|
||||
},
|
||||
.probe = wm8737_i2c_probe,
|
||||
.remove = __devexit_p(wm8737_i2c_remove),
|
||||
@ -711,6 +720,7 @@ static struct spi_driver wm8737_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8737",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8737_of_match,
|
||||
},
|
||||
.probe = wm8737_spi_probe,
|
||||
.remove = __devexit_p(wm8737_spi_remove),
|
||||
|
@ -17,9 +17,11 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -422,17 +424,35 @@ static int wm8741_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
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 = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
|
||||
return ret;
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
ret = wm8741_reset(codec);
|
||||
if (ret < 0) {
|
||||
dev_err(codec->dev, "Failed to issue reset\n");
|
||||
return ret;
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
/* Change some default settings - latch VU */
|
||||
@ -451,58 +471,61 @@ static int wm8741_probe(struct snd_soc_codec *codec)
|
||||
|
||||
dev_dbg(codec->dev, "Successful registration\n");
|
||||
return ret;
|
||||
|
||||
err_enable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
|
||||
err_get:
|
||||
regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8741_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
|
||||
regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
|
||||
.probe = wm8741_probe,
|
||||
.remove = wm8741_remove,
|
||||
.resume = wm8741_resume,
|
||||
.reg_cache_size = ARRAY_SIZE(wm8741_reg_defaults),
|
||||
.reg_word_size = sizeof(u16),
|
||||
.reg_cache_default = wm8741_reg_defaults,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8741_of_match[] = {
|
||||
{ .compatible = "wlf,wm8741", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8741_of_match);
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
static int wm8741_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct wm8741_priv *wm8741;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
|
||||
if (wm8741 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
|
||||
wm8741->supplies[i].supply = wm8741_supply_names[i];
|
||||
|
||||
ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies),
|
||||
wm8741->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&i2c->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(&i2c->dev, "Failed to enable supplies: %d\n", ret);
|
||||
goto err_get;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, wm8741);
|
||||
wm8741->control_type = SND_SOC_I2C;
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&soc_codec_dev_wm8741, &wm8741_dai, 1);
|
||||
if (ret < 0)
|
||||
goto err_enable;
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&soc_codec_dev_wm8741, &wm8741_dai, 1);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
||||
return ret;
|
||||
|
||||
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;
|
||||
@ -510,10 +533,7 @@ err:
|
||||
|
||||
static int wm8741_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct wm8741_priv *wm8741 = i2c_get_clientdata(client);
|
||||
|
||||
snd_soc_unregister_codec(&client->dev);
|
||||
regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
|
||||
kfree(i2c_get_clientdata(client));
|
||||
return 0;
|
||||
}
|
||||
@ -526,8 +546,9 @@ MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8741_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8741-codec",
|
||||
.name = "wm8741",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8741_of_match,
|
||||
},
|
||||
.probe = wm8741_i2c_probe,
|
||||
.remove = wm8741_i2c_remove,
|
||||
@ -535,6 +556,44 @@ static struct i2c_driver wm8741_i2c_driver = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8741_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct wm8741_priv *wm8741;
|
||||
int ret;
|
||||
|
||||
wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
|
||||
if (wm8741 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
wm8741->control_type = SND_SOC_SPI;
|
||||
spi_set_drvdata(spi, wm8741);
|
||||
|
||||
ret = snd_soc_register_codec(&spi->dev,
|
||||
&soc_codec_dev_wm8741, &wm8741_dai, 1);
|
||||
if (ret < 0)
|
||||
kfree(wm8741);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit wm8741_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
snd_soc_unregister_codec(&spi->dev);
|
||||
kfree(spi_get_drvdata(spi));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver wm8741_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8741",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8741_of_match,
|
||||
},
|
||||
.probe = wm8741_spi_probe,
|
||||
.remove = __devexit_p(wm8741_spi_remove),
|
||||
};
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
|
||||
static int __init wm8741_modinit(void)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -544,6 +603,13 @@ static int __init wm8741_modinit(void)
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8741 I2C driver: %d\n", ret);
|
||||
#endif
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
ret = spi_register_driver(&wm8741_spi_driver);
|
||||
if (ret != 0) {
|
||||
printk(KERN_ERR "Failed to register wm8741 SPI driver: %d\n",
|
||||
ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -551,6 +617,9 @@ module_init(wm8741_modinit);
|
||||
|
||||
static void __exit wm8741_exit(void)
|
||||
{
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
spi_unregister_driver(&wm8741_spi_driver);
|
||||
#endif
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
i2c_del_driver(&wm8741_i2c_driver);
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -751,6 +752,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
|
||||
.reg_cache_default = wm8750_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8750_of_match[] = {
|
||||
{ .compatible = "wlf,wm8750", },
|
||||
{ .compatible = "wlf,wm8987", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8750_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8750_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -787,8 +795,9 @@ MODULE_DEVICE_TABLE(spi, wm8750_spi_ids);
|
||||
|
||||
static struct spi_driver wm8750_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8750-codec",
|
||||
.name = "wm8750",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8750_of_match,
|
||||
},
|
||||
.id_table = wm8750_spi_ids,
|
||||
.probe = wm8750_spi_probe,
|
||||
@ -833,8 +842,9 @@ MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8750_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8750-codec",
|
||||
.name = "wm8750",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8750_of_match,
|
||||
},
|
||||
.probe = wm8750_i2c_probe,
|
||||
.remove = __devexit_p(wm8750_i2c_remove),
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
@ -1490,6 +1491,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8753 = {
|
||||
.reg_cache_default = wm8753_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8753_of_match[] = {
|
||||
{ .compatible = "wlf,wm8753", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8753_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8753_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -1519,8 +1526,9 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
|
||||
|
||||
static struct spi_driver wm8753_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8753-codec",
|
||||
.name = "wm8753",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8753_of_match,
|
||||
},
|
||||
.probe = wm8753_spi_probe,
|
||||
.remove = __devexit_p(wm8753_spi_remove),
|
||||
@ -1563,8 +1571,9 @@ MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8753_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8753-codec",
|
||||
.name = "wm8753",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8753_of_match,
|
||||
},
|
||||
.probe = wm8753_i2c_probe,
|
||||
.remove = __devexit_p(wm8753_i2c_remove),
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
@ -684,6 +685,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8770 = {
|
||||
.reg_cache_default = wm8770_reg_defs
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8770_of_match[] = {
|
||||
{ .compatible = "wlf,wm8770", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8770_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8770_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -715,6 +722,7 @@ static struct spi_driver wm8770_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8770",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8770_of_match,
|
||||
},
|
||||
.probe = wm8770_spi_probe,
|
||||
.remove = __devexit_p(wm8770_spi_remove)
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
@ -215,8 +216,6 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
|
||||
int ratio_shift, master;
|
||||
int i;
|
||||
|
||||
iface = 0;
|
||||
|
||||
switch (dai->driver->id) {
|
||||
case WM8776_DAI_DAC:
|
||||
iface_reg = WM8776_DACIFCTRL;
|
||||
@ -232,20 +231,23 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/* Set word length */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
switch (snd_pcm_format_width(params_format(params))) {
|
||||
case 16:
|
||||
iface = 0;
|
||||
case 20:
|
||||
iface = 0x10;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S20_3LE:
|
||||
iface |= 0x10;
|
||||
case 24:
|
||||
iface = 0x20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
iface |= 0x20;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
iface |= 0x30;
|
||||
case 32:
|
||||
iface = 0x30;
|
||||
break;
|
||||
default:
|
||||
dev_err(codec->dev, "Unsupported sample size: %i\n",
|
||||
snd_pcm_format_width(params_format(params)));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only need to set MCLK/LRCLK ratio if we're master */
|
||||
@ -320,11 +322,6 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WM8776_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000)
|
||||
|
||||
|
||||
#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
@ -349,7 +346,9 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
|
||||
.stream_name = "Playback",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = WM8776_RATES,
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
||||
.rate_min = 32000,
|
||||
.rate_max = 192000,
|
||||
.formats = WM8776_FORMATS,
|
||||
},
|
||||
.ops = &wm8776_dac_ops,
|
||||
@ -361,7 +360,9 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
|
||||
.stream_name = "Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = WM8776_RATES,
|
||||
.rates = SNDRV_PCM_RATE_CONTINUOUS,
|
||||
.rate_min = 32000,
|
||||
.rate_max = 96000,
|
||||
.formats = WM8776_FORMATS,
|
||||
},
|
||||
.ops = &wm8776_adc_ops,
|
||||
@ -452,6 +453,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
|
||||
.reg_cache_default = wm8776_reg,
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8776_of_match[] = {
|
||||
{ .compatible = "wlf,wm8776", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8776_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8776_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -481,8 +488,9 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi)
|
||||
|
||||
static struct spi_driver wm8776_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8776-codec",
|
||||
.name = "wm8776",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8776_of_match,
|
||||
},
|
||||
.probe = wm8776_spi_probe,
|
||||
.remove = __devexit_p(wm8776_spi_remove),
|
||||
@ -525,8 +533,9 @@ MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
|
||||
|
||||
static struct i2c_driver wm8776_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8776-codec",
|
||||
.name = "wm8776",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8776_of_match,
|
||||
},
|
||||
.probe = wm8776_i2c_probe,
|
||||
.remove = __devexit_p(wm8776_i2c_remove),
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
@ -717,6 +718,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
|
||||
.volatile_register = wm8804_volatile
|
||||
};
|
||||
|
||||
static const struct of_device_id wm8804_of_match[] = {
|
||||
{ .compatible = "wlf,wm8804", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wm8804_of_match);
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
static int __devinit wm8804_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -748,6 +755,7 @@ static struct spi_driver wm8804_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8804",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8804_of_match,
|
||||
},
|
||||
.probe = wm8804_spi_probe,
|
||||
.remove = __devexit_p(wm8804_spi_remove)
|
||||
@ -792,6 +800,7 @@ static struct i2c_driver wm8804_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm8804",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wm8804_of_match,
|
||||
},
|
||||
.probe = wm8804_i2c_probe,
|
||||
.remove = __devexit_p(wm8804_i2c_remove),
|
||||
|
@ -63,6 +63,8 @@ struct wm8962_priv {
|
||||
int fll_fref;
|
||||
int fll_fout;
|
||||
|
||||
u16 dsp2_ena;
|
||||
|
||||
struct delayed_work mic_work;
|
||||
struct snd_soc_jack *jack;
|
||||
|
||||
@ -837,7 +839,7 @@ static const struct wm8962_reg_access {
|
||||
[40] = { 0x00FF, 0x01FF, 0x0000 }, /* R40 - SPKOUTL volume */
|
||||
[41] = { 0x00FF, 0x01FF, 0x0000 }, /* R41 - SPKOUTR volume */
|
||||
|
||||
[47] = { 0x000F, 0x0000, 0x0000 }, /* R47 - Thermal Shutdown Status */
|
||||
[47] = { 0x000F, 0x0000, 0xFFFF }, /* R47 - Thermal Shutdown Status */
|
||||
[48] = { 0x7EC7, 0x7E07, 0xFFFF }, /* R48 - Additional Control (4) */
|
||||
[49] = { 0x00D3, 0x00D7, 0xFFFF }, /* R49 - Class D Control 1 */
|
||||
[51] = { 0x0047, 0x0047, 0x0000 }, /* R51 - Class D Control 2 */
|
||||
@ -965,7 +967,7 @@ static const struct wm8962_reg_access {
|
||||
[584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */
|
||||
[586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */
|
||||
[768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */
|
||||
[1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037 - DSP2_ExecControl */
|
||||
[1037] = { 0x0000, 0x003F, 0xFFFF }, /* R1037 - DSP2_ExecControl */
|
||||
[4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */
|
||||
[4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */
|
||||
[4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */
|
||||
@ -1986,6 +1988,122 @@ static const unsigned int classd_tlv[] = {
|
||||
};
|
||||
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
||||
|
||||
static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
|
||||
{
|
||||
u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME);
|
||||
u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME);
|
||||
u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1);
|
||||
|
||||
/* Mute the ADCs and DACs */
|
||||
snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0);
|
||||
snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
|
||||
snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
|
||||
WM8962_DAC_MUTE, WM8962_DAC_MUTE);
|
||||
|
||||
snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
|
||||
|
||||
/* Restore the ADCs and DACs */
|
||||
snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl);
|
||||
snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr);
|
||||
snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
|
||||
WM8962_DAC_MUTE, dac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8962_dsp2_start(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
wm8962_dsp2_write_config(codec);
|
||||
|
||||
snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
|
||||
|
||||
wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
|
||||
{
|
||||
wm8962_dsp2_set_enable(codec, 0);
|
||||
|
||||
snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WM8962_DSP2_ENABLE(xname, xshift) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = wm8962_dsp2_ena_info, \
|
||||
.get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \
|
||||
.private_value = xshift }
|
||||
|
||||
static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int shift = kcontrol->private_value;
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
int shift = kcontrol->private_value;
|
||||
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
int old = wm8962->dsp2_ena;
|
||||
int ret = 0;
|
||||
int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
|
||||
WM8962_DSP2_ENA;
|
||||
|
||||
mutex_lock(&codec->mutex);
|
||||
|
||||
if (ucontrol->value.integer.value[0])
|
||||
wm8962->dsp2_ena |= 1 << shift;
|
||||
else
|
||||
wm8962->dsp2_ena &= ~(1 << shift);
|
||||
|
||||
if (wm8962->dsp2_ena == old)
|
||||
goto out;
|
||||
|
||||
ret = 1;
|
||||
|
||||
if (dsp2_running) {
|
||||
if (wm8962->dsp2_ena)
|
||||
wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
|
||||
else
|
||||
wm8962_dsp2_stop(codec);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&codec->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The VU bits for the headphones are in a different register to the mute
|
||||
* bits and only take effect on the PGA if it is actually powered.
|
||||
*/
|
||||
@ -2049,6 +2167,14 @@ static const char *cap_hpf_mode_text[] = {
|
||||
static const struct soc_enum cap_hpf_mode =
|
||||
SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
|
||||
|
||||
|
||||
static const char *cap_lhpf_mode_text[] = {
|
||||
"LPF", "HPF"
|
||||
};
|
||||
|
||||
static const struct soc_enum cap_lhpf_mode =
|
||||
SOC_ENUM_SINGLE(WM8962_LHPF1, 1, 2, cap_lhpf_mode_text);
|
||||
|
||||
static const struct snd_kcontrol_new wm8962_snd_controls[] = {
|
||||
SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
|
||||
|
||||
@ -2077,6 +2203,8 @@ SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
|
||||
SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
|
||||
SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
|
||||
SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
|
||||
SOC_SINGLE("Capture LHPF Switch", WM8962_LHPF1, 0, 1, 0),
|
||||
SOC_ENUM("Capture LHPF Mode", cap_lhpf_mode),
|
||||
|
||||
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
|
||||
WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
|
||||
@ -2134,6 +2262,11 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
|
||||
WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
|
||||
SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
|
||||
WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
|
||||
|
||||
WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
|
||||
WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
|
||||
WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
|
||||
WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
|
||||
@ -2395,6 +2528,31 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
|
||||
}
|
||||
}
|
||||
|
||||
static int dsp2_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
if (wm8962->dsp2_ena)
|
||||
wm8962_dsp2_start(codec);
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
if (wm8962->dsp2_ena)
|
||||
wm8962_dsp2_stop(codec);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *st_text[] = { "None", "Right", "Left" };
|
||||
|
||||
static const struct soc_enum str_enum =
|
||||
@ -2517,6 +2675,9 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event,
|
||||
SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT,
|
||||
WM8962_DSP2_ENA_SHIFT, 0, dsp2_event,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
|
||||
SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
|
||||
inpgal, ARRAY_SIZE(inpgal)),
|
||||
@ -2612,11 +2773,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
|
||||
{ "ADCL", NULL, "TOCLK" },
|
||||
{ "ADCL", NULL, "MIXINL" },
|
||||
{ "ADCL", NULL, "DMIC" },
|
||||
{ "ADCL", NULL, "DSP2" },
|
||||
|
||||
{ "ADCR", NULL, "SYSCLK" },
|
||||
{ "ADCR", NULL, "TOCLK" },
|
||||
{ "ADCR", NULL, "MIXINR" },
|
||||
{ "ADCR", NULL, "DMIC" },
|
||||
{ "ADCR", NULL, "DSP2" },
|
||||
|
||||
{ "STL", "Left", "ADCL" },
|
||||
{ "STL", "Right", "ADCR" },
|
||||
@ -2628,11 +2791,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
|
||||
{ "DACL", NULL, "TOCLK" },
|
||||
{ "DACL", NULL, "Beep" },
|
||||
{ "DACL", NULL, "STL" },
|
||||
{ "DACL", NULL, "DSP2" },
|
||||
|
||||
{ "DACR", NULL, "SYSCLK" },
|
||||
{ "DACR", NULL, "TOCLK" },
|
||||
{ "DACR", NULL, "Beep" },
|
||||
{ "DACR", NULL, "STR" },
|
||||
{ "DACR", NULL, "DSP2" },
|
||||
|
||||
{ "HPMIXL", "IN4L Switch", "IN4L" },
|
||||
{ "HPMIXL", "IN4R Switch", "IN4R" },
|
||||
@ -3403,12 +3568,16 @@ static irqreturn_t wm8962_irq(int irq, void *data)
|
||||
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
|
||||
int mask;
|
||||
int active;
|
||||
int reg;
|
||||
|
||||
mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2_MASK);
|
||||
|
||||
active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
|
||||
active &= ~mask;
|
||||
|
||||
if (!active)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Acknowledge the interrupts */
|
||||
snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
|
||||
|
||||
@ -3420,9 +3589,21 @@ static irqreturn_t wm8962_irq(int irq, void *data)
|
||||
if (active & WM8962_FIFOS_ERR_EINT)
|
||||
dev_err(codec->dev, "FIFO error\n");
|
||||
|
||||
if (active & WM8962_TEMP_SHUT_EINT)
|
||||
if (active & WM8962_TEMP_SHUT_EINT) {
|
||||
dev_crit(codec->dev, "Thermal shutdown\n");
|
||||
|
||||
reg = snd_soc_read(codec, WM8962_THERMAL_SHUTDOWN_STATUS);
|
||||
|
||||
if (reg & WM8962_TEMP_ERR_HP)
|
||||
dev_crit(codec->dev, "Headphone thermal error\n");
|
||||
if (reg & WM8962_TEMP_WARN_HP)
|
||||
dev_crit(codec->dev, "Headphone thermal warning\n");
|
||||
if (reg & WM8962_TEMP_ERR_SPK)
|
||||
dev_crit(codec->dev, "Speaker thermal error\n");
|
||||
if (reg & WM8962_TEMP_WARN_SPK)
|
||||
dev_crit(codec->dev, "Speaker thermal warning\n");
|
||||
}
|
||||
|
||||
if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) {
|
||||
dev_dbg(codec->dev, "Microphone event detected\n");
|
||||
|
||||
@ -3479,31 +3660,6 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8962_mic_detect);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int wm8962_resume(struct snd_soc_codec *codec)
|
||||
{
|
||||
u16 *reg_cache = codec->reg_cache;
|
||||
int i;
|
||||
|
||||
/* Restore the registers */
|
||||
for (i = 1; i < codec->driver->reg_cache_size; i++) {
|
||||
switch (i) {
|
||||
case WM8962_SOFTWARE_RESET:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg_cache[i] != wm8962_reg[i])
|
||||
snd_soc_write(codec, i, reg_cache[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define wm8962_resume NULL
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
|
||||
static int beep_rates[] = {
|
||||
500, 1000, 2000, 4000,
|
||||
@ -4015,7 +4171,6 @@ static int wm8962_remove(struct snd_soc_codec *codec)
|
||||
static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
|
||||
.probe = wm8962_probe,
|
||||
.remove = wm8962_remove,
|
||||
.resume = wm8962_resume,
|
||||
.set_bias_level = wm8962_set_bias_level,
|
||||
.reg_cache_size = WM8962_MAX_REGISTER + 1,
|
||||
.reg_word_size = sizeof(u16),
|
||||
|
@ -847,6 +847,7 @@ SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0),
|
||||
SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0),
|
||||
@ -880,6 +881,9 @@ SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route routes[] = {
|
||||
{ "MICBIAS1", NULL, "VMID" },
|
||||
{ "MICBIAS2", NULL, "VMID" },
|
||||
|
||||
{ "ADCL", NULL, "CLK_SYS" },
|
||||
{ "ADCL", NULL, "CLK_DSP" },
|
||||
{ "ADCR", NULL, "CLK_SYS" },
|
||||
@ -1433,7 +1437,8 @@ static int wm8993_probe(struct snd_soc_codec *codec)
|
||||
int ret, i, val;
|
||||
|
||||
wm8993->hubs_data.hp_startup_mode = 1;
|
||||
wm8993->hubs_data.dcs_codes = -2;
|
||||
wm8993->hubs_data.dcs_codes_l = -2;
|
||||
wm8993->hubs_data.dcs_codes_r = -2;
|
||||
wm8993->hubs_data.series_startup = 1;
|
||||
|
||||
ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
|
||||
|
@ -1073,8 +1073,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
|
||||
{ 0x0000, 0x0000 }, /* R1069 */
|
||||
{ 0x0000, 0x0000 }, /* R1070 */
|
||||
{ 0x0000, 0x0000 }, /* R1071 */
|
||||
{ 0x0000, 0x0000 }, /* R1072 */
|
||||
{ 0x0000, 0x0000 }, /* R1073 */
|
||||
{ 0x006F, 0x006F }, /* R1072 - AIF1 DAC1 Noise Gate */
|
||||
{ 0x006F, 0x006F }, /* R1073 - AIF1 DAC2 Noise Gate */
|
||||
{ 0x0000, 0x0000 }, /* R1074 */
|
||||
{ 0x0000, 0x0000 }, /* R1075 */
|
||||
{ 0x0000, 0x0000 }, /* R1076 */
|
||||
@ -1329,7 +1329,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
|
||||
{ 0x0000, 0x0000 }, /* R1325 */
|
||||
{ 0x0000, 0x0000 }, /* R1326 */
|
||||
{ 0x0000, 0x0000 }, /* R1327 */
|
||||
{ 0x0000, 0x0000 }, /* R1328 */
|
||||
{ 0x006F, 0x006F }, /* R1328 - AIF2 DAC Noise Gate */
|
||||
{ 0x0000, 0x0000 }, /* R1329 */
|
||||
{ 0x0000, 0x0000 }, /* R1330 */
|
||||
{ 0x0000, 0x0000 }, /* R1331 */
|
||||
@ -1635,8 +1635,8 @@ const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
|
||||
0x0000, /* R58 - MICBIAS */
|
||||
0x000D, /* R59 - LDO 1 */
|
||||
0x0003, /* R60 - LDO 2 */
|
||||
0x0000, /* R61 */
|
||||
0x0000, /* R62 */
|
||||
0x0039, /* R61 - MICBIAS1 */
|
||||
0x0039, /* R62 - MICBIAS2 */
|
||||
0x0000, /* R63 */
|
||||
0x0000, /* R64 */
|
||||
0x0000, /* R65 */
|
||||
@ -2646,8 +2646,8 @@ const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
|
||||
0x0000, /* R1069 */
|
||||
0x0000, /* R1070 */
|
||||
0x0000, /* R1071 */
|
||||
0x0000, /* R1072 */
|
||||
0x0000, /* R1073 */
|
||||
0x0068, /* R1072 - AIF1 DAC1 Noise Gate */
|
||||
0x0068, /* R1073 - AIF1 DAC2 Noise Gate */
|
||||
0x0000, /* R1074 */
|
||||
0x0000, /* R1075 */
|
||||
0x0000, /* R1076 */
|
||||
@ -2902,7 +2902,7 @@ const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
|
||||
0x0000, /* R1325 */
|
||||
0x0000, /* R1326 */
|
||||
0x0000, /* R1327 */
|
||||
0x0000, /* R1328 */
|
||||
0x0068, /* R1328 - AIF2 DAC Noise Gate */
|
||||
0x0000, /* R1329 */
|
||||
0x0000, /* R1330 */
|
||||
0x0000, /* R1331 */
|
||||
|
@ -107,6 +107,7 @@ static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg)
|
||||
case WM8994_LDO_2:
|
||||
case WM8958_DSP2_EXECCONTROL:
|
||||
case WM8958_MIC_DETECT_3:
|
||||
case WM8994_DC_SERVO_4E:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
@ -281,6 +282,7 @@ static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
|
||||
static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(wm8994_3d_tlv, -1600, 183, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
|
||||
|
||||
#define WM8994_DRC_SWITCH(xname, reg, shift) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
@ -660,8 +662,45 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
|
||||
eq_tlv),
|
||||
};
|
||||
|
||||
static const char *wm8958_ng_text[] = {
|
||||
"30ms", "125ms", "250ms", "500ms",
|
||||
};
|
||||
|
||||
static const struct soc_enum wm8958_aif1dac1_ng_hold =
|
||||
SOC_ENUM_SINGLE(WM8958_AIF1_DAC1_NOISE_GATE,
|
||||
WM8958_AIF1DAC1_NG_THR_SHIFT, 4, wm8958_ng_text);
|
||||
|
||||
static const struct soc_enum wm8958_aif1dac2_ng_hold =
|
||||
SOC_ENUM_SINGLE(WM8958_AIF1_DAC2_NOISE_GATE,
|
||||
WM8958_AIF1DAC2_NG_THR_SHIFT, 4, wm8958_ng_text);
|
||||
|
||||
static const struct soc_enum wm8958_aif2dac_ng_hold =
|
||||
SOC_ENUM_SINGLE(WM8958_AIF2_DAC_NOISE_GATE,
|
||||
WM8958_AIF2DAC_NG_THR_SHIFT, 4, wm8958_ng_text);
|
||||
|
||||
static const struct snd_kcontrol_new wm8958_snd_controls[] = {
|
||||
SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
|
||||
|
||||
SOC_SINGLE("AIF1DAC1 Noise Gate Switch", WM8958_AIF1_DAC1_NOISE_GATE,
|
||||
WM8958_AIF1DAC1_NG_ENA_SHIFT, 1, 0),
|
||||
SOC_ENUM("AIF1DAC1 Noise Gate Hold Time", wm8958_aif1dac1_ng_hold),
|
||||
SOC_SINGLE_TLV("AIF1DAC1 Noise Gate Threshold Volume",
|
||||
WM8958_AIF1_DAC1_NOISE_GATE, WM8958_AIF1DAC1_NG_THR_SHIFT,
|
||||
7, 1, ng_tlv),
|
||||
|
||||
SOC_SINGLE("AIF1DAC2 Noise Gate Switch", WM8958_AIF1_DAC2_NOISE_GATE,
|
||||
WM8958_AIF1DAC2_NG_ENA_SHIFT, 1, 0),
|
||||
SOC_ENUM("AIF1DAC2 Noise Gate Hold Time", wm8958_aif1dac2_ng_hold),
|
||||
SOC_SINGLE_TLV("AIF1DAC2 Noise Gate Threshold Volume",
|
||||
WM8958_AIF1_DAC2_NOISE_GATE, WM8958_AIF1DAC2_NG_THR_SHIFT,
|
||||
7, 1, ng_tlv),
|
||||
|
||||
SOC_SINGLE("AIF2DAC Noise Gate Switch", WM8958_AIF2_DAC_NOISE_GATE,
|
||||
WM8958_AIF2DAC_NG_ENA_SHIFT, 1, 0),
|
||||
SOC_ENUM("AIF2DAC Noise Gate Hold Time", wm8958_aif2dac_ng_hold),
|
||||
SOC_SINGLE_TLV("AIF2DAC Noise Gate Threshold Volume",
|
||||
WM8958_AIF2_DAC_NOISE_GATE, WM8958_AIF2DAC_NG_THR_SHIFT,
|
||||
7, 1, ng_tlv),
|
||||
};
|
||||
|
||||
static int clk_sys_event(struct snd_soc_dapm_widget *w,
|
||||
@ -681,6 +720,97 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmid_reference(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
wm8994->vmid_refcount++;
|
||||
|
||||
dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n",
|
||||
wm8994->vmid_refcount);
|
||||
|
||||
if (wm8994->vmid_refcount == 1) {
|
||||
/* Startup bias, VMID ramp & buffer */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK,
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
(0x11 << WM8994_VMID_RAMP_SHIFT));
|
||||
|
||||
/* Main bias enable, VMID=2x40k */
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_BIAS_ENA |
|
||||
WM8994_VMID_SEL_MASK,
|
||||
WM8994_BIAS_ENA | 0x2);
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
}
|
||||
|
||||
static void vmid_dereference(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
wm8994->vmid_refcount--;
|
||||
|
||||
dev_dbg(codec->dev, "Dereferencing VMID, refcount is now %d\n",
|
||||
wm8994->vmid_refcount);
|
||||
|
||||
if (wm8994->vmid_refcount == 0) {
|
||||
/* Switch over to startup biases */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
|
||||
WM8994_BIAS_SRC |
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK,
|
||||
WM8994_BIAS_SRC |
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
(1 << WM8994_VMID_RAMP_SHIFT));
|
||||
|
||||
/* Disable main biases */
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_BIAS_ENA |
|
||||
WM8994_VMID_SEL_MASK, 0);
|
||||
|
||||
/* Discharge line */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
|
||||
WM8994_LINEOUT1_DISCH |
|
||||
WM8994_LINEOUT2_DISCH,
|
||||
WM8994_LINEOUT1_DISCH |
|
||||
WM8994_LINEOUT2_DISCH);
|
||||
|
||||
msleep(5);
|
||||
|
||||
/* Switch off startup biases */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
|
||||
WM8994_BIAS_SRC |
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int vmid_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
vmid_reference(codec);
|
||||
break;
|
||||
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
vmid_dereference(codec);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wm8994_update_class_w(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
@ -1208,6 +1338,8 @@ SND_SOC_DAPM_INPUT("Clock"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
|
||||
SND_SOC_DAPM_PRE_PMU),
|
||||
SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, vmid_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
@ -1525,6 +1657,8 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
|
||||
static const struct snd_soc_dapm_route wm8994_intercon[] = {
|
||||
{ "AIF2DACL", NULL, "AIF2DAC Mux" },
|
||||
{ "AIF2DACR", NULL, "AIF2DAC Mux" },
|
||||
{ "MICBIAS1", NULL, "VMID" },
|
||||
{ "MICBIAS2", NULL, "VMID" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route wm8958_intercon[] = {
|
||||
@ -1629,10 +1763,12 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
unsigned int freq_in, unsigned int freq_out)
|
||||
{
|
||||
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
|
||||
struct wm8994 *control = codec->control_data;
|
||||
int reg_offset, ret;
|
||||
struct fll_div fll;
|
||||
u16 reg, aif1, aif2;
|
||||
unsigned long timeout;
|
||||
bool was_enabled;
|
||||
|
||||
aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
|
||||
& WM8994_AIF1CLK_ENA;
|
||||
@ -1653,6 +1789,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = snd_soc_read(codec, WM8994_FLL1_CONTROL_1 + reg_offset);
|
||||
was_enabled = reg & WM8994_FLL1_ENA;
|
||||
|
||||
switch (src) {
|
||||
case 0:
|
||||
/* Allow no source specification when stopping */
|
||||
@ -1719,6 +1858,21 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
|
||||
/* Enable (with fractional mode if required) */
|
||||
if (freq_out) {
|
||||
/* Enable VMID if we need it */
|
||||
if (!was_enabled) {
|
||||
switch (control->type) {
|
||||
case WM8994:
|
||||
vmid_reference(codec);
|
||||
break;
|
||||
case WM8958:
|
||||
if (wm8994->revision < 1)
|
||||
vmid_reference(codec);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fll.k)
|
||||
reg = WM8994_FLL1_ENA | WM8994_FLL1_FRAC;
|
||||
else
|
||||
@ -1736,6 +1890,20 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
|
||||
} else {
|
||||
msleep(5);
|
||||
}
|
||||
} else {
|
||||
if (was_enabled) {
|
||||
switch (control->type) {
|
||||
case WM8994:
|
||||
vmid_dereference(codec);
|
||||
break;
|
||||
case WM8958:
|
||||
if (wm8994->revision < 1)
|
||||
vmid_dereference(codec);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wm8994->fll[id].in = freq_in;
|
||||
@ -1852,9 +2020,6 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
/* VMID=2x40k */
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_VMID_SEL_MASK, 0x2);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
@ -1896,65 +2061,13 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
|
||||
WM8994_LINEOUT2_DISCH,
|
||||
WM8994_LINEOUT1_DISCH |
|
||||
WM8994_LINEOUT2_DISCH);
|
||||
|
||||
/* Startup bias, VMID ramp & buffer */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK,
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
(0x11 << WM8994_VMID_RAMP_SHIFT));
|
||||
|
||||
/* Main bias enable, VMID=2x40k */
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_BIAS_ENA |
|
||||
WM8994_VMID_SEL_MASK,
|
||||
WM8994_BIAS_ENA | 0x2);
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
/* VMID=2x500k */
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_VMID_SEL_MASK, 0x4);
|
||||
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
|
||||
/* Switch over to startup biases */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
|
||||
WM8994_BIAS_SRC |
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK,
|
||||
WM8994_BIAS_SRC |
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
(1 << WM8994_VMID_RAMP_SHIFT));
|
||||
|
||||
/* Disable main biases */
|
||||
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
|
||||
WM8994_BIAS_ENA |
|
||||
WM8994_VMID_SEL_MASK, 0);
|
||||
|
||||
/* Discharge line */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
|
||||
WM8994_LINEOUT1_DISCH |
|
||||
WM8994_LINEOUT2_DISCH,
|
||||
WM8994_LINEOUT1_DISCH |
|
||||
WM8994_LINEOUT2_DISCH);
|
||||
|
||||
msleep(5);
|
||||
|
||||
/* Switch off startup biases */
|
||||
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
|
||||
WM8994_BIAS_SRC |
|
||||
WM8994_STARTUP_BIAS_ENA |
|
||||
WM8994_VMID_BUF_ENA |
|
||||
WM8994_VMID_RAMP_MASK, 0);
|
||||
|
||||
wm8994->cur_fw = NULL;
|
||||
|
||||
pm_runtime_put(codec->dev);
|
||||
@ -2384,6 +2497,21 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
|
||||
return snd_soc_update_bits(codec, reg, mask, val);
|
||||
}
|
||||
|
||||
static int wm8994_aif2_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
|
||||
/* Disable the pulls on the AIF if we're using it to save power. */
|
||||
snd_soc_update_bits(codec, WM8994_GPIO_3,
|
||||
WM8994_GPN_PU | WM8994_GPN_PD, 0);
|
||||
snd_soc_update_bits(codec, WM8994_GPIO_4,
|
||||
WM8994_GPN_PU | WM8994_GPN_PD, 0);
|
||||
snd_soc_update_bits(codec, WM8994_GPIO_5,
|
||||
WM8994_GPN_PU | WM8994_GPN_PD, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define WM8994_RATES SNDRV_PCM_RATE_8000_96000
|
||||
|
||||
#define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
@ -2451,6 +2579,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
|
||||
.rates = WM8994_RATES,
|
||||
.formats = WM8994_FORMATS,
|
||||
},
|
||||
.probe = wm8994_aif2_probe,
|
||||
.ops = &wm8994_aif2_dai_ops,
|
||||
},
|
||||
{
|
||||
@ -2916,6 +3045,24 @@ static irqreturn_t wm8994_fifo_error(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t wm8994_temp_warn(int irq, void *data)
|
||||
{
|
||||
struct snd_soc_codec *codec = data;
|
||||
|
||||
dev_err(codec->dev, "Thermal warning\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t wm8994_temp_shut(int irq, void *data)
|
||||
{
|
||||
struct snd_soc_codec *codec = data;
|
||||
|
||||
dev_crit(codec->dev, "Thermal shutdown\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8994 *control;
|
||||
@ -2972,13 +3119,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
switch (wm8994->revision) {
|
||||
case 2:
|
||||
case 3:
|
||||
wm8994->hubs.dcs_codes = -5;
|
||||
wm8994->hubs.dcs_codes_l = -5;
|
||||
wm8994->hubs.dcs_codes_r = -5;
|
||||
wm8994->hubs.hp_startup_mode = 1;
|
||||
wm8994->hubs.dcs_readback_mode = 1;
|
||||
wm8994->hubs.series_startup = 1;
|
||||
break;
|
||||
default:
|
||||
wm8994->hubs.dcs_readback_mode = 1;
|
||||
wm8994->hubs.dcs_readback_mode = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -2993,6 +3141,10 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
|
||||
|
||||
wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR,
|
||||
wm8994_fifo_error, "FIFO error", codec);
|
||||
wm8994_request_irq(wm8994->control_data, WM8994_IRQ_TEMP_WARN,
|
||||
wm8994_temp_warn, "Thermal warning", codec);
|
||||
wm8994_request_irq(wm8994->control_data, WM8994_IRQ_TEMP_SHUT,
|
||||
wm8994_temp_shut, "Thermal shutdown", codec);
|
||||
|
||||
ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
|
||||
wm_hubs_dcs_done, "DC servo done",
|
||||
@ -3257,6 +3409,8 @@ err_irq:
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
|
||||
&wm8994->hubs);
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec);
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec);
|
||||
err:
|
||||
kfree(wm8994);
|
||||
return ret;
|
||||
@ -3279,6 +3433,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
|
||||
&wm8994->hubs);
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec);
|
||||
wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec);
|
||||
|
||||
switch (control->type) {
|
||||
case WM8994:
|
||||
|
@ -83,6 +83,8 @@ struct wm8994_priv {
|
||||
struct completion fll_locked[2];
|
||||
bool fll_locked_irq;
|
||||
|
||||
int vmid_refcount;
|
||||
|
||||
int dac_rates[2];
|
||||
int lrclk_shared[2];
|
||||
|
||||
|
@ -1573,9 +1573,7 @@ static int wm8995_resume(struct snd_soc_codec *codec)
|
||||
static int wm8995_remove(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8995_priv *wm8995;
|
||||
struct i2c_client *i2c;
|
||||
|
||||
i2c = container_of(codec->dev, struct i2c_client, dev);
|
||||
wm8995 = snd_soc_codec_get_drvdata(codec);
|
||||
wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
return 0;
|
||||
@ -1642,6 +1640,7 @@ static int wm8995_probe(struct snd_soc_codec *codec)
|
||||
|
||||
if (ret != 0x8995) {
|
||||
dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
|
||||
ret = -EINVAL;
|
||||
goto err_reg_enable;
|
||||
}
|
||||
|
||||
|
@ -41,12 +41,11 @@
|
||||
#define HPOUT2L 4
|
||||
#define HPOUT2R 8
|
||||
|
||||
#define WM8996_NUM_SUPPLIES 4
|
||||
#define WM8996_NUM_SUPPLIES 3
|
||||
static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
|
||||
"DBVDD",
|
||||
"AVDD1",
|
||||
"AVDD2",
|
||||
"CPVDD",
|
||||
};
|
||||
|
||||
struct wm8996_priv {
|
||||
@ -71,6 +70,8 @@ struct wm8996_priv {
|
||||
|
||||
struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
|
||||
struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];
|
||||
struct regulator *cpvdd;
|
||||
int bg_ena;
|
||||
|
||||
struct wm8996_pdata pdata;
|
||||
|
||||
@ -112,7 +113,6 @@ static int wm8996_regulator_event_##n(struct notifier_block *nb, \
|
||||
WM8996_REGULATOR_EVENT(0)
|
||||
WM8996_REGULATOR_EVENT(1)
|
||||
WM8996_REGULATOR_EVENT(2)
|
||||
WM8996_REGULATOR_EVENT(3)
|
||||
|
||||
static const u16 wm8996_reg[WM8996_MAX_REGISTER] = {
|
||||
[WM8996_SOFTWARE_RESET] = 0x8996,
|
||||
@ -414,6 +414,7 @@ static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(threedstereo_tlv, -1600, 183, 1);
|
||||
|
||||
static const char *sidetone_hpf_text[] = {
|
||||
"2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
|
||||
@ -608,6 +609,14 @@ SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0),
|
||||
SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0),
|
||||
SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0),
|
||||
|
||||
SOC_SINGLE("DSP1 3D Stereo Switch", WM8996_DSP1_RX_FILTERS_2, 8, 1, 0),
|
||||
SOC_SINGLE("DSP2 3D Stereo Switch", WM8996_DSP2_RX_FILTERS_2, 8, 1, 0),
|
||||
|
||||
SOC_SINGLE_TLV("DSP1 3D Stereo Volume", WM8996_DSP1_RX_FILTERS_2, 10, 15,
|
||||
0, threedstereo_tlv),
|
||||
SOC_SINGLE_TLV("DSP2 3D Stereo Volume", WM8996_DSP2_RX_FILTERS_2, 10, 15,
|
||||
0, threedstereo_tlv),
|
||||
|
||||
SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4,
|
||||
8, 0, out_digital_tlv),
|
||||
SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4,
|
||||
@ -658,19 +667,75 @@ SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
|
||||
eq_tlv),
|
||||
};
|
||||
|
||||
static int cp_event(struct snd_soc_dapm_widget *w,
|
||||
static void wm8996_bg_enable(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
wm8996->bg_ena++;
|
||||
if (wm8996->bg_ena == 1) {
|
||||
snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
|
||||
WM8996_BG_ENA, WM8996_BG_ENA);
|
||||
msleep(2);
|
||||
}
|
||||
}
|
||||
|
||||
static void wm8996_bg_disable(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
wm8996->bg_ena--;
|
||||
if (!wm8996->bg_ena)
|
||||
snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
|
||||
WM8996_BG_ENA, 0);
|
||||
}
|
||||
|
||||
static int bg_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
msleep(5);
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
wm8996_bg_enable(codec);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
wm8996_bg_disable(codec);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cp_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_codec *codec = w->codec;
|
||||
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
ret = regulator_enable(wm8996->cpvdd);
|
||||
if (ret != 0)
|
||||
dev_err(codec->dev, "Failed to enable CPVDD: %d\n",
|
||||
ret);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
msleep(5);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
regulator_disable_deferred(wm8996->cpvdd, 20);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rmv_short_event(struct snd_soc_dapm_widget *w,
|
||||
@ -698,7 +763,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(codec->dev);
|
||||
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
|
||||
int i, ret;
|
||||
int ret;
|
||||
unsigned long timeout = 200;
|
||||
|
||||
snd_soc_write(codec, WM8996_DC_SERVO_2, mask);
|
||||
@ -713,15 +778,12 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
|
||||
|
||||
} else {
|
||||
msleep(1);
|
||||
if (--i) {
|
||||
timeout = 0;
|
||||
break;
|
||||
}
|
||||
timeout--;
|
||||
}
|
||||
|
||||
ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
|
||||
dev_dbg(codec->dev, "DC servo state: %x\n", ret);
|
||||
} while (ret & mask);
|
||||
} while (timeout && ret & mask);
|
||||
|
||||
if (timeout == 0)
|
||||
dev_err(codec->dev, "DC servo timed out for %x\n", mask);
|
||||
@ -979,9 +1041,12 @@ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
|
||||
SND_SOC_DAPM_POST_PMU),
|
||||
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0),
|
||||
SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
|
||||
SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),
|
||||
|
||||
@ -1035,14 +1100,14 @@ SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0),
|
||||
SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0),
|
||||
SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
|
||||
SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 0,
|
||||
WM8996_POWER_MANAGEMENT_4, 9, 0),
|
||||
SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
|
||||
SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 1,
|
||||
WM8996_POWER_MANAGEMENT_4, 8, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
|
||||
SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 0,
|
||||
WM8996_POWER_MANAGEMENT_6, 9, 0),
|
||||
SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
|
||||
SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 1,
|
||||
WM8996_POWER_MANAGEMENT_6, 8, 0),
|
||||
|
||||
SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
|
||||
@ -1137,17 +1202,23 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
|
||||
{ "Charge Pump", NULL, "SYSCLK" },
|
||||
|
||||
{ "MICB1", NULL, "LDO2" },
|
||||
{ "MICB1", NULL, "MICB1 Audio" },
|
||||
{ "MICB1", NULL, "Bandgap" },
|
||||
{ "MICB2", NULL, "LDO2" },
|
||||
{ "MICB2", NULL, "MICB2 Audio" },
|
||||
{ "MICB2", NULL, "Bandgap" },
|
||||
|
||||
{ "IN1L PGA", NULL, "IN2LN" },
|
||||
{ "IN1L PGA", NULL, "IN2LP" },
|
||||
{ "IN1L PGA", NULL, "IN1LN" },
|
||||
{ "IN1L PGA", NULL, "IN1LP" },
|
||||
{ "IN1L PGA", NULL, "Bandgap" },
|
||||
|
||||
{ "IN1R PGA", NULL, "IN2RN" },
|
||||
{ "IN1R PGA", NULL, "IN2RP" },
|
||||
{ "IN1R PGA", NULL, "IN1RN" },
|
||||
{ "IN1R PGA", NULL, "IN1RP" },
|
||||
{ "IN1R PGA", NULL, "Bandgap" },
|
||||
|
||||
{ "ADCL", NULL, "IN1L PGA" },
|
||||
|
||||
@ -1281,6 +1352,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
|
||||
{ "DAC2R", NULL, "DAC2R Mixer" },
|
||||
|
||||
{ "HPOUT2L PGA", NULL, "Charge Pump" },
|
||||
{ "HPOUT2L PGA", NULL, "Bandgap" },
|
||||
{ "HPOUT2L PGA", NULL, "DAC2L" },
|
||||
{ "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
|
||||
{ "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
|
||||
@ -1288,6 +1360,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
|
||||
{ "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
|
||||
|
||||
{ "HPOUT2R PGA", NULL, "Charge Pump" },
|
||||
{ "HPOUT2R PGA", NULL, "Bandgap" },
|
||||
{ "HPOUT2R PGA", NULL, "DAC2R" },
|
||||
{ "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
|
||||
{ "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
|
||||
@ -1295,6 +1368,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
|
||||
{ "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
|
||||
|
||||
{ "HPOUT1L PGA", NULL, "Charge Pump" },
|
||||
{ "HPOUT1L PGA", NULL, "Bandgap" },
|
||||
{ "HPOUT1L PGA", NULL, "DAC1L" },
|
||||
{ "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
|
||||
{ "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
|
||||
@ -1302,6 +1376,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
|
||||
{ "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
|
||||
|
||||
{ "HPOUT1R PGA", NULL, "Charge Pump" },
|
||||
{ "HPOUT1R PGA", NULL, "Bandgap" },
|
||||
{ "HPOUT1R PGA", NULL, "DAC1R" },
|
||||
{ "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
|
||||
{ "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
|
||||
@ -1620,14 +1695,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
|
||||
|
||||
switch (level) {
|
||||
case SND_SOC_BIAS_ON:
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
|
||||
snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
|
||||
WM8996_BG_ENA, WM8996_BG_ENA);
|
||||
msleep(2);
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
@ -1650,9 +1718,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
|
||||
codec->cache_only = false;
|
||||
snd_soc_cache_sync(codec);
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
|
||||
WM8996_BG_ENA, 0);
|
||||
break;
|
||||
|
||||
case SND_SOC_BIAS_OFF:
|
||||
@ -2041,7 +2106,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
||||
struct i2c_client *i2c = to_i2c_client(codec->dev);
|
||||
struct _fll_div fll_div;
|
||||
unsigned long timeout;
|
||||
int ret, reg;
|
||||
int ret, reg, retry;
|
||||
|
||||
/* Any change? */
|
||||
if (source == wm8996->fll_src && Fref == wm8996->fll_fref &&
|
||||
@ -2057,6 +2122,8 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
||||
snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
|
||||
WM8996_FLL_ENA, 0);
|
||||
|
||||
wm8996_bg_disable(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2111,6 +2178,11 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
||||
|
||||
snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda);
|
||||
|
||||
/* Enable the bandgap if it's not already enabled */
|
||||
ret = snd_soc_read(codec, WM8996_FLL_CONTROL_1);
|
||||
if (!(ret & WM8996_FLL_ENA))
|
||||
wm8996_bg_enable(codec);
|
||||
|
||||
/* Clear any pending completions (eg, from failed startups) */
|
||||
try_wait_for_completion(&wm8996->fll_lock);
|
||||
|
||||
@ -2128,17 +2200,29 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
|
||||
else
|
||||
timeout = msecs_to_jiffies(2);
|
||||
|
||||
/* Allow substantially longer if we've actually got the IRQ */
|
||||
/* Allow substantially longer if we've actually got the IRQ, poll
|
||||
* at a slightly higher rate if we don't.
|
||||
*/
|
||||
if (i2c->irq)
|
||||
timeout *= 1000;
|
||||
timeout *= 10;
|
||||
else
|
||||
timeout /= 2;
|
||||
|
||||
ret = wait_for_completion_timeout(&wm8996->fll_lock, timeout);
|
||||
for (retry = 0; retry < 10; retry++) {
|
||||
ret = wait_for_completion_timeout(&wm8996->fll_lock,
|
||||
timeout);
|
||||
if (ret != 0) {
|
||||
WARN_ON(!i2c->irq);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == 0 && i2c->irq) {
|
||||
ret = snd_soc_read(codec, WM8996_INTERRUPT_RAW_STATUS_2);
|
||||
if (ret & WM8996_FLL_LOCK_STS)
|
||||
break;
|
||||
}
|
||||
if (retry == 10) {
|
||||
dev_err(codec->dev, "Timed out waiting for FLL\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
|
||||
@ -2297,12 +2381,94 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
|
||||
|
||||
/* Enable interrupts and we're off */
|
||||
snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
|
||||
WM8996_IM_MICD_EINT, 0);
|
||||
WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8996_detect);
|
||||
|
||||
static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
|
||||
int val, reg, report;
|
||||
|
||||
/* Assume headphone in error conditions; we need to report
|
||||
* something or we stall our state machine.
|
||||
*/
|
||||
report = SND_JACK_HEADPHONE;
|
||||
|
||||
reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2);
|
||||
if (reg < 0) {
|
||||
dev_err(codec->dev, "Failed to read HPDET status\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(reg & WM8996_HP_DONE)) {
|
||||
dev_err(codec->dev, "Got HPDET IRQ but HPDET is busy\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
val = reg & WM8996_HP_LVL_MASK;
|
||||
|
||||
dev_dbg(codec->dev, "HPDET measured %d ohms\n", val);
|
||||
|
||||
/* If we've got high enough impedence then report as line,
|
||||
* otherwise assume headphone.
|
||||
*/
|
||||
if (val >= 126)
|
||||
report = SND_JACK_LINEOUT;
|
||||
else
|
||||
report = SND_JACK_HEADPHONE;
|
||||
|
||||
out:
|
||||
if (wm8996->jack_mic)
|
||||
report |= SND_JACK_MICROPHONE;
|
||||
|
||||
snd_soc_jack_report(wm8996->jack, report,
|
||||
SND_JACK_LINEOUT | SND_JACK_HEADSET);
|
||||
|
||||
wm8996->detecting = false;
|
||||
|
||||
/* If the output isn't running re-clamp it */
|
||||
if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) &
|
||||
(WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT)))
|
||||
snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
|
||||
WM8996_HPOUT1L_RMV_SHORT |
|
||||
WM8996_HPOUT1R_RMV_SHORT, 0);
|
||||
|
||||
/* Go back to looking at the microphone */
|
||||
snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
|
||||
WM8996_JD_MODE_MASK, 0);
|
||||
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
|
||||
WM8996_MICD_ENA);
|
||||
|
||||
snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
}
|
||||
|
||||
static void wm8996_hpdet_start(struct snd_soc_codec *codec)
|
||||
{
|
||||
/* Unclamp the output, we can't measure while we're shorting it */
|
||||
snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
|
||||
WM8996_HPOUT1L_RMV_SHORT |
|
||||
WM8996_HPOUT1R_RMV_SHORT,
|
||||
WM8996_HPOUT1L_RMV_SHORT |
|
||||
WM8996_HPOUT1R_RMV_SHORT);
|
||||
|
||||
/* We need bandgap for HPDET */
|
||||
snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
|
||||
snd_soc_dapm_sync(&codec->dapm);
|
||||
|
||||
/* Go into headphone detect left mode */
|
||||
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
|
||||
snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
|
||||
WM8996_JD_MODE_MASK, 1);
|
||||
|
||||
/* Trigger a measurement */
|
||||
snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1,
|
||||
WM8996_HP_POLL, WM8996_HP_POLL);
|
||||
}
|
||||
|
||||
static void wm8996_micd(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
|
||||
@ -2323,28 +2489,36 @@ static void wm8996_micd(struct snd_soc_codec *codec)
|
||||
wm8996->jack_mic = false;
|
||||
wm8996->detecting = true;
|
||||
snd_soc_jack_report(wm8996->jack, 0,
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0);
|
||||
SND_JACK_LINEOUT | SND_JACK_HEADSET |
|
||||
SND_JACK_BTN_0);
|
||||
|
||||
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
|
||||
WM8996_MICD_RATE_MASK,
|
||||
WM8996_MICD_RATE_MASK);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the measurement is very high we've got a microphone but
|
||||
* do a little debounce to account for mechanical issues.
|
||||
/* If the measurement is very high we've got a microphone,
|
||||
* either we just detected one or if we already reported then
|
||||
* we've got a button release event.
|
||||
*/
|
||||
if (val & 0x400) {
|
||||
dev_dbg(codec->dev, "Microphone detected\n");
|
||||
snd_soc_jack_report(wm8996->jack, SND_JACK_HEADSET,
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0);
|
||||
wm8996->jack_mic = true;
|
||||
wm8996->detecting = false;
|
||||
if (wm8996->detecting) {
|
||||
dev_dbg(codec->dev, "Microphone detected\n");
|
||||
wm8996->jack_mic = true;
|
||||
wm8996_hpdet_start(codec);
|
||||
|
||||
/* Increase poll rate to give better responsiveness
|
||||
* for buttons */
|
||||
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
|
||||
WM8996_MICD_RATE_MASK,
|
||||
5 << WM8996_MICD_RATE_SHIFT);
|
||||
/* Increase poll rate to give better responsiveness
|
||||
* for buttons */
|
||||
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
|
||||
WM8996_MICD_RATE_MASK,
|
||||
5 << WM8996_MICD_RATE_SHIFT);
|
||||
} else {
|
||||
dev_dbg(codec->dev, "Mic button up\n");
|
||||
snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we detected a lower impedence during initial startup
|
||||
@ -2376,15 +2550,11 @@ static void wm8996_micd(struct snd_soc_codec *codec)
|
||||
if (val & 0x3fc) {
|
||||
if (wm8996->jack_mic) {
|
||||
dev_dbg(codec->dev, "Mic button detected\n");
|
||||
snd_soc_jack_report(wm8996->jack,
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0,
|
||||
SND_JACK_HEADSET | SND_JACK_BTN_0);
|
||||
} else {
|
||||
dev_dbg(codec->dev, "Headphone detected\n");
|
||||
snd_soc_jack_report(wm8996->jack,
|
||||
SND_JACK_HEADPHONE,
|
||||
SND_JACK_HEADSET |
|
||||
snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0,
|
||||
SND_JACK_BTN_0);
|
||||
} else if (wm8996->detecting) {
|
||||
dev_dbg(codec->dev, "Headphone detected\n");
|
||||
wm8996_hpdet_start(codec);
|
||||
|
||||
/* Increase the detection rate a bit for
|
||||
* responsiveness.
|
||||
@ -2392,8 +2562,6 @@ static void wm8996_micd(struct snd_soc_codec *codec)
|
||||
snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
|
||||
WM8996_MICD_RATE_MASK,
|
||||
7 << WM8996_MICD_RATE_SHIFT);
|
||||
|
||||
wm8996->detecting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2412,6 +2580,9 @@ static irqreturn_t wm8996_irq(int irq, void *data)
|
||||
}
|
||||
irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK);
|
||||
|
||||
if (!irq_val)
|
||||
return IRQ_NONE;
|
||||
|
||||
snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val);
|
||||
|
||||
if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) {
|
||||
@ -2430,10 +2601,10 @@ static irqreturn_t wm8996_irq(int irq, void *data)
|
||||
if (irq_val & WM8996_MICD_EINT)
|
||||
wm8996_micd(codec);
|
||||
|
||||
if (irq_val)
|
||||
return IRQ_HANDLED;
|
||||
else
|
||||
return IRQ_NONE;
|
||||
if (irq_val & WM8996_HP_DONE_EINT)
|
||||
wm8996_hpdet_irq(codec);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t wm8996_edge_irq(int irq, void *data)
|
||||
@ -2548,7 +2719,13 @@ static int wm8996_probe(struct snd_soc_codec *codec)
|
||||
wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
|
||||
wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
|
||||
wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
|
||||
wm8996->disable_nb[3].notifier_call = wm8996_regulator_event_3;
|
||||
|
||||
wm8996->cpvdd = regulator_get(&i2c->dev, "CPVDD");
|
||||
if (IS_ERR(wm8996->cpvdd)) {
|
||||
ret = PTR_ERR(wm8996->cpvdd);
|
||||
dev_err(&i2c->dev, "Failed to get CPVDD: %d\n", ret);
|
||||
goto err_get;
|
||||
}
|
||||
|
||||
/* This should really be moved into the regulator core */
|
||||
for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
|
||||
@ -2565,7 +2742,7 @@ static int wm8996_probe(struct snd_soc_codec *codec)
|
||||
wm8996->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
|
||||
goto err_get;
|
||||
goto err_cpvdd;
|
||||
}
|
||||
|
||||
if (wm8996->pdata.ldo_ena >= 0) {
|
||||
@ -2808,6 +2985,8 @@ err_enable:
|
||||
gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
|
||||
err_cpvdd:
|
||||
regulator_put(wm8996->cpvdd);
|
||||
err_get:
|
||||
regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
|
||||
err:
|
||||
@ -2831,6 +3010,7 @@ static int wm8996_remove(struct snd_soc_codec *codec)
|
||||
for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
|
||||
regulator_unregister_notifier(wm8996->supplies[i].consumer,
|
||||
&wm8996->disable_nb[i]);
|
||||
regulator_put(wm8996->cpvdd);
|
||||
regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
|
||||
|
||||
return 0;
|
||||
|
@ -1120,8 +1120,8 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm9081_set_sysclk(struct snd_soc_codec *codec,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id,
|
||||
int source, unsigned int freq, int dir)
|
||||
{
|
||||
struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
|
||||
|
||||
|
@ -139,7 +139,6 @@ static const u16 wm9090_reg_defaults[] = {
|
||||
|
||||
/* This struct is used to save the context */
|
||||
struct wm9090_priv {
|
||||
struct mutex mutex;
|
||||
struct wm9090_platform_data pdata;
|
||||
void *control_data;
|
||||
};
|
||||
@ -663,7 +662,6 @@ static int wm9090_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
i2c_set_clientdata(i2c, wm9090);
|
||||
wm9090->control_data = i2c;
|
||||
mutex_init(&wm9090->mutex);
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev,
|
||||
&soc_codec_dev_wm9090, NULL, 0);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/wm8994/registers.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
@ -116,14 +117,23 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
|
||||
s8 offset;
|
||||
u16 reg, reg_l, reg_r, dcs_cfg;
|
||||
u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
|
||||
|
||||
switch (hubs->dcs_readback_mode) {
|
||||
case 2:
|
||||
dcs_reg = WM8994_DC_SERVO_4E;
|
||||
break;
|
||||
default:
|
||||
dcs_reg = WM8993_DC_SERVO_3;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we're using a digital only path and have a previously
|
||||
* callibrated DC servo offset stored then use that. */
|
||||
if (hubs->class_w && hubs->class_w_dcs) {
|
||||
dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
|
||||
hubs->class_w_dcs);
|
||||
snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs);
|
||||
snd_soc_write(codec, dcs_reg, hubs->class_w_dcs);
|
||||
wait_for_dc_servo(codec,
|
||||
WM8993_DCS_TRIG_DAC_WR_0 |
|
||||
WM8993_DCS_TRIG_DAC_WR_1);
|
||||
@ -154,8 +164,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
||||
reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
|
||||
& WM8993_DCS_INTEG_CHAN_1_MASK;
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
|
||||
reg = snd_soc_read(codec, dcs_reg);
|
||||
reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
|
||||
>> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
|
||||
reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
|
||||
@ -168,24 +179,25 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
|
||||
dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
|
||||
|
||||
/* Apply correction to DC servo result */
|
||||
if (hubs->dcs_codes) {
|
||||
dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
|
||||
hubs->dcs_codes);
|
||||
if (hubs->dcs_codes_l || hubs->dcs_codes_r) {
|
||||
dev_dbg(codec->dev,
|
||||
"Applying %d/%d code DC servo correction\n",
|
||||
hubs->dcs_codes_l, hubs->dcs_codes_r);
|
||||
|
||||
/* HPOUT1R */
|
||||
offset = reg_r;
|
||||
offset += hubs->dcs_codes;
|
||||
offset += hubs->dcs_codes_r;
|
||||
dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
|
||||
|
||||
/* HPOUT1L */
|
||||
offset = reg_l;
|
||||
offset += hubs->dcs_codes;
|
||||
offset += hubs->dcs_codes_l;
|
||||
dcs_cfg |= (u8)offset;
|
||||
|
||||
dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg);
|
||||
|
||||
/* Do it */
|
||||
snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
|
||||
snd_soc_write(codec, dcs_reg, dcs_cfg);
|
||||
wait_for_dc_servo(codec,
|
||||
WM8993_DCS_TRIG_DAC_WR_0 |
|
||||
WM8993_DCS_TRIG_DAC_WR_1);
|
||||
@ -217,7 +229,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
|
||||
|
||||
/* If we're applying an offset correction then updating the
|
||||
* callibration would be likely to introduce further offsets. */
|
||||
if (hubs->dcs_codes || hubs->no_series_update)
|
||||
if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
|
||||
return ret;
|
||||
|
||||
/* Only need to do this if the outputs are active */
|
||||
@ -699,6 +711,11 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
|
||||
{ "IN1L PGA", "IN1LP Switch", "IN1LP" },
|
||||
{ "IN1L PGA", "IN1LN Switch", "IN1LN" },
|
||||
|
||||
{ "IN1L PGA", NULL, "VMID" },
|
||||
{ "IN1R PGA", NULL, "VMID" },
|
||||
{ "IN2L PGA", NULL, "VMID" },
|
||||
{ "IN2R PGA", NULL, "VMID" },
|
||||
|
||||
{ "IN1R PGA", "IN1RP Switch", "IN1RP" },
|
||||
{ "IN1R PGA", "IN1RN Switch", "IN1RN" },
|
||||
|
||||
@ -716,12 +733,14 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
|
||||
{ "MIXINL", NULL, "Direct Voice" },
|
||||
{ "MIXINL", NULL, "IN1LP" },
|
||||
{ "MIXINL", NULL, "Left Output Mixer" },
|
||||
{ "MIXINL", NULL, "VMID" },
|
||||
|
||||
{ "MIXINR", "IN1R Switch", "IN1R PGA" },
|
||||
{ "MIXINR", "IN2R Switch", "IN2R PGA" },
|
||||
{ "MIXINR", NULL, "Direct Voice" },
|
||||
{ "MIXINR", NULL, "IN1RP" },
|
||||
{ "MIXINR", NULL, "Right Output Mixer" },
|
||||
{ "MIXINR", NULL, "VMID" },
|
||||
|
||||
{ "ADCL", NULL, "MIXINL" },
|
||||
{ "ADCR", NULL, "MIXINR" },
|
||||
@ -752,6 +771,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
|
||||
{ "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
|
||||
{ "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
|
||||
|
||||
{ "Earpiece Driver", NULL, "VMID" },
|
||||
{ "Earpiece Driver", NULL, "Earpiece Mixer" },
|
||||
{ "HPOUT2N", NULL, "Earpiece Driver" },
|
||||
{ "HPOUT2P", NULL, "Earpiece Driver" },
|
||||
@ -774,9 +794,11 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
|
||||
{ "SPKR Boost", "SPKR Switch", "SPKR" },
|
||||
{ "SPKR Boost", "SPKL Switch", "SPKL" },
|
||||
|
||||
{ "SPKL Driver", NULL, "VMID" },
|
||||
{ "SPKL Driver", NULL, "SPKL Boost" },
|
||||
{ "SPKL Driver", NULL, "CLK_SYS" },
|
||||
|
||||
{ "SPKR Driver", NULL, "VMID" },
|
||||
{ "SPKR Driver", NULL, "SPKR Boost" },
|
||||
{ "SPKR Driver", NULL, "CLK_SYS" },
|
||||
|
||||
@ -790,12 +812,18 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
|
||||
|
||||
{ "Headphone PGA", NULL, "Left Headphone Mux" },
|
||||
{ "Headphone PGA", NULL, "Right Headphone Mux" },
|
||||
{ "Headphone PGA", NULL, "VMID" },
|
||||
{ "Headphone PGA", NULL, "CLK_SYS" },
|
||||
{ "Headphone PGA", NULL, "Headphone Supply" },
|
||||
|
||||
{ "HPOUT1L", NULL, "Headphone PGA" },
|
||||
{ "HPOUT1R", NULL, "Headphone PGA" },
|
||||
|
||||
{ "LINEOUT1N Driver", NULL, "VMID" },
|
||||
{ "LINEOUT1P Driver", NULL, "VMID" },
|
||||
{ "LINEOUT2N Driver", NULL, "VMID" },
|
||||
{ "LINEOUT2P Driver", NULL, "VMID" },
|
||||
|
||||
{ "LINEOUT1N", NULL, "LINEOUT1N Driver" },
|
||||
{ "LINEOUT1P", NULL, "LINEOUT1P Driver" },
|
||||
{ "LINEOUT2N", NULL, "LINEOUT2N Driver" },
|
||||
|
@ -23,7 +23,8 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
|
||||
|
||||
/* This *must* be the first element of the codec->private_data struct */
|
||||
struct wm_hubs_data {
|
||||
int dcs_codes;
|
||||
int dcs_codes_l;
|
||||
int dcs_codes_r;
|
||||
int dcs_readback_mode;
|
||||
int hp_startup_mode;
|
||||
int series_startup;
|
||||
|
@ -732,16 +732,19 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
|
||||
davinci_hw_param(dev, substream->stream);
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_U8:
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
dma_params->data_type = 1;
|
||||
word_length = DAVINCI_AUDIO_WORD_8;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_U16_LE:
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
dma_params->data_type = 2;
|
||||
word_length = DAVINCI_AUDIO_WORD_16;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_FORMAT_U32_LE:
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
dma_params->data_type = 4;
|
||||
word_length = DAVINCI_AUDIO_WORD_32;
|
||||
@ -818,6 +821,13 @@ static struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
|
||||
|
||||
};
|
||||
|
||||
#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
|
||||
SNDRV_PCM_FMTBIT_U8 | \
|
||||
SNDRV_PCM_FMTBIT_S16_LE | \
|
||||
SNDRV_PCM_FMTBIT_U16_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE | \
|
||||
SNDRV_PCM_FMTBIT_U32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
|
||||
{
|
||||
.name = "davinci-mcasp.0",
|
||||
@ -825,17 +835,13 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = DAVINCI_MCASP_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.formats = DAVINCI_MCASP_PCM_FMTS,
|
||||
},
|
||||
.capture = {
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = DAVINCI_MCASP_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.formats = DAVINCI_MCASP_PCM_FMTS,
|
||||
},
|
||||
.ops = &davinci_mcasp_dai_ops,
|
||||
|
||||
@ -846,7 +852,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
|
||||
.channels_min = 1,
|
||||
.channels_max = 384,
|
||||
.rates = DAVINCI_MCASP_RATES,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.formats = DAVINCI_MCASP_PCM_FMTS,
|
||||
},
|
||||
.ops = &davinci_mcasp_dai_ops,
|
||||
},
|
||||
|
@ -180,7 +180,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct davinci_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int link = prtd->asp_link[0];
|
||||
unsigned int period_size;
|
||||
unsigned int dma_offset;
|
||||
dma_addr_t dma_pos;
|
||||
@ -198,7 +197,8 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
|
||||
fifo_level = prtd->params->fifo_level;
|
||||
|
||||
pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
|
||||
"dma_ptr = %x period_size=%x\n", link, dma_pos, period_size);
|
||||
"dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos,
|
||||
period_size);
|
||||
|
||||
data_type = prtd->params->data_type;
|
||||
count = period_size / data_type;
|
||||
@ -222,17 +222,19 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
|
||||
}
|
||||
|
||||
acnt = prtd->params->acnt;
|
||||
edma_set_src(link, src, INCR, W8BIT);
|
||||
edma_set_dest(link, dst, INCR, W8BIT);
|
||||
edma_set_src(prtd->asp_link[0], src, INCR, W8BIT);
|
||||
edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT);
|
||||
|
||||
edma_set_src_index(link, src_bidx, src_cidx);
|
||||
edma_set_dest_index(link, dst_bidx, dst_cidx);
|
||||
edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx);
|
||||
edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx);
|
||||
|
||||
if (!fifo_level)
|
||||
edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC);
|
||||
edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0,
|
||||
ASYNC);
|
||||
else
|
||||
edma_set_transfer_params(link, acnt, fifo_level, count,
|
||||
fifo_level, ABSYNC);
|
||||
edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
|
||||
count, fifo_level,
|
||||
ABSYNC);
|
||||
}
|
||||
|
||||
static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
|
||||
@ -305,7 +307,6 @@ static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
|
||||
unsigned int acnt = params->acnt;
|
||||
/* divide by 2 for ping/pong */
|
||||
unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
|
||||
int link = prtd->asp_link[1];
|
||||
unsigned int fifo_level = prtd->params->fifo_level;
|
||||
unsigned int count;
|
||||
if ((data_type == 0) || (data_type > 4)) {
|
||||
@ -316,28 +317,26 @@ static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
|
||||
dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
|
||||
ram_src_cidx = ping_size;
|
||||
ram_dst_cidx = -ping_size;
|
||||
edma_set_src(link, asp_src_pong, INCR, W8BIT);
|
||||
edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT);
|
||||
|
||||
link = prtd->asp_link[0];
|
||||
edma_set_src_index(link, data_type, data_type * fifo_level);
|
||||
link = prtd->asp_link[1];
|
||||
edma_set_src_index(link, data_type, data_type * fifo_level);
|
||||
edma_set_src_index(prtd->asp_link[0], data_type,
|
||||
data_type * fifo_level);
|
||||
edma_set_src_index(prtd->asp_link[1], data_type,
|
||||
data_type * fifo_level);
|
||||
|
||||
link = prtd->ram_link;
|
||||
edma_set_src(link, runtime->dma_addr, INCR, W32BIT);
|
||||
edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
|
||||
} else {
|
||||
dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
|
||||
ram_src_cidx = -ping_size;
|
||||
ram_dst_cidx = ping_size;
|
||||
edma_set_dest(link, asp_dst_pong, INCR, W8BIT);
|
||||
edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT);
|
||||
|
||||
link = prtd->asp_link[0];
|
||||
edma_set_dest_index(link, data_type, data_type * fifo_level);
|
||||
link = prtd->asp_link[1];
|
||||
edma_set_dest_index(link, data_type, data_type * fifo_level);
|
||||
edma_set_dest_index(prtd->asp_link[0], data_type,
|
||||
data_type * fifo_level);
|
||||
edma_set_dest_index(prtd->asp_link[1], data_type,
|
||||
data_type * fifo_level);
|
||||
|
||||
link = prtd->ram_link;
|
||||
edma_set_dest(link, runtime->dma_addr, INCR, W32BIT);
|
||||
edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
|
||||
}
|
||||
|
||||
if (!fifo_level) {
|
||||
@ -354,10 +353,9 @@ static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
|
||||
count, fifo_level, ABSYNC);
|
||||
}
|
||||
|
||||
link = prtd->ram_link;
|
||||
edma_set_src_index(link, ping_size, ram_src_cidx);
|
||||
edma_set_dest_index(link, ping_size, ram_dst_cidx);
|
||||
edma_set_transfer_params(link, ping_size, 2,
|
||||
edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx);
|
||||
edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx);
|
||||
edma_set_transfer_params(prtd->ram_link, ping_size, 2,
|
||||
runtime->periods, 2, ASYNC);
|
||||
|
||||
/* init master params */
|
||||
@ -406,32 +404,32 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
|
||||
{
|
||||
dma_addr_t asp_src_ping;
|
||||
dma_addr_t asp_dst_ping;
|
||||
int link;
|
||||
int ret;
|
||||
struct davinci_pcm_dma_params *params = prtd->params;
|
||||
|
||||
/* Request ram master channel */
|
||||
link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
|
||||
ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
|
||||
davinci_pcm_dma_irq, substream,
|
||||
prtd->params->ram_chan_q);
|
||||
if (link < 0)
|
||||
if (ret < 0)
|
||||
goto exit1;
|
||||
|
||||
/* Request ram link channel */
|
||||
link = prtd->ram_link = edma_alloc_slot(
|
||||
ret = prtd->ram_link = edma_alloc_slot(
|
||||
EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
|
||||
if (link < 0)
|
||||
if (ret < 0)
|
||||
goto exit2;
|
||||
|
||||
link = prtd->asp_link[1] = edma_alloc_slot(
|
||||
ret = prtd->asp_link[1] = edma_alloc_slot(
|
||||
EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
|
||||
if (link < 0)
|
||||
if (ret < 0)
|
||||
goto exit3;
|
||||
|
||||
prtd->ram_link2 = -1;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
link = prtd->ram_link2 = edma_alloc_slot(
|
||||
ret = prtd->ram_link2 = edma_alloc_slot(
|
||||
EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
|
||||
if (link < 0)
|
||||
if (ret < 0)
|
||||
goto exit4;
|
||||
}
|
||||
/* circle ping-pong buffers */
|
||||
@ -448,36 +446,33 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
|
||||
asp_dst_ping = iram_dma->addr;
|
||||
}
|
||||
/* ping */
|
||||
link = prtd->asp_link[0];
|
||||
edma_set_src(link, asp_src_ping, INCR, W16BIT);
|
||||
edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
|
||||
edma_set_src_index(link, 0, 0);
|
||||
edma_set_dest_index(link, 0, 0);
|
||||
edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT);
|
||||
edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT);
|
||||
edma_set_src_index(prtd->asp_link[0], 0, 0);
|
||||
edma_set_dest_index(prtd->asp_link[0], 0, 0);
|
||||
|
||||
edma_read_slot(link, &prtd->asp_params);
|
||||
edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
|
||||
prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
|
||||
prtd->asp_params.opt |= TCCHEN |
|
||||
EDMA_TCC(prtd->ram_channel & 0x3f);
|
||||
edma_write_slot(link, &prtd->asp_params);
|
||||
edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
|
||||
|
||||
/* pong */
|
||||
link = prtd->asp_link[1];
|
||||
edma_set_src(link, asp_src_ping, INCR, W16BIT);
|
||||
edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
|
||||
edma_set_src_index(link, 0, 0);
|
||||
edma_set_dest_index(link, 0, 0);
|
||||
edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT);
|
||||
edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT);
|
||||
edma_set_src_index(prtd->asp_link[1], 0, 0);
|
||||
edma_set_dest_index(prtd->asp_link[1], 0, 0);
|
||||
|
||||
edma_read_slot(link, &prtd->asp_params);
|
||||
edma_read_slot(prtd->asp_link[1], &prtd->asp_params);
|
||||
prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
|
||||
/* interrupt after every pong completion */
|
||||
prtd->asp_params.opt |= TCINTEN | TCCHEN |
|
||||
EDMA_TCC(prtd->ram_channel & 0x3f);
|
||||
edma_write_slot(link, &prtd->asp_params);
|
||||
edma_write_slot(prtd->asp_link[1], &prtd->asp_params);
|
||||
|
||||
/* ram */
|
||||
link = prtd->ram_link;
|
||||
edma_set_src(link, iram_dma->addr, INCR, W32BIT);
|
||||
edma_set_dest(link, iram_dma->addr, INCR, W32BIT);
|
||||
edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
|
||||
edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
|
||||
pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
|
||||
"for asp:%u %u %u\n", __func__,
|
||||
prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
|
||||
@ -494,7 +489,7 @@ exit2:
|
||||
edma_free_channel(prtd->ram_channel);
|
||||
prtd->ram_channel = -1;
|
||||
exit1:
|
||||
return link;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
|
||||
@ -502,22 +497,22 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
|
||||
struct snd_dma_buffer *iram_dma;
|
||||
struct davinci_runtime_data *prtd = substream->runtime->private_data;
|
||||
struct davinci_pcm_dma_params *params = prtd->params;
|
||||
int link;
|
||||
int ret;
|
||||
|
||||
if (!params)
|
||||
return -ENODEV;
|
||||
|
||||
/* Request asp master DMA channel */
|
||||
link = prtd->asp_channel = edma_alloc_channel(params->channel,
|
||||
ret = prtd->asp_channel = edma_alloc_channel(params->channel,
|
||||
davinci_pcm_dma_irq, substream,
|
||||
prtd->params->asp_chan_q);
|
||||
if (link < 0)
|
||||
if (ret < 0)
|
||||
goto exit1;
|
||||
|
||||
/* Request asp link channels */
|
||||
link = prtd->asp_link[0] = edma_alloc_slot(
|
||||
ret = prtd->asp_link[0] = edma_alloc_slot(
|
||||
EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
|
||||
if (link < 0)
|
||||
if (ret < 0)
|
||||
goto exit2;
|
||||
|
||||
iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data;
|
||||
@ -537,17 +532,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
|
||||
* the buffer and its length (ccnt) ... use it as a template
|
||||
* so davinci_pcm_enqueue_dma() takes less time in IRQ.
|
||||
*/
|
||||
edma_read_slot(link, &prtd->asp_params);
|
||||
edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
|
||||
prtd->asp_params.opt |= TCINTEN |
|
||||
EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
|
||||
prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5;
|
||||
edma_write_slot(link, &prtd->asp_params);
|
||||
prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5;
|
||||
edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
|
||||
return 0;
|
||||
exit2:
|
||||
edma_free_channel(prtd->asp_channel);
|
||||
prtd->asp_channel = -1;
|
||||
exit1:
|
||||
return link;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
|
@ -28,12 +28,6 @@
|
||||
#include <mach/hardware.h>
|
||||
#include "ep93xx-pcm.h"
|
||||
|
||||
#define edb93xx_has_audio() (machine_is_edb9301() || \
|
||||
machine_is_edb9302() || \
|
||||
machine_is_edb9302a() || \
|
||||
machine_is_edb9307a() || \
|
||||
machine_is_edb9315a())
|
||||
|
||||
static int edb93xx_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
@ -94,49 +88,61 @@ static struct snd_soc_card snd_soc_edb93xx = {
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct platform_device *edb93xx_snd_device;
|
||||
|
||||
static int __init edb93xx_init(void)
|
||||
static int __devinit edb93xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &snd_soc_edb93xx;
|
||||
int ret;
|
||||
|
||||
if (!edb93xx_has_audio())
|
||||
return -ENODEV;
|
||||
|
||||
ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
|
||||
EP93XX_SYSCON_I2SCLKDIV_ORIDE |
|
||||
EP93XX_SYSCON_I2SCLKDIV_SPOL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!edb93xx_snd_device) {
|
||||
ret = -ENOMEM;
|
||||
goto free_i2s;
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
|
||||
ret);
|
||||
ep93xx_i2s_release();
|
||||
}
|
||||
|
||||
platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
|
||||
ret = platform_device_add(edb93xx_snd_device);
|
||||
if (ret)
|
||||
goto device_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit edb93xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
ep93xx_i2s_release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_put:
|
||||
platform_device_put(edb93xx_snd_device);
|
||||
free_i2s:
|
||||
ep93xx_i2s_release();
|
||||
return ret;
|
||||
static struct platform_driver edb93xx_driver = {
|
||||
.driver = {
|
||||
.name = "edb93xx-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = edb93xx_probe,
|
||||
.remove = __devexit_p(edb93xx_remove),
|
||||
};
|
||||
|
||||
static int __init edb93xx_init(void)
|
||||
{
|
||||
return platform_driver_register(&edb93xx_driver);
|
||||
}
|
||||
module_init(edb93xx_init);
|
||||
|
||||
static void __exit edb93xx_exit(void)
|
||||
{
|
||||
platform_device_unregister(edb93xx_snd_device);
|
||||
ep93xx_i2s_release();
|
||||
platform_driver_unregister(&edb93xx_driver);
|
||||
}
|
||||
module_exit(edb93xx_exit);
|
||||
|
||||
MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
|
||||
MODULE_DESCRIPTION("ALSA SoC EDB93xx");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:edb93xx-audio");
|
||||
|
@ -355,3 +355,4 @@ module_exit(ep93xx_soc_platform_exit);
|
||||
MODULE_AUTHOR("Ryan Mallon");
|
||||
MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:ep93xx-pcm-audio");
|
||||
|
@ -39,53 +39,61 @@ static struct snd_soc_card snd_soc_simone = {
|
||||
};
|
||||
|
||||
static struct platform_device *simone_snd_ac97_device;
|
||||
static struct platform_device *simone_snd_device;
|
||||
|
||||
static int __devinit simone_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &snd_soc_simone;
|
||||
int ret;
|
||||
|
||||
simone_snd_ac97_device = platform_device_register_simple("ac97-codec",
|
||||
-1, NULL, 0);
|
||||
if (IS_ERR(simone_snd_ac97_device))
|
||||
return PTR_ERR(simone_snd_ac97_device);
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
|
||||
ret);
|
||||
platform_device_unregister(simone_snd_ac97_device);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit simone_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
platform_device_unregister(simone_snd_ac97_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver simone_driver = {
|
||||
.driver = {
|
||||
.name = "simone-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = simone_probe,
|
||||
.remove = __devexit_p(simone_remove),
|
||||
};
|
||||
|
||||
static int __init simone_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!machine_is_sim_one())
|
||||
return -ENODEV;
|
||||
|
||||
simone_snd_ac97_device = platform_device_alloc("ac97-codec", -1);
|
||||
if (!simone_snd_ac97_device)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = platform_device_add(simone_snd_ac97_device);
|
||||
if (ret)
|
||||
goto fail1;
|
||||
|
||||
simone_snd_device = platform_device_alloc("soc-audio", -1);
|
||||
if (!simone_snd_device) {
|
||||
ret = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
platform_set_drvdata(simone_snd_device, &snd_soc_simone);
|
||||
ret = platform_device_add(simone_snd_device);
|
||||
if (ret)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
platform_device_put(simone_snd_device);
|
||||
fail2:
|
||||
platform_device_del(simone_snd_ac97_device);
|
||||
fail1:
|
||||
platform_device_put(simone_snd_ac97_device);
|
||||
return ret;
|
||||
return platform_driver_register(&simone_driver);
|
||||
}
|
||||
module_init(simone_init);
|
||||
|
||||
static void __exit simone_exit(void)
|
||||
{
|
||||
platform_device_unregister(simone_snd_device);
|
||||
platform_device_unregister(simone_snd_ac97_device);
|
||||
platform_driver_unregister(&simone_driver);
|
||||
}
|
||||
module_exit(simone_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
|
||||
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:simone-audio");
|
||||
|
@ -104,37 +104,56 @@ static struct snd_soc_card snd_soc_snappercl15 = {
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
static struct platform_device *snappercl15_snd_device;
|
||||
|
||||
static int __init snappercl15_init(void)
|
||||
static int __devinit snappercl15_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = &snd_soc_snappercl15;
|
||||
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, &snd_soc_snappercl15);
|
||||
ret = platform_device_add(snappercl15_snd_device);
|
||||
if (ret)
|
||||
platform_device_put(snappercl15_snd_device);
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
|
||||
ret);
|
||||
ep93xx_i2s_release();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit snappercl15_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_card(card);
|
||||
ep93xx_i2s_release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver snappercl15_driver = {
|
||||
.driver = {
|
||||
.name = "snappercl15-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = snappercl15_probe,
|
||||
.remove = __devexit_p(snappercl15_remove),
|
||||
};
|
||||
|
||||
static int __init snappercl15_init(void)
|
||||
{
|
||||
return platform_driver_register(&snappercl15_driver);
|
||||
}
|
||||
|
||||
static void __exit snappercl15_exit(void)
|
||||
{
|
||||
platform_device_unregister(snappercl15_snd_device);
|
||||
ep93xx_i2s_release();
|
||||
platform_driver_unregister(&snappercl15_driver);
|
||||
}
|
||||
|
||||
module_init(snappercl15_init);
|
||||
@ -143,4 +162,4 @@ module_exit(snappercl15_exit);
|
||||
MODULE_AUTHOR("Ryan Mallon");
|
||||
MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS("platform:snappercl15-audio");
|
||||
|
@ -297,7 +297,6 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
|
||||
static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_card *card = rtd->card->snd_card;
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
static u64 fsl_dma_dmamask = DMA_BIT_MASK(36);
|
||||
int ret;
|
||||
|
@ -78,7 +78,6 @@
|
||||
* @second_stream: pointer to second stream
|
||||
* @playback: the number of playback streams opened
|
||||
* @capture: the number of capture streams opened
|
||||
* @asynchronous: 0=synchronous mode, 1=asynchronous mode
|
||||
* @cpu_dai: the CPU DAI for this device
|
||||
* @dev_attr: the sysfs device attribute structure
|
||||
* @stats: SSI statistics
|
||||
@ -90,9 +89,6 @@ struct fsl_ssi_private {
|
||||
unsigned int irq;
|
||||
struct snd_pcm_substream *first_stream;
|
||||
struct snd_pcm_substream *second_stream;
|
||||
unsigned int playback;
|
||||
unsigned int capture;
|
||||
int asynchronous;
|
||||
unsigned int fifo_depth;
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
struct device_attribute dev_attr;
|
||||
@ -281,24 +277,18 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
struct fsl_ssi_private *ssi_private =
|
||||
snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
|
||||
|
||||
/*
|
||||
* If this is the first stream opened, then request the IRQ
|
||||
* and initialize the SSI registers.
|
||||
*/
|
||||
if (!ssi_private->playback && !ssi_private->capture) {
|
||||
if (!ssi_private->first_stream) {
|
||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||
int ret;
|
||||
|
||||
/* The 'name' should not have any slashes in it. */
|
||||
ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0,
|
||||
ssi_private->name, ssi_private);
|
||||
if (ret < 0) {
|
||||
dev_err(substream->pcm->card->dev,
|
||||
"could not claim irq %u\n", ssi_private->irq);
|
||||
return ret;
|
||||
}
|
||||
ssi_private->first_stream = substream;
|
||||
|
||||
/*
|
||||
* Section 16.5 of the MPC8610 reference manual says that the
|
||||
@ -316,7 +306,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
clrsetbits_be32(&ssi->scr,
|
||||
CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
|
||||
CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
|
||||
| (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
|
||||
| (synchronous ? CCSR_SSI_SCR_SYN : 0));
|
||||
|
||||
out_be32(&ssi->stcr,
|
||||
CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
|
||||
@ -333,7 +323,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
* master.
|
||||
*/
|
||||
|
||||
/* 4. Enable the interrupts and DMA requests */
|
||||
/* Enable the interrupts and DMA requests */
|
||||
out_be32(&ssi->sier, SIER_FLAGS);
|
||||
|
||||
/*
|
||||
@ -362,58 +352,47 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
|
||||
* this is bad is because at this point, the PCM driver has not
|
||||
* finished initializing the DMA controller.
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
if (synchronous) {
|
||||
struct snd_pcm_runtime *first_runtime =
|
||||
ssi_private->first_stream->runtime;
|
||||
/*
|
||||
* This is the second stream open, and we're in
|
||||
* synchronous mode, so we need to impose sample
|
||||
* sample size constraints. This is because STCCR is
|
||||
* used for playback and capture in synchronous mode,
|
||||
* so there's no way to specify different word
|
||||
* lengths.
|
||||
*
|
||||
* Note that this can cause a race condition if the
|
||||
* second stream is opened before the first stream is
|
||||
* fully initialized. We provide some protection by
|
||||
* checking to make sure the first stream is
|
||||
* initialized, but it's not perfect. ALSA sometimes
|
||||
* re-initializes the driver with a different sample
|
||||
* rate or size. If the second stream is opened
|
||||
* before the first stream has received its final
|
||||
* parameters, then the second stream may be
|
||||
* constrained to the wrong sample rate or size.
|
||||
*/
|
||||
if (!first_runtime->sample_bits) {
|
||||
dev_err(substream->pcm->card->dev,
|
||||
"set sample size in %s stream first\n",
|
||||
substream->stream ==
|
||||
SNDRV_PCM_STREAM_PLAYBACK
|
||||
? "capture" : "playback");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (!ssi_private->first_stream)
|
||||
ssi_private->first_stream = substream;
|
||||
else {
|
||||
/* This is the second stream open, so we need to impose sample
|
||||
* rate and maybe sample size constraints. Note that this can
|
||||
* cause a race condition if the second stream is opened before
|
||||
* the first stream is fully initialized.
|
||||
*
|
||||
* We provide some protection by checking to make sure the first
|
||||
* stream is initialized, but it's not perfect. ALSA sometimes
|
||||
* re-initializes the driver with a different sample rate or
|
||||
* size. If the second stream is opened before the first stream
|
||||
* has received its final parameters, then the second stream may
|
||||
* be constrained to the wrong sample rate or size.
|
||||
*
|
||||
* FIXME: This code does not handle opening and closing streams
|
||||
* repeatedly. If you open two streams and then close the first
|
||||
* one, you may not be able to open another stream until you
|
||||
* close the second one as well.
|
||||
*/
|
||||
struct snd_pcm_runtime *first_runtime =
|
||||
ssi_private->first_stream->runtime;
|
||||
|
||||
if (!first_runtime->sample_bits) {
|
||||
dev_err(substream->pcm->card->dev,
|
||||
"set sample size in %s stream first\n",
|
||||
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
||||
? "capture" : "playback");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* If we're in synchronous mode, then we need to constrain
|
||||
* the sample size as well. We don't support independent sample
|
||||
* rates in asynchronous mode.
|
||||
*/
|
||||
if (!ssi_private->asynchronous)
|
||||
snd_pcm_hw_constraint_minmax(substream->runtime,
|
||||
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
|
||||
first_runtime->sample_bits,
|
||||
first_runtime->sample_bits);
|
||||
}
|
||||
|
||||
ssi_private->second_stream = substream;
|
||||
}
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ssi_private->playback++;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ssi_private->capture++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -434,24 +413,35 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||
unsigned int sample_size =
|
||||
snd_pcm_format_width(params_format(hw_params));
|
||||
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
|
||||
int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
|
||||
|
||||
if (substream == ssi_private->first_stream) {
|
||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||
unsigned int sample_size =
|
||||
snd_pcm_format_width(params_format(hw_params));
|
||||
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
|
||||
/*
|
||||
* If we're in synchronous mode, and the SSI is already enabled,
|
||||
* then STCCR is already set properly.
|
||||
*/
|
||||
if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
|
||||
return 0;
|
||||
|
||||
/* The SSI should always be disabled at this points (SSIEN=0) */
|
||||
/*
|
||||
* FIXME: The documentation says that SxCCR[WL] should not be
|
||||
* modified while the SSI is enabled. The only time this can
|
||||
* happen is if we're trying to do simultaneous playback and
|
||||
* capture in asynchronous mode. Unfortunately, I have been enable
|
||||
* to get that to work at all on the P1022DS. Therefore, we don't
|
||||
* bother to disable/enable the SSI when setting SxCCR[WL], because
|
||||
* the SSI will stop anyway. Maybe one day, this will get fixed.
|
||||
*/
|
||||
|
||||
/* In synchronous mode, the SSI uses STCCR for capture */
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
|
||||
!ssi_private->asynchronous)
|
||||
clrsetbits_be32(&ssi->stccr,
|
||||
CCSR_SSI_SxCCR_WL_MASK, wl);
|
||||
else
|
||||
clrsetbits_be32(&ssi->srccr,
|
||||
CCSR_SSI_SxCCR_WL_MASK, wl);
|
||||
}
|
||||
/* In synchronous mode, the SSI uses STCCR for capture */
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
|
||||
ssi_private->cpu_dai_drv.symmetric_rates)
|
||||
clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
|
||||
else
|
||||
clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -474,7 +464,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
setbits32(&ssi->scr,
|
||||
@ -510,27 +499,18 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
ssi_private->playback--;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
|
||||
ssi_private->capture--;
|
||||
|
||||
if (ssi_private->first_stream == substream)
|
||||
ssi_private->first_stream = ssi_private->second_stream;
|
||||
|
||||
ssi_private->second_stream = NULL;
|
||||
|
||||
/*
|
||||
* If this is the last active substream, disable the SSI and release
|
||||
* the IRQ.
|
||||
* If this is the last active substream, disable the SSI.
|
||||
*/
|
||||
if (!ssi_private->playback && !ssi_private->capture) {
|
||||
if (!ssi_private->first_stream) {
|
||||
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
|
||||
|
||||
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
|
||||
|
||||
free_irq(ssi_private->irq, ssi_private);
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,22 +655,33 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
|
||||
ret = of_address_to_resource(np, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not determine device resources\n");
|
||||
kfree(ssi_private);
|
||||
return ret;
|
||||
goto error_kmalloc;
|
||||
}
|
||||
ssi_private->ssi = of_iomap(np, 0);
|
||||
if (!ssi_private->ssi) {
|
||||
dev_err(&pdev->dev, "could not map device resources\n");
|
||||
kfree(ssi_private);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto error_kmalloc;
|
||||
}
|
||||
ssi_private->ssi_phys = res.start;
|
||||
|
||||
ssi_private->irq = irq_of_parse_and_map(np, 0);
|
||||
if (ssi_private->irq == NO_IRQ) {
|
||||
dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
|
||||
ret = -ENXIO;
|
||||
goto error_iomap;
|
||||
}
|
||||
|
||||
/* The 'name' should not have any slashes in it. */
|
||||
ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
|
||||
ssi_private);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
|
||||
goto error_irqmap;
|
||||
}
|
||||
|
||||
/* Are the RX and the TX clocks locked? */
|
||||
if (of_find_property(np, "fsl,ssi-asynchronous", NULL))
|
||||
ssi_private->asynchronous = 1;
|
||||
else
|
||||
if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
|
||||
ssi_private->cpu_dai_drv.symmetric_rates = 1;
|
||||
|
||||
/* Determine the FIFO depth. */
|
||||
@ -711,7 +702,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not create sysfs %s file\n",
|
||||
ssi_private->dev_attr.attr.name);
|
||||
goto error;
|
||||
goto error_irq;
|
||||
}
|
||||
|
||||
/* Register with ASoC */
|
||||
@ -720,7 +711,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
|
||||
ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
|
||||
goto error;
|
||||
goto error_dev;
|
||||
}
|
||||
|
||||
/* Trigger the machine driver's probe function. The platform driver
|
||||
@ -741,18 +732,28 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(ssi_private->pdev)) {
|
||||
ret = PTR_ERR(ssi_private->pdev);
|
||||
dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
|
||||
goto error;
|
||||
goto error_dai;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
error_dai:
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
|
||||
error_dev:
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
if (dev_attr)
|
||||
device_remove_file(&pdev->dev, dev_attr);
|
||||
device_remove_file(&pdev->dev, dev_attr);
|
||||
|
||||
error_irq:
|
||||
free_irq(ssi_private->irq, ssi_private);
|
||||
|
||||
error_irqmap:
|
||||
irq_dispose_mapping(ssi_private->irq);
|
||||
|
||||
error_iomap:
|
||||
iounmap(ssi_private->ssi);
|
||||
|
||||
error_kmalloc:
|
||||
kfree(ssi_private);
|
||||
|
||||
return ret;
|
||||
@ -766,6 +767,9 @@ static int fsl_ssi_remove(struct platform_device *pdev)
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
device_remove_file(&pdev->dev, &ssi_private->dev_attr);
|
||||
|
||||
free_irq(ssi_private->irq, ssi_private);
|
||||
irq_dispose_mapping(ssi_private->irq);
|
||||
|
||||
kfree(ssi_private);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
|
@ -505,7 +505,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
error_sound:
|
||||
platform_device_unregister(sound_device);
|
||||
platform_device_put(sound_device);
|
||||
error:
|
||||
kfree(machine_data);
|
||||
error_alloc:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user