Commit c8af20c5 authored by lamikr's avatar lamikr Committed by Tony Lindgren

[PATCH] ARM: OMAP: Alsa modularisations and support for tsc2101 2/3 (round 2)

This 2/3 patch moves omap alsa aic23 driver files to sound/arm/omap
directory.

In addition it adds cleaner separation of codec specific functionality
from the other functionality so that the same code can also be used by
the tsc2101 driver.

Structures required by the platform driver are now in
include/asm-arm/arch-omap/omap-alsa.h.

Unlike in the previous review, all other files required by this driver
are now located in the sound/arm/omap directory. MCBSP settings required
by the alsa driver are now initialized in the omap OSK board-code. Codec
specific function pointers are now initialized in the codec-file.
(omap-alsa-aic23.c)
Signed-off-by: default avatarMika Laitio <lamikr@cc.jyu.fi>
Signed-off-by: default avatarDaniel Petrini <d.pensator@gmail.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 419edf1b
......@@ -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[] = {
......
/*
* 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
......@@ -100,32 +101,44 @@ struct snd_card_omap_codec {
struct audio_stream s[2]; /* playback & capture */
};
/* 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);
};
/*********** 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
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
/*********** function prototype to function called from the dma interrupt handler ******/
void callback_omap_alsa_sound_dma(void *);
/* 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);
}
#endif /* CONFIG_SENSORS_TLV320AIC23 */
#endif
......@@ -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
/*
* 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,6 +439,31 @@ void snd_omap_resume_mixer(void)
}
#endif
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;
......@@ -493,4 +483,3 @@ int snd_omap_mixer(struct snd_card_omap_codec *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
*
......@@ -68,18 +68,9 @@
#include <asm/arch/mcbsp.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);
......@@ -186,9 +177,9 @@ int omap_request_alsa_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,
err = omap_request_dma(device_id,
device_name,
sound_dma_irq_handler,
sound_dma_irq_handler,
data,
&chan[i]);
......@@ -248,7 +239,7 @@ int omap_free_alsa_sound_dma(void *data, int **channels)
{
int i;
int *chan = NULL;
FN_IN;
if (unlikely(NULL == channels)) {
BUG();
......@@ -282,7 +273,7 @@ void omap_stop_alsa_sound_dma(struct audio_stream *s)
{
int *chan = s->lch;
int i;
FN_IN;
if (unlikely(NULL == chan)) {
BUG();
......@@ -320,7 +311,7 @@ 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,
(OMAP1510_MCBSP1_BASE + 0x06),
......@@ -338,7 +329,7 @@ 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,
(OMAP1510_MCBSP1_BASE + 0x02),
......@@ -368,8 +359,8 @@ 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_alsa_sound_dma(struct audio_stream *s,
dma_addr_t dma_ptr,
int omap_start_alsa_sound_dma(struct audio_stream *s,
dma_addr_t dma_ptr,
u_int dma_size)
{
int ret = -EPERM;
......
/*
* sound/arm/omap-alsa-dma.h
* linux/sound/arm/omap/omap-alsa-dma.h
*
* Common audio DMA handling for the OMAP processors
*
......@@ -32,7 +32,7 @@
/************************** INCLUDES *************************************/
#include "omap-aic23.h"
#include <asm/arch/omap-alsa.h>
/************************** GLOBAL DATA STRUCTURES *********************************/
......
/*
* 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_codec *omap_aic23 = NULL;
static struct clk *aic23_mclk = 0;
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 */
};
MODULE_DESCRIPTION("OMAP driver for ALSA");
MODULE_ALIAS("omap_alsa_mcbsp.1");
/*
* 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,140 +76,51 @@ 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_codec
*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_codec *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_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_alsa_sound_dma(s, &s->lch);
if (err < 0)
......@@ -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;
......@@ -359,7 +172,6 @@ static void audio_stop_dma(struct audio_stream *s)
s->period = 0;
s->periods = 0;
/* this stops the dma channel and clears the buffer ptrs */
/* this stops the dma channel and clears the buffer ptrs */
omap_stop_alsa_sound_dma(s);
......@@ -378,12 +190,21 @@ 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,);
#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);
......@@ -407,7 +228,8 @@ static void audio_process_dma(struct audio_stream *s)
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,19 +238,17 @@ void callback_omap_alsa_sound_dma(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_alsa_trigger(snd_pcm_substream_t * substream, int cmd)
{
struct snd_card_omap_codec *chip =
......@@ -436,8 +256,8 @@ static int snd_omap_alsa_trigger(snd_pcm_substream_t * substream, int cmd)
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) {
......@@ -461,13 +281,14 @@ static int snd_omap_alsa_trigger(snd_pcm_substream_t * substream, int cmd)
static int snd_omap_alsa_prepare(snd_pcm_substream_t * substream)
{
struct snd_card_omap_codec *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,59 +296,14 @@ static int snd_omap_alsa_prepare(snd_pcm_substream_t * substream)
return 0;
}
static snd_pcm_uframes_t snd_omap_alsa_pointer(snd_pcm_substream_t *
substream)
static snd_pcm_uframes_t snd_omap_alsa_pointer(snd_pcm_substream_t *substream)
{
struct snd_card_omap_codec *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_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,
};
static snd_pcm_hardware_t 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 int snd_card_omap_alsa_open(snd_pcm_substream_t * substream)
{
struct snd_card_omap_codec *chip =
......@@ -535,44 +311,40 @@ static int snd_card_omap_alsa_open(snd_pcm_substream_t * 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_alsa_playback;
else
runtime->hw = snd_omap_alsa_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_alsa_close(snd_pcm_substream_t * substream)
{
struct snd_card_omap_codec *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_alsa_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params)
{
......@@ -613,16 +385,14 @@ static snd_pcm_ops_t snd_card_omap_alsa_capture_ops = {
*
* Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
*/
static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa,
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 */
......@@ -636,23 +406,24 @@ static int __init snd_card_omap_alsa_pcm(struct snd_card_omap_codec *omap_alsa,
&snd_card_omap_alsa_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_omap_alsa_capture_ops);
pcm->private_data = omap_aic23;
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_request(&omap_alsa->s[SNDRV_PCM_STREAM_PLAYBACK],
callback_omap_alsa_sound_dma);
audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE],
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
/*
* Driver suspend/resume - calls alsa functions. Some hints from aaci.c
......@@ -668,7 +439,7 @@ int snd_omap_alsa_suspend(struct platform_device *pdev, pm_message_t state)
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();
alsa_codec_config->codec_clock_off();
snd_omap_suspend_mixer();
}
}
......@@ -680,24 +451,19 @@ 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) {
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);
omap_aic23_clock_on();
alsa_codec_config->codec_clock_on();
snd_omap_resume_mixer();
}
}
return 0;
}
#else
#define snd_omap_alsa_suspend NULL
#define snd_omap_alsa_resume NULL
#endif /* CONFIG_PM */
/*
*/
void snd_omap_alsa_free(snd_card_t * card)
{
struct snd_card_omap_codec *chip = card->private_data;
......@@ -708,197 +474,106 @@ void snd_omap_alsa_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_alsa_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_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_alsa_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:
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_alsa_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_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_alsa_aic23_probe,
.remove = snd_omap_alsa_remove,
.suspend = snd_omap_alsa_suspend,
.resume = snd_omap_alsa_resume,
.driver = {
.name = "omap_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);
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