1
0
Fork 0

Merge branch 'topic/asoc' into topic/remove-irqf_disable

hifive-unleashed-5.1
Takashi Iwai 2011-09-22 09:56:12 +02:00
commit af1910a817
131 changed files with 6681 additions and 1219 deletions

View File

@ -0,0 +1,11 @@
* Freescale SGTL5000 Stereo Codec
Required properties:
- compatible : "fsl,sgtl5000".
Example:
codec: sgtl5000@0a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View 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>;
};

View File

@ -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);
}
}

View File

@ -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")

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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__ */

View File

@ -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)
*/

View File

@ -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,

View File

@ -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;

View File

@ -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

View 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

View 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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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/

View File

@ -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;

View File

@ -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

View File

@ -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

View 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");

View 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");

View File

@ -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);

View File

@ -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");

View 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");

View 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");

View File

@ -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 */

View File

@ -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));

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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")) {

View 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");

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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);

View File

@ -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);

View File

@ -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),

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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),

View File

@ -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),

View File

@ -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,

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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

View File

@ -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),

View File

@ -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),

View File

@ -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)

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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);

View File

@ -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 */

View File

@ -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:

View File

@ -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];

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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" },

View File

@ -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;

View File

@ -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,
},

View File

@ -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)

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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