提交 e0b3032b 编写于 作者: T Takashi Iwai

Merge branch 'topic/asoc' into for-linus

* topic/asoc: (226 commits)
  ASoC: au1x: PSC-AC97 bugfixes
  ASoC: Fix WM835x Out4 capture enumeration
  ASoC: Remove unuused hw_read_t
  ASoC: fix pxa2xx-ac97.c breakage
  ASoC: Fully specify DC servo bits to update in wm_hubs
  ASoC: Debugged improper setting of PLL fields in WM8580 driver
  ASoC: new board driver to connect bfin-5xx with ad1836 codec
  ASoC: OMAP: Add functionality to set CLKR and FSR sources in McBSP DAI
  ASoC: davinci: i2c device creation moved into board files
  ASoC: Don't reconfigure WM8350 FLL if not needed
  ASoC: Fix s3c-i2s-v2 build
  ASoC: Make platform data optional for TLV320AIC3x
  ASoC: Add S3C24xx dependencies for Simtec machines
  ASoC: SDP3430: Fix TWL GPIO6 pin mux request
  ASoC: S3C platform: Fix s3c2410_dma_started() called at improper time
  ARM: OMAP: McBSP: Merge two functions into omap_mcbsp_start/_stop
  ASoC: OMAP: Fix setup of XCCR and RCCR registers in McBSP DAI
  OMAP: McBSP: Use textual values in DMA operating mode sysfs files
  ARM: OMAP: DMA: Add support for DMA channel self linking on OMAP1510
  ASoC: Select core DMA when building for S3C64xx
  ...
