Commit 6bc09656 authored by Juha Yrjola's avatar Juha Yrjola

Merge source.mvista.com:linux-omap

parents b6a3bbbf 7508d984
......@@ -40,8 +40,9 @@
#include <asm/arch/irda.h>
#include <asm/arch/usb.h>
#include <asm/arch/keypad.h>
#include <asm/arch/dma.h>
#include <asm/arch/common.h>
#include <asm/arch/mcbsp.h>
#include <asm/arch/omap-alsa.h>
extern int omap_gpio_init(void);
......@@ -285,6 +286,41 @@ static struct platform_device h2_lcd_device = {
.id = -1,
};
static struct omap_mcbsp_reg_cfg mcbsp_regs = {
.spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
.spcr1 = RINTM(3) | RRST,
.rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1),
.rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
.xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG,
.xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
.srgr1 = FWID(15),
.srgr2 = GSYNC | CLKSP | FSGM | FPER(31),
.pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP,
//.pcr0 = CLKXP | CLKRP, /* mcbsp: slave */
};
static struct omap_alsa_codec_config alsa_config = {
.name = "H2 TSC2101",
.mcbsp_regs_alsa = &mcbsp_regs,
.codec_configure_dev = NULL, // tsc2101_configure,
.codec_set_samplerate = NULL, // tsc2101_set_samplerate,
.codec_clock_setup = NULL, // tsc2101_clock_setup,
.codec_clock_on = NULL, // tsc2101_clock_on,
.codec_clock_off = NULL, // tsc2101_clock_off,
.get_default_samplerate = NULL, // tsc2101_get_default_samplerate,
};
static struct platform_device h2_mcbsp1_device = {
.name = "omap_alsa_mcbsp",
.id = 1,
.dev = {
.platform_data = &alsa_config,
},
};
static struct platform_device *h2_devices[] __initdata = {
&h2_nor_device,
&h2_nand_device,
......@@ -292,6 +328,7 @@ static struct platform_device *h2_devices[] __initdata = {
&h2_irda_device,
&h2_kp_device,
&h2_lcd_device,
&h2_mcbsp1_device,
};
static void __init h2_init_smc91x(void)
......
......@@ -47,6 +47,8 @@
#include <asm/arch/tc.h>
#include <asm/arch/keypad.h>
#include <asm/arch/common.h>
#include <asm/arch/mcbsp.h>
#include <asm/arch/omap-alsa.h>
static int osk_keymap[] = {
KEY(0, 0, KEY_F1),
......@@ -149,9 +151,40 @@ static struct platform_device osk5912_cf_device = {
.resource = osk5912_cf_resources,
};
#define DEFAULT_BITPERSAMPLE 16
static struct omap_mcbsp_reg_cfg mcbsp_regs = {
.spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
.spcr1 = RINTM(3) | RRST,
.rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0),
.rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
.xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG,
.xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
.srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
.srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
/*.pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,*/ /* mcbsp: master */
.pcr0 = CLKXP | CLKRP, /* mcbsp: slave */
};
static struct omap_alsa_codec_config alsa_config = {
.name = "OSK AIC23",
.mcbsp_regs_alsa = &mcbsp_regs,
.codec_configure_dev = NULL, // aic23_configure,
.codec_set_samplerate = NULL, // aic23_set_samplerate,
.codec_clock_setup = NULL, // aic23_clock_setup,
.codec_clock_on = NULL, // aic23_clock_on,
.codec_clock_off = NULL, // aic23_clock_off,
.get_default_samplerate = NULL, // aic23_get_default_samplerate,
};
static struct platform_device osk5912_mcbsp1_device = {
.name = "omap_mcbsp",
.id = 1,
.name = "omap_alsa_mcbsp",
.id = 1,
.dev = {
.platform_data = &alsa_config,
},
};
static struct resource osk5912_kp_resources[] = {
......
......@@ -193,8 +193,9 @@ static int omap_otg_probe(struct device *dev)
static int omap_otg_remove(struct device *dev)
{
tahvo_otg_dev = NULL;
free_irq(tahvo_otg_dev->resource[1].start, &tahvo_usb_device);
tahvo_otg_dev = NULL;
return 0;
}
......
/*
* sound/arm/omap-aic23.h
* linux/include/asm-arm/arch-omap/omap-alsa.h
*
* Alsa Driver for AIC23 codec on OSK5912 platform board
* Alsa Driver for AIC23 and TSC2101 codecs on OMAP platform boards.
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
......@@ -33,39 +35,38 @@
* 2005/07/25 INdT-10LE Kernel Team - Alsa driver for omap osk,
* original version based in sa1100 driver
* and omap oss driver.
*
* 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu
*/
#ifndef __OMAP_AIC23_H
#define __OMAP_AIC23_H
#ifndef __OMAP_ALSA_H
#define __OMAP_ALSA_H
#include <sound/driver.h>
#include <asm/arch/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <asm/arch/mcbsp.h>
#include <linux/platform_device.h>
/*
* Debug functions
*/
#undef DEBUG
//#define DEBUG
#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
#ifdef DEBUG
#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__)
#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
#else
#define DPRINTK(ARGS...) /* nop */
#define ADEBUG() /* nop */
#define FN_IN /* nop */
#define FN_OUT(n) /* nop */
#endif
#define DEFAULT_OUTPUT_VOLUME 0x60
#define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */
#define OUTPUT_VOLUME_MIN LHV_MIN
#define OUTPUT_VOLUME_MAX LHV_MAX
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
#define INPUT_VOLUME_MIN LIV_MIN
#define INPUT_VOLUME_MAX LIV_MAX
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
#define SIDETONE_MASK 0x1c0
#define SIDETONE_0 0x100
#define SIDETONE_6 0x000
#define SIDETONE_9 0x040
#define SIDETONE_12 0x080
#define SIDETONE_18 0x0c0
#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB
#define DMA_BUF_SIZE (1024 * 8)
/*
* Buffer management for alsa and dma
......@@ -93,38 +94,51 @@ struct audio_stream {
/*
* Alsa card structure for aic23
*/
struct snd_card_omap_aic23 {
struct snd_card_omap_codec {
snd_card_t *card;
snd_pcm_t *pcm;
long samplerate;
struct audio_stream s[2]; /* playback & capture */
};
/*********** Function Prototypes *************************/
/* Codec specific information and function pointers.
* Codec (omap-alsa-aic23.c and omap-alsa-tsc2101.c)
* are responsible for defining the function pointers.
*/
struct omap_alsa_codec_config {
char *name;
struct omap_mcbsp_reg_cfg *mcbsp_regs_alsa;
snd_pcm_hw_constraint_list_t *hw_constraints_rates;
snd_pcm_hardware_t *snd_omap_alsa_playback;
snd_pcm_hardware_t *snd_omap_alsa_capture;
void (*codec_configure_dev)(void);
void (*codec_set_samplerate)(long);
void (*codec_clock_setup)(void);
int (*codec_clock_on)(void);
int (*codec_clock_off)(void);
int (*get_default_samplerate)(void);
};
void audio_dma_callback(void *);
int snd_omap_mixer(struct snd_card_omap_aic23 *);
/*********** Mixer function prototypes *************************/
int snd_omap_mixer(struct snd_card_omap_codec *);
void snd_omap_init_mixer(void);
/* Clock functions */
int omap_aic23_clock_on(void);
int omap_aic23_clock_off(void);
#ifdef CONFIG_PM
void snd_omap_suspend_mixer(void);
void snd_omap_resume_mixer(void);
#endif
/* Codec AIC23 */
#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
extern int tlv320aic23_write_value(u8 reg, u16 value);
/* TLV320AIC23 is a write only device */
static __inline__ void audio_aic23_write(u8 address, u16 data)
{
tlv320aic23_write_value(address, data);
}
int snd_omap_alsa_post_probe(struct platform_device *pdev, struct omap_alsa_codec_config *config);
int snd_omap_alsa_remove(struct platform_device *pdev);
#ifdef CONFIG_PM
int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state);
int snd_omap_alsa_resume(struct platform_device *pdev);
#else
#define snd_omap_alsa_suspend NULL
#define snd_omap_alsa_resume NULL
#endif
#endif /* CONFIG_SENSORS_TLV320AIC23 */
/*********** function prototype to function called from the dma interrupt handler ******/
void callback_omap_alsa_sound_dma(void *);
#endif
......@@ -44,5 +44,19 @@ config SND_OMAP_AIC23
To compile this driver as a module, choose M here: the module
will be called snd-omap-aic23.
config SND_OMAP_TSC2101
tristate "OMAP TSC2101 alsa driver"
depends on ARCH_OMAP && SND
select SND_PCM
select OMAP_TSC2101
select OMAP_UWIRE if ARCH_OMAP
help
Say Y here if you have a OMAP platform board
and want to use its TSC2101 audio chip. Driver has
been tested with H2 and iPAQ h6300.
To compile this driver as a module, choose M here: the module
will be called snd-omap-tsc2101.
endmenu
......@@ -14,5 +14,4 @@ snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-aic23.o
snd-omap-aic23-objs := omap-aic23.o omap-alsa-dma.o omap-alsa-mixer.o
obj-$(CONFIG_SND) += omap/
#
## Makefile for ALSA OMAP
#
#
obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-alsa-aic23.o
snd-omap-alsa-aic23-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-aic23.o omap-alsa-aic23-mixer.o
obj-$(CONFIG_SND_OMAP_TSC2101) += snd-omap-alsa-tsc2101.o
snd-omap-alsa-tsc2101-objs := omap-alsa.o omap-alsa-dma.o omap-alsa-tsc2101.o omap-alsa-tsc2101-mixer.o
/*
* sound/arm/omap-alsa-mixer.c
* sound/arm/omap/omap-alsa-aic23-mixer.c
*
* Alsa Driver Mixer for generic codecs for omap boards
*
......@@ -39,20 +39,10 @@
#include <linux/config.h>
#include <sound/driver.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/dma.h>
#include <asm/arch/aic23.h>
#include "omap-aic23.h"
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-aic23.h"
#include <sound/initval.h>
#include <sound/control.h>
......@@ -67,7 +57,7 @@ MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA");
/* Codec AIC23 */
#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
extern __inline__ void audio_aic23_write(u8, u16);
extern void audio_aic23_write(u8, u16);
#define MIXER_NAME "Mixer AIC23"
#define SND_OMAP_WRITE(reg, val) audio_aic23_write(reg, val)
......@@ -411,31 +401,6 @@ static snd_kcontrol_new_t snd_omap_controls[] = {
OMAP_MUX("Capture Source", ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, INSEL_MIC),
};
void snd_omap_init_mixer(void)
{
u16 vol_reg;
/* Line's default values */
omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
omap_regs[LINE_INDEX].sw = 0;
SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
/* Analog Audio Control's default values */
omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
/* Headphone's default values */
vol_reg = LZC_ON;
vol_reg &= ~OUTPUT_VOLUME_MASK;
vol_reg |= DEFAULT_OUTPUT_VOLUME;
omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME;
omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME;
omap_regs[PCM_INDEX].sw = 1;
SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg);
SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg);
}
#ifdef CONFIG_PM
void snd_omap_suspend_mixer(void)
......@@ -474,7 +439,32 @@ void snd_omap_resume_mixer(void)
}
#endif
int snd_omap_mixer(struct snd_card_omap_aic23 *chip)
void snd_omap_init_mixer(void)
{
u16 vol_reg;
/* Line's default values */
omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK;
omap_regs[LINE_INDEX].sw = 0;
SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK);
/* Analog Audio Control's default values */
omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL;
/* Headphone's default values */
vol_reg = LZC_ON;
vol_reg &= ~OUTPUT_VOLUME_MASK;
vol_reg |= DEFAULT_OUTPUT_VOLUME;
omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME;
omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME;
omap_regs[PCM_INDEX].sw = 1;
SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg);
SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg);
}
int snd_omap_mixer(struct snd_card_omap_codec *chip)
{
snd_card_t *card;
unsigned int idx;
......@@ -493,4 +483,3 @@ int snd_omap_mixer(struct snd_card_omap_aic23 *chip)
return 0;
}
/*
* arch/arm/mach-omap1/omap-alsa-aic23.c
*
* Alsa codec Driver for AIC23 chip on OSK5912 platform board
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
* {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Based in former alsa driver for osk and oss driver
*
* 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 <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <linux/clk.h>
#include <asm/arch/clock.h>
#include <asm/arch/aic23.h>
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-aic23.h"
static struct clk *aic23_mclk = 0;
/* aic23 related */
static const struct aic23_samplerate_reg_info
rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
{4000, 0x06, 1}, /* 4000 */
{8000, 0x06, 0}, /* 8000 */
{16000, 0x0C, 1}, /* 16000 */
{22050, 0x11, 1}, /* 22050 */
{24000, 0x00, 1}, /* 24000 */
{32000, 0x0C, 0}, /* 32000 */
{44100, 0x11, 0}, /* 44100 */
{48000, 0x00, 0}, /* 48000 */
{88200, 0x1F, 0}, /* 88200 */
{96000, 0x0E, 0}, /* 96000 */
};
/*
* Hardware capabilities
*/
/*
* DAC USB-mode sampling rates (MCLK = 12 MHz)
* The rates and rate_reg_into MUST be in the same order
*/
static unsigned int rates[] = {
4000, 8000, 16000, 22050,
24000, 32000, 44100,
48000, 88200, 96000,
};
static snd_pcm_hw_constraint_list_t aic23_hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static snd_pcm_hardware_t aic23_snd_omap_alsa_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
static snd_pcm_hardware_t aic23_snd_omap_alsa_capture = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
/*
* Codec/mcbsp init and configuration section
* codec dependent code.
*/
extern int tlv320aic23_write_value(u8 reg, u16 value);
/* TLV320AIC23 is a write only device */
void audio_aic23_write(u8 address, u16 data)
{
tlv320aic23_write_value(address, data);
}
EXPORT_SYMBOL_GPL(audio_aic23_write);
/*
* Sample rate changing
*/
void aic23_set_samplerate(long rate)
{
u8 count = 0;
u16 data = 0;
/* Fix the rate if it has a wrong value */
if (rate >= 96000)
rate = 96000;
else if (rate >= 88200)
rate = 88200;
else if (rate >= 48000)
rate = 48000;
else if (rate >= 44100)
rate = 44100;
else if (rate >= 32000)
rate = 32000;
else if (rate >= 24000)
rate = 24000;
else if (rate >= 22050)
rate = 22050;
else if (rate >= 16000)
rate = 16000;
else if (rate >= 8000)
rate = 8000;
else
rate = 4000;
/* Search for the right sample rate */
/* Verify what happens if the rate is not supported
* now it goes to 96Khz */
while ((rate_reg_info[count].sample_rate != rate) &&
(count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) {
count++;
}
data = (rate_reg_info[count].divider << CLKIN_SHIFT) |
(rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
}
inline void aic23_configure(void)
{
/* Reset codec */
audio_aic23_write(RESET_CONTROL_ADDR, 0);
/* Initialize the AIC23 internal state */
/* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL);
/* Digital audio path control, de-emphasis control 44.1kHz */
audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
/* Digital audio interface, master/slave mode, I2S, 16 bit */
#ifdef AIC23_MASTER
audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR,
MS_MASTER | IWL_16 | FOR_DSP);
#else
audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
#endif
/* Enable digital interface */
audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
}
/*
* Omap MCBSP clock configuration and Power Management
*
* Here we have some functions that allows clock to be enabled and
* disabled only when needed. Besides doing clock configuration
* it allows turn on/turn off audio when necessary.
*/
/*
* Do clock framework mclk search
*/
void aic23_clock_setup(void)
{
aic23_mclk = clk_get(0, "mclk");
}
/*
* Do some sanity check, set clock rate, starts it and
* turn codec audio on
*/
int aic23_clock_on(void)
{
if (clk_get_usecount(aic23_mclk) > 0) {
/* MCLK is already in use */
printk(KERN_WARNING
"MCLK in use at %d Hz. We change it to %d Hz\n",
(uint) clk_get_rate(aic23_mclk),
CODEC_CLOCK);
}
if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) {
printk(KERN_ERR
"Cannot set MCLK for AIC23 CODEC\n");
return -ECANCELED;
}
clk_enable(aic23_mclk);
printk(KERN_DEBUG
"MCLK = %d [%d], usecount = %d\n",
(uint) clk_get_rate(aic23_mclk), CODEC_CLOCK,
clk_get_usecount(aic23_mclk));
/* Now turn the audio on */
audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF &
~ADC_OFF & ~MIC_OFF & ~LINE_OFF);
return 0;
}
/*
* Do some sanity check, turn clock off and then turn
* codec audio off
*/
int aic23_clock_off(void)
{
if (clk_get_usecount(aic23_mclk) > 0) {
if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) {
printk(KERN_WARNING
"MCLK for audio should be %d Hz. But is %d Hz\n",
(uint) clk_get_rate(aic23_mclk),
CODEC_CLOCK);
}
clk_disable(aic23_mclk);
}
audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
DEVICE_POWER_OFF | OUT_OFF | DAC_OFF |
ADC_OFF | MIC_OFF | LINE_OFF);
return 0;
}
int aic23_get_default_samplerate(void)
{
return DEFAULT_SAMPLE_RATE;
}
static int __init snd_omap_alsa_aic23_probe(struct platform_device *pdev)
{
int ret;
struct omap_alsa_codec_config *codec_cfg;
codec_cfg = pdev->dev.platform_data;
if (codec_cfg != NULL) {
codec_cfg->hw_constraints_rates = &aic23_hw_constraints_rates;
codec_cfg->snd_omap_alsa_playback = &aic23_snd_omap_alsa_playback;
codec_cfg->snd_omap_alsa_capture = &aic23_snd_omap_alsa_capture;
codec_cfg->codec_configure_dev = aic23_configure;
codec_cfg->codec_set_samplerate = aic23_set_samplerate;
codec_cfg->codec_clock_setup = aic23_clock_setup;
codec_cfg->codec_clock_on = aic23_clock_on;
codec_cfg->codec_clock_off = aic23_clock_off;
codec_cfg->get_default_samplerate = aic23_get_default_samplerate;
ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
}
else
ret = -ENODEV;
return ret;
}
static struct platform_driver omap_alsa_driver = {
.probe = snd_omap_alsa_aic23_probe,
.remove = snd_omap_alsa_remove,
.suspend = snd_omap_alsa_suspend,
.resume = snd_omap_alsa_resume,
.driver = {
.name = "omap_alsa_mcbsp",
},
};
static int __init omap_alsa_aic23_init(void)
{
int err;
ADEBUG();
err = platform_driver_register(&omap_alsa_driver);
return err;
}
static void __exit omap_alsa_aic23_exit(void)
{
ADEBUG();
platform_driver_unregister(&omap_alsa_driver);
}
module_init(omap_alsa_aic23_init);
module_exit(omap_alsa_aic23_exit);
/*
* sound/arm/omap-alsa-aic23.h
*
* Alsa Driver for AIC23 codec on OSK5912 platform board
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
* {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* 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 __OMAP_ALSA_AIC23_H
#define __OMAP_ALSA_AIC23_H
#include <sound/driver.h>
#include <asm/arch/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <asm/arch/mcbsp.h>
/* Define to set the AIC23 as the master w.r.t McBSP */
#define AIC23_MASTER
#define NUMBER_SAMPLE_RATES_SUPPORTED 10
/*
* AUDIO related MACROS
*/
#ifndef DEFAULT_BITPERSAMPLE
#define DEFAULT_BITPERSAMPLE 16
#endif
#define DEFAULT_SAMPLE_RATE 44100
#define CODEC_CLOCK 12000000
#define AUDIO_MCBSP OMAP_MCBSP1
#define DEFAULT_OUTPUT_VOLUME 0x60
#define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */
#define OUTPUT_VOLUME_MIN LHV_MIN
#define OUTPUT_VOLUME_MAX LHV_MAX
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
#define INPUT_VOLUME_MIN LIV_MIN
#define INPUT_VOLUME_MAX LIV_MAX
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
#define SIDETONE_MASK 0x1c0
#define SIDETONE_0 0x100
#define SIDETONE_6 0x000
#define SIDETONE_9 0x040
#define SIDETONE_12 0x080
#define SIDETONE_18 0x0c0
#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB
struct aic23_samplerate_reg_info {
u32 sample_rate;
u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
};
/*
* Defines codec specific functions pointers that can be used from the
* common omap-alse base driver for all omap codecs. (tsc2101 and aic23)
*/
void define_codec_functions(struct omap_alsa_codec_config *codec_config);
inline void aic23_configure(void);
void aic23_set_samplerate(long rate);
void aic23_clock_setup(void);
int aic23_clock_on(void);
int aic23_clock_off(void);
int aic23_get_default_samplerate(void);
#endif
/*
* sound/arm/omap-alsa-dma.c
* sound/arm/omap/omap-alsa-dma.c
*
* Common audio DMA handling for the OMAP processors
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
*
* Copyright (C) 2004 Texas Instruments, Inc.
......@@ -66,20 +68,9 @@
#include <asm/arch/mcbsp.h>
#include "omap-aic23.h"
#include <asm/arch/omap-alsa.h>
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
#else
#define DPRINTK( x... )
#define FN_IN
#define FN_OUT(x)
#endif
#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
......@@ -165,7 +156,7 @@ static void omap_sound_dma_link_lch(void *data)
FN_OUT(0);
}
int omap_request_sound_dma(int device_id, const char *device_name,
int omap_request_alsa_sound_dma(int device_id, const char *device_name,
void *data, int **channels)
{
int i, err = 0;
......@@ -186,10 +177,11 @@ int omap_request_sound_dma(int device_id, const char *device_name,
}
spin_lock(&dma_list_lock);
for (i = 0; i < nr_linked_channels; i++) {
err =
omap_request_dma(device_id, device_name,
sound_dma_irq_handler, data,
&chan[i]);
err = omap_request_dma(device_id,
device_name,
sound_dma_irq_handler,
data,
&chan[i]);
/* Handle Failure condition here */
if (err < 0) {
......@@ -223,7 +215,7 @@ int omap_request_sound_dma(int device_id, const char *device_name,
**************************************************************************************/
static void omap_sound_dma_unlink_lch(void *data)
{
struct audio_stream *s = (struct audio_stream *) data;
struct audio_stream *s = (struct audio_stream *)data;
int *chan = s->lch;
int i;
......@@ -243,11 +235,11 @@ static void omap_sound_dma_unlink_lch(void *data)
FN_OUT(0);
}
int omap_free_sound_dma(void *data, int **channels)
int omap_free_alsa_sound_dma(void *data, int **channels)
{
int i;
int *chan = NULL;
FN_IN;
if (unlikely(NULL == channels)) {
BUG();
......@@ -277,10 +269,11 @@ int omap_free_sound_dma(void *data, int **channels)
* Stop all the DMA channels of the stream
*
**************************************************************************************/
void omap_audio_stop_dma(struct audio_stream *s)
void omap_stop_alsa_sound_dma(struct audio_stream *s)
{
int *chan = s->lch;
int i;
FN_IN;
if (unlikely(NULL == chan)) {
BUG();
......@@ -299,7 +292,7 @@ void omap_audio_stop_dma(struct audio_stream *s)
* Clear any pending transfers
*
**************************************************************************************/
void omap_clear_sound_dma(struct audio_stream * s)
void omap_clear_alsa_sound_dma(struct audio_stream * s)
{
FN_IN;
omap_clear_dma(s->lch[s->dma_q_head]);
......@@ -307,13 +300,6 @@ void omap_clear_sound_dma(struct audio_stream * s)
return;
}
/*********************************** MODULE FUNCTIONS DEFINTIONS ***********************/
#ifdef OMAP1610_MCBSP1_BASE
#undef OMAP1610_MCBSP1_BASE
#endif
#define OMAP1610_MCBSP1_BASE 0xE1011000
/***************************************************************************************
*
* DMA related functions
......@@ -325,9 +311,10 @@ static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
int dt = 0x1; /* data type 16 */
int cen = 32; /* Stereo */
int cfn = dma_size / (2 * cen);
FN_IN;
omap_set_dma_dest_params(channel, 0x05, 0x00,
(OMAP1610_MCBSP1_BASE + 0x806),
(OMAP1510_MCBSP1_BASE + 0x06),
0, 0);
omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr,
0, 0);
......@@ -341,11 +328,11 @@ static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
{
int dt = 0x1; /* data type 16 */
int cen = 32; /* stereo */
int cfn = dma_size / (2 * cen);
FN_IN;
omap_set_dma_src_params(channel, 0x05, 0x00,
(OMAP1610_MCBSP1_BASE + 0x802),
(OMAP1510_MCBSP1_BASE + 0x02),
0, 0);
omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
......@@ -358,7 +345,7 @@ static int audio_start_dma_chain(struct audio_stream *s)
int channel = s->lch[s->dma_q_head];
FN_IN;
if (!s->started) {
s->hw_stop(); /* stops McBSP Interface */
s->hw_stop(); /* stops McBSP Interface */
omap_start_dma(channel);
s->started = 1;
s->hw_start(); /* start McBSP interface */
......@@ -372,8 +359,9 @@ static int audio_start_dma_chain(struct audio_stream *s)
* Do the initial set of work to initialize all the channels as required.
* We shall then initate a transfer
*/
int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr,
u_int dma_size)
int omap_start_alsa_sound_dma(struct audio_stream *s,
dma_addr_t dma_ptr,
u_int dma_size)
{
int ret = -EPERM;
......@@ -439,18 +427,17 @@ static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status,
}
if (ch_status & DCSR_END_BLOCK)
audio_dma_callback(s);
callback_omap_alsa_sound_dma(s);
FN_OUT(0);
return;
}
MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION
("Common DMA handling for Audio driver on OMAP processors");
MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(omap_start_sound_dma);
EXPORT_SYMBOL(omap_clear_sound_dma);
EXPORT_SYMBOL(omap_request_sound_dma);
EXPORT_SYMBOL(omap_free_sound_dma);
EXPORT_SYMBOL(omap_audio_stop_dma);
EXPORT_SYMBOL(omap_start_alsa_sound_dma);
EXPORT_SYMBOL(omap_clear_alsa_sound_dma);
EXPORT_SYMBOL(omap_request_alsa_sound_dma);
EXPORT_SYMBOL(omap_free_alsa_sound_dma);
EXPORT_SYMBOL(omap_stop_alsa_sound_dma);
/*
* linux/sound/arm/omap-alsa-dma.h
* linux/sound/arm/omap/omap-alsa-dma.h
*
* Common audio DMA handling for the OMAP processors
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
*
* Copyright (C) 2004 Texas Instruments, Inc.
......@@ -30,14 +32,7 @@
/************************** INCLUDES *************************************/
#include "omap-aic23.h"
/************************** GLOBAL MACROS *************************************/
/* Provide the Macro interfaces common across platforms */
#define DMA_REQUEST(e,s, cb) {e=omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);}
#define DMA_FREE(s) omap_free_sound_dma(s, &s->lch)
#define DMA_CLEAR(s) omap_clear_sound_dma(s)
#include <asm/arch/omap-alsa.h>
/************************** GLOBAL DATA STRUCTURES *********************************/
......@@ -45,15 +40,14 @@ typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data);
/**************** ARCH SPECIFIC FUNCIONS *******************************************/
void omap_clear_sound_dma(struct audio_stream * s);
void omap_clear_alsa_sound_dma(struct audio_stream * s);
int omap_request_sound_dma(int device_id, const char *device_name,
int omap_request_alsa_sound_dma(int device_id, const char *device_name,
void *data, int **channels);
int omap_free_sound_dma(void *data, int **channels);
int omap_free_alsa_sound_dma(void *data, int **channels);
int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr,
u_int dma_size);
int omap_start_alsa_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr, u_int dma_size);
void omap_audio_stop_dma(struct audio_stream *s);
void omap_stop_alsa_sound_dma(struct audio_stream *s);
#endif
/*
* sound/arm/omap/omap-alsa-tsc2101-mixer.c
*
* Alsa Driver for TSC2101 codec for OMAP platform boards.
*
* Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and
* Everett Coleman II <gcc80x86@fuzzyneural.net>
*
* Board initialization code is based on the code in TSC2101 OSS driver.
* Copyright (C) 2004 Texas Instruments, Inc.
* Written by Nishanth Menon and Sriram Kannan
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
*
* 2006-03-01 Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
* Can switch between headset and loudspeaker playback,
* mute and unmute dgc, set dgc volume. Record source switch,
* keyclick, buzzer and headset volume and handset volume control
* are still missing.
*
*/
#include "omap-alsa-tsc2101.h"
#include "omap-alsa-tsc2101-mixer.h"
#include <linux/types.h>
#include <sound/initval.h>
#include <sound/control.h>
//#define M_DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
#define M_DPRINTK(ARGS...) /* nop */
#define DGC_DALVL_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
#define DGC_DARVL_EXTRACT(ARG) ((ARG & 0x007f))
#define GET_DGC_DALMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
#define GET_DGC_DARMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(7)) >> 7)
#define IS_DGC_DALMU_UNMUTED(ARG) (((GET_DGC_DALMU_BIT_VALUE(ARG)) == 0))
#define IS_DGC_DARMU_UNMUTED(ARG) (((GET_DGC_DARMU_BIT_VALUE(ARG)) == 0))
#define HGC_ADPGA_HED_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
#define GET_DGC_HGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
#define IS_DGC_HGCMU_UNMUTED(ARG) (((GET_DGC_HGCMU_BIT_VALUE(ARG)) == 0))
#define HNGC_ADPGA_HND_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
#define GET_DGC_HNGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
#define IS_DGC_HNGCMU_UNMUTED(ARG) (((GET_DGC_HNGCMU_BIT_VALUE(ARG)) == 0))
static int current_playback_target = PLAYBACK_TARGET_LOUDSPEAKER;
static int current_rec_src = REC_SRC_SINGLE_ENDED_MICIN_HED;
/*
* Used for switching between TSC2101 recourd sources.
* Logic is adjusted from the TSC2101 OSS code.
*/
static int set_record_source(int val)
{
u16 data;
int maskedVal;
FN_IN;
maskedVal = 0xe0 & val;
data = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_MIXER_PGA_CTRL);
data &= ~MPC_MICSEL(7); /* clear all MICSEL bits */
data |= maskedVal;
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_MIXER_PGA_CTRL,
data);
current_rec_src = val;
FN_OUT(0);
return 0;
}
/*
* Converts the Alsa mixer volume (0 - 100) to real
* Digital Gain Control (DGC) value that can be written
* or read from the TSC2101 registry.
*
* Note that the number "OUTPUT_VOLUME_MAX" is smaller than OUTPUT_VOLUME_MIN
* because DGC works as a volume decreaser. (The more bigger value is put
* to DGC, the more the volume of controlled channel is decreased)
*
* In addition the TCS2101 chip would allow the maximum volume reduction be 63.5 DB
* but according to some tests user can not hear anything with this chip
* when the volume is set to be less than 25 db.
* Therefore this function will return a value that means 38.5 db (63.5 db - 25 db)
* reduction in the channel volume, when mixer is set to 0.
* For mixer value 100, this will return a value that means 0 db volume reduction.
* ([mute_left_bit]0000000[mute_right_bit]0000000)
*/
int get_mixer_volume_as_dac_gain_control_volume(int vol)
{
u16 retVal;
/* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
retVal = ((vol * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX;
/* invert the value for getting the proper range 0 min and 100 max */
retVal = OUTPUT_VOLUME_MIN - retVal;
return retVal;
}
/*
* Converts the Alsa mixer volume (0 - 100) to TSC2101
* Digital Gain Control (DGC) volume. Alsa mixer volume 0
* is converted to value meaning the volume reduction of -38.5 db
* and Alsa mixer volume 100 is converted to value meaning the
* reduction of 0 db.
*/
int set_mixer_volume_as_dac_gain_control_volume(int mixerVolL, int mixerVolR)
{
u16 val;
int retVal;
int volL;
int volR;
if ((mixerVolL < 0) ||
(mixerVolL > 100) ||
(mixerVolR < 0) ||
(mixerVolR > 100)) {
printk(KERN_ERR "Trying a bad mixer volume as dac gain control volume value, left (%d), right (%d)!\n", mixerVolL, mixerVolR);
return -EPERM;
}
M_DPRINTK("mixer volume left = %d, right = %d\n", mixerVolL, mixerVolR);
volL = get_mixer_volume_as_dac_gain_control_volume(mixerVolL);
volR = get_mixer_volume_as_dac_gain_control_volume(mixerVolR);
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
/* keep the old mute bit settings */
val &= ~(DGC_DALVL(OUTPUT_VOLUME_MIN) | DGC_DARVL(OUTPUT_VOLUME_MIN));
val |= DGC_DALVL(volL) | DGC_DARVL(volR);
retVal = 2;
if (retVal) {
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_DAC_GAIN_CTRL,
val);
}
M_DPRINTK("to registry: left = %d, right = %d, total = %d\n", DGC_DALVL_EXTRACT(val), DGC_DARVL_EXTRACT(val), val);
return retVal;
}
int dac_gain_control_unmute_control(int muteLeft, int muteRight)
{
u16 val;
int count;
count = 0;
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
/* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
* so if values are same, it's time to change the registry value.
*/
if (muteLeft == GET_DGC_DALMU_BIT_VALUE(val)) {
if (muteLeft == 0) {
/* mute --> turn bit on */
val = val | DGC_DALMU;
}
else {
/* unmute --> turn bit off */
val = val & ~DGC_DALMU;
}
count++;
} /* L */
if (muteRight == GET_DGC_DARMU_BIT_VALUE(val)) {
if (muteRight == 0) {
/* mute --> turn bit on */
val = val | DGC_DARMU;
}
else {
/* unmute --> turn bit off */
val = val & ~DGC_DARMU;
}
count++;
} /* R */
if (count) {
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL, val);
M_DPRINTK("changed value, is_unmuted left = %d, right = %d\n",
IS_DGC_DALMU_UNMUTED(val),
IS_DGC_DARMU_UNMUTED(val));
}
return count;
}
/*
* Converts the DGC registry value read from the TSC2101 registry to
* Alsa mixer volume format (0 - 100).
*/
int get_dac_gain_control_volume_as_mixer_volume(u16 vol)
{
u16 retVal;
retVal = OUTPUT_VOLUME_MIN - vol;
retVal = ((retVal - OUTPUT_VOLUME_MAX) * 100) / OUTPUT_VOLUME_RANGE;
/* fix scaling error */
if ((retVal > 0) && (retVal < 100)) {
retVal++;
}
return retVal;
}
/*
* Converts the headset gain control volume (0 - 63.5 db)
* to Alsa mixer volume (0 - 100)
*/
int get_headset_gain_control_volume_as_mixer_volume(u16 registerVal)
{
u16 retVal;
retVal = ((registerVal * 100) / INPUT_VOLUME_RANGE);
return retVal;
}
/*
* Converts the handset gain control volume (0 - 63.5 db)
* to Alsa mixer volume (0 - 100)
*/
int get_handset_gain_control_volume_as_mixer_volume(u16 registerVal)
{
return get_headset_gain_control_volume_as_mixer_volume(registerVal);
}
/*
* Converts the Alsa mixer volume (0 - 100) to
* headset gain control volume (0 - 63.5 db)
*/
int get_mixer_volume_as_headset_gain_control_volume(u16 mixerVal)
{
u16 retVal;
retVal = ((mixerVal * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
return retVal;
}
/*
* Writes Alsa mixer volume (0 - 100) to TSC2101 headset volume registry in
* a TSC2101 format. (0 - 63.5 db)
* In TSC2101 OSS driver this functionality was controlled with "SET_LINE" parameter.
*/
int set_mixer_volume_as_headset_gain_control_volume(int mixerVol)
{
int volume;
int retVal;
u16 val;
if (mixerVol < 0 || mixerVol > 100) {
M_DPRINTK("Trying a bad headset mixer volume value(%d)!\n", mixerVol);
return -EPERM;
}
M_DPRINTK("mixer volume = %d\n", mixerVol);
/* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
/* NOTE: 0 is minimum volume and not mute */
volume = get_mixer_volume_as_headset_gain_control_volume(mixerVol);
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HEADSET_GAIN_CTRL);
/* preserve the old mute settings */
val &= ~(HGC_ADPGA_HED(INPUT_VOLUME_MAX));
val |= HGC_ADPGA_HED(volume);
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HEADSET_GAIN_CTRL,
val);
retVal = 1;
M_DPRINTK("to registry = %d\n", val);
return retVal;
}
/*
* Writes Alsa mixer volume (0 - 100) to TSC2101 handset volume registry in
* a TSC2101 format. (0 - 63.5 db)
* In TSC2101 OSS driver this functionality was controlled with "SET_MIC" parameter.
*/
int set_mixer_volume_as_handset_gain_control_volume(int mixerVol)
{
int volume;
int retVal;
u16 val;
if (mixerVol < 0 || mixerVol > 100) {
M_DPRINTK("Trying a bad mic mixer volume value(%d)!\n", mixerVol);
return -EPERM;
}
M_DPRINTK("mixer volume = %d\n", mixerVol);
/* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range
* NOTE: 0 is minimum volume and not mute
*/
volume = get_mixer_volume_as_headset_gain_control_volume(mixerVol);
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
/* preserve the old mute settigns */
val &= ~(HNGC_ADPGA_HND(INPUT_VOLUME_MAX));
val |= HNGC_ADPGA_HND(volume);
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HANDSET_GAIN_CTRL,
val);
retVal = 1;
M_DPRINTK("to registry = %d\n", val);
return retVal;
}
void init_record_sources(void)
{
/* Mute Analog Sidetone
* analog sidetone gain db?
* Cell Phone In not connected to ADC
* Input selected by MICSEL connected to ADC
*/
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_MIXER_PGA_CTRL,
MPC_ASTMU | MPC_ASTG(0x40) | ~MPC_CPADC | MPC_MICADC);
/* Set record source, Select MIC_INHED input for headset */
set_record_source(REC_SRC_SINGLE_ENDED_MICIN_HED);
}
void set_loudspeaker_to_playback_target(void)
{
u16 val;
/* power down sp1, sp2 and loudspeaker */
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_CODEC_POWER_CTRL,
CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);
/* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled
* 1dB AGC hysteresis
* MICes bias 2V
*/
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_4,
AC4_MB_HED(0));
/* DAC left and right routed to SPK1/SPK2
* SPK1/SPK2 unmuted
* keyclicks routed to SPK1/SPK2
*/
val = AC5_DIFFIN |
AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
AC5_HDSCPTC;
val = val & ~AC5_HDSCPTC;
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_5,
val);
/* powerdown spk1/out32n and spk2 */
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_POWERDOWN_STS);
val = val & ~(~PS_SPK1FL | ~PS_HNDFL | PS_LSPKFL);
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_POWERDOWN_STS,
val);
/* routing selected to SPK1 goes to OUT8P/OUT84 alsa. (loudspeaker)
* analog sidetone routed to loudspeaker
* buzzer pga routed to loudspeaker
* keyclick routing to loudspeaker
* cellphone input routed to loudspeaker
* mic selection (control register 04h/page2) routed to cell phone output (CP_OUT)
* routing selected for SPK1 goes also to cellphone output (CP_OUT)
* OUT8P/OUT8N (loudspeakers) unmuted (0 = unmuted)
* Cellphone output is not muted (0 = unmuted)
* Enable loudspeaker short protection control (0 = enable protection)
* VGND short protection control (0 = enable protection)
*/
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_6,
AC6_SPL2LSK | AC6_AST2LSK | AC6_BUZ2LSK | AC6_KCL2LSK |
AC6_CPI2LSK | AC6_MIC2CPO | AC6_SPL2CPO |
~AC6_MUTLSPK | ~AC6_MUTSPK2 | ~AC6_LDSCPTC | ~AC6_VGNDSCPTC);
current_playback_target = PLAYBACK_TARGET_LOUDSPEAKER;
}
void set_headphone_to_playback_target(void)
{
/* power down sp1, sp2 and loudspeaker */
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_CODEC_POWER_CTRL,
CPC_SP1PWDN | CPC_SP2PWDN | CPC_LDAPWDF);
/* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
/* 1dB AGC hysteresis */
/* MICes bias 2V */
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_4,
AC4_MB_HED(0));
/* DAC left and right routed to SPK2 */
/* SPK1/2 unmuted */
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_5,
AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 |
AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 |
AC5_HDSCPTC);
/* OUT8P/N muted, CPOUT muted */
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_6,
AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC |
AC6_VGNDSCPTC);
current_playback_target = PLAYBACK_TARGET_HEADPHONE;
}
/*
* Checks whether the headset is detected.
* If headset is detected, the type is returned. Type can be
* 0x01 = stereo headset detected
* 0x02 = cellurar headset detected
* 0x03 = stereo + cellurar headset detected
* If headset is not detected 0 is returned.
*/
u16 get_headset_detected(void)
{
u16 curDetected;
u16 curType;
u16 curVal;
curType = 0; /* not detected */
curVal = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_7);
curDetected = curVal & AC7_HDDETFL;
if (curDetected) {
printk("headset detected, checking type from %d \n", curVal);
curType = ((curVal & 0x6000) >> 13);
printk("headset type detected = %d \n", curType);
}
else {
printk("headset not detected\n");
}
return curType;
}
void init_playback_targets(void)
{
u16 val;
set_loudspeaker_to_playback_target();
/* Left line input volume control
* = SET_LINE in the OSS driver
*/
set_mixer_volume_as_headset_gain_control_volume(DEFAULT_INPUT_VOLUME);
/* Set headset to be controllable by handset mixer
* AGC enable for handset input
* Handset input not muted
*/
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HANDSET_GAIN_CTRL);
val = val | HNGC_AGCEN_HND;
val = val & ~HNGC_ADMUT_HND;
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HANDSET_GAIN_CTRL,
val);
/* mic input volume control
* SET_MIC in the OSS driver
*/
set_mixer_volume_as_handset_gain_control_volume(DEFAULT_INPUT_VOLUME);
/* Left/Right headphone channel volume control
* Zero-cross detect on
*/
set_mixer_volume_as_dac_gain_control_volume(DEFAULT_OUTPUT_VOLUME, DEFAULT_OUTPUT_VOLUME);
/* unmute */
dac_gain_control_unmute_control(1, 1);
}
/*
* Initializes tsc2101 recourd source (to line) and playback target (to loudspeaker)
*/
void snd_omap_init_mixer(void)
{
FN_IN;
/* Headset/Hook switch detect enabled */
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_AUDIO_CTRL_7,
AC7_DETECT);
init_record_sources();
init_playback_targets();
FN_OUT(0);
}
static int __pcm_playback_target_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
static char *texts[PLAYBACK_TARGET_COUNT] = {
"Loudspeaker", "Headphone"
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = PLAYBACK_TARGET_COUNT;
if (uinfo->value.enumerated.item > PLAYBACK_TARGET_COUNT - 1) {
uinfo->value.enumerated.item = PLAYBACK_TARGET_COUNT - 1;
}
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int __pcm_playback_target_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ucontrol->value.integer.value[0] = current_playback_target;
return 0;
}
static int __pcm_playback_target_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
int retVal;
int curVal;
retVal = 0;
curVal = ucontrol->value.integer.value[0];
if ((curVal >= 0) &&
(curVal < PLAYBACK_TARGET_COUNT) &&
(curVal != current_playback_target)) {
if (curVal == 0) {
set_loudspeaker_to_playback_target();
}
else {
set_headphone_to_playback_target();
}
retVal = 1;
}
return retVal;
}
static int __pcm_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 100;
return 0;
}
/*
* Alsa mixer interface function for getting the volume read from the DGC in a
* 0 -100 alsa mixer format.
*/
static int __pcm_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
u16 volL;
u16 volR;
u16 val;
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
M_DPRINTK("registry value = %d!\n", val);
volL = DGC_DALVL_EXTRACT(val);
volR = DGC_DARVL_EXTRACT(val);
/* make sure that other bits are not on */
volL = volL & ~DGC_DALMU;
volR = volR & ~DGC_DARMU;
volL = get_dac_gain_control_volume_as_mixer_volume(volL);
volR = get_dac_gain_control_volume_as_mixer_volume(volR);
ucontrol->value.integer.value[0] = volL; /* L */
ucontrol->value.integer.value[1] = volR; /* R */
M_DPRINTK("mixer volume left = %ld, right = %ld\n", ucontrol->value.integer.value[0], ucontrol->value.integer.value[1]);
return 0;
}
static int __pcm_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
return set_mixer_volume_as_dac_gain_control_volume(ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1]);
}
static int __pcm_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
/*
* When DGC_DALMU (bit 15) is 1, the left channel is muted.
* When DGC_DALMU is 0, left channel is not muted.
* Same logic apply also for the right channel.
*/
static int __pcm_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_DAC_GAIN_CTRL);
ucontrol->value.integer.value[0] = IS_DGC_DALMU_UNMUTED(val);
ucontrol->value.integer.value[1] = IS_DGC_DARMU_UNMUTED(val);
return 0;
}
static int __pcm_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
return dac_gain_control_unmute_control(ucontrol->value.integer.value[0],
ucontrol->value.integer.value[1]);
}
static int __headset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 100;
return 0;
}
static int __headset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
u16 val;
u16 vol;
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HEADSET_GAIN_CTRL);
M_DPRINTK("registry value = %d\n", val);
vol = HGC_ADPGA_HED_EXTRACT(val);
vol = vol & ~HGC_ADMUT_HED;
vol = get_headset_gain_control_volume_as_mixer_volume(vol);
ucontrol->value.integer.value[0] = vol;
M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
return 0;
}
static int __headset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
return set_mixer_volume_as_headset_gain_control_volume(ucontrol->value.integer.value[0]);
}
static int __headset_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
/* When HGC_ADMUT_HED (bit 15) is 1, the headset is muted.
* When HGC_ADMUT_HED is 0, headset is not muted.
*/
static int __headset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HEADSET_GAIN_CTRL);
ucontrol->value.integer.value[0] = IS_DGC_HGCMU_UNMUTED(val);
return 0;
}
static int __headset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
int count = 0;
u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HEADSET_GAIN_CTRL);
/* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
* so if values are same, it's time to change the registry value...
*/
if (ucontrol->value.integer.value[0] == GET_DGC_HGCMU_BIT_VALUE(val)) {
if (ucontrol->value.integer.value[0] == 0) {
/* mute --> turn bit on */
val = val | HGC_ADMUT_HED;
}
else {
/* unmute --> turn bit off */
val = val & ~HGC_ADMUT_HED;
}
count++;
M_DPRINTK("changed value, is_unmuted = %d\n", IS_DGC_HGCMU_UNMUTED(val));
}
if (count) {
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HEADSET_GAIN_CTRL,
val);
}
return count;
}
static int __handset_playback_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 100;
return 0;
}
static int __handset_playback_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
u16 val;
u16 vol;
val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
M_DPRINTK("registry value = %d\n", val);
vol = HNGC_ADPGA_HND_EXTRACT(val);
vol = vol & ~HNGC_ADMUT_HND;
vol = get_handset_gain_control_volume_as_mixer_volume(vol);
ucontrol->value.integer.value[0] = vol;
M_DPRINTK("mixer volume returned = %ld\n", ucontrol->value.integer.value[0]);
return 0;
}
static int __handset_playback_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
return set_mixer_volume_as_handset_gain_control_volume(ucontrol->value.integer.value[0]);
}
static int __handset_playback_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
/* When HNGC_ADMUT_HND (bit 15) is 1, the handset is muted.
* When HNGC_ADMUT_HND is 0, handset is not muted.
*/
static int __handset_playback_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
ucontrol->value.integer.value[0] = IS_DGC_HNGCMU_UNMUTED(val);
return 0;
}
static int __handset_playback_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
int count = 0;
u16 val = omap_tsc2101_read(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2, TSC2101_HANDSET_GAIN_CTRL);
/* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
* so if values are same, it's time to change the registry value...
*/
if (ucontrol->value.integer.value[0] == GET_DGC_HNGCMU_BIT_VALUE(val)) {
if (ucontrol->value.integer.value[0] == 0) {
/* mute --> turn bit on */
val = val | HNGC_ADMUT_HND;
}
else {
/* unmute --> turn bit off */
val = val & ~HNGC_ADMUT_HND;
}
M_DPRINTK("changed value, is_unmuted = %d\n", IS_DGC_HNGCMU_UNMUTED(val));
count++;
}
if (count) {
omap_tsc2101_write(TSC2101_AUDIO_CODEC_REGISTERS_PAGE2,
TSC2101_HANDSET_GAIN_CTRL,
val);
}
return count;
}
static snd_kcontrol_new_t tsc2101_control[] __devinitdata = {
{
.name = "Playback Playback Route",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __pcm_playback_target_info,
.get = __pcm_playback_target_get,
.put = __pcm_playback_target_put,
}, {
.name = "Master Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __pcm_playback_volume_info,
.get = __pcm_playback_volume_get,
.put = __pcm_playback_volume_put,
}, {
.name = "Master Playback Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __pcm_playback_switch_info,
.get = __pcm_playback_switch_get,
.put = __pcm_playback_switch_put,
}, {
.name = "Headset Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 1,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __headset_playback_volume_info,
.get = __headset_playback_volume_get,
.put = __headset_playback_volume_put,
}, {
.name = "Headset Playback Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 1,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __headset_playback_switch_info,
.get = __headset_playback_switch_get,
.put = __headset_playback_switch_put,
}, {
.name = "Handset Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 2,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __handset_playback_volume_info,
.get = __handset_playback_volume_get,
.put = __handset_playback_volume_put,
}, {
.name = "Handset Playback Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 2,
.access= SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = __handset_playback_switch_info,
.get = __handset_playback_switch_get,
.put = __handset_playback_switch_put,
}
};
#ifdef CONFIG_PM
void snd_omap_suspend_mixer(void)
{
}
void snd_omap_resume_mixer(void)
{
snd_omap_init_mixer();
}
#endif
int snd_omap_mixer(struct snd_card_omap_codec *tsc2101)
{
int i=0;
int err=0;
if (!tsc2101) {
return -EINVAL;
}
for (i=0; i < ARRAY_SIZE(tsc2101_control); i++) {
if ((err = snd_ctl_add(tsc2101->card,
snd_ctl_new1(&tsc2101_control[i],
tsc2101->card))) < 0) {
return err;
}
}
return 0;
}
/*
* sound/arm/omap/omap-alsa-tsc2101-mixer.c
*
* Alsa Driver for TSC2101 codec for OMAP platform boards.
*
* Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and
* Everett Coleman II <gcc80x86@fuzzyneural.net>
*
* Based on the ideas in omap-aic23.c and sa11xx-uda1341.c
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
*
* 2006-03-01 Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
* Can switch between headset and loudspeaker playback,
* mute and unmute dgc, set dgc volume. Record source switch,
* keyclick, buzzer and headset volume and handset volume control
* are still missing.
*/
#ifndef OMAPALSATSC2101MIXER_H_
#define OMAPALSATSC2101MIXER_H_
#include <asm/hardware/tsc2101.h>
#include <../drivers/ssi/omap-tsc2101.h>
#include "omap-alsa-dma.h"
/* tsc2101 DAC gain control volume specific */
#define OUTPUT_VOLUME_MIN 0x7F // 1111111 = -63.5 DB
#define OUTPUT_VOLUME_MAX 0x32 // 110010
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
/* use input vol of 75 for 0dB gain */
#define INPUT_VOLUME_MIN 0x0
#define INPUT_VOLUME_MAX 0x7D
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define PLAYBACK_TARGET_COUNT 0x02
#define PLAYBACK_TARGET_LOUDSPEAKER 0x00
#define PLAYBACK_TARGET_HEADPHONE 0x01
/* following are used for register 03h Mixer PGA control bits D7-D5 for selecting record source */
#define REC_SRC_TARGET_COUNT 0x08
#define REC_SRC_SINGLE_ENDED_MICIN_HED MPC_MICSEL(0) // oss code referred to MIXER_LINE
#define REC_SRC_SINGLE_ENDED_MICIN_HND MPC_MICSEL(1) // oss code referred to MIXER_MIC
#define REC_SRC_SINGLE_ENDED_AUX1 MPC_MICSEL(2)
#define REC_SRC_SINGLE_ENDED_AUX2 MPC_MICSEL(3)
#define REC_SRC_MICIN_HED_AND_AUX1 MPC_MICSEL(4)
#define REC_SRC_MICIN_HED_AND_AUX2 MPC_MICSEL(5)
#define REC_SRC_MICIN_HND_AND_AUX1 MPC_MICSEL(6)
#define REC_SRC_MICIN_HND_AND_AUX2 MPC_MICSEL(7)
#define DEFAULT_OUTPUT_VOLUME 90 // default output volume to dac dgc
#define DEFAULT_INPUT_VOLUME 20 // default record volume
#define TSC2101_AUDIO_CODEC_REGISTERS_PAGE2 (2)
#endif /*OMAPALSATSC2101MIXER_H_*/
/*
* arch/arm/mach-omap1/omap-alsa-tsc2101.c
*
* Alsa codec Driver for TSC2101 chip for OMAP platform boards.
* Code obtained from oss omap drivers
*
* Copyright (C) 2004 Texas Instruments, Inc.
* Written by Nishanth Menon and Sriram Kannan
*
* Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* 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/delay.h>
#include <linux/soundcard.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/arch/mcbsp.h>
#include <linux/slab.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <asm/mach-types.h>
#include <asm/arch/dma.h>
#include <asm/arch/clock.h>
#include <asm/hardware/tsc2101.h>
#include <../drivers/ssi/omap-tsc2101.h>
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-tsc2101.h"
static struct clk *tsc2101_mclk = 0;
//#define DUMP_TSC2101_AUDIO_REGISTERS
#undef DUMP_TSC2101_AUDIO_REGISTERS
/*
* Hardware capabilities
*/
/*
* DAC USB-mode sampling rates (MCLK = 12 MHz)
* The rates and rate_reg_into MUST be in the same order
*/
static unsigned int rates[] = {
7350, 8000, 8018, 8727,
8820, 9600, 11025, 12000,
14700, 16000, 22050, 24000,
29400, 32000, 44100, 48000,
};
static snd_pcm_hw_constraint_list_t tsc2101_hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static const struct tsc2101_samplerate_reg_info
rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
/* Div 6 */
{7350, 7, 1},
{8000, 7, 0},
/* Div 5.5 */
{8018, 6, 1},
{8727, 6, 0},
/* Div 5 */
{8820, 5, 1},
{9600, 5, 0},
/* Div 4 */
{11025, 4, 1},
{12000, 4, 0},
/* Div 3 */
{14700, 3, 1},
{16000, 3, 0},
/* Div 2 */
{22050, 2, 1},
{24000, 2, 0},
/* Div 1.5 */
{29400, 1, 1},
{32000, 1, 0},
/* Div 1 */
{44100, 0, 1},
{48000, 0, 0},
};
static snd_pcm_hardware_t tsc2101_snd_omap_alsa_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 7350,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
static snd_pcm_hardware_t tsc2101_snd_omap_alsa_capture = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 7350,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
/*
* Simplified write for tsc Audio
*/
inline void tsc2101_audio_write(u8 address, u16 data)
{
omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS, address, data);
}
/*
* Simplified read for tsc Audio
*/
inline u16 tsc2101_audio_read(u8 address)
{
return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address));
}
#ifdef DUMP_TSC2101_AUDIO_REGISTERS
void dump_tsc2101_audio_reg(void) {
printk("TSC2101_AUDIO_CTRL_1 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_1));
printk("TSC2101_HEADSET_GAIN_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_HEADSET_GAIN_CTRL));
printk("TSC2101_DAC_GAIN_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_DAC_GAIN_CTRL));
printk("TSC2101_MIXER_PGA_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_MIXER_PGA_CTRL));
printk("TSC2101_AUDIO_CTRL_2 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_2));
printk("TSC2101_CODEC_POWER_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_CODEC_POWER_CTRL));
printk("TSC2101_AUDIO_CTRL_3 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_3));
printk("TSC2101_LCH_BASS_BOOST_N0 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N0));
printk("TSC2101_LCH_BASS_BOOST_N1 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N1));
printk("TSC2101_LCH_BASS_BOOST_N2 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N2));
printk("TSC2101_LCH_BASS_BOOST_N3 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N3));
printk("TSC2101_LCH_BASS_BOOST_N4 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N4));
printk("TSC2101_LCH_BASS_BOOST_N5 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_N5));
printk("TSC2101_LCH_BASS_BOOST_D1 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D1));
printk("TSC2101_LCH_BASS_BOOST_D2 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D2));
printk("TSC2101_LCH_BASS_BOOST_D4 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D4));
printk("TSC2101_LCH_BASS_BOOST_D5 = 0x%04x\n", tsc2101_audio_read(TSC2101_LCH_BASS_BOOST_D5));
printk("TSC2101_RCH_BASS_BOOST_N0 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N0));
printk("TSC2101_RCH_BASS_BOOST_N1 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N1));
printk("TSC2101_RCH_BASS_BOOST_N2 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N2));
printk("TSC2101_RCH_BASS_BOOST_N3 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N3));
printk("TSC2101_RCH_BASS_BOOST_N4 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N4));
printk("TSC2101_RCH_BASS_BOOST_N5 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_N5));
printk("TSC2101_RCH_BASS_BOOST_D1 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D1));
printk("TSC2101_RCH_BASS_BOOST_D2 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D2));
printk("TSC2101_RCH_BASS_BOOST_D4 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D4));
printk("TSC2101_RCH_BASS_BOOST_D5 = 0x%04x\n", tsc2101_audio_read(TSC2101_RCH_BASS_BOOST_D5));
printk("TSC2101_PLL_PROG_1 = 0x%04x\n", tsc2101_audio_read(TSC2101_PLL_PROG_1));
printk("TSC2101_PLL_PROG_1 = 0x%04x\n", tsc2101_audio_read(TSC2101_PLL_PROG_2));
printk("TSC2101_AUDIO_CTRL_4 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_4));
printk("TSC2101_HANDSET_GAIN_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_HANDSET_GAIN_CTRL));
printk("TSC2101_BUZZER_GAIN_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_BUZZER_GAIN_CTRL));
printk("TSC2101_AUDIO_CTRL_5 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_5));
printk("TSC2101_AUDIO_CTRL_6 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_6));
printk("TSC2101_AUDIO_CTRL_7 = 0x%04x\n", tsc2101_audio_read(TSC2101_AUDIO_CTRL_7));
printk("TSC2101_GPIO_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_GPIO_CTRL));
printk("TSC2101_AGC_CTRL = 0x%04x\n", tsc2101_audio_read(TSC2101_AGC_CTRL));
printk("TSC2101_POWERDOWN_STS = 0x%04x\n", tsc2101_audio_read(TSC2101_POWERDOWN_STS));
printk("TSC2101_MIC_AGC_CONTROL = 0x%04x\n", tsc2101_audio_read(TSC2101_MIC_AGC_CONTROL));
printk("TSC2101_CELL_AGC_CONTROL = 0x%04x\n", tsc2101_audio_read(TSC2101_CELL_AGC_CONTROL));
}
#endif
/*
* ALSA operations according to board file
*/
/*
* Sample rate changing
*/
void tsc2101_set_samplerate(long sample_rate)
{
u8 count = 0;
u16 data = 0;
int clkgdv = 0;
u16 srgr1, srgr2;
/* wait for any frame to complete */
udelay(125);
ADEBUG();
sample_rate = sample_rate;
/* Search for the right sample rate */
while ((rate_reg_info[count].sample_rate != sample_rate) &&
(count < NUMBER_SAMPLE_RATES_SUPPORTED)) {
count++;
}
if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
printk(KERN_ERR "Invalid Sample Rate %d requested\n",
(int) sample_rate);
return; // -EPERM;
}
/* Set AC1 */
data = tsc2101_audio_read(TSC2101_AUDIO_CTRL_1);
/* Clear prev settings */
data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07));
data |= AC1_DACFS(rate_reg_info[count].divisor) |
AC1_ADCFS(rate_reg_info[count].divisor);
tsc2101_audio_write(TSC2101_AUDIO_CTRL_1, data);
/* Set the AC3 */
data = tsc2101_audio_read(TSC2101_AUDIO_CTRL_3);
/*Clear prev settings */
data &= ~(AC3_REFFS | AC3_SLVMS);
data |= (rate_reg_info[count].fs_44kHz) ? AC3_REFFS : 0;
#ifdef TSC_MASTER
data |= AC3_SLVMS;
#endif /* #ifdef TSC_MASTER */
tsc2101_audio_write(TSC2101_AUDIO_CTRL_3, data);
/* program the PLLs */
if (rate_reg_info[count].fs_44kHz) {
/* 44.1 khz - 12 MHz Mclk */
tsc2101_audio_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL |
PLL1_PVAL(1) | PLL1_I_VAL(7)); /* PVAL 1; I_VAL 7 */
tsc2101_audio_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490)); /* D_VAL 5264 */
} else {
/* 48 khz - 12 Mhz Mclk */
tsc2101_audio_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL |
PLL1_PVAL(1) | PLL1_I_VAL(8)); /* PVAL 1; I_VAL 8 */
tsc2101_audio_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780)); /* D_VAL 1920 */
}
/* Set the sample rate */
#ifndef TSC_MASTER
clkgdv = CODEC_CLOCK / (sample_rate * (DEFAULT_BITPERSAMPLE * 2 - 1));
if (clkgdv)
srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
else
return (1);
/* Stereo Mode */
srgr2 = (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
#else
srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
srgr2 = ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)));
#endif /* end of #ifdef TSC_MASTER */
OMAP_MCBSP_WRITE(OMAP1610_MCBSP1_BASE, SRGR2, srgr2);
OMAP_MCBSP_WRITE(OMAP1610_MCBSP1_BASE, SRGR1, srgr1);
}
void tsc2101_configure(void)
{
}
/*
* Omap MCBSP clock and Power Management configuration
*
* Here we have some functions that allows clock to be enabled and
* disabled only when needed. Besides doing clock configuration
* it allows turn on/turn off audio when necessary.
*/
/*
* Do clock framework mclk search
*/
void tsc2101_clock_setup(void)
{
tsc2101_mclk = clk_get(0, "mclk");
}
/*
* Do some sanity check, set clock rate, starts it and turn codec audio on
*/
int tsc2101_clock_on(void)
{
int curUseCount;
uint curRate;
int err;
curUseCount = clk_get_usecount(tsc2101_mclk);
DPRINTK("clock use count = %d\n", curUseCount);
if (curUseCount > 0) {
// MCLK is already in use
printk(KERN_WARNING
"MCLK already in use at %d Hz. We change it to %d Hz\n",
(uint) clk_get_rate(tsc2101_mclk),
CODEC_CLOCK);
}
curRate = (uint)clk_get_rate(tsc2101_mclk);
DPRINTK("old clock rate = %d\n", curRate);
if (curRate != CODEC_CLOCK) {
err = clk_set_rate(tsc2101_mclk, CODEC_CLOCK);
if (err) {
printk(KERN_WARNING
"Cannot set MCLK clock rate for TSC2101 CODEC, error code = %d\n", err);
//return -ECANCELED;
}
}
else
{
printk(KERN_INFO
"omap_alsa_tsc2101_clock_on(), no need to change rate, no need to change clock rate, rate already %d Hz.\n",
CODEC_CLOCK);
}
err = clk_enable(tsc2101_mclk);
curRate = (uint)clk_get_rate(tsc2101_mclk);
curUseCount = clk_get_usecount(tsc2101_mclk);
DPRINTK("MCLK = %d [%d], usecount = %d, clk_enable retval = %d\n",
curRate,
CODEC_CLOCK,
curUseCount,
err);
// Now turn the audio on
omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS,
TSC2101_CODEC_POWER_CTRL,
0x0000);
return 0;
}
/*
* Do some sanity check, turn clock off and then turn
* codec audio off
*/
int tsc2101_clock_off(void)
{
int curUseCount;
int curRate;
curUseCount = clk_get_usecount(tsc2101_mclk);
DPRINTK("clock use count = %d\n", curUseCount);
if (curUseCount > 0) {
curRate = clk_get_rate(tsc2101_mclk);
DPRINTK("clock rate = %d\n", curRate);
if (curRate != CODEC_CLOCK) {
printk(KERN_WARNING
"MCLK for audio should be %d Hz. But is %d Hz\n",
(uint) clk_get_rate(tsc2101_mclk),
CODEC_CLOCK);
}
clk_disable(tsc2101_mclk);
DPRINTK("clock disabled\n");
}
tsc2101_audio_write(TSC2101_CODEC_POWER_CTRL,
~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC));
DPRINTK("audio codec off\n");
#ifdef DUMP_TSC2101_AUDIO_REGISTERS
printk("tsc2101_clock_off()\n");
dump_tsc2101_audio_reg();
#endif
return 0;
}
int tsc2101_get_default_samplerate(void)
{
return DEFAULT_SAMPLE_RATE;
}
static int __init snd_omap_alsa_tsc2101_probe(struct platform_device *pdev)
{
int ret;
struct omap_alsa_codec_config *codec_cfg;
codec_cfg = pdev->dev.platform_data;
if (codec_cfg != NULL) {
codec_cfg->hw_constraints_rates = &tsc2101_hw_constraints_rates;
codec_cfg->snd_omap_alsa_playback = &tsc2101_snd_omap_alsa_playback;
codec_cfg->snd_omap_alsa_capture = &tsc2101_snd_omap_alsa_capture;
codec_cfg->codec_configure_dev = tsc2101_configure;
codec_cfg->codec_set_samplerate = tsc2101_set_samplerate;
codec_cfg->codec_clock_setup = tsc2101_clock_setup;
codec_cfg->codec_clock_on = tsc2101_clock_on;
codec_cfg->codec_clock_off = tsc2101_clock_off;
codec_cfg->get_default_samplerate = tsc2101_get_default_samplerate;
ret = snd_omap_alsa_post_probe(pdev, codec_cfg);
}
else
ret = -ENODEV;
return ret;
}
static struct platform_driver omap_alsa_driver = {
.probe = snd_omap_alsa_tsc2101_probe,
.remove = snd_omap_alsa_remove,
.suspend = snd_omap_alsa_suspend,
.resume = snd_omap_alsa_resume,
.driver = {
.name = "omap_alsa_mcbsp",
},
};
static int __init omap_alsa_tsc2101_init(void)
{
int err;
ADEBUG();
err = platform_driver_register(&omap_alsa_driver);
return err;
}
static void __exit omap_alsa_tsc2101_exit(void)
{
ADEBUG();
platform_driver_unregister(&omap_alsa_driver);
}
module_init(omap_alsa_tsc2101_init);
module_exit(omap_alsa_tsc2101_exit);
/*
* arch/arc/mach-omap1/omap-alsa-tsc2101.h
*
* Alsa Driver for TSC2101 codec for OMAP platform boards.
*
* Based on former omap-aic23.h and tsc2101 OSS drivers.
* Copyright (C) 2004 Texas Instruments, Inc.
* Written by Nishanth Menon and Sriram Kannan
*
* Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* 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 OMAP_ALSA_TSC2101_H_
#define OMAP_ALSA_TSC2101_H_
#include <linux/types.h>
/* Define to set the tsc as the master w.r.t McBSP */
#define TSC_MASTER
#define NUMBER_SAMPLE_RATES_SUPPORTED 16
/*
* AUDIO related MACROS
*/
#ifndef DEFAULT_BITPERSAMPLE
#define DEFAULT_BITPERSAMPLE 16
#endif
#define DEFAULT_SAMPLE_RATE 44100
#define CODEC_CLOCK 12000000
#define AUDIO_MCBSP OMAP_MCBSP1
#define PAGE2_AUDIO_CODEC_REGISTERS (2)
struct tsc2101_samplerate_reg_info {
u16 sample_rate;
u8 divisor;
u8 fs_44kHz; /* if 0 48 khz, if 1 44.1 khz fsref */
};
/*
* Defines codec specific functions pointers that can be used from the
* common omap-alse base driver for all omap codecs. (tsc2101 and aic23)
*/
inline void tsc2101_configure(void);
void tsc2101_set_samplerate(long rate);
void tsc2101_clock_setup(void);
int tsc2101_clock_on(void);
int tsc2101_clock_off(void);
int tsc2101_get_default_samplerate(void);
#endif /*OMAP_ALSA_TSC2101_H_*/
/*
* sound/arm/omap-aic23.c
* sound/arm/omap-alsa.c
*
* Alsa Driver for AIC23 codec on OSK5912 platform board
* Alsa Driver for OMAP
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
* {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Based on sa11xx-uda1341.c,
* Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
*
......@@ -34,130 +36,30 @@
*
* 2005-07-29 INdT Kernel Team - Alsa driver for omap osk. Creation of new
* file omap-aic23.c
*
* 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed
* by Ajaya Babu
*
* 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu
*/
#include <linux/config.h>
#include <sound/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/clk.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/dma.h>
#include <asm/arch/aic23.h>
#include <asm/arch/mcbsp.h>
#include <asm/arch/clock.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/memalloc.h>
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-dma.h"
#include "omap-aic23.h"
#undef DEBUG
#ifdef DEBUG
#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__)
#else
#define ADEBUG() /* nop */
#endif
/* Define to set the AIC23 as the master w.r.t McBSP */
#define AIC23_MASTER
/*
* AUDIO related MACROS
*/
#define DEFAULT_BITPERSAMPLE 16
#define AUDIO_RATE_DEFAULT 44100
#define AUDIO_MCBSP OMAP_MCBSP1
#define NUMBER_SAMPLE_RATES_SUPPORTED 10
MODULE_AUTHOR("Daniel Petrini, David Cohen, Anderson Briglia - INdT");
MODULE_AUTHOR("Mika Laitio, Daniel Petrini, David Cohen, Anderson Briglia - INdT");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("OMAP AIC23 driver for ALSA");
MODULE_SUPPORTED_DEVICE("{{AIC23,OMAP AIC23}}");
MODULE_ALIAS("omap_mcbsp.1");
static char *id = NULL;
MODULE_PARM_DESC(id, "OMAP OSK ALSA Driver for AIC23 chip.");
static struct snd_card_omap_aic23 *omap_aic23 = NULL;
static struct clk *aic23_mclk = 0;
MODULE_DESCRIPTION("OMAP driver for ALSA");
MODULE_ALIAS("omap_alsa_mcbsp.1");
struct sample_rate_rate_reg_info {
u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
};
/*
* DAC USB-mode sampling rates (MCLK = 12 MHz)
* The rates and rate_reg_into MUST be in the same order
*/
static unsigned int rates[] = {
4000, 8000, 16000, 22050,
24000, 32000, 44100,
48000, 88200, 96000,
};
static const struct sample_rate_rate_reg_info
rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
{0x06, 1}, /* 4000 */
{0x06, 0}, /* 8000 */
{0x0C, 1}, /* 16000 */
{0x11, 1}, /* 22050 */
{0x00, 1}, /* 24000 */
{0x0C, 0}, /* 32000 */
{0x11, 0}, /* 44100 */
{0x00, 0}, /* 48000 */
{0x1F, 0}, /* 88200 */
{0x0E, 0}, /* 96000 */
};
/*
* mcbsp configuration structure
*/
static struct omap_mcbsp_reg_cfg initial_config_mcbsp = {
.spcr2 = FREE | FRST | GRST | XRST | XINTM(3),
.spcr1 = RINTM(3) | RRST,
.rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) |
RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0),
.rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16),
.xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) |
XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG,
.xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16),
.srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
.srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
#ifndef AIC23_MASTER
/* configure McBSP to be the I2S master */
.pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,
#else
/* configure McBSP to be the I2S slave */
.pcr0 = CLKXP | CLKRP,
#endif /* AIC23_MASTER */
};
static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static char *id = NULL;
static struct snd_card_omap_codec *alsa_codec = NULL;
static struct omap_alsa_codec_config *alsa_codec_config = NULL;
/*
* HW interface start and stop helper functions
......@@ -174,142 +76,53 @@ static int audio_ifc_stop(void)
return 0;
}
/*
* Codec/mcbsp init and configuration section
* codec dependent code.
*/
/*
* Sample rate changing
*/
static void omap_aic23_set_samplerate(struct snd_card_omap_aic23
*omap_aic23, long rate)
{
u8 count = 0;
u16 data = 0;
/* Fix the rate if it has a wrong value */
if (rate >= 96000)
rate = 96000;
else if (rate >= 88200)
rate = 88200;
else if (rate >= 48000)
rate = 48000;
else if (rate >= 44100)
rate = 44100;
else if (rate >= 32000)
rate = 32000;
else if (rate >= 24000)
rate = 24000;
else if (rate >= 22050)
rate = 22050;
else if (rate >= 16000)
rate = 16000;
else if (rate >= 8000)
rate = 8000;
else
rate = 4000;
/* Search for the right sample rate */
/* Verify what happens if the rate is not supported
* now it goes to 96Khz */
while ((rates[count] != rate) &&
(count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) {
count++;
}
data = (rate_reg_info[count].divider << CLKIN_SHIFT) |
(rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON;
audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data);
omap_aic23->samplerate = rate;
}
static inline void aic23_configure(void)
{
/* Reset codec */
audio_aic23_write(RESET_CONTROL_ADDR, 0);
/* Initialize the AIC23 internal state */
/* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL);
/* Digital audio path control, de-emphasis control 44.1kHz */
audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K);
/* Digital audio interface, master/slave mode, I2S, 16 bit */
#ifdef AIC23_MASTER
audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR,
MS_MASTER | IWL_16 | FOR_DSP);
#else
audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP);
#endif
/* Enable digital interface */
audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON);
}
static void omap_aic23_audio_init(struct snd_card_omap_aic23 *omap_aic23)
static void omap_alsa_audio_init(struct snd_card_omap_codec *omap_alsa)
{
/* Setup DMA stuff */
omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa AIC23 out";
omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa omap out";
omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
SNDRV_PCM_STREAM_PLAYBACK;
omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev =
omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev =
OMAP_DMA_MCBSP1_TX;
omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start =
omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start =
audio_ifc_start;
omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop =
omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop =
audio_ifc_stop;
omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa AIC23 in";
omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa omap in";
omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
SNDRV_PCM_STREAM_CAPTURE;
omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev =
omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev =
OMAP_DMA_MCBSP1_RX;
omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_start =
omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_start =
audio_ifc_start;
omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop =
omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop =
audio_ifc_stop;
/* configuring the McBSP */
omap_mcbsp_request(AUDIO_MCBSP);
/* if configured, then stop mcbsp */
omap_mcbsp_stop(AUDIO_MCBSP);
omap_mcbsp_config(AUDIO_MCBSP, &initial_config_mcbsp);
omap_mcbsp_start(AUDIO_MCBSP);
aic23_configure();
}
/*
* DMA functions
* Depends on omap-aic23-dma.c functions and (omap) dma.c
* Depends on omap-alsa-dma.c functions and (omap) dma.c
*
*/
#define DMA_BUF_SIZE 1024 * 8
static int audio_dma_request(struct audio_stream *s,
void (*callback) (void *))
{
int err;
ADEBUG();
err = omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);
err = omap_request_alsa_sound_dma(s->dma_dev, s->id, s, &s->lch);
if (err < 0)
printk(KERN_ERR "unable to grab audio dma 0x%x\n",
s->dma_dev);
printk(KERN_ERR "Unable to grab audio dma 0x%x\n", s->dma_dev);
return err;
}
static int audio_dma_free(struct audio_stream *s)
{
int err = 0;
ADEBUG();
err = omap_free_sound_dma(s, &s->lch);
err = omap_free_alsa_sound_dma(s, &s->lch);
if (err < 0)
printk(KERN_ERR "Unable to free audio dma channels!\n");
return err;
......@@ -340,7 +153,7 @@ static u_int audio_get_dma_pos(struct audio_stream *s)
/* Now, the position related to the end of that period */
offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count);
if (offset >= runtime->buffer_size || offset < 0)
if (offset >= runtime->buffer_size)
offset = 0;
return offset;
......@@ -360,9 +173,9 @@ static void audio_stop_dma(struct audio_stream *s)
s->periods = 0;
/* this stops the dma channel and clears the buffer ptrs */
omap_audio_stop_dma(s);
omap_stop_alsa_sound_dma(s);
omap_clear_sound_dma(s);
omap_clear_alsa_sound_dma(s);
spin_unlock_irqrestore(&s->dma_lock, flags);
}
......@@ -377,14 +190,22 @@ static void audio_process_dma(struct audio_stream *s)
unsigned int dma_size;
unsigned int offset;
int ret;
#ifdef CONFIG_MACH_OMAP_H6300
unsigned long flags;
#endif
ADEBUG();
runtime = substream->runtime;
if (s->active) {
dma_size = frames_to_bytes(runtime, runtime->period_size);
offset = dma_size * s->period;
snd_assert(dma_size <= DMA_BUF_SIZE,);
ret =
omap_start_sound_dma(s,
#ifdef CONFIG_MACH_OMAP_H6300
spin_lock_irqsave(&s->dma_lock, flags);
omap_stop_alsa_sound_dma(s);
spin_unlock_irqrestore(&s->dma_lock, flags);
#endif
ret = omap_start_alsa_sound_dma(s,
(dma_addr_t) runtime->dma_area +
offset, dma_size);
if (ret) {
......@@ -404,10 +225,11 @@ static void audio_process_dma(struct audio_stream *s)
/*
* This is called when dma IRQ occurs at the end of each transmited block
*/
void audio_dma_callback(void *data)
void callback_omap_alsa_sound_dma(void *data)
{
struct audio_stream *s = data;
ADEBUG();
/*
* If we are getting a callback for an active stream then we inform
* the PCM middle layer we've finished a period
......@@ -416,28 +238,26 @@ void audio_dma_callback(void *data)
snd_pcm_period_elapsed(s->stream);
spin_lock(&s->dma_lock);
if (s->periods > 0) {
if (s->periods > 0)
s->periods--;
}
audio_process_dma(s);
spin_unlock(&s->dma_lock);
}
/*
* Alsa section
* PCM settings and callbacks
*/
static int snd_omap_aic23_trigger(snd_pcm_substream_t * substream, int cmd)
static int snd_omap_alsa_trigger(snd_pcm_substream_t * substream, int cmd)
{
struct snd_card_omap_aic23 *chip =
struct snd_card_omap_codec *chip =
snd_pcm_substream_chip(substream);
int stream_id = substream->pstr->stream;
struct audio_stream *s = &chip->s[stream_id];
int err = 0;
ADEBUG();
/* note local interrupts are already disabled in the midlevel code */
spin_lock(&s->dma_lock);
switch (cmd) {
......@@ -459,15 +279,16 @@ static int snd_omap_aic23_trigger(snd_pcm_substream_t * substream, int cmd)
return err;
}
static int snd_omap_aic23_prepare(snd_pcm_substream_t * substream)
static int snd_omap_alsa_prepare(snd_pcm_substream_t * substream)
{
struct snd_card_omap_aic23 *chip =
snd_pcm_substream_chip(substream);
struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
struct audio_stream *s = &chip->s[substream->pstr->stream];
ADEBUG();
/* set requested samplerate */
omap_aic23_set_samplerate(chip, runtime->rate);
alsa_codec_config->codec_set_samplerate(runtime->rate);
chip->samplerate = runtime->rate;
s->period = 0;
s->periods = 0;
......@@ -475,138 +296,88 @@ static int snd_omap_aic23_prepare(snd_pcm_substream_t * substream)
return 0;
}
static snd_pcm_uframes_t snd_omap_aic23_pointer(snd_pcm_substream_t *
substream)
static snd_pcm_uframes_t snd_omap_alsa_pointer(snd_pcm_substream_t *substream)
{
struct snd_card_omap_aic23 *chip =
snd_pcm_substream_chip(substream);
struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
ADEBUG();
return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
}
/* Hardware capabilities */
static snd_pcm_hardware_t snd_omap_aic23_capture = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
static snd_pcm_hardware_t snd_omap_aic23_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_S16_LE),
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8 * 1024,
.periods_min = 16,
.periods_max = 255,
.fifo_size = 0,
};
static int snd_card_omap_aic23_open(snd_pcm_substream_t * substream)
static int snd_card_omap_alsa_open(snd_pcm_substream_t * substream)
{
struct snd_card_omap_aic23 *chip =
struct snd_card_omap_codec *chip =
snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
int stream_id = substream->pstr->stream;
int err;
ADEBUG();
chip->s[stream_id].stream = substream;
alsa_codec_config->codec_clock_on();
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = *(alsa_codec_config->snd_omap_alsa_playback);
else
runtime->hw = *(alsa_codec_config->snd_omap_alsa_capture);
omap_aic23_clock_on();
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = snd_omap_aic23_playback;
else
runtime->hw = snd_omap_aic23_capture;
if ((err =
snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS)) <
0)
if ((err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
if ((err =
snd_pcm_hw_constraint_list(runtime, 0,
if ((err = snd_pcm_hw_constraint_list(runtime,
0,
SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_rates)) < 0)
alsa_codec_config->hw_constraints_rates)) < 0)
return err;
return 0;
}
static int snd_card_omap_aic23_close(snd_pcm_substream_t * substream)
static int snd_card_omap_alsa_close(snd_pcm_substream_t * substream)
{
struct snd_card_omap_aic23 *chip =
snd_pcm_substream_chip(substream);
ADEBUG();
struct snd_card_omap_codec *chip = snd_pcm_substream_chip(substream);
omap_aic23_clock_off();
ADEBUG();
alsa_codec_config->codec_clock_off();
chip->s[substream->pstr->stream].stream = NULL;
return 0;
}
/* HW params & free */
static int snd_omap_aic23_hw_params(snd_pcm_substream_t * substream,
static int snd_omap_alsa_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int snd_omap_aic23_hw_free(snd_pcm_substream_t * substream)
static int snd_omap_alsa_hw_free(snd_pcm_substream_t * substream)
{
return snd_pcm_lib_free_pages(substream);
}
/* pcm operations */
static snd_pcm_ops_t snd_card_omap_aic23_playback_ops = {
.open = snd_card_omap_aic23_open,
.close = snd_card_omap_aic23_close,
static snd_pcm_ops_t snd_card_omap_alsa_playback_ops = {
.open = snd_card_omap_alsa_open,
.close = snd_card_omap_alsa_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_omap_aic23_hw_params,
.hw_free = snd_omap_aic23_hw_free,
.prepare = snd_omap_aic23_prepare,
.trigger = snd_omap_aic23_trigger,
.pointer = snd_omap_aic23_pointer,
.hw_params = snd_omap_alsa_hw_params,
.hw_free = snd_omap_alsa_hw_free,
.prepare = snd_omap_alsa_prepare,
.trigger = snd_omap_alsa_trigger,
.pointer = snd_omap_alsa_pointer,
};
static snd_pcm_ops_t snd_card_omap_aic23_capture_ops = {
.open = snd_card_omap_aic23_open,
.close = snd_card_omap_aic23_close,
static snd_pcm_ops_t snd_card_omap_alsa_capture_ops = {
.open = snd_card_omap_alsa_open,
.close = snd_card_omap_alsa_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_omap_aic23_hw_params,
.hw_free = snd_omap_aic23_hw_free,
.prepare = snd_omap_aic23_prepare,
.trigger = snd_omap_aic23_trigger,
.pointer = snd_omap_aic23_pointer,
.hw_params = snd_omap_alsa_hw_params,
.hw_free = snd_omap_alsa_hw_free,
.prepare = snd_omap_alsa_prepare,
.trigger = snd_omap_alsa_trigger,
.pointer = snd_omap_alsa_pointer,
};
/*
......@@ -614,16 +385,14 @@ static snd_pcm_ops_t snd_card_omap_aic23_capture_ops = {
*
* Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
*/
static int __init snd_card_omap_aic23_pcm(struct snd_card_omap_aic23
*omap_aic23, int device)
static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa,
int device)
{
snd_pcm_t *pcm;
int err;
ADEBUG();
if ((err =
snd_pcm_new(omap_aic23->card, "AIC23 PCM", device, 1, 1,
&pcm)) < 0)
if ((err = snd_pcm_new(omap_alsa->card, "OMAP PCM", device, 1, 1, &pcm)) < 0)
return err;
/* sets up initial buffer with continuous allocation */
......@@ -634,98 +403,70 @@ static int __init snd_card_omap_aic23_pcm(struct snd_card_omap_aic23
128 * 1024, 128 * 1024);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_omap_aic23_playback_ops);
&snd_card_omap_alsa_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_omap_aic23_capture_ops);
pcm->private_data = omap_aic23;
&snd_card_omap_alsa_capture_ops);
pcm->private_data = omap_alsa;
pcm->info_flags = 0;
strcpy(pcm->name, "omap aic23 pcm");
strcpy(pcm->name, "omap alsa pcm");
omap_aic23_audio_init(omap_aic23);
omap_alsa_audio_init(omap_alsa);
/* setup DMA controller */
audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK],
audio_dma_callback);
audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE],
audio_dma_callback);
audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK],
callback_omap_alsa_sound_dma);
audio_dma_request(&omap_alsa->s[SNDRV_PCM_STREAM_CAPTURE],
callback_omap_alsa_sound_dma);
omap_aic23->pcm = pcm;
omap_alsa->pcm = pcm;
return 0;
}
#ifdef CONFIG_PM
static int snd_omap_aic23_suspend(snd_card_t * card, pm_message_t state)
{
struct snd_card_omap_aic23 *chip = card->private_data;
ADEBUG();
if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
/* Mutes and turn clock off */
omap_aic23_clock_off();
snd_omap_suspend_mixer();
}
return 0;
}
/*
* Prepare hardware for resume
*/
static int snd_omap_aic23_resume(snd_card_t * card)
{
struct snd_card_omap_aic23 *chip = card->private_data;
ADEBUG();
if (chip->card->power_state != SNDRV_CTL_POWER_D0) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
omap_aic23_clock_on();
snd_omap_resume_mixer();
}
return 0;
}
/*
* Driver suspend/resume - calls alsa functions. Some hints from aaci.c
*/
static int omap_aic23_suspend(struct platform_device *pdev, pm_message_t state)
int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_card_omap_codec *chip;
snd_card_t *card = platform_get_drvdata(pdev);
if (card->power_state != SNDRV_CTL_POWER_D3hot) {
snd_omap_aic23_suspend(card, PMSG_SUSPEND);
chip = card->private_data;
if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
/* Mutes and turn clock off */
alsa_codec_config->codec_clock_off();
snd_omap_suspend_mixer();
}
}
return 0;
}
static int omap_aic23_resume(struct platform_device *pdev)
int snd_omap_alsa_resume(struct platform_device *pdev)
{
struct snd_card_omap_codec *chip;
snd_card_t *card = platform_get_drvdata(pdev);
if (card->power_state != SNDRV_CTL_POWER_D0) {
snd_omap_aic23_resume(card);
if (card->power_state != SNDRV_CTL_POWER_D0) {
chip = card->private_data;
if (chip->card->power_state != SNDRV_CTL_POWER_D0) {
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
alsa_codec_config->codec_clock_on();
snd_omap_resume_mixer();
}
}
return 0;
}
#else
#define snd_omap_aic23_suspend NULL
#define snd_omap_aic23_resume NULL
#define omap_aic23_suspend NULL
#define omap_aic23_resume NULL
#endif /* CONFIG_PM */
/*
*/
void snd_omap_aic23_free(snd_card_t * card)
void snd_omap_alsa_free(snd_card_t * card)
{
struct snd_card_omap_aic23 *chip = card->private_data;
struct snd_card_omap_codec *chip = card->private_data;
ADEBUG();
/*
......@@ -733,197 +474,106 @@ void snd_omap_aic23_free(snd_card_t * card)
* Can't do it immediately, since it may still have
* buffered data.
*/
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2);
schedule_timeout_interruptible(2);
omap_mcbsp_stop(AUDIO_MCBSP);
omap_mcbsp_free(AUDIO_MCBSP);
audio_aic23_write(RESET_CONTROL_ADDR, 0);
audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff);
audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
}
/*
* Omap MCBSP clock configuration
*
* Here we have some functions that allows clock to be enabled and
* disabled only when needed. Besides doing clock configuration
* it allows turn on/turn off audio when necessary.
*/
#define CODEC_CLOCK 12000000
#define AUDIO_RATE_DEFAULT 44100
/*
* Do clock framework mclk search
*/
static __init void omap_aic23_clock_setup(void)
{
aic23_mclk = clk_get(0, "mclk");
}
/*
* Do some sanity check, set clock rate, starts it and
* turn codec audio on
*/
int omap_aic23_clock_on(void)
{
if (clk_get_usecount(aic23_mclk) > 0) {
/* MCLK is already in use */
printk(KERN_WARNING
"MCLK in use at %d Hz. We change it to %d Hz\n",
(uint) clk_get_rate(aic23_mclk),
CODEC_CLOCK);
}
if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) {
printk(KERN_ERR
"Cannot set MCLK for AIC23 CODEC\n");
return -ECANCELED;
}
clk_enable(aic23_mclk);
printk(KERN_DEBUG
"MCLK = %d [%d], usecount = %d\n",
(uint) clk_get_rate(aic23_mclk), CODEC_CLOCK,
clk_get_usecount(aic23_mclk));
/* Now turn the audio on */
audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF &
~ADC_OFF & ~MIC_OFF & ~LINE_OFF);
return 0;
}
/*
* Do some sanity check, turn clock off and then turn
* codec audio off
*/
int omap_aic23_clock_off(void)
{
if (clk_get_usecount(aic23_mclk) > 0) {
if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) {
printk(KERN_WARNING
"MCLK for audio should be %d Hz. But is %d Hz\n",
(uint) clk_get_rate(aic23_mclk),
CODEC_CLOCK);
}
clk_disable(aic23_mclk);
}
audio_aic23_write(POWER_DOWN_CONTROL_ADDR,
DEVICE_POWER_OFF | OUT_OFF | DAC_OFF |
ADC_OFF | MIC_OFF | LINE_OFF);
return 0;
}
/* module init & exit */
/*
* Inits alsa soudcard structure
* Inits alsa soudcard structure.
* Called by the probe method in codec after function pointers has been set.
*/
static int __init snd_omap_aic23_probe(struct platform_device *pdev)
int snd_omap_alsa_post_probe(struct platform_device *pdev, struct omap_alsa_codec_config *config)
{
int err = 0;
int def_rate;
snd_card_t *card;
ADEBUG();
alsa_codec_config = config;
alsa_codec_config->codec_clock_setup();
alsa_codec_config->codec_clock_on();
omap_mcbsp_request(AUDIO_MCBSP);
omap_mcbsp_stop(AUDIO_MCBSP);
omap_mcbsp_config(AUDIO_MCBSP, alsa_codec_config->mcbsp_regs_alsa);
omap_mcbsp_start(AUDIO_MCBSP);
/* gets clock from clock framework */
omap_aic23_clock_setup();
if (alsa_codec_config && alsa_codec_config->codec_configure_dev)
alsa_codec_config->codec_configure_dev();
alsa_codec_config->codec_clock_off();
/* register the soundcard */
card = snd_card_new(-1, id, THIS_MODULE, sizeof(omap_aic23));
card = snd_card_new(-1, id, THIS_MODULE, sizeof(alsa_codec));
if (card == NULL)
return -ENOMEM;
goto nodev1;
omap_aic23 = kcalloc(1, sizeof(*omap_aic23), GFP_KERNEL);
if (omap_aic23 == NULL)
return -ENOMEM;
alsa_codec = kcalloc(1, sizeof(*alsa_codec), GFP_KERNEL);
if (alsa_codec == NULL)
goto nodev2;
card->private_data = (void *) omap_aic23;
card->private_free = snd_omap_aic23_free;
card->private_data = (void *)alsa_codec;
card->private_free = snd_omap_alsa_free;
omap_aic23->card = card;
omap_aic23->samplerate = AUDIO_RATE_DEFAULT;
alsa_codec->card = card;
def_rate = alsa_codec_config->get_default_samplerate();
alsa_codec->samplerate = def_rate;
spin_lock_init(&omap_aic23->s[0].dma_lock);
spin_lock_init(&omap_aic23->s[1].dma_lock);
spin_lock_init(&alsa_codec->s[0].dma_lock);
spin_lock_init(&alsa_codec->s[1].dma_lock);
/* mixer */
if ((err = snd_omap_mixer(omap_aic23)) < 0)
goto nodev;
if ((err = snd_omap_mixer(alsa_codec)) < 0)
goto nodev3;
/* PCM */
if ((err = snd_card_omap_aic23_pcm(omap_aic23, 0)) < 0)
goto nodev;
if ((err = snd_card_omap_alsa_pcm(alsa_codec, 0)) < 0)
goto nodev3;
strcpy(card->driver, "AIC23");
strcpy(card->shortname, "OSK AIC23");
sprintf(card->longname, "OMAP OSK with AIC23");
strcpy(card->driver, "OMAP_ALSA");
strcpy(card->shortname, alsa_codec_config->name);
sprintf(card->longname, alsa_codec_config->name);
snd_omap_init_mixer();
snd_card_set_dev(card, &pdev->dev);
if ((err = snd_card_register(card)) == 0) {
printk(KERN_INFO "OSK audio support initialized\n");
printk(KERN_INFO "audio support initialized\n");
platform_set_drvdata(pdev, card);
return 0;
}
nodev:
snd_omap_aic23_free(card);
nodev3:
kfree(alsa_codec);
nodev2:
snd_card_free(card);
nodev1:
omap_mcbsp_stop(AUDIO_MCBSP);
omap_mcbsp_free(AUDIO_MCBSP);
return err;
}
static int snd_omap_aic23_remove(struct platform_device *pdev)
int snd_omap_alsa_remove(struct platform_device *pdev)
{
snd_card_t *card = platform_get_drvdata(pdev);
struct snd_card_omap_aic23 *chip = card->private_data;
struct snd_card_omap_codec *chip = card->private_data;
snd_card_free(card);
omap_aic23 = NULL;
alsa_codec = NULL;
card->private_data = NULL;
kfree(chip);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver omap_alsa_driver = {
.probe = snd_omap_aic23_probe,
.remove = snd_omap_aic23_remove,
.suspend = omap_aic23_suspend,
.resume = omap_aic23_resume,
.driver = {
.name = "omap_mcbsp",
},
};
static int __init omap_aic23_init(void)
{
int err;
ADEBUG();
err = platform_driver_register(&omap_alsa_driver);
return err;
}
static void __exit omap_aic23_exit(void)
{
ADEBUG();
platform_driver_unregister(&omap_alsa_driver);
}
module_init(omap_aic23_init);
module_exit(omap_aic23_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment