ARM: Davinci: tlvaic32 audio oss driver support

parent 017a079d
...@@ -756,6 +756,7 @@ CONFIG_I2C_DAVINCI=y ...@@ -756,6 +756,7 @@ CONFIG_I2C_DAVINCI=y
# CONFIG_SENSORS_PCA9539 is not set # CONFIG_SENSORS_PCA9539 is not set
# CONFIG_SENSORS_PCF8591 is not set # CONFIG_SENSORS_PCF8591 is not set
CONFIG_SENSORS_TLV320AIC23=y CONFIG_SENSORS_TLV320AIC23=y
CONFIG_SENSORS_TLV320AIC32=y
CONFIG_SENSORS_TLV320AIC33=y CONFIG_SENSORS_TLV320AIC33=y
# CONFIG_SENSORS_MAX6875 is not set # CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set # CONFIG_SENSORS_TSL2550 is not set
...@@ -940,7 +941,7 @@ CONFIG_SOUND_PRIME=y ...@@ -940,7 +941,7 @@ CONFIG_SOUND_PRIME=y
# CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDCLAS is not set
# CONFIG_SOUND_MSNDPIN is not set # CONFIG_SOUND_MSNDPIN is not set
CONFIG_SOUND_DAVINCI=y CONFIG_SOUND_DAVINCI=y
CONFIG_SOUND_DAVINCI_AIC33=y CONFIG_SOUND_DAVINCI_AIC32=y
# #
# DaVinci Audio Options # DaVinci Audio Options
......
...@@ -123,6 +123,11 @@ config SENSORS_TLV320AIC23 ...@@ -123,6 +123,11 @@ config SENSORS_TLV320AIC23
If you say yes here you get support for the I2C control If you say yes here you get support for the I2C control
interface for Texas Instruments TLV320AIC23 audio codec. interface for Texas Instruments TLV320AIC23 audio codec.
config SENSORS_TLV320AIC32
tristate "Texas Instruments TLV320AIC32 Codec"
depends on I2C && I2C_DAVINCI
select SENSORS_TLV320AIC23
config SENSORS_TLV320AIC33 config SENSORS_TLV320AIC33
tristate "Texas Instruments TLV320AIC33 Codec" tristate "Texas Instruments TLV320AIC33 Codec"
depends on I2C && I2C_DAVINCI depends on I2C && I2C_DAVINCI
......
...@@ -107,6 +107,18 @@ int tlv320aic33_write_value(u8 reg, u16 value) ...@@ -107,6 +107,18 @@ int tlv320aic33_write_value(u8 reg, u16 value)
} }
#endif /* CONFIG_SENSORS_TLV320AIC33 */ #endif /* CONFIG_SENSORS_TLV320AIC33 */
#ifdef CONFIG_SENSORS_TLV320AIC32
int tlv320aic32_write_value(u8 reg, u16 value)
{
static struct i2c_client *client;
u8 val = value & 0xff;
client = new_client;
return i2c_smbus_write_byte_data(client, reg, val);
}
#endif /* CONFIG_SENSORS_TLV320AIC32 */
static int aic23_detect_client(struct i2c_adapter *adapter, int address, static int aic23_detect_client(struct i2c_adapter *adapter, int address,
int kind) int kind)
{ {
...@@ -685,3 +697,12 @@ module_exit(aic23_exit) ...@@ -685,3 +697,12 @@ module_exit(aic23_exit)
EXPORT_SYMBOL(aic23_write_value); EXPORT_SYMBOL(aic23_write_value);
EXPORT_SYMBOL(aic23_power_up); EXPORT_SYMBOL(aic23_power_up);
EXPORT_SYMBOL(aic23_power_down); EXPORT_SYMBOL(aic23_power_down);
#ifdef CONFIG_SENSORS_TLV320AIC33
EXPORT_SYMBOL(tlv320aic33_write_value);
#endif
#ifdef CONFIG_SENSORS_TLV320AIC32
EXPORT_SYMBOL(tlv320aic32_write_value);
#endif
...@@ -661,6 +661,18 @@ config SOUND_DAVINCI ...@@ -661,6 +661,18 @@ config SOUND_DAVINCI
---help--- ---help---
DaVinci Sound driver DaVinci Sound driver
choice
prompt "Sound driver"
depends on SOUND_DAVINCI
config SOUND_DAVINCI_AIC32
tristate "AIC32 Stereo Codec"
depends on SOUND_DAVINCI
select SENSORS_TLV320AIC32
---help---
If you say yes here you get support for the I2C control
interface for Texas Instruments TLV320AIC32 audio codec.
config SOUND_DAVINCI_AIC33 config SOUND_DAVINCI_AIC33
tristate "AIC33 Stereo Codec" tristate "AIC33 Stereo Codec"
depends on SOUND_DAVINCI depends on SOUND_DAVINCI
...@@ -668,6 +680,7 @@ config SOUND_DAVINCI_AIC33 ...@@ -668,6 +680,7 @@ config SOUND_DAVINCI_AIC33
---help--- ---help---
If you say yes here you get support for the I2C control If you say yes here you get support for the I2C control
interface for Texas Instruments TLV320AIC33 audio codec. interface for Texas Instruments TLV320AIC33 audio codec.
endchoice
menu "DaVinci Audio Options" menu "DaVinci Audio Options"
depends on SOUND_DAVINCI depends on SOUND_DAVINCI
......
...@@ -14,6 +14,7 @@ obj-$(CONFIG_SOUND_OMAP_AIC23) += omap-audio-aic23.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_SOUND_OMAP_AIC23) += omap-audio-aic23.o
obj-$(CONFIG_SOUND_DAVINCI) += davinci-audio-dma-intfc.o davinci-audio.o obj-$(CONFIG_SOUND_DAVINCI) += davinci-audio-dma-intfc.o davinci-audio.o
obj-$(CONFIG_SOUND_DAVINCI_AIC33) += davinci-audio-aic33.o obj-$(CONFIG_SOUND_DAVINCI_AIC33) += davinci-audio-aic33.o
obj-$(CONFIG_SOUND_DAVINCI_AIC32) += davinci-audio-aic32.o
# Please leave it as is, cause the link order is significant ! # Please leave it as is, cause the link order is significant !
......
/*
* linux/sound/oss/davinci-aic32.h
*
* Glue driver for AIC32 for Davinci processors
*
* Copyright (C) 2008 Neuros Technology.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef __ASM_ARCH_AIC32_H
#define __ASM_ARCH_AIC32_H
/* aic32 control registers */
#define PAGE_SELECT_REG 0
#define SOFT_RESET_REG 1
#define CODEC_SAMPLERATE_SELECT_REG 2
#define PLL_A_REG 3
#define PLL_B_REG 4
#define PLL_C_REG 5
#define PLL_D_REG 6
#define CODEC_DATAPATH_SETUP_REG 7
#define SERIAL_DATA_CTRL_REG 8
#define LEFT_ADC_GAIN_CTRL_REG 15
#define RIGHT_ADC_GAIN_CTRL_REG 16
#define MIC3_TO_LEFT_ADC_CTRL_REG 17
#define MIC3_TO_RIGHT_ADC_CTRL_REG 18
#define LINE1L_TO_LEFT_ADC_CTRL_REG 19
#define LINE1R_TO_RIGHT_ADC_CTRL_REG 22
#define MCBIAS_CTRL_REG 25
#define LEFT_AGC_CTRL_A_REG 26
#define LEFT_AGC_CTRL_B_REG 27
#define LEFT_AGC_CTRL_C_REG 28
#define RIGHT_AGC_CTRL_A_REG 29
#define RIGHT_AGC_CTRL_B_REG 30
#define RIGHT_AGC_CTRL_C_REG 31
#define DAC_POWER_CTRL_REG 37
#define OUTPUT_REDUCTION_REG 42
#define LEFT_DAC_VOLUME_CTRL_REG 43
#define RIGHT_DAC_VOLUME_CTRL_REG 44
#define DACL1_HPLOUT_VOLUME_REG 47
#define DACR1_HPLOUT_VOLUME_REG 64
#define DACL1_LEFT_LOP_VOLUME_REG 82
#define DACR1_RIGHT_LOP_VOLUME_REG 92
#define LEFTLOP_OUTPUT_LEVEL_CTRL_REG 86
#define RIGHTLOP_OUTPUT_LEVEL_CTRL_REG 93
#define HPLOUT_LEVEL_CTRL_REG 51
#define HPROUT_LEVEL_CTRL_REG 65
#define LEFTLOP_LEVEL_CTRL_REG 86
#define RIGHTLOP_LEVEL_CTRL_REG 93
/* aic32 control registers value */
#define PAGE0 0x0
/* Codec Datapath setup register 7 */
#define FS_REF_44_1 0x80
#define FS_REF_DEFAULT_48 0x00
#define ADC_DUAL_RATE_MODE 0x40
#define DAC_DUAL_RATE_MODE 0x20
#define LDAC_LCHAN 0x08
#define LDAC_RCHAN 0x10
#define LDAC_MONO_MIX 0x18
#define RDAC_RCHAN 0x02
#define RDAC_LCHAN 0x04
#define RDAC_MONO_MIX 0x06
/* soft reset control register 1 */
#define SELF_CLEAR_SOFT_RESET 0x80
/* used by register 17 */
#define MIC3L_LEFT_ADC_NOT_CONNECT 0xF0
#define MIC3R_LEFT_ADC_NOT_CONNECT 0x0F
/* used by register 18 */
#define MIC3L_RIGHT_ADC_NOT_CONNECT 0xF0
#define MIC3R_RIGHT_ADC_NOT_CONNECT 0x0F
/* used by register 19 */
#define LINE1L_NOT_CONNECT 0x78
#define LEFT_ADC_POWER_UP 0x04
/* used by register 22 */
#define LINE1R_NOT_CONNECT 0x78
#define RIGHT_ADC_POWER_UP 0x04
/* MICBIAS control register 25 */
#define MICBIAS_OUTPUT_2_0V 0x40
#define MICBIAS_OUTPUT_2_5V 0x80
#define MICBIAS_OUTPUT_AVDD 0xC0
/* output reduction register 42 */
#define OUTPUT_POWERON_DELAY 0x60
#define RAMPUP_STEP_TIME 0x0C
/* HPLCOM output level register 51 */
#define HPLCOM_OUTPUT_LEVEL 0x90
#define HPLOUT_IS_MUTE 0x08
#define HPLOUT_FULL_POWER_UP 0x01
/* HPRCOM output level register 65 */
#define HPRCOM_OUTPUT_LEVEL 0x90
#define HPROUT_IS_MUTE 0x08
#define HPROUT_FULL_POWER_UP 0x01
/* LEFT_LOP output level register 86 */
#define LEFTLOP_OUTPUT_LEVEL 0x90
#define LEFTLOP_IS_MUTE 0x08
#define LEFTLOP_FULL_POWER_UP 0x01
/* RIGHT_LOP output level register 86 */
#define RIGHTLOP_OUTPUT_LEVEL 0x90
#define RIGHTLOP_IS_MUTE 0x08
#define RIGHTLOP_FULL_POWER_UP 0x01
/* output level control register 86&93 */
#define OUTPUT_LEVEL 0x90
#define LEFTLOP_NOT_MUTE 0x80
#define LEFTLOP_FULL_POWER_UP 0x01
#define RIGHTLOP_NOT_MUTE 0x80
#define RIGHTLOP_FULL_POWER_UP 0x01
/* PLL Programming registerA 3 */
#define PLL_ENABLE 0x80
/* Audio serial data interface control registerA 8 */
#define BIT_CLK_MASTER 0x80
#define WORD_CLK_MASTER 0x40
#define DOUT_TRI_STATE 0x20
#define CLK_TRANS_MASTER 0x10
#define ENABLE_3D 0x04
#define DM_ENABLE_128 0x01
#define DM_ENABLE_64 0x02
#define DM_ENABLE_32 0x03
/*Audio serial data interface control registerB 9 */
#define DSP_MODE 0x40
#define RJ_MODE 0x80
#define LJ_MODE 0xC0
#define WORD_LENGTH20 0x10
#define WORD_LENGTH24 0x20
#define WORD_LENGTH32 0x30
#define BITCLOCK_256CLK_FRAME 0x08
/*Left/Right ADC PGA gain control register 15 & 16 */
#define ADC_PGA_MUTE 0x80
#define ADC_PGA_GAIN_MAX 0x78
#define ADC_PGA_GAIN_MIN 0x00
/* MIC3L/R to left/right ADC control register 17 & 18 */
#define ADCPGA_GAIN_MAX 0x00
#define MIC3L_ADCPGA_GAIN_MIN 0x80
#define MIC3L_ADCPGA_DISCONNECT 0xF0
#define MIC3R_ADCPGA_GAIN_MIN 0x08
#define MIC3R_ADCPGA_DISCONNECT 0x0F
/*LINE1L to left ADC Control Register 19 */
#define DIFF_MODE 0x80
#define LINE_ADCPGA_GAIN_MIN 0x40
#define LINE_ADCPGA_DISCONNECT 0x78
#define ADC_CHAN_ON 0x04
#define ADCPGA_SOFT_STEP2FS 0x01
#define ADCPGA_SOFT_STEP_OFF 0x03
/*LINE2L to left ADC Control Register 20 */
#define ADC_WEAK_INPUT_BIAS 0x04
/*LEFT/RIGHT AGC Control registerA 26 & 29 */
#define AGC_ENABLE 0x80
#define AGC_TARGET_GAIN_MAX 0x00
#define AGC_TARGET_GAIN_MIN 0x70
#define AGC_ATTACK_TIME_11 0x04
#define AGC_ATTACK_TIME_16 0x08
#define AGC_ATTACK_TIME_20 0x0C
#define AGC_DECAY_TIME_200 0x01
#define AGC_DECAY_TIME_400 0x02
#define AGC_DECAY_TIME_500 0x03
/*LEFT AGC Control registerB 27 & 30 */
#define AGC_GAIN_ALLOWED_MAX 0xEE
#define AGC_GAIN_ALLOWED_MIN 0x00
/*DAC Power and output driver control register 37 */
#define LEFT_DAC_POWER_ON 0x80
#define RIGHT_DAC_POWER_ON 0x40
#define HPLCOM_INDEPENDENT 0x20
/*High Power Output Stage Control Register 40 */
#define LINE2L_BYPASS_DISABLE_DEFAULT 0x00
#define LINE2LP_BYPASS_SINGLE 0x10
#define LINE2LM_BYPASS_SINGLE 0x20
#define LINE2LPM_BYPASS_DIFFERENTIAL 0x30
#define LINE2R_BYPASS_DISABLE_DEFAULT 0x00
#define LINE2RP_BYPASS_SINGLE 0x04
#define LINE2RM_BYPASS_SINGLE 0x08
#define LINE2RPM_BYPASS_DIFFERENTIAL 0x0C
/*DAC Output Switching Control Register 41 */
#define LEFT_DAC_DEFAULT_L1 0x00
#define LEFT_DAC_L2 0x80
#define LEFT_DAC_L3 0x40
#define RIGHT_DAC_DEFAULT_R1 0x00
#define RIGHT_DAC_R2 0x08
#define RIGHT_DAC_R3 0x04
/*LEFT/RIGHT DAC Digital volume control register 43 & 44 */
#define DAC_CHAN_MUTE 0x80
#define DAC_DIG_VOL_GAIN_MAX 0x00 /* 0.0db */
#define DAC_DIG_VOL_GAIN_MIN 0x7F /* -63.5db */
/*LINE2L to HPLOUT Volume Control Register 45 */
#define LINE2L_HPLOUT_ROUTED 0x80
/*PGA_L to HPLOUT Volume Control Register 46 */
#define PGAL_HPLOUT_ROUTED 0x80
/*any to LOP/M Volume control */
#define LOPM_ON 0x80
#define LOPM_VOL_GAIN_MAX 0x00 /*0 db */
#define LOPM_VOL_GAIN_MIN 0x76 /*-78.3 db is MUTE */
/*MONO_LOP/M output level volume control register 79 */
#define LOPM_POWER_ON 0x01
#define LOPM_MUTE_OFF 0x08
#define LOPM_OUTPUT_LEVEL_MIN 0x00
#define LOPM_OUTPUT_LEVEL_MAX 0x90
/*Module Power Status Register 94 */
#define HPROUT_DRIVER_POWER_ON 0x02
#define LIV_MAX 0x0077
#define LIV_MIN 0x0000
#define LHV_MAX 0x0077
#define LHV_MIN 0x0000
#define LIG_MAX 0x0077
#define LIG_MIN 0x0000
#define LOG_MAX 0x007f
#define LOG_MIN 0x0000
#endif /* __ASM_ARCH_AIC32_H */
/*
* linux/sound/oss/davinci-audio-aic32.c
*
* Glue driver for AIC32 for Davinci processors
*
* Copyright (C) 2008 Neuros Technology.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/clk.h>
#include <sound/davincisound.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/mcbsp.h>
#include "davinci-aic32.h"
#include "davinci-audio.h"
#include "davinci-audio-dma-intfc.h"
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#define PROC_START_FILE "driver/aic32-audio-start"
#define PROC_STOP_FILE "driver/aic32-audio-stop"
#endif
/*# define DEBUG */
#ifdef DEBUG
#define DPRINTK(ARGS...) \
do { \
printk(KERN_INFO, "<%s>: ", __FUNCTION__);\
printk(KERN_INFO, ARGS); \
} while (0)
#define FN_IN printk("<%s>: start\n", __FUNCTION__)
#else
#define DPRINTK(x...)
#define FN_IN
#endif
#define CODEC_NAME "AIC32"
#define PLATFORM_NAME "DAVINCI"
/* Define to set the AIC32 as the master w.r.t McBSP */
#define AIC32_MASTER
/* codec clock frequency */
#define MCLK 27
/*
* AUDIO related MACROS
*/
#define DEFAULT_BITPERSAMPLE 16
#define AUDIO_RATE_DEFAULT 48000
#define DEFAULT_MCBSP_CLOCK 81000000
/* Select the McBSP For Audio */
#define AUDIO_MCBSP DAVINCI_MCBSP1
#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC)
#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME)
#define MONO 1
#define STEREO 2
#define SET_VOLUME 1
#define SET_LINE 2
#define SET_MIC 3
#define SET_RECSRC 4
#define SET_IGAIN 5
#define SET_OGAIN 6
#define SET_BASS 7
#define SET_TREBLE 8
#define SET_MICBIAS 9
#define DEFAULT_OUTPUT_VOLUME 50
#define DEFAULT_INPUT_VOLUME 20 /* 0 ==> mute line in */
#define DEFAULT_INPUT_IGAIN 20
#define DEFAULT_INPUT_OGAIN 100
#define OUTPUT_VOLUME_MIN LHV_MIN
#define OUTPUT_VOLUME_MAX LHV_MAX
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
#define INPUT_VOLUME_MIN LIV_MIN
#define INPUT_VOLUME_MAX LIV_MAX
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define INPUT_GAIN_MIN LIG_MIN
#define INPUT_GAIN_MAX LIG_MAX
#define INPUT_GAIN_RANGE (INPUT_GAIN_MAX - INPUT_GAIN_MIN)
#define OUTPUT_GAIN_MIN LOG_MIN
#define OUTPUT_GAIN_MAX LOG_MAX
#define OUTPUT_GAIN_RANGE (INPUT_GAIN_MAX - INPUT_GAIN_MIN)
#define NUMBER_SAMPLE_RATES_SUPPORTED 11
#define MSB8BIT(x) ((unsigned char)((x) >> 6))
#define LSB6BIT(x) ((unsigned char)((x) << 2))
#define CLK_OUT0_FUNCTION_BIT 16
#define ASP_FUNCTION_BIT 10
#define SET_BIT(addr, bit) outl(inl(addr) | (1U << bit), addr)
#define CLEAR_BIT(addr, bit) outl(inl(addr) & ~(1U << bit), addr)
#define PINMUX1 0x01C40004
static audio_stream_t output_stream = {
.id = "AIC32 out",
.dma_dev = DAVINCI_DMA_MCBSP1_TX,
.input_or_output = FMODE_WRITE
};
static audio_stream_t input_stream = {
.id = "AIC32 in",
.dma_dev = DAVINCI_DMA_MCBSP1_RX,
.input_or_output = FMODE_READ
};
static int audio_dev_id, mixer_dev_id;
static struct aic32_local_info {
u8 volume;
u16 volume_reg;
u8 line;
u8 mic;
int recsrc;
int nochan;
u8 igain;
u8 ogain;
u8 micbias;
u8 bass;
u8 treble;
u16 input_volume_reg;
int mod_cnt;
} aic32_local;
struct sample_rate_reg_info {
u32 sample_rate;
u32 Fsref;
float divider;
u8 data;
};
/* To Store the default sample rate */
static long audio_samplerate = AUDIO_RATE_DEFAULT;
struct clk *davinci_mcbsp_get_clock(void);
/* DAC USB-mode sampling rates*/
static const struct sample_rate_reg_info
reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = {
/* {sample_rate, Fsref, divider, data}*/
{96000, 96000, 1, 0x00},
{88200, 88200, 1, 0x00},
{48000, 48000, 1, 0x00},
{44100, 44100, 1, 0x00},
{32000, 48000, 1.5, 0x11},
{24000, 96000, 4, 0x66},
{22050, 44100, 2, 0x22},
{16000, 48000, 3, 0x44},
{12000, 48000, 4, 0x66},
{11025, 44100, 4, 0x66},
{8000, 48000, 6, 0xAA},
};
static struct davinci_mcbsp_reg_cfg initial_config = {
.spcr2 = FREE | XINTM(3),
.spcr1 = RINTM(3),
.rcr2 = RWDLEN2(DAVINCI_MCBSP_WORD_16) | RDATDLY(1),
.rcr1 = RFRLEN1(1) | RWDLEN1(DAVINCI_MCBSP_WORD_16),
.xcr2 = XWDLEN2(DAVINCI_MCBSP_WORD_16) | XDATDLY(1) | XFIG,
.xcr1 = XFRLEN1(1) | XWDLEN1(DAVINCI_MCBSP_WORD_16),
.srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1),
.srgr2 = FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1),
#ifndef AIC32_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 /* AIC32_MASTER */
};
static void davinci_aic32_initialize(void *dummy);
static void davinci_aic32_shutdown(void *dummy);
static int davinci_aic32_ioctl(struct inode *inode, struct file *file,
uint cmd, ulong arg);
static int davinci_aic32_probe(void);
#ifdef MODULE
static void davinci_aic32_remove(void);
#endif
static int davinci_aic32_suspend(void);
static int davinci_aic32_resume(void);
static inline void davinci_enable_mclk(void);
static inline void davinci_disable_mclk(void);
static inline void aic32_configure(void);
static int mixer_open(struct inode *inode, struct file *file);
static int mixer_release(struct inode *inode, struct file *file);
static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd,
ulong arg);
#ifdef CONFIG_PROC_FS
static int codec_start(char *buf, char **start, off_t offset, int count,
int *eof, void *data);
static int codec_stop(char *buf, char **start, off_t offset, int count,
int *eof, void *data);
#endif
/* File Op structure for mixer */
static struct file_operations davinci_mixer_fops = {
.open = mixer_open,
.release = mixer_release,
.ioctl = mixer_ioctl,
.owner = THIS_MODULE
};
/* To store characteristic info regarding the codec for the audio driver */
static audio_state_t aic32_state = {
.owner = THIS_MODULE,
.output_stream = &output_stream,
.input_stream = &input_stream,
/* .need_tx_for_rx = 1, //Once the Full Duplex works */
.need_tx_for_rx = 0,
.hw_init = davinci_aic32_initialize,
.hw_shutdown = davinci_aic32_shutdown,
.client_ioctl = davinci_aic32_ioctl,
.hw_probe = davinci_aic32_probe,
.hw_remove = __exit_p(davinci_aic32_remove),
.hw_suspend = davinci_aic32_suspend,
.hw_resume = davinci_aic32_resume,
.sem = __SEMAPHORE_INIT(aic32_state.sem, 1),
};
/* This will be defined in the audio.h */
static struct file_operations *davinci_audio_fops;
int tlv320aic32_write_value(u8 reg, u16 value);
/* TLV320AIC32 write */
static __inline__ void audio_aic32_write(u8 address, u16 data)
{
if (tlv320aic32_write_value(address, data) < 0)
printk(KERN_INFO "aic32 write failed for reg = %d\n", address);
}
static int aic32_update(int flag, int val)
{
u16 volume;
u16 gain;
/* Ignore separate left/right channel for now,
even the codec does support it. */
val &= 0xff;
switch (flag) {
case SET_VOLUME:
if (val < 0 || val > 100) {
DPRINTK("Trying a bad volume value(%d)!\n", val);
return -EPERM;
}
volume =
((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN;
aic32_local.volume_reg = OUTPUT_VOLUME_MAX - volume;
audio_aic32_write(DACL1_HPLOUT_VOLUME_REG,
LOPM_ON | aic32_local.volume_reg);
audio_aic32_write(DACR1_HPLOUT_VOLUME_REG,
LOPM_ON | aic32_local.volume_reg);
audio_aic32_write(DACL1_LEFT_LOP_VOLUME_REG,
LOPM_ON | aic32_local.volume_reg);
audio_aic32_write(DACR1_RIGHT_LOP_VOLUME_REG,
LOPM_ON | aic32_local.volume_reg);
break;
case SET_LINE:
case SET_MIC:
if (val < 0 || val > 100) {
DPRINTK("Trying a bad volume value(%d)!\n", val);
return -EPERM;
}
volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN;
aic32_local.input_volume_reg = volume;
audio_aic32_write(LEFT_ADC_GAIN_CTRL_REG,
aic32_local.input_volume_reg);
audio_aic32_write(RIGHT_ADC_GAIN_CTRL_REG,
aic32_local.input_volume_reg);
break;
case SET_RECSRC:
if (hweight32(val) > 1)
val &= ~aic32_local.recsrc;
if (val == SOUND_MASK_MIC) {
/* enable the mic input*/
DPRINTK("Enabling mic\n");
audio_aic32_write(MIC3_TO_LEFT_ADC_CTRL_REG,
0x0);
audio_aic32_write(MIC3_TO_RIGHT_ADC_CTRL_REG,
0x0);
/* enable ADC's and disable the line input*/
audio_aic32_write(LINE1L_TO_LEFT_ADC_CTRL_REG,
LINE1L_NOT_CONNECT |
LEFT_ADC_POWER_UP);
audio_aic32_write(LINE1R_TO_RIGHT_ADC_CTRL_REG,
LINE1R_NOT_CONNECT |
RIGHT_ADC_POWER_UP);
} else if (val == SOUND_MASK_LINE) {
/* enable ADC's, enable line iput */
DPRINTK(" Enabling line in\n");
audio_aic32_write(LINE1L_TO_LEFT_ADC_CTRL_REG,
LEFT_ADC_POWER_UP);
audio_aic32_write(LINE1R_TO_RIGHT_ADC_CTRL_REG,
RIGHT_ADC_POWER_UP);
/* disable the mic input */
audio_aic32_write(MIC3_TO_LEFT_ADC_CTRL_REG,
MIC3L_LEFT_ADC_NOT_CONNECT |
MIC3R_LEFT_ADC_NOT_CONNECT);
audio_aic32_write(MIC3_TO_RIGHT_ADC_CTRL_REG,
MIC3L_RIGHT_ADC_NOT_CONNECT |
MIC3R_RIGHT_ADC_NOT_CONNECT);
}
aic32_local.recsrc = val;
break;
case SET_IGAIN:
if (val < 0 || val > 100) {
DPRINTK("Trying a bad igain value(%d)!\n", val);
return -EPERM;
}
gain = ((val * INPUT_GAIN_RANGE) / 100) + INPUT_GAIN_MIN;
DPRINTK("gain reg val = 0x%x", gain << 1);
/* Left AGC control */
audio_aic32_write(LEFT_AGC_CTRL_A_REG,
AGC_ENABLE);
audio_aic32_write(LEFT_AGC_CTRL_B_REG,
gain << 1);
audio_aic32_write(LEFT_AGC_CTRL_C_REG,
0x0);
/* Right AGC control */
audio_aic32_write(RIGHT_AGC_CTRL_A_REG,
AGC_ENABLE);
audio_aic32_write(RIGHT_AGC_CTRL_B_REG,
gain << 1);
audio_aic32_write(RIGHT_AGC_CTRL_C_REG,
0x0);
break;
case SET_OGAIN:
if (val < 0 || val > 100) {
DPRINTK("Trying a bad igain value(%d)!\n", val);
return -EPERM;
}
gain = ((val * OUTPUT_GAIN_RANGE) / 100) + OUTPUT_GAIN_MIN;
gain = OUTPUT_GAIN_MAX - gain;
/* Left/Right DAC digital volume gain */
audio_aic32_write(LEFT_DAC_VOLUME_CTRL_REG,
gain);
audio_aic32_write(RIGHT_DAC_VOLUME_CTRL_REG,
gain);
break;
case SET_MICBIAS:
if (val < 0 || val > 3) {
DPRINTK
("Request for non supported mic bias level(%d)!\n",
val);
return -EPERM;
}
if (val == 0)
audio_aic32_write(MCBIAS_CTRL_REG,
0x00);
else if (val == 1)
audio_aic32_write(MCBIAS_CTRL_REG,
MICBIAS_OUTPUT_2_0V);
else if (val == 2)
audio_aic32_write(MCBIAS_CTRL_REG,
MICBIAS_OUTPUT_2_5V);
else if (val == 3)
audio_aic32_write(MCBIAS_CTRL_REG,
MICBIAS_OUTPUT_AVDD);
break;
case SET_BASS:
break;
case SET_TREBLE:
break;
}
return 0;
}
static int mixer_open(struct inode *inode, struct file *file)
{
/* Any mixer specific initialization */
return 0;
}
static int mixer_release(struct inode *inode, struct file *file)
{
/* Any mixer specific Un-initialization */
return 0;
}
static int
mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
int val;
int ret = 0;
int nr = _IOC_NR(cmd);
/*
* We only accept mixer (type 'M') ioctls.
*/
if (_IOC_TYPE(cmd) != 'M')
return -EINVAL;
DPRINTK(" 0x%08x\n", cmd);
if (cmd == SOUND_MIXER_INFO) {
struct mixer_info mi;
strncpy(mi.id, "AIC32", sizeof(mi.id));
strncpy(mi.name, "TI AIC32", sizeof(mi.name));
mi.modify_counter = aic32_local.mod_cnt;
return copy_to_user((void *)arg, &mi, sizeof(mi));
}
if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = get_user(val, (int *)arg);
if (ret)
goto out;
switch (nr) {
case SOUND_MIXER_VOLUME:
aic32_local.mod_cnt++;
ret = aic32_update(SET_VOLUME, val);
if (!ret)
aic32_local.volume = val;
break;
case SOUND_MIXER_LINE:
aic32_local.mod_cnt++;
ret = aic32_update(SET_LINE, val);
if (!ret)
aic32_local.line = val;
break;
case SOUND_MIXER_MIC:
aic32_local.mod_cnt++;
ret = aic32_update(SET_MIC, val);
if (!ret)
aic32_local.mic = val;
break;
case SOUND_MIXER_RECSRC:
if ((val & SOUND_MASK_LINE) ||
(val & SOUND_MASK_MIC)) {
if (aic32_local.recsrc != val) {
aic32_local.mod_cnt++;
aic32_update(SET_RECSRC, val);
}
} else
ret = -EINVAL;
break;
case SOUND_MIXER_BASS:
aic32_local.mod_cnt++;
ret = aic32_update(SET_BASS, val);
if (!ret)
aic32_local.bass = val;
break;
case SOUND_MIXER_TREBLE:
aic32_local.mod_cnt++;
ret = aic32_update(SET_TREBLE, val);
if (!ret)
aic32_local.treble = val;
break;
case SOUND_MIXER_IGAIN:
aic32_local.mod_cnt++;
ret = aic32_update(SET_IGAIN, val);
if (!ret)
aic32_local.igain = val;
break;
case SOUND_MIXER_OGAIN:
aic32_local.mod_cnt++;
ret = aic32_update(SET_OGAIN, val);
if (!ret)
aic32_local.ogain = val;
break;
case SOUND_MIXER_MICBIAS:
aic32_local.mod_cnt++;
ret = aic32_update(SET_MICBIAS, val);
if (!ret)
aic32_local.micbias = val;
break;
default:
ret = -EINVAL;
}
}
if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
ret = 0;
switch (nr) {
case SOUND_MIXER_VOLUME:
val = aic32_local.volume;
break;
case SOUND_MIXER_LINE:
val = aic32_local.line;
break;
case SOUND_MIXER_MIC:
val = aic32_local.mic;
break;
case SOUND_MIXER_RECSRC:
val = aic32_local.recsrc;
break;
case SOUND_MIXER_RECMASK:
val = REC_MASK;
break;
case SOUND_MIXER_IGAIN:
val = aic32_local.igain;
break;
case SOUND_MIXER_OGAIN:
val = aic32_local.ogain;
break;
case SOUND_MIXER_DEVMASK:
val = DEV_MASK;
break;
case SOUND_MIXER_BASS:
val = aic32_local.bass;
break;
case SOUND_MIXER_TREBLE:
val = aic32_local.treble;
break;
case SOUND_MIXER_CAPS:
val = 0;
break;
case SOUND_MIXER_STEREODEVS:
val = SOUND_MASK_VOLUME;
break;
case SOUND_MIXER_MICBIAS:
val = aic32_local.micbias;
break;
default:
val = 0;
ret = -EINVAL;
break;
}
if (ret == 0)
ret = put_user(val, (int *)arg);
}
out:
return ret;
}
int davinci_set_samplerate(long sample_rate)
{
u8 count = 0;
/* wait for any frame to complete */
udelay(125);
/* Search for the right sample rate */
while ((reg_info[count].sample_rate != sample_rate) &&
(count < NUMBER_SAMPLE_RATES_SUPPORTED))
count++;
if (count == NUMBER_SAMPLE_RATES_SUPPORTED) {
DPRINTK("Invalid Sample Rate %d requested\n", (int)sample_rate);
return -EPERM;
}
/* CODEC DATAPATH SETUP */
/* Fsref to 48kHz, dual rate mode upto 96kHz */
if (reg_info[count].Fsref == 96000)
audio_aic32_write(CODEC_DATAPATH_SETUP_REG,
FS_REF_DEFAULT_48 | ADC_DUAL_RATE_MODE |
DAC_DUAL_RATE_MODE | LDAC_LCHAN | RDAC_RCHAN);
/* Fsref to 44.1kHz, dual rate mode upto 88.2kHz */
else if (reg_info[count].Fsref == 88200)
audio_aic32_write(CODEC_DATAPATH_SETUP_REG,
FS_REF_44_1 | ADC_DUAL_RATE_MODE |
DAC_DUAL_RATE_MODE | LDAC_LCHAN |
RDAC_RCHAN);
/* Fsref to 48kHz */
else if (reg_info[count].Fsref == 48000)
audio_aic32_write(CODEC_DATAPATH_SETUP_REG,
FS_REF_DEFAULT_48 | LDAC_LCHAN | RDAC_RCHAN);
/* Fsref to 44.1kHz */
else if (reg_info[count].Fsref == 44100)
audio_aic32_write(CODEC_DATAPATH_SETUP_REG,
FS_REF_44_1 | LDAC_LCHAN | RDAC_RCHAN);
/* Codec sample rate select */
audio_aic32_write(CODEC_SAMPLERATE_SELECT_REG,
reg_info[count].data);
/* If PLL is to be used for generation of Fsref
Generate the Fsref using the PLL */
#if (MCLK == 33)
if ((reg_info[count].Fsref == 96000) |
(reg_info[count].Fsref == 48000)) {
/* For MCLK = 33.8688 MHz and to get Fsref = 48kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R= 1, K = 5.8049,
which results in J = 5, D = 8049 */
/*Enable the PLL | Q-value | P-value */
audio_aic32_write(PLL_A_REG,
PLL_ENABLE | 0x10 | 0x02);
audio_aic32_write(PLL_B_REG,
0x14); /* J-value */
audio_aic32_write(PLL_C_REG,
MSB8BIT(8049)); /* D-value 8-MSB's */
audio_aic32_write(PLL_D_REG,
LSB6BIT(8049)); /* D-value 6-LSB's */
}
else if ((reg_info[count].Fsref == 88200) |
(reg_info[count].Fsref == 44100)) {
/* MCLK = 33.8688 MHz and to get Fsref = 44.1kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R =1, K = 5.3333,
which results in J = 5, D = 3333 */
/*Enable the PLL | Q-value | P-value */
audio_aic32_write(PLL_A_REG,
PLL_ENABLE | 0x10 | 0x02);
audio_aic32_write(PLL_B_REG,
(5 << 2)); /* J-value */
audio_aic32_write(PLL_C_REG,
MSB8BIT(3333)); /* D-value 8-MSB's */
audio_aic32_write(PLL_D_REG,
LSB6BIT(3333)); /* D-value 6-LSB's */
}
#elif(MCLK == 22)
if ((reg_info[count].Fsref == 96000) |
(reg_info[count].Fsref == 48000)) {
/* For MCLK = 22.5792 MHz and to get Fsref = 48kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R= 1, K = 8.7075,
which results in J = 8, D = 7075 */
/*Enable the PLL | Q-value | P-value */
audio_aic32_write(PLL_A_REG,
PLL_ENABLE | 0x10 | 0x02);
audio_aic32_write(PLL_B_REG,
(8 << 2)); /* J-value */
audio_aic32_write(PLL_C_REG,
MSB8BIT(7075)); /* D-value 8-MSB's */
audio_aic32_write(PLL_D_REG,
LSB6BIT(7075)); /* D-value 6-LSB's */
}
else if ((reg_info[count].Fsref == 88200) |
(reg_info[count].Fsref == 44100)) {
/* MCLK = 22.5792 MHz and to get Fsref = 44.1kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R =1, K = 8.0000,
which results in J = 8, D = 0000 */
/*Enable the PLL | Q-value | P-value */
audio_aic32_write(PLL_A_REG,
PLL_ENABLE | 0x10 | 0x02);
audio_aic32_write(PLL_B_REG,
(8 << 2)); /* J-value */
audio_aic32_write(PLL_C_REG,
MSB8BIT(0x00)); /* D-value 8-MSB's */
audio_aic32_write(PLL_D_REG,
LSB6BIT(0x00)); /* D-value 6-LSB's */
}
#elif(MCLK == 27)
if ((reg_info[count].Fsref == 96000) |
(reg_info[count].Fsref == 48000)) {
/* For MCLK = 27 MHz and to get Fsref = 48kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 1, R= 1, K = 7.2817,
which results in J = 3, D = 6408 */
/*Enable the PLL | Q-value | P-value */
audio_aic32_write(PLL_A_REG,
PLL_ENABLE | 0x01);
audio_aic32_write(PLL_B_REG,
(3 << 2)); /* J-value */
audio_aic32_write(PLL_C_REG,
MSB8BIT(6408)); /* D-value 8-MSB's */
audio_aic32_write(PLL_D_REG,
LSB6BIT(6408)); /* D-value 6-LSB's */
}
else if ((reg_info[count].Fsref == 88200) |
(reg_info[count].Fsref == 44100)) {
/* MCLK = 27 MHz and to get Fsref = 44.1kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 1, R =1, K = 3.3450,
which results in J = 3, D = 3450 */
/*Enable the PLL | Q-value | P-value */
audio_aic32_write(PLL_A_REG,
PLL_ENABLE | 0x01);
audio_aic32_write(PLL_B_REG,
(3 << 2)); /* J-value */
audio_aic32_write(PLL_C_REG,
MSB8BIT(3450)); /* D-value 8-MSB's */
audio_aic32_write(PLL_D_REG,
LSB6BIT(3450)); /* D-value 6-LSB's */
}
#else
#error "unknown audio codec frequency"
#endif
audio_samplerate = sample_rate;
#ifndef AIC32_MASTER
{
int clkgdv = 0;
unsigned long clkval = 0;
struct clk *mbspclk;
/*
Set Sample Rate at McBSP
Formula :
Codec System Clock = Input clock to McBSP;
clkgdv = ((Codec System Clock /
(SampleRate * BitsPerSample * 2)) - 1);
FWID = BitsPerSample - 1;
FPER = (BitsPerSample * 2) - 1;
*/
mbspclk = davinci_mcbsp_get_clock();
if (mbspclk == NULL) {
DPRINTK(" Failed to get internal clock to MCBSP");
return -EPERM;
}
clkval = clk_get_rate(mbspclk);
DPRINTK("mcbsp_clk = %ld\n", clkval);
if (clkval)
clkgdv =
(clkval /
(sample_rate * DEFAULT_BITPERSAMPLE * 2)) - 1;
else {
DPRINTK(" Failed to get the MCBSP clock\n");
return -EPERM;
}
DPRINTK("clkgdv = %d\n", clkgdv);
if (clkgdv > 255 || clkgdv < 0) {
/* For requested sampling rate, the input clock to MCBSP
cant be devided down to get the in range clock
devider value for 16 bits sample */
DPRINTK("Invalid Sample Rate %d requested\n",
(int)sample_rate);
return -EPERM;
}
initial_config.srgr1 =
(FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv));
initial_config.srgr2 =
(CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1));
davinci_mcbsp_stop(AUDIO_MCBSP);
davinci_mcbsp_config(AUDIO_MCBSP, &initial_config);
}
#endif /* AIC32_MASTER */
return 0;
}
static inline void davinci_enable_mclk(void)
{
/* Enable CLK_OUT0 function on default GPIO[48] pin */
SET_BIT(IO_ADDRESS(PINMUX1), CLK_OUT0_FUNCTION_BIT);
/* Enable ASP function on default GPIO[29:34] pins */
SET_BIT(IO_ADDRESS(PINMUX1), ASP_FUNCTION_BIT);
}
static inline void davinci_disable_mclk(void)
{
/* Disbale ASP function on default GPIO[29:34] pins */
CLEAR_BIT(IO_ADDRESS(PINMUX1), ASP_FUNCTION_BIT);
/* Disbale CLK_OUT0 function on default GPIO[48] pin */
CLEAR_BIT(IO_ADDRESS(PINMUX1), CLK_OUT0_FUNCTION_BIT);
}
static void davinci_aic32_shutdown(void *dummy)
{
/*
Turn off codec after it is done.
Can't do it immediately, since it may still have
buffered data.
Wait 20ms (arbitrary value) and then turn it off.
*/
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2);
davinci_mcbsp_stop(AUDIO_MCBSP);
davinci_mcbsp_free(AUDIO_MCBSP);
/* Self clearing aic32 software reset */
audio_aic32_write(SOFT_RESET_REG,
SELF_CLEAR_SOFT_RESET);
davinci_disable_mclk();
}
static void davinci_set_mono_stereo(int mode)
{
if (mode == MONO) {
/* Driver power ON pop control */
audio_aic32_write(OUTPUT_REDUCTION_REG,
OUTPUT_POWERON_DELAY | RAMPUP_STEP_TIME);
/* LEFT_LOP/M, RIGHT_LOP/M output level control */
audio_aic32_write(LEFTLOP_OUTPUT_LEVEL_CTRL_REG,
OUTPUT_LEVEL | LEFTLOP_NOT_MUTE |
LEFTLOP_FULL_POWER_UP);
audio_aic32_write(RIGHTLOP_OUTPUT_LEVEL_CTRL_REG,
OUTPUT_LEVEL | RIGHTLOP_NOT_MUTE |
RIGHTLOP_FULL_POWER_UP);
/* Left DAC power up, Right DAC power down */
audio_aic32_write(DAC_POWER_CTRL_REG,
LEFT_DAC_POWER_ON | HPLCOM_INDEPENDENT);
} else if (mode == STEREO) {
/* Driver power ON pop control */
audio_aic32_write(OUTPUT_REDUCTION_REG,
OUTPUT_POWERON_DELAY | RAMPUP_STEP_TIME);
/* HPLOUT/HPROUT output level control */
audio_aic32_write(HPLOUT_LEVEL_CTRL_REG,
HPLCOM_OUTPUT_LEVEL | HPLOUT_IS_MUTE |
HPLOUT_FULL_POWER_UP);
audio_aic32_write(HPROUT_LEVEL_CTRL_REG,
HPRCOM_OUTPUT_LEVEL | HPROUT_IS_MUTE |
HPROUT_FULL_POWER_UP);
/* LEFT_LOP/M, RIGHT_LOP/M output level control */
audio_aic32_write(LEFTLOP_LEVEL_CTRL_REG,
LEFTLOP_OUTPUT_LEVEL | LEFTLOP_IS_MUTE |
LEFTLOP_FULL_POWER_UP);
audio_aic32_write(RIGHTLOP_LEVEL_CTRL_REG,
RIGHTLOP_OUTPUT_LEVEL | RIGHTLOP_IS_MUTE |
RIGHTLOP_FULL_POWER_UP);
/* Left/Right DAC power up */
audio_aic32_write(DAC_POWER_CTRL_REG,
LEFT_DAC_POWER_ON | RIGHT_DAC_POWER_ON |
HPLCOM_INDEPENDENT);
} else
DPRINTK(" REQUEST FOR INVALID MODE\n");
}
static inline void aic32_configure()
{
FN_IN;
/* Page select register */
audio_aic32_write(PAGE_SELECT_REG, PAGE0);
davinci_set_mono_stereo(aic32_local.nochan);
#ifdef AIC32_MASTER
/* Enable bit and word clock as Master mode, 3-d disabled */
audio_aic32_write(SERIAL_DATA_CTRL_REG,
BIT_CLK_MASTER | WORD_CLK_MASTER);
#endif
aic32_update(SET_LINE, aic32_local.line);
aic32_update(SET_VOLUME, aic32_local.volume);
aic32_update(SET_RECSRC, aic32_local.recsrc);
aic32_update(SET_IGAIN, aic32_local.igain);
aic32_update(SET_OGAIN, aic32_local.ogain);
aic32_update(SET_MICBIAS, aic32_local.micbias);
}
static void davinci_aic32_initialize(void *dummy)
{
FN_IN;
/* initialize with default sample rate */
audio_samplerate = AUDIO_RATE_DEFAULT;
if (davinci_mcbsp_request(AUDIO_MCBSP) < 0) {
DPRINTK("MCBSP request failed\n");
return;
}
/* if configured, then stop mcbsp */
davinci_mcbsp_stop(AUDIO_MCBSP);
/* set initial (default) sample rate */
davinci_set_samplerate(audio_samplerate);
davinci_mcbsp_config(AUDIO_MCBSP, &initial_config);
/* enable davinci MCLK */
davinci_enable_mclk();
}
static int
davinci_aic32_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
{
long val;
int ret = 0;
DPRINTK(" 0x%08x\n", cmd);
/*
* These are platform dependent ioctls which are not handled by the
* generic davinci-audio module.
*/
switch (cmd) {
case SNDCTL_DSP_STEREO:
ret = get_user(val, (int *)arg);
if (ret)
return ret;
/* the Davinci supports AIC32 as stereo, mono on stereo jack */
ret = (val == 0) ? -EINVAL : 1;
return put_user(ret, (int *)arg);
case SNDCTL_DSP_CHANNELS:
ret = get_user(val, (long *)arg);
if (ret) {
DPRINTK("get_user failed\n");
break;
}
if (val == STEREO) {
DPRINTK("Driver support for AIC32 as stereo\n");
aic32_local.nochan = STEREO;
davinci_set_mono_stereo(aic32_local.nochan);
} else if (val == MONO) {
DPRINTK("Driver support for AIC32 as mono\n");
aic32_local.nochan = MONO;
davinci_set_mono_stereo(aic32_local.nochan);
} else {
DPRINTK
("Driver support for AIC32 as stereo/mono mode\n");
return -EPERM;
}
case SOUND_PCM_READ_CHANNELS:
/* the Davinci supports AIC32 as stereo, mono on stereo jack */
if (aic32_local.nochan == MONO)
return put_user(MONO, (long *)arg);
else
return put_user(STEREO, (long *)arg);
case SNDCTL_DSP_SPEED:
ret = get_user(val, (long *)arg);
if (ret) {
DPRINTK("get_user failed\n");
break;
}
ret = davinci_set_samplerate(val);
if (ret) {
DPRINTK("davinci_set_samplerate failed\n");
break;
}
/* fall through */
case SOUND_PCM_READ_RATE:
return put_user(audio_samplerate, (long *)arg);
case SNDCTL_DSP_SETFMT: /* set Format */
ret = get_user(val, (long *)arg);
if (ret) {
DPRINTK("get_user failed\n");
break;
}
if (val != AFMT_S16_LE) {
DPRINTK
("Driver supports only AFMT_S16_LE audio format\n");
return -EPERM;
}
case SOUND_PCM_READ_BITS:
case SNDCTL_DSP_GETFMTS:
/* we can do 16-bit only */
return put_user(AFMT_S16_LE, (long *)arg);
default:
/* Maybe this is meant for the mixer (As per OSS Docs) */
return mixer_ioctl(inode, file, cmd, arg);
}
return ret;
}
static int davinci_aic32_probe(void)
{
/* Get the fops from audio oss driver */
davinci_audio_fops = audio_get_fops();
if (!davinci_audio_fops) {
DPRINTK
("Unable to get the file operations forAIC32 OSS driver\n");
audio_unregister_codec(&aic32_state);
return -EPERM;
}
aic32_local.volume = DEFAULT_OUTPUT_VOLUME;
aic32_local.line = DEFAULT_INPUT_VOLUME;
/* either of SOUND_MASK_LINE/SOUND_MASK_MIC */
aic32_local.recsrc = SOUND_MASK_LINE;
aic32_local.igain = DEFAULT_INPUT_IGAIN;
aic32_local.ogain = DEFAULT_INPUT_OGAIN;
aic32_local.nochan = STEREO;
aic32_local.micbias = 1;
aic32_local.mod_cnt = 0;
/* register devices */
audio_dev_id = register_sound_dsp(davinci_audio_fops, -1);
mixer_dev_id = register_sound_mixer(&davinci_mixer_fops, -1);
#ifdef CONFIG_PROC_FS
create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ ,
NULL /* parent dir */ ,
codec_start, NULL/* client data */);
create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ ,
NULL /* parent dir */ ,
codec_stop, NULL/* client data */);
#endif
/* Announcement Time */
DPRINTK(PLATFORM_NAME " " CODEC_NAME " audio support initialized\n");
return 0;
}
#ifdef MODULE
static void __exit davinci_aic32_remove(void)
{
/* Un-Register the codec with the audio driver */
unregister_sound_dsp(audio_dev_id);
unregister_sound_mixer(mixer_dev_id);
#ifdef CONFIG_PROC_FS
remove_proc_entry(PROC_START_FILE, NULL);
remove_proc_entry(PROC_STOP_FILE, NULL);
#endif
}
#endif /* MODULE */
static int davinci_aic32_suspend(void)
{
/* Empty for the moment */
return 0;
}
static int davinci_aic32_resume(void)
{
/* Empty for the moment */
return 0;
}
static int __init audio_aic32_init(void)
{
int err = 0;
/* register the codec with the audio driver */
err = audio_register_codec(&aic32_state);
if (err) {
DPRINTK
("Failed to register AIC32 driver with Audio OSS Driver\n");
} else {
DPRINTK("codec driver register success\n");
}
/* configure aic32 with default params */
aic32_configure();
return err;
}
static void __exit audio_aic32_exit(void)
{
davinci_aic32_shutdown(NULL);
(void)audio_unregister_codec(&aic32_state);
return;
}
#ifdef CONFIG_PROC_FS
static int codec_start(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
davinci_aic32_initialize(NULL);
DPRINTK("AIC32 codec initialization done.\n");
return 0;
}
static int codec_stop(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
davinci_aic32_shutdown(NULL);
DPRINTK("AIC32 codec shutdown.\n");
return 0;
}
#endif /* CONFIG_PROC_FS */
module_init(audio_aic32_init);
module_exit(audio_aic32_exit);
MODULE_AUTHOR("Neuros Technology");
MODULE_DESCRIPTION("Glue audio driver for the TI AIC32 codec.");
MODULE_LICENSE("GPL");
...@@ -120,6 +120,8 @@ static int audio_suspend(struct platform_device *dev, pm_message_t state); ...@@ -120,6 +120,8 @@ static int audio_suspend(struct platform_device *dev, pm_message_t state);
static int audio_resume(struct platform_device *dev); static int audio_resume(struct platform_device *dev);
static void audio_device_release(struct device *dev);
/***************************** Data Structures ********************************/ /***************************** Data Structures ********************************/
/* /*
...@@ -160,6 +162,7 @@ static struct platform_device davinci_audio_device = { ...@@ -160,6 +162,7 @@ static struct platform_device davinci_audio_device = {
.name = DAVINCI_AUDIO_NAME, .name = DAVINCI_AUDIO_NAME,
.dev = { .dev = {
.driver_data = &audio_state, .driver_data = &audio_state,
.release = &audio_device_release,
}, },
.id = 0, .id = 0,
}; };
...@@ -297,6 +300,18 @@ static int audio_remove(struct platform_device *dev) ...@@ -297,6 +300,18 @@ static int audio_remove(struct platform_device *dev)
return 0; return 0;
} }
/*******************************************************************************
*
* audio_device_release Fucntion to handle release operations
*
*****************************************************************************/
static void audio_device_release(struct device *dev)
{
FN_IN;
/* do nothing */
FN_OUT(0);
}
/******************************************************************************* /*******************************************************************************
* *
* audio_shutdown(): Function to handle shutdown operations * audio_shutdown(): Function to handle shutdown operations
......
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