[alsa-devel] [PATCHv2 1/2] McBSP: OMAP3: Add sidetone feature

Tony Lindgren tony at atomide.com
Thu Feb 18 01:25:43 CET 2010


* Ilkka Koskinen <ilkka.koskinen at nokia.com> [100217 06:41]:
> From: Eero Nurkkala <ext-eero.nurkkala at nokia.com>
> 
> Add sidetone feature to McBSP instances 2 and 3 on OMAP3 based devices.
> 
> Signed-off-by: Ilkka Koskinen <ilkka.koskinen at nokia.com>

This one looks good to me and can be merged via Alsa list along with
the other patch.

Acked-by: Tony Lindgren <tony at atomide.com>


> ---
>  arch/arm/mach-omap2/mcbsp.c             |    2 +
>  arch/arm/plat-omap/include/plat/mcbsp.h |   63 +++++
>  arch/arm/plat-omap/mcbsp.c              |  396 ++++++++++++++++++++++++++++++-
>  3 files changed, 460 insertions(+), 1 deletions(-)
> 
> diff --git a/arch/arm/mach-omap2/mcbsp.c b/arch/arm/mach-omap2/mcbsp.c
> index d601f94..be8fce3 100644
> --- a/arch/arm/mach-omap2/mcbsp.c
> +++ b/arch/arm/mach-omap2/mcbsp.c
> @@ -136,6 +136,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
>  	},
>  	{
>  		.phys_base	= OMAP34XX_MCBSP2_BASE,
> +		.phys_base_st	= OMAP34XX_MCBSP2_ST_BASE,
>  		.dma_rx_sync	= OMAP24XX_DMA_MCBSP2_RX,
>  		.dma_tx_sync	= OMAP24XX_DMA_MCBSP2_TX,
>  		.rx_irq		= INT_24XX_MCBSP2_IRQ_RX,
> @@ -145,6 +146,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
>  	},
>  	{
>  		.phys_base	= OMAP34XX_MCBSP3_BASE,
> +		.phys_base_st	= OMAP34XX_MCBSP3_ST_BASE,
>  		.dma_rx_sync	= OMAP24XX_DMA_MCBSP3_RX,
>  		.dma_tx_sync	= OMAP24XX_DMA_MCBSP3_TX,
>  		.rx_irq		= INT_24XX_MCBSP3_IRQ_RX,
> diff --git a/arch/arm/plat-omap/include/plat/mcbsp.h b/arch/arm/plat-omap/include/plat/mcbsp.h
> index 4df957b..5db1653 100644
> --- a/arch/arm/plat-omap/include/plat/mcbsp.h
> +++ b/arch/arm/plat-omap/include/plat/mcbsp.h
> @@ -49,6 +49,9 @@
>  
>  #define OMAP34XX_MCBSP1_BASE	0x48074000
>  #define OMAP34XX_MCBSP2_BASE	0x49022000
> +#define OMAP34XX_MCBSP2_ST_BASE	0x49028000
> +#define OMAP34XX_MCBSP3_BASE	0x49024000
> +#define OMAP34XX_MCBSP3_ST_BASE	0x4902A000
>  #define OMAP34XX_MCBSP3_BASE	0x49024000
>  #define OMAP34XX_MCBSP4_BASE	0x49026000
>  #define OMAP34XX_MCBSP5_BASE	0x48096000
> @@ -146,6 +149,15 @@
>  #define OMAP_MCBSP_REG_WAKEUPEN	0xA8
>  #define OMAP_MCBSP_REG_XCCR	0xAC
>  #define OMAP_MCBSP_REG_RCCR	0xB0
> +#define OMAP_MCBSP_REG_SSELCR	0xBC
> +
> +#define OMAP_ST_REG_REV		0x00
> +#define OMAP_ST_REG_SYSCONFIG	0x10
> +#define OMAP_ST_REG_IRQSTATUS	0x18
> +#define OMAP_ST_REG_IRQENABLE	0x1C
> +#define OMAP_ST_REG_SGAINCR	0x24
> +#define OMAP_ST_REG_SFIRCR	0x28
> +#define OMAP_ST_REG_SSELCR	0x2C
>  
>  #define AUDIO_MCBSP_DATAWRITE	(OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1)
>  #define AUDIO_MCBSP_DATAREAD	(OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1)
> @@ -264,6 +276,24 @@
>  #define ENAWAKEUP		0x0004
>  #define SOFTRST			0x0002
>  
> +/********************** McBSP SSELCR bit definitions ***********************/
> +#define SIDETONEEN		0x0400
> +
> +/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/
> +#define ST_AUTOIDLE		0x0001
> +
> +/********************** McBSP Sidetone SGAINCR bit definitions *************/
> +#define ST_CH1GAIN(value)	((value<<16))	/* Bits 16:31 */
> +#define ST_CH0GAIN(value)	(value)		/* Bits 0:15 */
> +
> +/********************** McBSP Sidetone SFIRCR bit definitions **************/
> +#define ST_FIRCOEFF(value)	(value)		/* Bits 0:15 */
> +
> +/********************** McBSP Sidetone SSELCR bit definitions **************/
> +#define ST_COEFFWRDONE		0x0004
> +#define ST_COEFFWREN		0x0002
> +#define ST_SIDETONEEN		0x0001
> +
>  /********************** McBSP DMA operating modes **************************/
>  #define MCBSP_DMA_MODE_ELEMENT		0
>  #define MCBSP_DMA_MODE_THRESHOLD	1
> @@ -374,10 +404,25 @@ struct omap_mcbsp_platform_data {
>  	u16 rx_irq, tx_irq;
>  	struct omap_mcbsp_ops *ops;
>  #ifdef CONFIG_ARCH_OMAP3
> +	/* Sidetone block for McBSP 2 and 3 */
> +	unsigned long phys_base_st;
>  	u16 buffer_size;
>  #endif
>  };
>  
> +#define OMAP_MCBSP_ST_CHANNEL_0 (1 << 0)
> +#define OMAP_MCBSP_ST_CHANNEL_1 (1 << 1)
> +
> +struct omap_mcbsp_st_data {
> +	void __iomem *io_base_st;
> +	bool running;
> +	bool enabled;
> +	s16 taps[128];	/* Sidetone filter coefficients */
> +	int nr_taps;	/* Number of filter coefficients in use */
> +	s16 ch0gain;
> +	s16 ch1gain;
> +};
> +
>  struct omap_mcbsp {
>  	struct device *dev;
>  	unsigned long phys_base;
> @@ -410,6 +455,7 @@ struct omap_mcbsp {
>  	struct clk *iclk;
>  	struct clk *fclk;
>  #ifdef CONFIG_ARCH_OMAP3
> +	struct omap_mcbsp_st_data *st_data;
>  	int dma_op_mode;
>  	u16 max_tx_thres;
>  	u16 max_rx_thres;
> @@ -459,4 +505,21 @@ int omap_mcbsp_pollread(unsigned int id, u16 * buf);
>  int omap_mcbsp_pollwrite(unsigned int id, u16 buf);
>  int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type);
>  
> +#ifdef CONFIG_ARCH_OMAP3
> +/* Sidetone specific API */
> +int omap_st_set_chgain(unsigned int id, s16 ch0gain, s16 ch1gain, int channels);
> +int omap_st_get_chgain(unsigned int id, s16 *ch0gain, s16 *ch1gain);
> +int omap_st_enable(unsigned int id);
> +int omap_st_disable(unsigned int id);
> +int omap_st_is_enabled(unsigned int id);
> +#else
> +static inline int omap_st_set_chgain(unsigned int id, s16 ch0gain,
> +				     s16 ch1gain, int channels) { return 0; }
> +static inline int omap_st_get_chgain(unsigned int id, s16 *ch0gain,
> +				     s16 *ch1gain) { return 0; }
> +static inline int omap_st_enable(unsigned int id) { return 0; }
> +static inline int omap_st_disable(unsigned int id) { return 0; }
> +static inline int omap_st_is_enabled(unsigned int id) {  return 0; }
> +#endif
> +
>  #endif
> diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
> index 473be3d..921a5c1 100644
> --- a/arch/arm/plat-omap/mcbsp.c
> +++ b/arch/arm/plat-omap/mcbsp.c
> @@ -27,6 +27,8 @@
>  #include <plat/dma.h>
>  #include <plat/mcbsp.h>
>  
> +#include "../mach-omap2/cm-regbits-34xx.h"
> +
>  struct omap_mcbsp **mcbsp_ptr;
>  int omap_mcbsp_count, omap_mcbsp_cache_size;
>  
> @@ -58,6 +60,18 @@ int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
>  	}
>  }
>  
> +#ifdef CONFIG_ARCH_OMAP3
> +void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
> +{
> +	__raw_writel(val, mcbsp->st_data->io_base_st + reg);
> +}
> +
> +int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
> +{
> +	return __raw_readl(mcbsp->st_data->io_base_st + reg);
> +}
> +#endif
> +
>  #define MCBSP_READ(mcbsp, reg) \
>  		omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
>  #define MCBSP_WRITE(mcbsp, reg, val) \
> @@ -68,6 +82,11 @@ int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
>  #define omap_mcbsp_check_valid_id(id)	(id < omap_mcbsp_count)
>  #define id_to_mcbsp_ptr(id)		mcbsp_ptr[id];
>  
> +#define MCBSP_ST_READ(mcbsp, reg) \
> +			omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
> +#define MCBSP_ST_WRITE(mcbsp, reg, val) \
> +			omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
> +
>  static void omap_mcbsp_dump_reg(u8 id)
>  {
>  	struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
> @@ -211,6 +230,251 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
>  EXPORT_SYMBOL(omap_mcbsp_config);
>  
>  #ifdef CONFIG_ARCH_OMAP3
> +static void omap_st_on(struct omap_mcbsp *mcbsp)
> +{
> +	unsigned int w;
> +
> +	/*
> +	 * Sidetone uses McBSP ICLK - which must not idle when sidetones
> +	 * are enabled or sidetones start sounding ugly.
> +	 */
> +	w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
> +	w &= ~(1 << (mcbsp->id - 2));
> +	cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
> +
> +	/* Enable McBSP Sidetone */
> +	w = MCBSP_READ(mcbsp, SSELCR);
> +	MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
> +
> +	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
> +	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
> +
> +	/* Enable Sidetone from Sidetone Core */
> +	w = MCBSP_ST_READ(mcbsp, SSELCR);
> +	MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
> +}
> +
> +static void omap_st_off(struct omap_mcbsp *mcbsp)
> +{
> +	unsigned int w;
> +
> +	w = MCBSP_ST_READ(mcbsp, SSELCR);
> +	MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
> +
> +	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
> +	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
> +
> +	w = MCBSP_READ(mcbsp, SSELCR);
> +	MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
> +
> +	w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
> +	w |= 1 << (mcbsp->id - 2);
> +	cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
> +}
> +
> +static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
> +{
> +	u16 val, i;
> +
> +	val = MCBSP_ST_READ(mcbsp, SYSCONFIG);
> +	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, val & ~(ST_AUTOIDLE));
> +
> +	val = MCBSP_ST_READ(mcbsp, SSELCR);
> +
> +	if (val & ST_COEFFWREN)
> +		MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
> +
> +	MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
> +
> +	for (i = 0; i < 128; i++)
> +		MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
> +
> +	i = 0;
> +
> +	val = MCBSP_ST_READ(mcbsp, SSELCR);
> +	while (!(val & ST_COEFFWRDONE) && (++i < 1000))
> +		val = MCBSP_ST_READ(mcbsp, SSELCR);
> +
> +	MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
> +
> +	if (i == 1000)
> +		dev_err(mcbsp->dev, "McBSP FIR load error!\n");
> +}
> +
> +static void omap_st_chgain(struct omap_mcbsp *mcbsp)
> +{
> +	u16 w;
> +	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
> +
> +	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
> +	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
> +
> +	w = MCBSP_ST_READ(mcbsp, SSELCR);
> +
> +	MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
> +		      ST_CH1GAIN(st_data->ch1gain));
> +}
> +
> +int omap_st_set_chgain(unsigned int id, s16 ch0gain, s16 ch1gain,
> +		       int channels)
> +{
> +	struct omap_mcbsp *mcbsp;
> +	struct omap_mcbsp_st_data *st_data;
> +
> +	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);
> +	st_data = mcbsp->st_data;
> +
> +	if (!st_data)
> +		return -ENOENT;
> +
> +	spin_lock_irq(&mcbsp->lock);
> +	if (channels & OMAP_MCBSP_ST_CHANNEL_0)
> +		st_data->ch0gain = ch0gain;
> +
> +	if (channels &  OMAP_MCBSP_ST_CHANNEL_1)
> +		st_data->ch1gain = ch1gain;
> +
> +	if (st_data->enabled)
> +		omap_st_chgain(mcbsp);
> +	spin_unlock_irq(&mcbsp->lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_st_set_chgain);
> +
> +int omap_st_get_chgain(unsigned int id, s16 *ch0gain, s16 *ch1gain)
> +{
> +	struct omap_mcbsp *mcbsp;
> +	struct omap_mcbsp_st_data *st_data;
> +
> +	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);
> +	st_data = mcbsp->st_data;
> +
> +	if (!st_data)
> +		return -ENOENT;
> +
> +	spin_lock_irq(&mcbsp->lock);
> +	*ch0gain = st_data->ch0gain;
> +	*ch1gain = st_data->ch1gain;
> +	spin_unlock_irq(&mcbsp->lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_st_get_chgain);
> +
> +static int omap_st_start(struct omap_mcbsp *mcbsp)
> +{
> +	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
> +
> +	if (st_data && st_data->enabled && !st_data->running) {
> +		omap_st_fir_write(mcbsp, st_data->taps);
> +		omap_st_chgain(mcbsp);
> +
> +		if (!mcbsp->free) {
> +			omap_st_on(mcbsp);
> +			st_data->running = 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int omap_st_enable(unsigned int id)
> +{
> +	struct omap_mcbsp *mcbsp;
> +	struct omap_mcbsp_st_data *st_data;
> +
> +	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);
> +	st_data = mcbsp->st_data;
> +
> +	if (!st_data)
> +		return -ENODEV;
> +
> +	spin_lock_irq(&mcbsp->lock);
> +	st_data->enabled = 1;
> +	omap_st_start(mcbsp);
> +	spin_unlock_irq(&mcbsp->lock);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(omap_st_enable);
> +
> +static int omap_st_stop(struct omap_mcbsp *mcbsp)
> +{
> +	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
> +
> +	if (st_data && st_data->running) {
> +		if (!mcbsp->free) {
> +			omap_st_off(mcbsp);
> +			st_data->running = 0;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int omap_st_disable(unsigned int id)
> +{
> +	struct omap_mcbsp *mcbsp;
> +	struct omap_mcbsp_st_data *st_data;
> +	int ret = 0;
> +
> +	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);
> +	st_data = mcbsp->st_data;
> +
> +	if (!st_data)
> +		return -ENODEV;
> +
> +	spin_lock_irq(&mcbsp->lock);
> +	omap_st_stop(mcbsp);
> +	st_data->enabled = 0;
> +	spin_unlock_irq(&mcbsp->lock);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(omap_st_disable);
> +
> +int omap_st_is_enabled(unsigned int id)
> +{
> +	struct omap_mcbsp *mcbsp;
> +	struct omap_mcbsp_st_data *st_data;
> +
> +	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);
> +	st_data = mcbsp->st_data;
> +
> +	if (!st_data)
> +		return -ENODEV;
> +
> +
> +	return st_data->enabled;
> +}
> +EXPORT_SYMBOL(omap_st_is_enabled);
> +
>  /*
>   * omap_mcbsp_set_tx_threshold configures how to deal
>   * with transmit threshold. the threshold value and handler can be
> @@ -363,6 +627,8 @@ static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
>  #else
>  static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
>  static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
> +static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
> +static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
>  #endif
>  
>  /*
> @@ -546,6 +812,9 @@ void omap_mcbsp_start(unsigned int id, int tx, int rx)
>  	}
>  	mcbsp = id_to_mcbsp_ptr(id);
>  
> +	if (cpu_is_omap34xx())
> +		omap_st_start(mcbsp);
> +
>  	mcbsp->rx_word_length = (MCBSP_READ_CACHE(mcbsp, RCR1) >> 5) & 0x7;
>  	mcbsp->tx_word_length = (MCBSP_READ_CACHE(mcbsp, XCR1) >> 5) & 0x7;
>  
> @@ -637,6 +906,9 @@ void omap_mcbsp_stop(unsigned int id, int tx, int rx)
>  		w = MCBSP_READ_CACHE(mcbsp, SPCR2);
>  		MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
>  	}
> +
> +	if (cpu_is_omap34xx())
> +		omap_st_stop(mcbsp);
>  }
>  EXPORT_SYMBOL(omap_mcbsp_stop);
>  
> @@ -1212,6 +1484,64 @@ unlock:
>  
>  static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
>  
> +static ssize_t st_taps_show(struct device *dev,
> +			    struct device_attribute *attr, char *buf)
> +{
> +	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
> +	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
> +	ssize_t status = 0;
> +	int i;
> +
> +	spin_lock_irq(&mcbsp->lock);
> +	for (i = 0; i < st_data->nr_taps; i++)
> +		status += sprintf(&buf[status], (i ? ", %d" : "%d"),
> +				  st_data->taps[i]);
> +	if (i)
> +		status += sprintf(&buf[status], "\n");
> +	spin_unlock_irq(&mcbsp->lock);
> +
> +	return status;
> +}
> +
> +static ssize_t st_taps_store(struct device *dev,
> +			     struct device_attribute *attr,
> +			     const char *buf, size_t size)
> +{
> +	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
> +	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
> +	int val, tmp, status, i = 0;
> +
> +	spin_lock_irq(&mcbsp->lock);
> +	memset(st_data->taps, 0, sizeof(st_data->taps));
> +	st_data->nr_taps = 0;
> +
> +	do {
> +		status = sscanf(buf, "%d%n", &val, &tmp);
> +		if (status < 0 || status == 0) {
> +			size = -EINVAL;
> +			goto out;
> +		}
> +		if (val < -32768 || val > 32767) {
> +			size = -EINVAL;
> +			goto out;
> +		}
> +		st_data->taps[i++] = val;
> +		buf += tmp;
> +		if (*buf != ',')
> +			break;
> +		buf++;
> +	} while (1);
> +
> +	st_data->nr_taps = i;
> +
> +out:
> +	spin_unlock_irq(&mcbsp->lock);
> +
> +	return size;
> +}
> +
> +static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
> +
>  static const struct attribute *additional_attrs[] = {
>  	&dev_attr_max_tx_thres.attr,
>  	&dev_attr_max_rx_thres.attr,
> @@ -1233,6 +1563,60 @@ static inline void __devexit omap_additional_remove(struct device *dev)
>  	sysfs_remove_group(&dev->kobj, &additional_attr_group);
>  }
>  
> +static const struct attribute *sidetone_attrs[] = {
> +	&dev_attr_st_taps.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group sidetone_attr_group = {
> +	.attrs = (struct attribute **)sidetone_attrs,
> +};
> +
> +int __devinit omap_st_add(struct omap_mcbsp *mcbsp)
> +{
> +	struct omap_mcbsp_platform_data *pdata = mcbsp->pdata;
> +	struct omap_mcbsp_st_data *st_data;
> +	int err;
> +
> +	st_data = kzalloc(sizeof(*mcbsp->st_data), GFP_KERNEL);
> +	if (!st_data) {
> +		err = -ENOMEM;
> +		goto err1;
> +	}
> +
> +	st_data->io_base_st = ioremap(pdata->phys_base_st, SZ_4K);
> +	if (!st_data->io_base_st) {
> +		err = -ENOMEM;
> +		goto err2;
> +	}
> +
> +	err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
> +	if (err)
> +		goto err3;
> +
> +	mcbsp->st_data = st_data;
> +	return 0;
> +
> +err3:
> +	iounmap(st_data->io_base_st);
> +err2:
> +	kfree(st_data);
> +err1:
> +	return err;
> +
> +}
> +
> +static void __devexit omap_st_remove(struct omap_mcbsp *mcbsp)
> +{
> +	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
> +
> +	if (st_data) {
> +		sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
> +		iounmap(st_data->io_base_st);
> +		kfree(st_data);
> +	}
> +}
> +
>  static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
>  {
>  	mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
> @@ -1246,6 +1630,12 @@ static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
>  		if (omap_additional_add(mcbsp->dev))
>  			dev_warn(mcbsp->dev,
>  				"Unable to create additional controls\n");
> +
> +		if (mcbsp->id == 2 || mcbsp->id == 3)
> +			if (omap_st_add(mcbsp))
> +				dev_warn(mcbsp->dev,
> +				 "Unable to create sidetone controls\n");
> +
>  	} else {
>  		mcbsp->max_tx_thres = -EINVAL;
>  		mcbsp->max_rx_thres = -EINVAL;
> @@ -1254,8 +1644,12 @@ static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
>  
>  static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
>  {
> -	if (cpu_is_omap34xx())
> +	if (cpu_is_omap34xx()) {
>  		omap_additional_remove(mcbsp->dev);
> +
> +		if (mcbsp->id == 2 || mcbsp->id == 3)
> +			omap_st_remove(mcbsp);
> +	}
>  }
>  #else
>  static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
> -- 
> 1.6.0.4
> 


More information about the Alsa-devel mailing list