......@@ -128,6 +128,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP1_IRQ_RX,
.tx_irq = INT_24XX_MCBSP1_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
},
{
.phys_base = OMAP34XX_MCBSP2_BASE,
......@@ -136,6 +137,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP2_IRQ_RX,
.tx_irq = INT_24XX_MCBSP2_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x3FF,
},
{
.phys_base = OMAP34XX_MCBSP3_BASE,
......@@ -144,6 +146,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP3_IRQ_RX,
.tx_irq = INT_24XX_MCBSP3_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
},
{
.phys_base = OMAP34XX_MCBSP4_BASE,
......@@ -152,6 +155,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP4_IRQ_RX,
.tx_irq = INT_24XX_MCBSP4_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
},
{
.phys_base = OMAP34XX_MCBSP5_BASE,
......@@ -160,6 +164,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
.rx_irq = INT_24XX_MCBSP5_IRQ_RX,
.tx_irq = INT_24XX_MCBSP5_IRQ_TX,
.ops = &omap2_mcbsp_ops,
.buffer_size = 0x6F,
},
};
#define OMAP34XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap34xx_mcbsp_pdata)
......
......@@ -3,10 +3,12 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
/*
* @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
* a -1 value means no gpio will be used for reset
* @codec_pdata: AC97 codec platform_data
* reset_gpio should only be specified for pxa27x CPUs where a silicon
* bug prevents correct operation of the reset line. If not specified,
......@@ -20,6 +22,7 @@ typedef struct {
void (*resume)(void *);
void *priv;
int reset_gpio;
void *codec_pdata[AC97_BUS_MAX_DEVICES];
} pxa2xx_audio_ops_t;
extern void pxa_set_ac97_info(pxa2xx_audio_ops_t *ops);
......
......@@ -1127,6 +1127,11 @@ int omap_dma_running(void)
void omap_dma_link_lch(int lch_head, int lch_queue)
{
if (omap_dma_in_1510_mode()) {
if (lch_head == lch_queue) {
dma_write(dma_read(CCR(lch_head)) | (3 << 8),
CCR(lch_head));
return;
}
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
BUG();
return;
......@@ -1149,6 +1154,11 @@ EXPORT_SYMBOL(omap_dma_link_lch);
void omap_dma_unlink_lch(int lch_head, int lch_queue)
{
if (omap_dma_in_1510_mode()) {
if (lch_head == lch_queue) {
dma_write(dma_read(CCR(lch_head)) & ~(3 << 8),
CCR(lch_head));
return;
}
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
BUG();
return;
......
......@@ -134,6 +134,11 @@
#define OMAP_MCBSP_REG_XCERG 0x74
#define OMAP_MCBSP_REG_XCERH 0x78
#define OMAP_MCBSP_REG_SYSCON 0x8C
#define OMAP_MCBSP_REG_THRSH2 0x90
#define OMAP_MCBSP_REG_THRSH1 0x94
#define OMAP_MCBSP_REG_IRQST 0xA0
#define OMAP_MCBSP_REG_IRQEN 0xA4
#define OMAP_MCBSP_REG_WAKEUPEN 0xA8
#define OMAP_MCBSP_REG_XCCR 0xAC
#define OMAP_MCBSP_REG_RCCR 0xB0
......@@ -249,8 +254,27 @@
#define RDISABLE 0x0001
/********************** McBSP SYSCONFIG bit definitions ********************/
#define CLOCKACTIVITY(value) ((value)<<8)
#define SIDLEMODE(value) ((value)<<3)
#define ENAWAKEUP 0x0004
#define SOFTRST 0x0002
/********************** McBSP DMA operating modes **************************/
#define MCBSP_DMA_MODE_ELEMENT 0
#define MCBSP_DMA_MODE_THRESHOLD 1
#define MCBSP_DMA_MODE_FRAME 2
/********************** McBSP WAKEUPEN bit definitions *********************/
#define XEMPTYEOFEN 0x4000
#define XRDYEN 0x0400
#define XEOFEN 0x0200
#define XFSXEN 0x0100
#define XSYNCERREN 0x0080
#define RRDYEN 0x0008
#define REOFEN 0x0004
#define RFSREN 0x0002
#define RSYNCERREN 0x0001
/* we don't do multichannel for now */
struct omap_mcbsp_reg_cfg {
u16 spcr2;
......@@ -344,6 +368,9 @@ struct omap_mcbsp_platform_data {
u8 dma_rx_sync, dma_tx_sync;
u16 rx_irq, tx_irq;
struct omap_mcbsp_ops *ops;
#ifdef CONFIG_ARCH_OMAP34XX
u16 buffer_size;
#endif
};
struct omap_mcbsp {
......@@ -377,6 +404,11 @@ struct omap_mcbsp {
struct omap_mcbsp_platform_data *pdata;
struct clk *iclk;
struct clk *fclk;
#ifdef CONFIG_ARCH_OMAP34XX
int dma_op_mode;
u16 max_tx_thres;
u16 max_rx_thres;
#endif
};
extern struct omap_mcbsp **mcbsp_ptr;
extern int omap_mcbsp_count;
......@@ -385,10 +417,25 @@ int omap_mcbsp_init(void);
void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
int size);
void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config);
#ifdef CONFIG_ARCH_OMAP34XX
void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold);
void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
int omap_mcbsp_get_dma_op_mode(unsigned int id);
#else
static inline void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
{ }
static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
{ }
static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }
#endif
int omap_mcbsp_request(unsigned int id);
void omap_mcbsp_free(unsigned int id);
void omap_mcbsp_start(unsigned int id);
void omap_mcbsp_stop(unsigned int id);
void omap_mcbsp_start(unsigned int id, int tx, int rx);
void omap_mcbsp_stop(unsigned int id, int tx, int rx);
void omap_mcbsp_xmit_word(unsigned int id, u32 word);
u32 omap_mcbsp_recv_word(unsigned int id);
......
......@@ -198,6 +198,170 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
}
EXPORT_SYMBOL(omap_mcbsp_config);
#ifdef CONFIG_ARCH_OMAP34XX
/*
* omap_mcbsp_set_tx_threshold configures how to deal
* with transmit threshold. the threshold value and handler can be
* configure in here.
*/
void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
{
struct omap_mcbsp *mcbsp;
void __iomem *io_base;
if (!cpu_is_omap34xx())
return;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
return;
}
mcbsp = id_to_mcbsp_ptr(id);
io_base = mcbsp->io_base;
OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
}
EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
/*
* omap_mcbsp_set_rx_threshold configures how to deal
* with receive threshold. the threshold value and handler can be
* configure in here.
*/
void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
{
struct omap_mcbsp *mcbsp;
void __iomem *io_base;
if (!cpu_is_omap34xx())
return;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
return;
}
mcbsp = id_to_mcbsp_ptr(id);
io_base = mcbsp->io_base;
OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
}
EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
/*
* omap_mcbsp_get_max_tx_thres just return the current configured
* maximum threshold for transmission
*/
u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
{
struct omap_mcbsp *mcbsp;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
return -ENODEV;
}
mcbsp = id_to_mcbsp_ptr(id);
return mcbsp->max_tx_thres;
}
EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
/*
* omap_mcbsp_get_max_rx_thres just return the current configured
* maximum threshold for reception
*/
u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
{
struct omap_mcbsp *mcbsp;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
return -ENODEV;
}
mcbsp = id_to_mcbsp_ptr(id);
return mcbsp->max_rx_thres;
}
EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
/*
* omap_mcbsp_get_dma_op_mode just return the current configured
* operating mode for the mcbsp channel
*/
int omap_mcbsp_get_dma_op_mode(unsigned int id)
{
struct omap_mcbsp *mcbsp;
int dma_op_mode;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
return -ENODEV;
}
mcbsp = id_to_mcbsp_ptr(id);
spin_lock_irq(&mcbsp->lock);
dma_op_mode = mcbsp->dma_op_mode;
spin_unlock_irq(&mcbsp->lock);
return dma_op_mode;
}
EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
{
/*
* Enable wakup behavior, smart idle and all wakeups
* REVISIT: some wakeups may be unnecessary
*/
if (cpu_is_omap34xx()) {
u16 syscon;
syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
spin_lock_irq(&mcbsp->lock);
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
CLOCKACTIVITY(0x02));
OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
XRDYEN | RRDYEN);
} else {
syscon |= SIDLEMODE(0x01);
}
spin_unlock_irq(&mcbsp->lock);
OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
}
}
static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
{
/*
* Disable wakup behavior, smart idle and all wakeups
*/
if (cpu_is_omap34xx()) {
u16 syscon;
syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
/*
* HW bug workaround - If no_idle mode is taken, we need to
* go to smart_idle before going to always_idle, or the
* device will not hit retention anymore.
*/
syscon |= SIDLEMODE(0x02);
OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
syscon &= ~(SIDLEMODE(0x03));
OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
}
}
#else
static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
#endif
/*
* We can choose between IRQ based or polled IO.
* This needs to be called before omap_mcbsp_request().
......@@ -257,6 +421,9 @@ int omap_mcbsp_request(unsigned int id)
clk_enable(mcbsp->iclk);
clk_enable(mcbsp->fclk);
/* Do procedure specific to omap34xx arch, if applicable */
omap34xx_mcbsp_request(mcbsp);
/*
* Make sure that transmitter, receiver and sample-rate generator are
* not running before activating IRQs.
......@@ -305,6 +472,9 @@ void omap_mcbsp_free(unsigned int id)
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(id);
/* Do procedure specific to omap34xx arch, if applicable */
omap34xx_mcbsp_free(mcbsp);
clk_disable(mcbsp->fclk);
clk_disable(mcbsp->iclk);
......@@ -328,14 +498,15 @@ void omap_mcbsp_free(unsigned int id)
EXPORT_SYMBOL(omap_mcbsp_free);
/*
* Here we start the McBSP, by enabling the sample
* generator, both transmitter and receivers,
* and the frame sync.
* Here we start the McBSP, by enabling transmitter, receiver or both.
* If no transmitter or receiver is active prior calling, then sample-rate
* generator and frame sync are started.
*/
void omap_mcbsp_start(unsigned int id)
void omap_mcbsp_start(unsigned int id, int tx, int rx)
{
struct omap_mcbsp *mcbsp;
void __iomem *io_base;
int idle;
u16 w;
if (!omap_mcbsp_check_valid_id(id)) {
......@@ -348,32 +519,58 @@ void omap_mcbsp_start(unsigned int id)
mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
/* Start the sample generator */
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
if (idle) {
/* Start the sample generator */
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
}
/* Enable transmitter and receiver */
tx &= 1;
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1);
OMAP_MCBSP_WRITE(io_base, SPCR2, w | tx);
rx &= 1;
w = OMAP_MCBSP_READ(io_base, SPCR1);
OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1);
OMAP_MCBSP_WRITE(io_base, SPCR1, w | rx);
udelay(100);
/*
* Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
* REVISIT: 100us may give enough time for two CLKSRG, however
* due to some unknown PM related, clock gating etc. reason it
* is now at 500us.
*/
udelay(500);
/* Start frame sync */
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
if (idle) {
/* Start frame sync */
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
}
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
/* Release the transmitter and receiver */
w = OMAP_MCBSP_READ(io_base, XCCR);
w &= ~(tx ? XDISABLE : 0);
OMAP_MCBSP_WRITE(io_base, XCCR, w);
w = OMAP_MCBSP_READ(io_base, RCCR);
w &= ~(rx ? RDISABLE : 0);
OMAP_MCBSP_WRITE(io_base, RCCR, w);
}
/* Dump McBSP Regs */
omap_mcbsp_dump_reg(id);
}
EXPORT_SYMBOL(omap_mcbsp_start);
void omap_mcbsp_stop(unsigned int id)
void omap_mcbsp_stop(unsigned int id, int tx, int rx)
{
struct omap_mcbsp *mcbsp;
void __iomem *io_base;
int idle;
u16 w;
if (!omap_mcbsp_check_valid_id(id)) {
......@@ -385,16 +582,33 @@ void omap_mcbsp_stop(unsigned int id)
io_base = mcbsp->io_base;
/* Reset transmitter */
tx &= 1;
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
w = OMAP_MCBSP_READ(io_base, XCCR);
w |= (tx ? XDISABLE : 0);
OMAP_MCBSP_WRITE(io_base, XCCR, w);
}
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1));
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~tx);
/* Reset receiver */
rx &= 1;
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
w = OMAP_MCBSP_READ(io_base, RCCR);
w |= (tx ? RDISABLE : 0);
OMAP_MCBSP_WRITE(io_base, RCCR, w);
}
w = OMAP_MCBSP_READ(io_base, SPCR1);
OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1));
OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~rx);
/* Reset the sample rate generator */
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
if (idle) {
/* Reset the sample rate generator */
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
}
}
EXPORT_SYMBOL(omap_mcbsp_stop);
......@@ -883,6 +1097,149 @@ void omap_mcbsp_set_spi_mode(unsigned int id,
}
EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
#ifdef CONFIG_ARCH_OMAP34XX
#define max_thres(m) (mcbsp->pdata->buffer_size)
#define valid_threshold(m, val) ((val) <= max_thres(m))
#define THRESHOLD_PROP_BUILDER(prop) \
static ssize_t prop##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
\
return sprintf(buf, "%u\n", mcbsp->prop); \
} \
\
static ssize_t prop##_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t size) \
{ \
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
unsigned long val; \
int status; \
\
status = strict_strtoul(buf, 0, &val); \
if (status) \
return status; \
\
if (!valid_threshold(mcbsp, val)) \
return -EDOM; \
\
mcbsp->prop = val; \
return size; \
} \
\
static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
THRESHOLD_PROP_BUILDER(max_tx_thres);
THRESHOLD_PROP_BUILDER(max_rx_thres);
static const char *dma_op_modes[] = {
"element", "threshold", "frame",
};
static ssize_t dma_op_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
int dma_op_mode, i = 0;
ssize_t len = 0;
const char * const *s;
spin_lock_irq(&mcbsp->lock);
dma_op_mode = mcbsp->dma_op_mode;
spin_unlock_irq(&mcbsp->lock);
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
if (dma_op_mode == i)
len += sprintf(buf + len, "[%s] ", *s);
else
len += sprintf(buf + len, "%s ", *s);
}
len += sprintf(buf + len, "\n");
return len;
}
static ssize_t dma_op_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
const char * const *s;
int i = 0;
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
if (sysfs_streq(buf, *s))
break;
if (i == ARRAY_SIZE(dma_op_modes))
return -EINVAL;
spin_lock_irq(&mcbsp->lock);
if (!mcbsp->free) {
size = -EBUSY;
goto unlock;
}
mcbsp->dma_op_mode = i;
unlock:
spin_unlock_irq(&mcbsp->lock);
return size;
}
static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
static const struct attribute *additional_attrs[] = {
&dev_attr_max_tx_thres.attr,
&dev_attr_max_rx_thres.attr,
&dev_attr_dma_op_mode.attr,
NULL,
};
static const struct attribute_group additional_attr_group = {
.attrs = (struct attribute **)additional_attrs,
};
static inline int __devinit omap_additional_add(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &additional_attr_group);
}
static inline void __devexit omap_additional_remove(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &additional_attr_group);
}
static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
{
mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
if (cpu_is_omap34xx()) {
mcbsp->max_tx_thres = max_thres(mcbsp);
mcbsp->max_rx_thres = max_thres(mcbsp);
/*
* REVISIT: Set dmap_op_mode to THRESHOLD as default
* for mcbsp2 instances.
*/
if (omap_additional_add(mcbsp->dev))
dev_warn(mcbsp->dev,
"Unable to create additional controls\n");
} else {
mcbsp->max_tx_thres = -EINVAL;
mcbsp->max_rx_thres = -EINVAL;
}
}
static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
{
if (cpu_is_omap34xx())
omap_additional_remove(mcbsp->dev);
}
#else
static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
#endif /* CONFIG_ARCH_OMAP34XX */
/*
* McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
* 730 has only 2 McBSP, and both of them are MPU peripherals.
......@@ -953,6 +1310,10 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
mcbsp->dev = &pdev->dev;
mcbsp_ptr[id] = mcbsp;
platform_set_drvdata(pdev, mcbsp);
/* Initialize mcbsp properties for OMAP34XX if needed / applicable */
omap34xx_device_init(mcbsp);
return 0;
err_fclk:
......@@ -976,6 +1337,8 @@ static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(mcbsp->id);
omap34xx_device_exit(mcbsp);
clk_disable(mcbsp->fclk);
clk_disable(mcbsp->iclk);
clk_put(mcbsp->fclk);
......
/* arch/arm/plat-s3c/include/plat/audio-simtec.h
*
* Copyright 2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* 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.
*
* Simtec Audio support.
*/
/**
* struct s3c24xx_audio_simtec_pdata - platform data for simtec audio
* @use_mpllin: Select codec clock from MPLLin
* @output_cdclk: Need to output CDCLK to the codec
* @have_mic: Set if we have a MIC socket
* @have_lout: Set if we have a LineOut socket
* @amp_gpio: GPIO pin to enable the AMP
* @amp_gain: Option GPIO to control AMP gain
*/
struct s3c24xx_audio_simtec_pdata {
unsigned int use_mpllin:1;
unsigned int output_cdclk:1;
unsigned int have_mic:1;
unsigned int have_lout:1;
int amp_gpio;
int amp_gain[2];
void (*startup)(void);
};
extern int simtec_audio_add(const char *codec_name,
struct s3c24xx_audio_simtec_pdata *pdata);
......@@ -33,6 +33,11 @@
#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
#define S3C64XX_IISMOD_BLC_16BIT (0 << 13)
#define S3C64XX_IISMOD_BLC_8BIT (1 << 13)
#define S3C64XX_IISMOD_BLC_24BIT (2 << 13)
#define S3C64XX_IISMOD_BLC_MASK (3 << 13)
#define S3C64XX_IISMOD_IMS_PCLK (0 << 10)
#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10)
......
......@@ -23,7 +23,7 @@
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
#define NR_LDISCS 19
#define NR_LDISCS 20
/* line disciplines */
#define N_TTY 0
......@@ -47,6 +47,8 @@
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
#define N_PPS 18 /* Pulse per Second */
#define N_V253 19 /* Codec control over voice modem */
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
* a c_cc[] character, but indicates that a particular special character
......
......@@ -32,6 +32,9 @@
#include "control.h"
#include "info.h"
/* maximum number of devices on the AC97 bus */
#define AC97_BUS_MAX_DEVICES 4
/*
* AC'97 codec registers
*/
......@@ -642,4 +645,10 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime);
/* ad hoc AC97 device driver access */
extern struct bus_type ac97_bus_type;
/* AC97 platform_data adding function */
static inline void snd_ac97_dev_add_pdata(struct snd_ac97 *ac97, void *data)
{
ac97->dev.platform_data = data;
}
#endif /* __SOUND_AC97_CODEC_H */
#ifndef __SOUND_FSI_H
#define __SOUND_FSI_H
/*
* Fifo-attached Serial Interface (FSI) support for SH7724
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* flags format
* 0xABCDEEFF
*
* A: channel size for TDM (input)
* B: channel size for TDM (ooutput)
* C: inversion
* D: mode
* E: input format
* F: output format
*/
#include <linux/clk.h>
#include <sound/soc.h>
/* TDM channel */
#define SH_FSI_SET_CH_I(x) ((x & 0xF) << 28)
#define SH_FSI_SET_CH_O(x) ((x & 0xF) << 24)
#define SH_FSI_CH_IMASK 0xF0000000
#define SH_FSI_CH_OMASK 0x0F000000
#define SH_FSI_GET_CH_I(x) ((x & SH_FSI_CH_IMASK) >> 28)
#define SH_FSI_GET_CH_O(x) ((x & SH_FSI_CH_OMASK) >> 24)
/* clock inversion */
#define SH_FSI_INVERSION_MASK 0x00F00000
#define SH_FSI_LRM_INV (1 << 20)
#define SH_FSI_BRM_INV (1 << 21)
#define SH_FSI_LRS_INV (1 << 22)
#define SH_FSI_BRS_INV (1 << 23)
/* mode */
#define SH_FSI_MODE_MASK 0x000F0000
#define SH_FSI_IN_SLAVE_MODE (1 << 16) /* default master mode */
#define SH_FSI_OUT_SLAVE_MODE (1 << 17) /* default master mode */
/* DI format */
#define SH_FSI_FMT_MASK 0x000000FF
#define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
#define SH_FSI_OFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK)
#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK)
#define SH_FSI_FMT_MONO (1 << 0)
#define SH_FSI_FMT_MONO_DELAY (1 << 1)
#define SH_FSI_FMT_PCM (1 << 2)
#define SH_FSI_FMT_I2S (1 << 3)
#define SH_FSI_FMT_TDM (1 << 4)
#define SH_FSI_FMT_TDM_DELAY (1 << 5)
#define SH_FSI_IFMT_TDM_CH(x) \
(SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x))
#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
(SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
#define SH_FSI_OFMT_TDM_CH(x) \
(SH_FSI_OFMT(TDM) | SH_FSI_SET_CH_O(x))
#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
(SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
struct sh_fsi_platform_info {
unsigned long porta_flags;
unsigned long portb_flags;
};
extern struct snd_soc_dai fsi_soc_dai[2];
extern struct snd_soc_platform fsi_soc_platform;
#endif /* __SOUND_FSI_H */
......@@ -27,8 +27,8 @@ struct snd_pcm_substream;
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */
#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */
#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */
#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
/* left and right justified also known as MSB and LSB respectively */
......@@ -38,7 +38,7 @@ struct snd_pcm_substream;
/*
* DAI Clock gating.
*
* DAI bit clocks can be be gated (disabled) when not the DAI is not
* DAI bit clocks can be be gated (disabled) when the DAI is not
* sending or receiving PCM data in a frame. This can be used to save power.
*/
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
......@@ -51,21 +51,21 @@ struct snd_pcm_substream;
* format.
*/
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal BCLK + inv FRM */
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert BCLK + nor FRM */
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert BCLK + FRM */
/*
* DAI hardware clock masters.
*
* This is wrt the codec, the inverse is true for the interface
* i.e. if the codec is clk and frm master then the interface is
* i.e. if the codec is clk and FRM master then the interface is
* clk and frame slave.
*/
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & FRM master */
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & FRM master */
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & FRM slave */
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
......@@ -78,7 +78,13 @@ struct snd_pcm_substream;
#define SND_SOC_CLOCK_IN 0
#define SND_SOC_CLOCK_OUT 1
#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 |\
SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S20_3BE |\
SNDRV_PCM_FMTBIT_S24_3LE |\
SNDRV_PCM_FMTBIT_S24_3BE |\
SNDRV_PCM_FMTBIT_S32_LE |\
SNDRV_PCM_FMTBIT_S32_BE)
......@@ -106,7 +112,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int mask, int slots);
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
......@@ -116,12 +122,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
/*
* Digital Audio Interface.
*
* Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
* operations an capabilities. Codec and platfom drivers will register a this
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
* operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
* interface a
* interface.
*/
struct snd_soc_dai_ops {
/*
......@@ -140,7 +146,8 @@ struct snd_soc_dai_ops {
*/
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int mask, int slots);
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*
......@@ -179,6 +186,7 @@ struct snd_soc_dai {
int ac97_control;
struct device *dev;
void *ac97_pdata; /* platform_data for the ac97 codec */
/* DAI callbacks */
int (*probe)(struct platform_device *pdev,
......
......@@ -137,6 +137,12 @@
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
/* stream domain */
#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
.reg = wreg, .shift = wshift, .invert = winvert }
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
.reg = wreg, .shift = wshift, .invert = winvert }
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
.shift = wshift, .invert = winvert}
......@@ -279,9 +285,11 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
/* dapm events */
int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
int event);
void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
/* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev);
void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec);
/* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
......@@ -311,6 +319,8 @@ enum snd_soc_dapm_type {
snd_soc_dapm_pre, /* machine specific pre widget - exec first */
snd_soc_dapm_post, /* machine specific post widget - exec last */
snd_soc_dapm_supply, /* power/clock supply */
snd_soc_dapm_aif_in, /* audio interface input */
snd_soc_dapm_aif_out, /* audio interface output */
};
/*
......
......@@ -135,6 +135,28 @@
.info = snd_soc_info_volsw, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
xhandler_get, xhandler_put, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .shift = shift_left, .rshift = shift_right, \
.max = xmax, .invert = xinvert} }
#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
xhandler_get, xhandler_put, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw_2r, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
.max = xmax, .invert = xinvert} }
#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_bool_ext, \
......@@ -183,14 +205,28 @@ struct snd_soc_jack_gpio;
#endif
typedef int (*hw_write_t)(void *,const char* ,int);
typedef int (*hw_read_t)(void *,char* ,int);
extern struct snd_ac97_bus_ops soc_ac97_ops;
enum snd_soc_control_type {
SND_SOC_CUSTOM,
SND_SOC_I2C,
SND_SOC_SPI,
};
int snd_soc_register_platform(struct snd_soc_platform *platform);
void snd_soc_unregister_platform(struct snd_soc_platform *platform);
int snd_soc_register_codec(struct snd_soc_codec *codec);
void snd_soc_unregister_codec(struct snd_soc_codec *codec);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
#ifdef CONFIG_PM
int snd_soc_suspend_device(struct device *dev);
int snd_soc_resume_device(struct device *dev);
#endif
/* pcm <-> DAI connect */
void snd_soc_free_pcms(struct snd_soc_device *socdev);
......@@ -216,9 +252,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
/* codec register bit access */
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned short mask, unsigned short value);
unsigned int mask, unsigned int value);
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
unsigned short mask, unsigned short value);
unsigned int mask, unsigned int value);
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
struct snd_ac97_bus_ops *ops, int num);
......@@ -356,8 +392,10 @@ struct snd_soc_codec {
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*display_register)(struct snd_soc_codec *, char *,
size_t, unsigned int);
int (*volatile_register)(unsigned int);
int (*readable_register)(unsigned int);
hw_write_t hw_write;
hw_read_t hw_read;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
void *reg_cache;
short reg_cache_size;
short reg_cache_step;
......@@ -369,8 +407,6 @@ struct snd_soc_codec {
enum snd_soc_bias_level bias_level;
enum snd_soc_bias_level suspend_bias_level;
struct delayed_work delayed_work;
struct list_head up_list;
struct list_head down_list;
/* codec DAI's */
struct snd_soc_dai *dai;
......@@ -379,6 +415,7 @@ struct snd_soc_codec {
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_reg;
struct dentry *debugfs_pop_time;
struct dentry *debugfs_dapm;
#endif
};
......
/*
* UDA1380 ALSA SoC Codec driver
*
* Copyright 2009 Philipp Zabel
*
* 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 __UDA1380_H
#define __UDA1380_H
struct uda1380_platform_data {
int gpio_power;
int gpio_reset;
int dac_clk;
#define UDA1380_DAC_CLK_SYSCLK 0
#define UDA1380_DAC_CLK_WSPLL 1
};
#endif /* __UDA1380_H */
/*
* linux/sound/wm8993.h -- Platform data for WM8993
*
* Copyright 2009 Wolfson Microelectronics. PLC.
*
* 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 __LINUX_SND_WM8993_H
#define __LINUX_SND_WM8993_H
/* Note that EQ1 only contains the enable/disable bit so will be
ignored but is included for simplicity.
*/
struct wm8993_retune_mobile_setting {
const char *name;
unsigned int rate;
u16 config[24];
};
struct wm8993_platform_data {
struct wm8993_retune_mobile_setting *retune_configs;
int num_retune_configs;
/* LINEOUT can be differential or single ended */
unsigned int lineout1_diff:1;
unsigned int lineout2_diff:1;
/* Common mode feedback */
unsigned int lineout1fb:1;
unsigned int lineout2fb:1;
/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
unsigned int micbias1_lvl:1;
unsigned int micbias2_lvl:1;
/* Jack detect threashold levels, see datasheet for values */
unsigned int jd_scthr:2;
unsigned int jd_thr:2;
};
#endif
......@@ -170,6 +170,13 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
if (dev->id >= 0) {
dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
ret = -ENXIO;
goto err_dev;
}
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
......@@ -200,6 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
snprintf(card->longname, sizeof(card->longname),
"%s (%s)", dev->dev.driver->name, card->mixername);
if (pdata && pdata->codec_pdata[0])
snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
snd_card_set_dev(card, &dev->dev);
ret = snd_card_register(card);
if (ret == 0) {
......@@ -212,6 +221,7 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
err:
if (card)
snd_card_free(card);
err_dev:
return ret;
}
......
......@@ -136,6 +136,9 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
if (!prtd || !prtd->params)
return 0;
DCSR(prtd->dma_ch) &= ~DCSR_RUN;
DCSR(prtd->dma_ch) = 0;
DCMD(prtd->dma_ch) = 0;
......
......@@ -29,6 +29,7 @@ source "sound/soc/au1x/Kconfig"
source "sound/soc/blackfin/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/fsl/Kconfig"
source "sound/soc/imx/Kconfig"
source "sound/soc/omap/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
......
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
......@@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += blackfin/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += imx/
obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += s3c24xx/
......
......@@ -56,30 +56,14 @@
#define MCLK_RATE 12000000
static struct clk *mclk;
static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
clk_disable(mclk);
return ret;
}
return 0;
}
static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
/*
* As shipped the board does not have inputs. However, it is relatively
* straightforward to modify the board to hook them up so support is left
* in the driver.
*/
#undef ENABLE_MIC_INPUT
dev_dbg(rtd->socdev->dev, "shutdown");
}
static struct clk *mclk;
static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
......@@ -87,102 +71,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
struct ssc_device *ssc = ssc_p->ssc;
int ret;
unsigned int rate;
int cmr_div, period;
if (ssc == NULL) {
printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
return -EINVAL;
}
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/*
* The SSC clock dividers depend on the sample rate. The CMR.DIV
* field divides the system master clock MCK to drive the SSC TK
* signal which provides the codec BCLK. The TCMR.PERIOD and
* RCMR.PERIOD fields further divide the BCLK signal to drive
* the SSC TF and RF signals which provide the codec DACLRC and
* ADCLRC clocks.
*
* The dividers were determined through trial and error, where a
* CMR.DIV value is chosen such that the resulting BCLK value is
* divisible, or almost divisible, by (2 * sample rate), and then
* the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
*/
rate = params_rate(params);
switch (rate) {
case 8000:
cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */
period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
break;
case 11025:
cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */
period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
break;
case 16000:
cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */
period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
break;
case 22050:
cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */
period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */
break;
case 32000:
cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */
period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
break;
case 44100:
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */
break;
case 48000:
cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */
period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
break;
case 88200:
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */
break;
case 96000:
cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */
period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */
break;
default:
printk(KERN_WARNING "unsupported rate %d"
" on at91sam9g20ek board\n", rate);
return -EINVAL;
}
/* set the MCK divider for BCLK */
ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
if (ret < 0)
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* set the BCLK divider for DACLRC */
ret = snd_soc_dai_set_clkdiv(cpu_dai,
ATMEL_SSC_TCMR_PERIOD, period);
} else {
/* set the BCLK divider for ADCLRC */
ret = snd_soc_dai_set_clkdiv(cpu_dai,
ATMEL_SSC_RCMR_PERIOD, period);
}
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
......@@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops at91sam9g20ek_ops = {
.startup = at91sam9g20ek_startup,
.hw_params = at91sam9g20ek_hw_params,
.shutdown = at91sam9g20ek_shutdown,
};
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
......@@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = {
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
{
struct snd_soc_dai *codec_dai = &codec->dai[0];
int ret;
printk(KERN_DEBUG
"at91sam9g20ek_wm8731 "
": at91sam9g20ek_wm8731_init() called\n");
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
return ret;
}
/* Add specific widgets */
snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
......@@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
snd_soc_dapm_nc_pin(codec, "RLINEIN");
snd_soc_dapm_nc_pin(codec, "LLINEIN");
/* always connected */
#ifdef ENABLE_MIC_INPUT
snd_soc_dapm_enable_pin(codec, "Int Mic");
#else
snd_soc_dapm_nc_pin(codec, "Int Mic");
#endif
/* always connected */
snd_soc_dapm_enable_pin(codec, "Ext Spk");
snd_soc_dapm_sync(codec);
......
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <mano@roarinelk.homelinux.net>
* (c) 2007-2009 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <manuel.lauss@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/suspend.h>
#include <sound/core.h>
#include <sound/pcm.h>
......@@ -29,6 +30,9 @@
#include "psc.h"
/* how often to retry failed codec register reads/writes */
#define AC97_RW_RETRIES 5
#define AC97_DIR \
(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
......@@ -45,6 +49,9 @@
#define AC97PCR_CLRFIFO(stype) \
((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
#define AC97STAT_BUSY(stype) \
((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
......@@ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned short data, tmo;
unsigned short data, retry, tmo;
au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
tmo = 1000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
udelay(2);
retry = AC97_RW_RETRIES;
do {
mutex_lock(&pscdata->lock);
au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
AC97_CDC(pscdata));
au_sync();
tmo = 2000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
&& --tmo)
udelay(2);
if (!tmo)
data = 0xffff;
else
data = au_readl(AC97_CDC(pscdata)) & 0xffff;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
mutex_unlock(&pscdata->lock);
} while (--retry && !tmo);
return data;
return retry ? data : 0xffff;
}
/* AC97 controller writes to codec register */
......@@ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned int tmo;
unsigned int tmo, retry;
au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
tmo = 1000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
retry = AC97_RW_RETRIES;
do {
mutex_lock(&pscdata->lock);
au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
AC97_CDC(pscdata));
au_sync();
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
tmo = 2000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
&& --tmo)
udelay(2);
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
mutex_unlock(&pscdata->lock);
} while (--retry && !tmo);
}
/* AC97 controller asserts a warm reset */
......@@ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
au_sync();
/* wait for PSC to indicate it's ready */
i = 100000;
i = 1000;
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
au_sync();
msleep(1);
if (i == 0) {
printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
......@@ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
au_sync();
/* wait for AC97 core to become ready */
i = 100000;
i = 1000;
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
au_sync();
msleep(1);
if (i == 0)
printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
}
......@@ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned long r, stat;
unsigned long r, ro, stat;
int chans, stype = SUBSTREAM_TYPE(substream);
chans = params_channels(params);
r = au_readl(AC97_CFG(pscdata));
r = ro = au_readl(AC97_CFG(pscdata));
stat = au_readl(AC97_STAT(pscdata));
/* already active? */
......@@ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
(pscdata->rate != params_rate(params)))
return -EINVAL;
} else {
/* disable AC97 device controller first */
au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
r &= ~PSC_AC97CFG_LEN_MASK;
......@@ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
r |= PSC_AC97CFG_RXSLOT_ENA(4);
}
/* finally enable the AC97 controller again */
/* do we need to poke the hardware? */
if (!(r ^ ro))
goto out;
/* ac97 engine is about to be disabled */
mutex_lock(&pscdata->lock);
/* disable AC97 device controller first... */
au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
/* ...wait for it... */
while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
asm volatile ("nop");
/* ...write config... */
au_writel(r, AC97_CFG(pscdata));
au_sync();
/* ...enable the AC97 controller again... */
au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
/* ...and wait for ready bit */
while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
asm volatile ("nop");
mutex_unlock(&pscdata->lock);
pscdata->cfg = r;
pscdata->rate = params_rate(params);
}
out:
return 0;
}
......@@ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
au_sync();
au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
au_sync();
break;
......@@ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
au_sync();
while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
asm volatile ("nop");
au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
au_sync();
break;
default:
ret = -EINVAL;
......@@ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
if (!au1xpsc_ac97_workdata)
return -ENOMEM;
mutex_init(&au1xpsc_ac97_workdata->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
......@@ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
goto out1;
/* configuration: max dma trigger threshold, enable ac97 */
au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
PSC_AC97CFG_TT_FIFO8 |
PSC_AC97CFG_DE_ENABLE;
au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
PSC_AC97CFG_TT_FIFO8 |
PSC_AC97CFG_DE_ENABLE;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
......@@ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
......@@ -29,6 +29,7 @@ struct au1xpsc_audio_data {
unsigned long pm[2];
struct resource *ioarea;
struct mutex lock;
};
#define PCM_TX 0
......
......@@ -7,6 +7,15 @@ config SND_BF5XX_I2S
mode (supports single stereo In/Out).
You will also need to select the audio interfaces to support below.
config SND_BF5XX_TDM
tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip"
depends on (BLACKFIN && SND_SOC)
help
Say Y or M if you want to add support for codecs attached to
the Blackfin SPORT (synchronous serial ports) interface in TDM
mode.
You will also need to select the audio interfaces to support below.
config SND_BF5XX_SOC_SSM2602
tristate "SoC SSM2602 Audio support for BF52x ezkit"
depends on SND_BF5XX_I2S
......@@ -69,12 +78,24 @@ config SND_BF5XX_SOC_I2S
tristate
select SND_BF5XX_SOC_SPORT
config SND_BF5XX_SOC_TDM
tristate
select SND_BF5XX_SOC_SPORT
config SND_BF5XX_SOC_AC97
tristate
select AC97_BUS
select SND_SOC_AC97_BUS
select SND_BF5XX_SOC_SPORT
config SND_BF5XX_SOC_AD1836
tristate "SoC AD1836 Audio support for BF5xx"
depends on SND_BF5XX_TDM
select SND_BF5XX_SOC_TDM
select SND_SOC_AD1836
help
Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
config SND_BF5XX_SOC_AD1980
tristate "SoC AD1980/1 Audio support for BF5xx"
depends on SND_BF5XX_AC97
......@@ -83,9 +104,17 @@ config SND_BF5XX_SOC_AD1980
help
Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
config SND_BF5XX_SOC_AD1938
tristate "SoC AD1938 Audio support for Blackfin"
depends on SND_BF5XX_TDM
select SND_BF5XX_SOC_TDM
select SND_SOC_AD1938
help
Say Y if you want to add support for AD1938 codec on Blackfin.
config SND_BF5XX_SPORT_NUM
int "Set a SPORT for Sound chip"
depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM)
range 0 3 if BF54x
range 0 1 if !BF54x
default 0
......
# Blackfin Platform Support
snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
snd-soc-bf5xx-sport-objs := bf5xx-sport.o
snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
# Blackfin Machine Support
snd-ad1836-objs := bf5xx-ad1836.o
snd-ad1980-objs := bf5xx-ad1980.o
snd-ssm2602-objs := bf5xx-ssm2602.o
snd-ad73311-objs := bf5xx-ad73311.o
snd-ad1938-objs := bf5xx-ad1938.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o
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_AD1938) += snd-ad1938.o
......@@ -277,28 +277,24 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
if (!dai->active)
return 0;
ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
ret = sport_set_multichannel(sport, 16, 0x1F, 1);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1));
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1));
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
if (dai->capture.active)
sport_rx_start(sport);
if (dai->playback.active)
sport_tx_start(sport);
return 0;
}
......
/*
* File: sound/soc/blackfin/bf5xx-ad1836.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Aug 4 2009
* Description: Board driver for ad1836 sound chip
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm_params.h>
#include <asm/blackfin.h>
#include <asm/cacheflush.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/portmux.h>
#include "../codecs/ad1836.h"
#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836;
static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
cpu_dai->private_data = sport_handle;
return 0;
}
static int bf5xx_ad1836_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->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops bf5xx_ad1836_ops = {
.startup = bf5xx_ad1836_startup,
.hw_params = bf5xx_ad1836_hw_params,
};
static struct snd_soc_dai_link bf5xx_ad1836_dai = {
.name = "ad1836",
.stream_name = "AD1836",
.cpu_dai = &bf5xx_tdm_dai,
.codec_dai = &ad1836_dai,
.ops = &bf5xx_ad1836_ops,
};
static struct snd_soc_card bf5xx_ad1836 = {
.name = "bf5xx_ad1836",
.platform = &bf5xx_tdm_soc_platform,
.dai_link = &bf5xx_ad1836_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ad1836_snd_devdata = {
.card = &bf5xx_ad1836,
.codec_dev = &soc_codec_dev_ad1836,
};
static struct platform_device *bfxx_ad1836_snd_device;
static int __init bf5xx_ad1836_init(void)
{
int ret;
bfxx_ad1836_snd_device = platform_device_alloc("soc-audio", -1);
if (!bfxx_ad1836_snd_device)
return -ENOMEM;
platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836_snd_devdata);
bf5xx_ad1836_snd_devdata.dev = &bfxx_ad1836_snd_device->dev;
ret = platform_device_add(bfxx_ad1836_snd_device);
if (ret)
platform_device_put(bfxx_ad1836_snd_device);
return ret;
}
static void __exit bf5xx_ad1836_exit(void)
{
platform_device_unregister(bfxx_ad1836_snd_device);
}
module_init(bf5xx_ad1836_init);
module_exit(bf5xx_ad1836_exit);
/* Module information */
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("ALSA SoC AD1836 board driver");
MODULE_LICENSE("GPL");
/*
* File: sound/soc/blackfin/bf5xx-ad1938.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Thur June 4 2009
* Description: Board driver for ad1938 sound chip
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm_params.h>
#include <asm/blackfin.h>
#include <asm/cacheflush.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/portmux.h>
#include "../codecs/ad1938.h"
#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1938;
static int bf5xx_ad1938_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
cpu_dai->private_data = sport_handle;
return 0;
}
static int bf5xx_ad1938_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->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
int ret = 0;
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set codec DAI configuration */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops bf5xx_ad1938_ops = {
.startup = bf5xx_ad1938_startup,
.hw_params = bf5xx_ad1938_hw_params,
};
static struct snd_soc_dai_link bf5xx_ad1938_dai = {
.name = "ad1938",
.stream_name = "AD1938",
.cpu_dai = &bf5xx_tdm_dai,
.codec_dai = &ad1938_dai,
.ops = &bf5xx_ad1938_ops,
};
static struct snd_soc_card bf5xx_ad1938 = {
.name = "bf5xx_ad1938",
.platform = &bf5xx_tdm_soc_platform,
.dai_link = &bf5xx_ad1938_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ad1938_snd_devdata = {
.card = &bf5xx_ad1938,
.codec_dev = &soc_codec_dev_ad1938,
};
static struct platform_device *bfxx_ad1938_snd_device;
static int __init bf5xx_ad1938_init(void)
{
int ret;
bfxx_ad1938_snd_device = platform_device_alloc("soc-audio", -1);
if (!bfxx_ad1938_snd_device)
return -ENOMEM;
platform_set_drvdata(bfxx_ad1938_snd_device, &bf5xx_ad1938_snd_devdata);
bf5xx_ad1938_snd_devdata.dev = &bfxx_ad1938_snd_device->dev;
ret = platform_device_add(bfxx_ad1938_snd_device);
if (ret)
platform_device_put(bfxx_ad1938_snd_device);
return ret;
}
static void __exit bf5xx_ad1938_exit(void)
{
platform_device_unregister(bfxx_ad1938_snd_device);
}
module_init(bf5xx_ad1938_init);
module_exit(bf5xx_ad1938_exit);
/* Module information */
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("ALSA SoC AD1938 board driver");
MODULE_LICENSE("GPL");
......@@ -203,23 +203,23 @@ static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
.codec_dev = &soc_codec_dev_ad73311,
};
static struct platform_device *bf52x_ad73311_snd_device;
static struct platform_device *bf5xx_ad73311_snd_device;
static int __init bf5xx_ad73311_init(void)
{
int ret;
pr_debug("%s enter\n", __func__);
bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
if (!bf52x_ad73311_snd_device)
bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
if (!bf5xx_ad73311_snd_device)
return -ENOMEM;
platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
ret = platform_device_add(bf52x_ad73311_snd_device);
platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev;
ret = platform_device_add(bf5xx_ad73311_snd_device);
if (ret)
platform_device_put(bf52x_ad73311_snd_device);
platform_device_put(bf5xx_ad73311_snd_device);
return ret;
}
......@@ -227,7 +227,7 @@ static int __init bf5xx_ad73311_init(void)
static void __exit bf5xx_ad73311_exit(void)
{
pr_debug("%s enter\n", __func__);
platform_device_unregister(bf52x_ad73311_snd_device);
platform_device_unregister(bf5xx_ad73311_snd_device);
}
module_init(bf5xx_ad73311_init);
......
......@@ -259,22 +259,18 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
if (!dai->active)
return 0;
ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
ret = sport_config_rx(sport, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
ret = sport_config_tx(sport, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
if (dai->capture.active)
sport_rx_start(sport);
if (dai->playback.active)
sport_tx_start(sport);
return 0;
}
......
......@@ -148,24 +148,24 @@ static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
.codec_data = &bf5xx_ssm2602_setup,
};
static struct platform_device *bf52x_ssm2602_snd_device;
static struct platform_device *bf5xx_ssm2602_snd_device;
static int __init bf5xx_ssm2602_init(void)
{
int ret;
pr_debug("%s enter\n", __func__);
bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
if (!bf52x_ssm2602_snd_device)
bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
if (!bf5xx_ssm2602_snd_device)
return -ENOMEM;
platform_set_drvdata(bf52x_ssm2602_snd_device,
platform_set_drvdata(bf5xx_ssm2602_snd_device,
&bf5xx_ssm2602_snd_devdata);
bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
ret = platform_device_add(bf52x_ssm2602_snd_device);
bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev;
ret = platform_device_add(bf5xx_ssm2602_snd_device);
if (ret)
platform_device_put(bf52x_ssm2602_snd_device);
platform_device_put(bf5xx_ssm2602_snd_device);
return ret;
}
......@@ -173,7 +173,7 @@ static int __init bf5xx_ssm2602_init(void)
static void __exit bf5xx_ssm2602_exit(void)
{
pr_debug("%s enter\n", __func__);
platform_device_unregister(bf52x_ssm2602_snd_device);
platform_device_unregister(bf5xx_ssm2602_snd_device);
}
module_init(bf5xx_ssm2602_init);
......
/*
* File: sound/soc/blackfin/bf5xx-tdm-pcm.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Tue June 06 2009
* Description: DMA driver for tdm codec
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#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/dma.h>
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
#include "bf5xx-sport.h"
#define PCM_BUFFER_MAX 0x10000
#define FRAGMENT_SIZE_MIN (4*1024)
#define FRAGMENTS_MIN 2
#define FRAGMENTS_MAX 32
static void bf5xx_dma_irq(void *data)
{
struct snd_pcm_substream *pcm = data;
snd_pcm_period_elapsed(pcm);
}
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_48000,
.channels_min = 2,
.channels_max = 8,
.buffer_bytes_max = PCM_BUFFER_MAX,
.period_bytes_min = FRAGMENT_SIZE_MIN,
.period_bytes_max = PCM_BUFFER_MAX/2,
.periods_min = FRAGMENTS_MIN,
.periods_max = FRAGMENTS_MAX,
};
static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
snd_pcm_lib_malloc_pages(substream, size * 4);
return 0;
}
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_lib_free_pages(substream);
return 0;
}
static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
fragsize_bytes /= runtime->channels;
/* inflate the fragsize to match the dma width of SPORT */
fragsize_bytes *= 8;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
sport_config_tx_dma(sport, runtime->dma_area,
runtime->periods, fragsize_bytes);
} else {
sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
sport_config_rx_dma(sport, runtime->dma_area,
runtime->periods, fragsize_bytes);
}
return 0;
}
static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_tx_start(sport);
else
sport_rx_start(sport);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_tx_stop(sport);
else
sport_rx_stop(sport);
break;
default:
ret = -EINVAL;
}
return ret;
}
static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
unsigned int diff;
snd_pcm_uframes_t frames;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff = sport_curr_offset_tx(sport);
frames = diff / (8*4); /* 32 bytes per frame */
} else {
diff = sport_curr_offset_rx(sport);
frames = diff / (8*4);
}
return frames;
}
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
goto out;
if (sport_handle != NULL)
runtime->private_data = sport_handle;
else {
pr_err("sport_handle is NULL\n");
ret = -ENODEV;
}
out:
return ret;
}
static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
{
unsigned int *src;
unsigned int *dst;
int i;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
src = buf;
dst = (unsigned int *)substream->runtime->dma_area;
dst += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
*(dst + i) = *src++;
dst += 8;
}
} else {
src = (unsigned int *)substream->runtime->dma_area;
dst = buf;
src += pos * 8;
while (count--) {
for (i = 0; i < substream->runtime->channels; i++)
*dst++ = *(src+i);
src += 8;
}
}
return 0;
}
static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
{
unsigned char *buf = substream->runtime->dma_area;
buf += pos * 8 * 4;
memset(buf, '\0', count * 8 * 4);
return 0;
}
struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,
.hw_free = bf5xx_pcm_hw_free,
.prepare = bf5xx_pcm_prepare,
.trigger = bf5xx_pcm_trigger,
.pointer = bf5xx_pcm_pointer,
.copy = bf5xx_pcm_copy,
.silence = bf5xx_pcm_silence,
};
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
&buf->addr, GFP_KERNEL);
if (!buf->area) {
pr_err("Failed to allocate dma memory \
Please increase uncached DMA memory region\n");
return -ENOMEM;
}
buf->bytes = size;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
return 0;
}
static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < 2; stream++) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
if (sport_handle)
sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
struct snd_pcm *pcm)
{
int ret = 0;
if (!card->dev->dma_mask)
card->dev->dma_mask = &bf5xx_pcm_dmamask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (dai->playback.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
}
out:
return ret;
}
struct snd_soc_platform bf5xx_tdm_soc_platform = {
.name = "bf5xx-audio",
.pcm_ops = &bf5xx_pcm_tdm_ops,
.pcm_new = bf5xx_pcm_tdm_new,
.pcm_free = bf5xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform);
static int __init bfin_pcm_tdm_init(void)
{
return snd_soc_register_platform(&bf5xx_tdm_soc_platform);
}
module_init(bfin_pcm_tdm_init);
static void __exit bfin_pcm_tdm_exit(void)
{
snd_soc_unregister_platform(&bf5xx_tdm_soc_platform);
}
module_exit(bfin_pcm_tdm_exit);
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
MODULE_LICENSE("GPL");
/*
* sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin
*
* Copyright 2009 Analog Device Inc.
*
* 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 _BF5XX_TDM_PCM_H
#define _BF5XX_TDM_PCM_H
struct bf5xx_pcm_dma_params {
char *name; /* stream identifier */
};
/* platform data */
extern struct snd_soc_platform bf5xx_tdm_soc_platform;
#endif
/*
* File: sound/soc/blackfin/bf5xx-tdm.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Thurs June 04 2009
* Description: Blackfin I2S(TDM) CPU DAI driver
* Even though TDM mode can be as part of I2S DAI, but there
* are so much difference in configuration and data flow,
* it's very ugly to integrate I2S and TDM into a module
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/irq.h>
#include <asm/portmux.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
struct bf5xx_tdm_port {
u16 tcr1;
u16 rcr1;
u16 tcr2;
u16 rcr2;
int configured;
};
static struct bf5xx_tdm_port bf5xx_tdm;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
static struct sport_param sport_params[2] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
.err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
.err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
}
};
/*
* Setting the TFS pin selector for SPORT 0 based on whether the selected
* port id F or G. If the port is F then no conflict should exist for the
* TFS. When Port G is selected and EMAC then there is a conflict between
* the PHY interrupt line and TFS. Current settings prevent the conflict
* by ignoring the TFS pin when Port G is selected. This allows both
* ssm2602 using Port G and EMAC concurrently.
*/
#ifdef CONFIG_BF527_SPORT0_PORTF
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
#else
#define LOCAL_SPORT0_TFS (0)
#endif
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
int ret = 0;
/* interface format:support TDM,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
break;
default:
printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
ret = -EINVAL;
break;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
break;
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
ret = -EINVAL;
break;
default:
printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
ret = -EINVAL;
break;
}
return ret;
}
static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int ret = 0;
bf5xx_tdm.tcr2 &= ~0x1f;
bf5xx_tdm.rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_tdm.tcr2 |= 31;
bf5xx_tdm.rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
default:
pr_err("not supported PCM format yet\n");
return -EINVAL;
break;
}
if (!bf5xx_tdm.configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
* need to configure both of them at the time when the first
* stream is opened.
*
* CPU DAI:slave mode.
*/
ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
bf5xx_tdm.rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
bf5xx_tdm.tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
bf5xx_tdm.configured = 1;
}
return 0;
}
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_tdm.configured = 0;
}
#ifdef CONFIG_PM
static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport =
(struct sport_device *)dai->private_data;
if (!dai->active)
return 0;
if (dai->capture.active)
sport_rx_stop(sport);
if (dai->playback.active)
sport_tx_stop(sport);
return 0;
}
static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport =
(struct sport_device *)dai->private_data;
if (!dai->active)
return 0;
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
}
return 0;
}
#else
#define bf5xx_tdm_suspend NULL
#define bf5xx_tdm_resume NULL
#endif
static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
.hw_params = bf5xx_tdm_hw_params,
.set_fmt = bf5xx_tdm_set_dai_fmt,
.shutdown = bf5xx_tdm_shutdown,
};
struct snd_soc_dai bf5xx_tdm_dai = {
.name = "bf5xx-tdm",
.id = 0,
.suspend = bf5xx_tdm_suspend,
.resume = bf5xx_tdm_resume,
.playback = {
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
.capture = {
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
.ops = &bf5xx_tdm_dai_ops,
};
EXPORT_SYMBOL_GPL(bf5xx_tdm_dai);
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{
int ret = 0;
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
return -EFAULT;
}
/* request DMA for SPORT */
sport_handle = sport_init(&sport_params[sport_num], 4, \
8 * sizeof(u32), NULL);
if (!sport_handle) {
peripheral_free_list(&sport_req[sport_num][0]);
return -ENODEV;
}
/* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
goto sport_config_err;
}
ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
goto sport_config_err;
}
ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
ret = -EBUSY;
goto sport_config_err;
}
ret = snd_soc_register_dai(&bf5xx_tdm_dai);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
return 0;
sport_config_err:
peripheral_free_list(&sport_req[sport_num][0]);
return ret;
}
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{
peripheral_free_list(&sport_req[sport_num][0]);
snd_soc_unregister_dai(&bf5xx_tdm_dai);
return 0;
}
static struct platform_driver bfin_tdm_driver = {
.probe = bfin_tdm_probe,
.remove = __devexit_p(bfin_tdm_remove),
.driver = {
.name = "bfin-tdm",
.owner = THIS_MODULE,
},
};
static int __init bfin_tdm_init(void)
{
return platform_driver_register(&bfin_tdm_driver);
}
module_init(bfin_tdm_init);
static void __exit bfin_tdm_exit(void)
{
platform_driver_unregister(&bfin_tdm_driver);
}
module_exit(bfin_tdm_exit);
/* Module information */
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("TDM driver for ADI Blackfin");
MODULE_LICENSE("GPL");
/*
* sound/soc/blackfin/bf5xx-tdm.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _BF5XX_TDM_H
#define _BF5XX_TDM_H
extern struct snd_soc_dai bf5xx_tdm_dai;
#endif
......@@ -12,11 +12,15 @@ config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
select SND_SOC_L3
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD1938 if SPI_MASTER
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
......@@ -30,18 +34,23 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8523 if I2C
select SND_SOC_WM8580 if I2C
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8960 if I2C
select SND_SOC_WM8961 if I2C
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8974 if I2C
select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8990 if I2C
select SND_SOC_WM8993 if I2C
select SND_SOC_WM9081 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
......@@ -57,11 +66,21 @@ config SND_SOC_ALL_CODECS
If unsure select "N".
config SND_SOC_WM_HUBS
tristate
default y if SND_SOC_WM8993=y
default m if SND_SOC_WM8993=m
config SND_SOC_AC97_CODEC
tristate
select SND_AC97_CODEC
config SND_SOC_AD1836
tristate
config SND_SOC_AD1938
tristate
config SND_SOC_AD1980
tristate
......@@ -74,6 +93,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
config SND_SOC_AK4642
tristate
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
......@@ -86,6 +108,9 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
config SND_SOC_CX20442
tristate
config SND_SOC_L3
tristate
......@@ -129,6 +154,9 @@ config SND_SOC_WM8400
config SND_SOC_WM8510
tristate
config SND_SOC_WM8523
tristate
config SND_SOC_WM8580
tristate
......@@ -144,6 +172,9 @@ config SND_SOC_WM8750
config SND_SOC_WM8753
tristate
config SND_SOC_WM8776
tristate
config SND_SOC_WM8900
tristate
......@@ -156,15 +187,24 @@ config SND_SOC_WM8940
config SND_SOC_WM8960
tristate
config SND_SOC_WM8961
tristate
config SND_SOC_WM8971
tristate
config SND_SOC_WM8974
tristate
config SND_SOC_WM8988
tristate
config SND_SOC_WM8990
tristate
config SND_SOC_WM8993
tristate
config SND_SOC_WM9081
tristate
......@@ -176,3 +216,7 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
# Amp
config SND_SOC_MAX9877
tristate
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
snd-soc-ad1938-objs := ad1938.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-l3-objs := l3.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-spdif-objs := spdif_transciever.o
......@@ -18,29 +22,42 @@ snd-soc-uda1380-objs := uda1380.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8523-objs := wm8523.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8961-objs := wm8961.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
......@@ -55,19 +72,28 @@ obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o
obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o
obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
/*
* File: sound/soc/codecs/ad1836.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Aug 04 2009
* Description: Driver for AD1836 sound chip
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/soc-dapm.h>
#include <linux/spi/spi.h>
#include "ad1836.h"
/* codec private data */
struct ad1836_priv {
struct snd_soc_codec codec;
u16 reg_cache[AD1836_NUM_REGS];
};
static struct snd_soc_codec *ad1836_codec;
struct snd_soc_codec_device soc_codec_dev_ad1836;
static int ad1836_register(struct ad1836_priv *ad1836);
static void ad1836_unregister(struct ad1836_priv *ad1836);
/*
* AD1836 volume/mute/de-emphasis etc. controls
*/
static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
static const struct soc_enum ad1836_deemp_enum =
SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
static const struct snd_kcontrol_new ad1836_snd_controls[] = {
/* DAC volume control */
SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
/* ADC switch control */
SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
AD1836_ADCR1_MUTE, 1, 1),
SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
AD1836_ADCR2_MUTE, 1, 1),
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
AD1836_DACR1_MUTE, 1, 1),
SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
AD1836_DACR2_MUTE, 1, 1),
SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
AD1836_DACR3_MUTE, 1, 1),
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
AD1836_ADC_HIGHPASS_FILTER, 1, 0),
/* DAC de-emphasis */
SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
};
static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
AD1836_DAC_POWERDOWN, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
AD1836_ADC_POWERDOWN, 1, NULL, 0),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", "DAC1 Switch", "DAC" },
{ "DAC2OUT", "DAC2 Switch", "DAC" },
{ "DAC3OUT", "DAC3 Switch", "DAC" },
{ "ADC", "ADC1 Switch", "ADC1IN" },
{ "ADC", "ADC2 Switch", "ADC2IN" },
};
/*
* DAI ops entries
*/
static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
/* at present, we support adc aux mode to interface with
* blackfin sport tdm mode
*/
case SND_SOC_DAIFMT_DSP_A:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
/* ALCLK,ABCLK are both output, AD1836 can only be master */
case SND_SOC_DAIFMT_CBM_CFM:
break;
default:
return -EINVAL;
}
return 0;
}
static int ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int word_len = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
word_len = 3;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
word_len = 1;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
word_len = 0;
break;
}
snd_soc_update_bits(codec, AD1836_DAC_CTRL1,
AD1836_DAC_WORD_LEN_MASK, word_len);
snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
AD1836_ADC_WORD_LEN_MASK, word_len);
return 0;
}
/*
* interface to read/write ad1836 register
*/
#define AD1836_SPI_REG_SHFT 12
#define AD1836_SPI_READ (1 << 11)
#define AD1836_SPI_VAL_MSK 0x3FF
/*
* write to the ad1836 register space
*/
static int ad1836_write_reg(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u16 *reg_cache = codec->reg_cache;
int ret = 0;
if (value != reg_cache[reg]) {
unsigned short buf;
struct spi_transfer t = {
.tx_buf = &buf,
.len = 2,
};
struct spi_message m;
buf = (reg << AD1836_SPI_REG_SHFT) |
(value & AD1836_SPI_VAL_MSK);
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(codec->control_data, &m);
if (ret == 0)
reg_cache[reg] = value;
}
return ret;
}
/*
* read from the ad1836 register space cache
*/
static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *reg_cache = codec->reg_cache;
if (reg >= codec->reg_cache_size)
return -EINVAL;
return reg_cache[reg];
}
static int __devinit ad1836_spi_probe(struct spi_device *spi)
{
struct snd_soc_codec *codec;
struct ad1836_priv *ad1836;
ad1836 = kzalloc(sizeof(struct ad1836_priv), GFP_KERNEL);
if (ad1836 == NULL)
return -ENOMEM;
codec = &ad1836->codec;
codec->control_data = spi;
codec->dev = &spi->dev;
dev_set_drvdata(&spi->dev, ad1836);
return ad1836_register(ad1836);
}
static int __devexit ad1836_spi_remove(struct spi_device *spi)
{
struct ad1836_priv *ad1836 = dev_get_drvdata(&spi->dev);
ad1836_unregister(ad1836);
return 0;
}
static struct spi_driver ad1836_spi_driver = {
.driver = {
.name = "ad1836-spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = ad1836_spi_probe,
.remove = __devexit_p(ad1836_spi_remove),
};
static struct snd_soc_dai_ops ad1836_dai_ops = {
.hw_params = ad1836_hw_params,
.set_fmt = ad1836_set_dai_fmt,
};
/* codec DAI instance */
struct snd_soc_dai ad1836_dai = {
.name = "AD1836",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 6,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.ops = &ad1836_dai_ops,
};
EXPORT_SYMBOL_GPL(ad1836_dai);
static int ad1836_register(struct ad1836_priv *ad1836)
{
int ret;
struct snd_soc_codec *codec = &ad1836->codec;
if (ad1836_codec) {
dev_err(codec->dev, "Another ad1836 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = ad1836;
codec->reg_cache = ad1836->reg_cache;
codec->reg_cache_size = AD1836_NUM_REGS;
codec->name = "AD1836";
codec->owner = THIS_MODULE;
codec->dai = &ad1836_dai;
codec->num_dai = 1;
codec->write = ad1836_write_reg;
codec->read = ad1836_read_reg_cache;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
ad1836_dai.dev = codec->dev;
ad1836_codec = codec;
/* default setting for ad1836 */
/* de-emphasis: 48kHz, power-on dac */
codec->write(codec, AD1836_DAC_CTRL1, 0x300);
/* unmute dac channels */
codec->write(codec, AD1836_DAC_CTRL2, 0x0);
/* high-pass filter enable, power-on adc */
codec->write(codec, AD1836_ADC_CTRL1, 0x100);
/* unmute adc channles, adc aux mode */
codec->write(codec, AD1836_ADC_CTRL2, 0x180);
/* left/right diff:PGA/MUX */
codec->write(codec, AD1836_ADC_CTRL3, 0x3A);
/* volume */
codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF);
codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
kfree(ad1836);
return ret;
}
ret = snd_soc_register_dai(&ad1836_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
kfree(ad1836);
return ret;
}
return 0;
}
static void ad1836_unregister(struct ad1836_priv *ad1836)
{
snd_soc_unregister_dai(&ad1836_dai);
snd_soc_unregister_codec(&ad1836->codec);
kfree(ad1836);
ad1836_codec = NULL;
}
static int ad1836_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (ad1836_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = ad1836_codec;
codec = ad1836_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
snd_soc_add_controls(codec, ad1836_snd_controls,
ARRAY_SIZE(ad1836_snd_controls));
snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
ARRAY_SIZE(ad1836_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
/* power down chip */
static int ad1836_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_ad1836 = {
.probe = ad1836_probe,
.remove = ad1836_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
static int __init ad1836_init(void)
{
int ret;
ret = spi_register_driver(&ad1836_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n",
ret);
}
return ret;
}
module_init(ad1836_init);
static void __exit ad1836_exit(void)
{
spi_unregister_driver(&ad1836_spi_driver);
}
module_exit(ad1836_exit);
MODULE_DESCRIPTION("ASoC ad1836 driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_LICENSE("GPL");
/*
* File: sound/soc/codecs/ad1836.h
* Based on:
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: Aug 04, 2009
* Description: definitions for AD1836 registers
*
* Modified:
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __AD1836_H__
#define __AD1836_H__
#define AD1836_DAC_CTRL1 0
#define AD1836_DAC_POWERDOWN 2
#define AD1836_DAC_SERFMT_MASK 0xE0
#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5)
#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5)
#define AD1836_DAC_WORD_LEN_MASK 0x18
#define AD1836_DAC_CTRL2 1
#define AD1836_DACL1_MUTE 0
#define AD1836_DACR1_MUTE 1
#define AD1836_DACL2_MUTE 2
#define AD1836_DACR2_MUTE 3
#define AD1836_DACL3_MUTE 4
#define AD1836_DACR3_MUTE 5
#define AD1836_DAC_L1_VOL 2
#define AD1836_DAC_R1_VOL 3
#define AD1836_DAC_L2_VOL 4
#define AD1836_DAC_R2_VOL 5
#define AD1836_DAC_L3_VOL 6
#define AD1836_DAC_R3_VOL 7
#define AD1836_ADC_CTRL1 12
#define AD1836_ADC_POWERDOWN 7
#define AD1836_ADC_HIGHPASS_FILTER 8
#define AD1836_ADC_CTRL2 13
#define AD1836_ADCL1_MUTE 0
#define AD1836_ADCR1_MUTE 1
#define AD1836_ADCL2_MUTE 2
#define AD1836_ADCR2_MUTE 3
#define AD1836_ADC_WORD_LEN_MASK 0x30
#define AD1836_ADC_SERFMT_MASK (7 << 6)
#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6)
#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6)
#define AD1836_ADC_CTRL3 14
#define AD1836_NUM_REGS 16
extern struct snd_soc_dai ad1836_dai;
extern struct snd_soc_codec_device soc_codec_dev_ad1836;
#endif
/*
* File: sound/soc/codecs/ad1938.c
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: June 04 2009
* Description: Driver for AD1938 sound chip
*
* Modified:
* Copyright 2009 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/soc-dapm.h>
#include <linux/spi/spi.h>
#include "ad1938.h"
/* codec private data */
struct ad1938_priv {
struct snd_soc_codec codec;
u8 reg_cache[AD1938_NUM_REGS];
};
static struct snd_soc_codec *ad1938_codec;
struct snd_soc_codec_device soc_codec_dev_ad1938;
static int ad1938_register(struct ad1938_priv *ad1938);
static void ad1938_unregister(struct ad1938_priv *ad1938);
/*
* AD1938 volume/mute/de-emphasis etc. controls
*/
static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
static const struct soc_enum ad1938_deemp_enum =
SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp);
static const struct snd_kcontrol_new ad1938_snd_controls[] = {
/* DAC volume control */
SOC_DOUBLE_R("DAC1 Volume", AD1938_DAC_L1_VOL,
AD1938_DAC_R1_VOL, 0, 0xFF, 1),
SOC_DOUBLE_R("DAC2 Volume", AD1938_DAC_L2_VOL,
AD1938_DAC_R2_VOL, 0, 0xFF, 1),
SOC_DOUBLE_R("DAC3 Volume", AD1938_DAC_L3_VOL,
AD1938_DAC_R3_VOL, 0, 0xFF, 1),
SOC_DOUBLE_R("DAC4 Volume", AD1938_DAC_L4_VOL,
AD1938_DAC_R4_VOL, 0, 0xFF, 1),
/* ADC switch control */
SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE,
AD1938_ADCR1_MUTE, 1, 1),
SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE,
AD1938_ADCR2_MUTE, 1, 1),
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE,
AD1938_DACR1_MUTE, 1, 1),
SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE,
AD1938_DACR2_MUTE, 1, 1),
SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE,
AD1938_DACR3_MUTE, 1, 1),
SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE,
AD1938_DACR4_MUTE, 1, 1),
/* ADC high-pass filter */
SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0,
AD1938_ADC_HIGHPASS_FILTER, 1, 0),
/* DAC de-emphasis */
SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum),
};
static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
SND_SOC_DAPM_OUTPUT("DAC4OUT"),
SND_SOC_DAPM_INPUT("ADC1IN"),
SND_SOC_DAPM_INPUT("ADC2IN"),
};
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
{ "DAC1OUT", "DAC1 Switch", "DAC" },
{ "DAC2OUT", "DAC2 Switch", "DAC" },
{ "DAC3OUT", "DAC3 Switch", "DAC" },
{ "DAC4OUT", "DAC4 Switch", "DAC" },
{ "ADC", "ADC1 Switch", "ADC1IN" },
{ "ADC", "ADC2 Switch", "ADC2IN" },
};
/*
* DAI ops entries
*/
static int ad1938_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
int reg;
reg = codec->read(codec, AD1938_DAC_CTRL2);
reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg &
(~AD1938_DAC_MASTER_MUTE);
codec->write(codec, AD1938_DAC_CTRL2, reg);
return 0;
}
static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
{
int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg |
AD1938_PLL_POWERDOWN;
codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
return 0;
}
static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int mask, int slots, int width)
{
struct snd_soc_codec *codec = dai->codec;
int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
dac_reg &= ~AD1938_DAC_CHAN_MASK;
adc_reg &= ~AD1938_ADC_CHAN_MASK;
switch (slots) {
case 2:
dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT;
adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT;
break;
case 4:
dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT;
adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT;
break;
case 8:
dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT;
adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT;
break;
case 16:
dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT;
adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT;
break;
default:
return -EINVAL;
}
codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
return 0;
}
static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
int adc_reg, dac_reg;
adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
* with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
*/
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
adc_reg &= ~AD1938_ADC_SERFMT_MASK;
adc_reg |= AD1938_ADC_SERFMT_TDM;
break;
case SND_SOC_DAIFMT_DSP_A:
adc_reg &= ~AD1938_ADC_SERFMT_MASK;
adc_reg |= AD1938_ADC_SERFMT_AUX;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
adc_reg &= ~AD1938_ADC_LEFT_HIGH;
adc_reg &= ~AD1938_ADC_BCLK_INV;
dac_reg &= ~AD1938_DAC_LEFT_HIGH;
dac_reg &= ~AD1938_DAC_BCLK_INV;
break;
case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
adc_reg |= AD1938_ADC_LEFT_HIGH;
adc_reg &= ~AD1938_ADC_BCLK_INV;
dac_reg |= AD1938_DAC_LEFT_HIGH;
dac_reg &= ~AD1938_DAC_BCLK_INV;
break;
case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
adc_reg &= ~AD1938_ADC_LEFT_HIGH;
adc_reg |= AD1938_ADC_BCLK_INV;
dac_reg &= ~AD1938_DAC_LEFT_HIGH;
dac_reg |= AD1938_DAC_BCLK_INV;
break;
case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
adc_reg |= AD1938_ADC_LEFT_HIGH;
adc_reg |= AD1938_ADC_BCLK_INV;
dac_reg |= AD1938_DAC_LEFT_HIGH;
dac_reg |= AD1938_DAC_BCLK_INV;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
adc_reg |= AD1938_ADC_LCR_MASTER;
adc_reg |= AD1938_ADC_BCLK_MASTER;
dac_reg |= AD1938_DAC_LCR_MASTER;
dac_reg |= AD1938_DAC_BCLK_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
adc_reg |= AD1938_ADC_LCR_MASTER;
adc_reg &= ~AD1938_ADC_BCLK_MASTER;
dac_reg |= AD1938_DAC_LCR_MASTER;
dac_reg &= ~AD1938_DAC_BCLK_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
adc_reg &= ~AD1938_ADC_LCR_MASTER;
adc_reg |= AD1938_ADC_BCLK_MASTER;
dac_reg &= ~AD1938_DAC_LCR_MASTER;
dac_reg |= AD1938_DAC_BCLK_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
adc_reg &= ~AD1938_ADC_LCR_MASTER;
adc_reg &= ~AD1938_ADC_BCLK_MASTER;
dac_reg &= ~AD1938_DAC_LCR_MASTER;
dac_reg &= ~AD1938_DAC_BCLK_MASTER;
break;
default:
return -EINVAL;
}
codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
return 0;
}
static int ad1938_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int word_len = 0, reg = 0;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
word_len = 3;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
word_len = 1;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
word_len = 0;
break;
}
reg = codec->read(codec, AD1938_DAC_CTRL2);
reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
codec->write(codec, AD1938_DAC_CTRL2, reg);
reg = codec->read(codec, AD1938_ADC_CTRL1);
reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
codec->write(codec, AD1938_ADC_CTRL1, reg);
return 0;
}
static int ad1938_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
ad1938_pll_powerctrl(codec, 1);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
case SND_SOC_BIAS_OFF:
ad1938_pll_powerctrl(codec, 0);
break;
}
codec->bias_level = level;
return 0;
}
/*
* interface to read/write ad1938 register
*/
#define AD1938_SPI_ADDR 0x4
#define AD1938_SPI_READ 0x1
#define AD1938_SPI_BUFLEN 3
/*
* write to the ad1938 register space
*/
static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 *reg_cache = codec->reg_cache;
int ret = 0;
if (value != reg_cache[reg]) {
uint8_t buf[AD1938_SPI_BUFLEN];
struct spi_transfer t = {
.tx_buf = buf,
.len = AD1938_SPI_BUFLEN,
};
struct spi_message m;
buf[0] = AD1938_SPI_ADDR << 1;
buf[1] = reg;
buf[2] = value;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(codec->control_data, &m);
if (ret == 0)
reg_cache[reg] = value;
}
return ret;
}
/*
* read from the ad1938 register space cache
*/
static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 *reg_cache = codec->reg_cache;
if (reg >= codec->reg_cache_size)
return -EINVAL;
return reg_cache[reg];
}
/*
* read from the ad1938 register space
*/
static unsigned int ad1938_read_reg(struct snd_soc_codec *codec,
unsigned int reg)
{
char w_buf[AD1938_SPI_BUFLEN];
char r_buf[AD1938_SPI_BUFLEN];
int ret;
struct spi_transfer t = {
.tx_buf = w_buf,
.rx_buf = r_buf,
.len = AD1938_SPI_BUFLEN,
};
struct spi_message m;
w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
w_buf[1] = reg;
w_buf[2] = 0;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(codec->control_data, &m);
if (ret == 0)
return r_buf[2];
else
return -EIO;
}
static int ad1938_fill_cache(struct snd_soc_codec *codec)
{
int i;
u8 *reg_cache = codec->reg_cache;
struct spi_device *spi = codec->control_data;
for (i = 0; i < codec->reg_cache_size; i++) {
int ret = ad1938_read_reg(codec, i);
if (ret == -EIO) {
dev_err(&spi->dev, "AD1938 SPI read failure\n");
return ret;
}
reg_cache[i] = ret;
}
return 0;
}
static int __devinit ad1938_spi_probe(struct spi_device *spi)
{
struct snd_soc_codec *codec;
struct ad1938_priv *ad1938;
ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL);
if (ad1938 == NULL)
return -ENOMEM;
codec = &ad1938->codec;
codec->control_data = spi;
codec->dev = &spi->dev;
dev_set_drvdata(&spi->dev, ad1938);
return ad1938_register(ad1938);
}
static int __devexit ad1938_spi_remove(struct spi_device *spi)
{
struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev);
ad1938_unregister(ad1938);
return 0;
}
static struct spi_driver ad1938_spi_driver = {
.driver = {
.name = "ad1938",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = ad1938_spi_probe,
.remove = __devexit_p(ad1938_spi_remove),
};
static struct snd_soc_dai_ops ad1938_dai_ops = {
.hw_params = ad1938_hw_params,
.digital_mute = ad1938_mute,
.set_tdm_slot = ad1938_set_tdm_slot,
.set_fmt = ad1938_set_dai_fmt,
};
/* codec DAI instance */
struct snd_soc_dai ad1938_dai = {
.name = "AD1938",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 4,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.ops = &ad1938_dai_ops,
};
EXPORT_SYMBOL_GPL(ad1938_dai);
static int ad1938_register(struct ad1938_priv *ad1938)
{
int ret;
struct snd_soc_codec *codec = &ad1938->codec;
if (ad1938_codec) {
dev_err(codec->dev, "Another ad1938 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = ad1938;
codec->reg_cache = ad1938->reg_cache;
codec->reg_cache_size = AD1938_NUM_REGS;
codec->name = "AD1938";
codec->owner = THIS_MODULE;
codec->dai = &ad1938_dai;
codec->num_dai = 1;
codec->write = ad1938_write_reg;
codec->read = ad1938_read_reg_cache;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
ad1938_dai.dev = codec->dev;
ad1938_codec = codec;
/* default setting for ad1938 */
/* unmute dac channels */
codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
/* de-emphasis: 48kHz, powedown dac */
codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
/* powerdown dac, dac in tdm mode */
codec->write(codec, AD1938_DAC_CTRL0, 0x41);
/* high-pass filter enable */
codec->write(codec, AD1938_ADC_CTRL0, 0x3);
/* sata delay=1, adc aux mode */
codec->write(codec, AD1938_ADC_CTRL1, 0x43);
/* pll input: mclki/xi */
codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
ad1938_fill_cache(codec);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
kfree(ad1938);
return ret;
}
ret = snd_soc_register_dai(&ad1938_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
kfree(ad1938);
return ret;
}
return 0;
}
static void ad1938_unregister(struct ad1938_priv *ad1938)
{
ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&ad1938_dai);
snd_soc_unregister_codec(&ad1938->codec);
kfree(ad1938);
ad1938_codec = NULL;
}
static int ad1938_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
if (ad1938_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = ad1938_codec;
codec = ad1938_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
snd_soc_add_controls(codec, ad1938_snd_controls,
ARRAY_SIZE(ad1938_snd_controls));
snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
ARRAY_SIZE(ad1938_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
snd_soc_dapm_new_widgets(codec);
ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
/* power down chip */
static int ad1938_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
#ifdef CONFIG_PM
static int ad1938_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ad1938_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
return 0;
}
#else
#define ad1938_suspend NULL
#define ad1938_resume NULL
#endif
struct snd_soc_codec_device soc_codec_dev_ad1938 = {
.probe = ad1938_probe,
.remove = ad1938_remove,
.suspend = ad1938_suspend,
.resume = ad1938_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
static int __init ad1938_init(void)
{
int ret;
ret = spi_register_driver(&ad1938_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n",
ret);
}
return ret;
}
module_init(ad1938_init);
static void __exit ad1938_exit(void)
{
spi_unregister_driver(&ad1938_spi_driver);
}
module_exit(ad1938_exit);
MODULE_DESCRIPTION("ASoC ad1938 driver");
MODULE_AUTHOR("Barry Song ");
MODULE_LICENSE("GPL");
/*
* File: sound/soc/codecs/ad1836.h
* Based on:
* Author: Barry Song <Barry.Song@analog.com>
*
* Created: May 25, 2009
* Description: definitions for AD1938 registers
*
* Modified:
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AD1938_H__
#define __AD1938_H__
#define AD1938_PLL_CLK_CTRL0 0
#define AD1938_PLL_POWERDOWN 0x01
#define AD1938_PLL_CLK_CTRL1 1
#define AD1938_DAC_CTRL0 2
#define AD1938_DAC_POWERDOWN 0x01
#define AD1938_DAC_SERFMT_MASK 0xC0
#define AD1938_DAC_SERFMT_STEREO (0 << 6)
#define AD1938_DAC_SERFMT_TDM (1 << 6)
#define AD1938_DAC_CTRL1 3
#define AD1938_DAC_2_CHANNELS 0
#define AD1938_DAC_4_CHANNELS 1
#define AD1938_DAC_8_CHANNELS 2
#define AD1938_DAC_16_CHANNELS 3
#define AD1938_DAC_CHAN_SHFT 1
#define AD1938_DAC_CHAN_MASK (3 << AD1938_DAC_CHAN_SHFT)
#define AD1938_DAC_LCR_MASTER (1 << 4)
#define AD1938_DAC_BCLK_MASTER (1 << 5)
#define AD1938_DAC_LEFT_HIGH (1 << 3)
#define AD1938_DAC_BCLK_INV (1 << 7)
#define AD1938_DAC_CTRL2 4
#define AD1938_DAC_WORD_LEN_MASK 0xC
#define AD1938_DAC_MASTER_MUTE 1
#define AD1938_DAC_CHNL_MUTE 5
#define AD1938_DACL1_MUTE 0
#define AD1938_DACR1_MUTE 1
#define AD1938_DACL2_MUTE 2
#define AD1938_DACR2_MUTE 3
#define AD1938_DACL3_MUTE 4
#define AD1938_DACR3_MUTE 5
#define AD1938_DACL4_MUTE 6
#define AD1938_DACR4_MUTE 7
#define AD1938_DAC_L1_VOL 6
#define AD1938_DAC_R1_VOL 7
#define AD1938_DAC_L2_VOL 8
#define AD1938_DAC_R2_VOL 9
#define AD1938_DAC_L3_VOL 10
#define AD1938_DAC_R3_VOL 11
#define AD1938_DAC_L4_VOL 12
#define AD1938_DAC_R4_VOL 13
#define AD1938_ADC_CTRL0 14
#define AD1938_ADC_POWERDOWN 0x01
#define AD1938_ADC_HIGHPASS_FILTER 1
#define AD1938_ADCL1_MUTE 2
#define AD1938_ADCR1_MUTE 3
#define AD1938_ADCL2_MUTE 4
#define AD1938_ADCR2_MUTE 5
#define AD1938_ADC_CTRL1 15
#define AD1938_ADC_SERFMT_MASK 0x60
#define AD1938_ADC_SERFMT_STEREO (0 << 5)
#define AD1938_ADC_SERFMT_TDM (1 << 2)
#define AD1938_ADC_SERFMT_AUX (2 << 5)
#define AD1938_ADC_WORD_LEN_MASK 0x3
#define AD1938_ADC_CTRL2 16
#define AD1938_ADC_2_CHANNELS 0
#define AD1938_ADC_4_CHANNELS 1
#define AD1938_ADC_8_CHANNELS 2
#define AD1938_ADC_16_CHANNELS 3
#define AD1938_ADC_CHAN_SHFT 4
#define AD1938_ADC_CHAN_MASK (3 << AD1938_ADC_CHAN_SHFT)
#define AD1938_ADC_LCR_MASTER (1 << 3)
#define AD1938_ADC_BCLK_MASTER (1 << 6)
#define AD1938_ADC_LEFT_HIGH (1 << 2)
#define AD1938_ADC_BCLK_INV (1 << 1)
#define AD1938_NUM_REGS 17
extern struct snd_soc_dai ad1938_dai;
extern struct snd_soc_codec_device soc_codec_dev_ad1938;
#endif
......@@ -59,21 +59,6 @@ static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
return cache[reg];
}
static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 data;
data = reg;
if (codec->hw_write(codec->control_data, &data, 1) != 1)
return -EIO;
if (codec->hw_read(codec->control_data, &data, 1) != 1)
return -EIO;
return data;
};
/*
* write ak4535 register cache
*/
......@@ -635,7 +620,6 @@ static int ak4535_probe(struct platform_device *pdev)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
codec->hw_read = (hw_read_t)i2c_master_recv;
ret = ak4535_add_i2c_device(pdev, setup);
}
#endif
......
/*
* ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on wm8731.c by Richard Purdie
* Based on ak4535.c by Richard Purdie
* Based on wm8753.c by Liam Girdwood
*
* 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.
*/
/* ** CAUTION **
*
* This is very simple driver.
* It can use headphone output / stereo input only
*
* AK4642 is not tested.
* AK4643 is tested.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include "ak4642.h"
#define AK4642_VERSION "0.0.1"
#define PW_MGMT1 0x00
#define PW_MGMT2 0x01
#define SG_SL1 0x02
#define SG_SL2 0x03
#define MD_CTL1 0x04
#define MD_CTL2 0x05
#define TIMER 0x06
#define ALC_CTL1 0x07
#define ALC_CTL2 0x08
#define L_IVC 0x09
#define L_DVC 0x0a
#define ALC_CTL3 0x0b
#define R_IVC 0x0c
#define R_DVC 0x0d
#define MD_CTL3 0x0e
#define MD_CTL4 0x0f
#define PW_MGMT3 0x10
#define DF_S 0x11
#define FIL3_0 0x12
#define FIL3_1 0x13
#define FIL3_2 0x14
#define FIL3_3 0x15
#define EQ_0 0x16
#define EQ_1 0x17
#define EQ_2 0x18
#define EQ_3 0x19
#define EQ_4 0x1a
#define EQ_5 0x1b
#define FIL1_0 0x1c
#define FIL1_1 0x1d
#define FIL1_2 0x1e
#define FIL1_3 0x1f
#define PW_MGMT4 0x20
#define MD_CTL5 0x21
#define LO_MS 0x22
#define HP_MS 0x23
#define SPK_MS 0x24
#define AK4642_CACHEREGNUM 0x25
struct snd_soc_codec_device soc_codec_dev_ak4642;
/* codec private data */
struct ak4642_priv {
struct snd_soc_codec codec;
unsigned int sysclk;
};
static struct snd_soc_codec *ak4642_codec;
/*
* ak4642 register cache
*/
static const u16 ak4642_reg[AK4642_CACHEREGNUM] = {
0x0000, 0x0000, 0x0001, 0x0000,
0x0002, 0x0000, 0x0000, 0x0000,
0x00e1, 0x00e1, 0x0018, 0x0000,
0x00e1, 0x0018, 0x0011, 0x0008,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
};
/*
* read ak4642 register cache
*/
static inline unsigned int ak4642_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg >= AK4642_CACHEREGNUM)
return -1;
return cache[reg];
}
/*
* write ak4642 register cache
*/
static inline void ak4642_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
if (reg >= AK4642_CACHEREGNUM)
return;
cache[reg] = value;
}
/*
* write to the AK4642 register space
*/
static int ak4642_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 data[2];
/* data is
* D15..D8 AK4642 register offset
* D7...D0 register data
*/
data[0] = reg & 0xff;
data[1] = value & 0xff;
if (codec->hw_write(codec->control_data, data, 2) == 2) {
ak4642_write_reg_cache(codec, reg, value);
return 0;
} else
return -EIO;
}
static int ak4642_sync(struct snd_soc_codec *codec)
{
u16 *cache = codec->reg_cache;
int i, r = 0;
for (i = 0; i < AK4642_CACHEREGNUM; i++)
r |= ak4642_write(codec, i, cache[i]);
return r;
};
static int ak4642_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_codec *codec = dai->codec;
if (is_play) {
/*
* start headphone output
*
* PLL, Master Mode
* Audio I/F Format :MSB justified (ADC & DAC)
* Sampling Frequency: 44.1kHz
* Digital Volume: −8dB
* Bass Boost Level : Middle
*
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
*
* Example code use 0x39, 0x79 value for 0x01 address,
* But we need MCKO (0x02) bit now
*/
ak4642_write(codec, 0x05, 0x27);
ak4642_write(codec, 0x0f, 0x09);
ak4642_write(codec, 0x0e, 0x19);
ak4642_write(codec, 0x09, 0x91);
ak4642_write(codec, 0x0c, 0x91);
ak4642_write(codec, 0x0a, 0x28);
ak4642_write(codec, 0x0d, 0x28);
ak4642_write(codec, 0x00, 0x64);
ak4642_write(codec, 0x01, 0x3b); /* + MCKO bit */
ak4642_write(codec, 0x01, 0x7b); /* + MCKO bit */
} else {
/*
* start stereo input
*
* PLL Master Mode
* Audio I/F Format:MSB justified (ADC & DAC)
* Sampling Frequency:44.1kHz
* Pre MIC AMP:+20dB
* MIC Power On
* ALC setting:Refer to Table 35
* ALC bit=“1”
*
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
ak4642_write(codec, 0x05, 0x27);
ak4642_write(codec, 0x02, 0x05);
ak4642_write(codec, 0x06, 0x3c);
ak4642_write(codec, 0x08, 0xe1);
ak4642_write(codec, 0x0b, 0x00);
ak4642_write(codec, 0x07, 0x21);
ak4642_write(codec, 0x00, 0x41);
ak4642_write(codec, 0x10, 0x01);
}
return 0;
}
static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_codec *codec = dai->codec;
if (is_play) {
/* stop headphone output */
ak4642_write(codec, 0x01, 0x3b);
ak4642_write(codec, 0x01, 0x0b);
ak4642_write(codec, 0x00, 0x40);
ak4642_write(codec, 0x0e, 0x11);
ak4642_write(codec, 0x0f, 0x08);
} else {
/* stop stereo input */
ak4642_write(codec, 0x00, 0x40);
ak4642_write(codec, 0x10, 0x00);
ak4642_write(codec, 0x07, 0x01);
}
}
static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct ak4642_priv *ak4642 = codec->private_data;
ak4642->sysclk = freq;
return 0;
}
static struct snd_soc_dai_ops ak4642_dai_ops = {
.startup = ak4642_dai_startup,
.shutdown = ak4642_dai_shutdown,
.set_sysclk = ak4642_dai_set_sysclk,
};
struct snd_soc_dai ak4642_dai = {
.name = "AK4642",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.ops = &ak4642_dai_ops,
};
EXPORT_SYMBOL_GPL(ak4642_dai);
static int ak4642_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
ak4642_sync(codec);
return 0;
}
/*
* initialise the AK4642 driver
* register the mixer and dsp interfaces with the kernel
*/
static int ak4642_init(struct ak4642_priv *ak4642)
{
struct snd_soc_codec *codec = &ak4642->codec;
int ret = 0;
if (ak4642_codec) {
dev_err(codec->dev, "Another ak4642 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = ak4642;
codec->name = "AK4642";
codec->owner = THIS_MODULE;
codec->read = ak4642_read_reg_cache;
codec->write = ak4642_write;
codec->dai = &ak4642_dai;
codec->num_dai = 1;
codec->hw_write = (hw_write_t)i2c_master_send;
codec->reg_cache_size = ARRAY_SIZE(ak4642_reg);
codec->reg_cache = kmemdup(ak4642_reg,
sizeof(ak4642_reg), GFP_KERNEL);
if (!codec->reg_cache)
return -ENOMEM;
ak4642_dai.dev = codec->dev;
ak4642_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto reg_cache_err;
}
ret = snd_soc_register_dai(&ak4642_dai);
if (ret) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
goto reg_cache_err;
}
/*
* clock setting
*
* Audio I/F Format: MSB justified (ADC & DAC)
* BICK frequency at Master Mode: 64fs
* Input Master Clock Select at PLL Mode: 11.2896MHz
* MCKO: Enable
* Sampling Frequency: 44.1kHz
*
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p89.
*
* please fix-me
*/
ak4642_write(codec, 0x01, 0x08);
ak4642_write(codec, 0x04, 0x4a);
ak4642_write(codec, 0x05, 0x27);
ak4642_write(codec, 0x00, 0x40);
ak4642_write(codec, 0x01, 0x0b);
return ret;
reg_cache_err:
kfree(codec->reg_cache);
codec->reg_cache = NULL;
return ret;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct ak4642_priv *ak4642;
struct snd_soc_codec *codec;
int ret;
ak4642 = kzalloc(sizeof(struct ak4642_priv), GFP_KERNEL);
if (!ak4642)
return -ENOMEM;
codec = &ak4642->codec;
codec->dev = &i2c->dev;
i2c_set_clientdata(i2c, ak4642);
codec->control_data = i2c;
ret = ak4642_init(ak4642);
if (ret < 0)
printk(KERN_ERR "failed to initialise AK4642\n");
return ret;
}
static int ak4642_i2c_remove(struct i2c_client *client)
{
struct ak4642_priv *ak4642 = i2c_get_clientdata(client);
snd_soc_unregister_dai(&ak4642_dai);
snd_soc_unregister_codec(&ak4642->codec);
kfree(ak4642->codec.reg_cache);
kfree(ak4642);
ak4642_codec = NULL;
return 0;
}
static const struct i2c_device_id ak4642_i2c_id[] = {
{ "ak4642", 0 },
{ "ak4643", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
static struct i2c_driver ak4642_i2c_driver = {
.driver = {
.name = "AK4642 I2C Codec",
.owner = THIS_MODULE,
},
.probe = ak4642_i2c_probe,
.remove = ak4642_i2c_remove,
.id_table = ak4642_i2c_id,
};
#endif
static int ak4642_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
int ret;
if (!ak4642_codec) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
socdev->card->codec = ak4642_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "ak4642: failed to create pcms\n");
goto pcm_err;
}
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ak4642: failed to register card\n");
goto card_err;
}
dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
return ret;
}
/* power down chip */
static int ak4642_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_ak4642 = {
.probe = ak4642_probe,
.remove = ak4642_remove,
.resume = ak4642_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ak4642);
static int __init ak4642_modinit(void)
{
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&ak4642_i2c_driver);
#endif
return ret;
}
module_init(ak4642_modinit);
static void __exit ak4642_exit(void)
{
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&ak4642_i2c_driver);
#endif
}
module_exit(ak4642_exit);
MODULE_DESCRIPTION("Soc AK4642 driver");
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
MODULE_LICENSE("GPL");
/*
* ak4642.h -- AK4642 Soc Audio driver
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on ak4535.c
*
* 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 _AK4642_H
#define _AK4642_H
extern struct snd_soc_dai ak4642_dai;
extern struct snd_soc_codec_device soc_codec_dev_ak4642;
#endif
......@@ -806,15 +806,30 @@ static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(client);
struct snd_soc_codec *codec = &cs4270->codec;
int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
return snd_soc_write(codec, CS4270_PWRCTL, reg);
return snd_soc_suspend_device(codec->dev);
}
static int cs4270_i2c_resume(struct i2c_client *client)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(client);
struct snd_soc_codec *codec = &cs4270->codec;
return snd_soc_resume_device(codec->dev);
}
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct snd_soc_codec *codec = cs4270_codec;
int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
return snd_soc_write(codec, CS4270_PWRCTL, reg);
}
static int cs4270_soc_resume(struct platform_device *pdev)
{
struct snd_soc_codec *codec = cs4270_codec;
struct i2c_client *i2c_client = codec->control_data;
int reg;
/* In case the device was put to hard reset during sleep, we need to
......@@ -825,7 +840,7 @@ static int cs4270_i2c_resume(struct i2c_client *client)
for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) {
u8 val = snd_soc_read(codec, reg);
if (i2c_smbus_write_byte_data(client, reg, val)) {
if (i2c_smbus_write_byte_data(i2c_client, reg, val)) {
dev_err(codec->dev, "i2c write failed\n");
return -EIO;
}
......@@ -840,6 +855,8 @@ static int cs4270_i2c_resume(struct i2c_client *client)
#else
#define cs4270_i2c_suspend NULL
#define cs4270_i2c_resume NULL
#define cs4270_soc_suspend NULL
#define cs4270_soc_resume NULL
#endif /* CONFIG_PM */
/*
......@@ -868,7 +885,9 @@ static struct i2c_driver cs4270_i2c_driver = {
*/
struct snd_soc_codec_device soc_codec_device_cs4270 = {
.probe = cs4270_probe,
.remove = cs4270_remove
.remove = cs4270_remove,
.suspend = cs4270_soc_suspend,
.resume = cs4270_soc_resume,
};
EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
......
此差异已折叠。
/*
* cx20442.h -- audio driver for CX20442
*
* Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _CX20442_CODEC_H
#define _CX20442_CODEC_H
extern struct snd_soc_dai cx20442_dai;
extern struct snd_soc_codec_device cx20442_codec_dev;
extern struct tty_ldisc_ops v253_ops;
#endif
此差异已折叠。
/*
* max9877.h -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef _MAX9877_H
#define _MAX9877_H
#define MAX9877_INPUT_MODE 0x00
#define MAX9877_SPK_VOLUME 0x01
#define MAX9877_HPL_VOLUME 0x02
#define MAX9877_HPR_VOLUME 0x03
#define MAX9877_OUTPUT_MODE 0x04
/* MAX9877_INPUT_MODE */
#define MAX9877_INB (1 << 4)
#define MAX9877_INA (1 << 5)
#define MAX9877_ZCD (1 << 6)
/* MAX9877_OUTPUT_MODE */
#define MAX9877_OUTMODE_MASK (15 << 0)
#define MAX9877_OSC_MASK (3 << 4)
#define MAX9877_OSC_OFFSET 4
#define MAX9877_BYPASS (1 << 6)
#define MAX9877_SHDN (1 << 7)
extern int max9877_add_controls(struct snd_soc_codec *codec);
#endif
......@@ -21,6 +21,8 @@
#include "spdif_transciever.h"
MODULE_LICENSE("GPL");
#define STUB_RATES SNDRV_PCM_RATE_8000_96000
#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
......@@ -34,6 +36,7 @@ struct snd_soc_dai dit_stub_dai = {
.formats = STUB_FORMATS,
},
};
EXPORT_SYMBOL_GPL(dit_stub_dai);
static int spdif_dit_probe(struct platform_device *pdev)
{
......
......@@ -149,7 +149,7 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
if (reg / 2 > ARRAY_SIZE(stac9766_reg))
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
soc_ac97_ops.write(codec->ac97, reg, val);
......@@ -168,7 +168,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
if (reg / 2 > ARRAY_SIZE(stac9766_reg))
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
......
此差异已折叠。
......@@ -282,8 +282,6 @@ int aic3x_headset_detected(struct snd_soc_codec *codec);
int aic3x_button_pressed(struct snd_soc_codec *codec);
struct aic3x_setup_data {
int i2c_bus;
unsigned short i2c_address;
unsigned int gpio_func[2];
};
......
此差异已折叠。
......@@ -274,6 +274,8 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030;
struct twl4030_setup_data {
unsigned int ramp_delay_value;
unsigned int sysclk;
unsigned int hs_extmute:1;
void (*set_hs_extmute)(int mute);
};
#endif /* End of __TWL4030_AUDIO_H__ */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册