📄 s3c24xx-uda1341.c
字号:
/*
* Driver for Philips UDA1341TS on SAMSUNG S3C24XX series soundcard
*
* Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
* 2008 Jerry Zhang <Jie.Zhang@elektrobit.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* History:
*
* 2008-10-22 Jerry Zhang initial release - based on s3c24xx-uda1341.c from OSS and sa11xx-uda1341.c from ALSA
* 2002-10-22 Jerry Zhang playback over ALSA is working
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/dma.h>
#include <asm/arch/dma.h>
#include <asm/arch/regs-gpio.h>
#include <asm/plat-s3c24xx/regs-iis.h>
#include <asm/plat-s3c24xx/bitfield.h>
#include <asm/plat-s3c24xx/clock.h>
#include <asm/arch/regs-clock.h>
#include <asm/dma-mapping.h>
#include <asm/arch/hardware.h>
#include <asm/arch/map.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/control.h>
#include <sound/info.h>
#include <sound/initval.h>
MODULE_LICENSE ( "GPL" );
MODULE_AUTHOR ( "zhangjie<jie.zhang@elektrobit.com>" );
MODULE_DESCRIPTION ( "S3C24XX uda1341 sound driver (ALSA)" );
#define MIXER_EN 1
static char * id;
module_param( id, charp, 0444 );
MODULE_PARM_DESC( id, "ID String for S3C24XX + UDA1341TS Driver" );
/*
* Added by Zhj EB
* add debug level control
*/
enum dbg_channels
{
dbg_err = ( 1 << 0 ),
dbg_debug = ( 1 << 1 ),
dbg_info = ( 1 << 2 ),
dbg_irq = ( 1 << 3 ),
dbg_sg = ( 1 << 4 ),
dbg_dma = ( 1 << 5 ),
dbg_pio = ( 1 << 6 ),
dbg_fail = ( 1 << 7 ),
dbg_conf = ( 1 << 8 ),
};
static const int dbgmap_err = dbg_err | dbg_fail;
static const int dbgmap_info = dbg_info | dbg_conf;
static const int dbgmap_debug = dbg_debug;
#define dbg( channels, format, args... ) \
do { \
if ( dbgmap_err & ( channels ) ) { \
printk( KERN_ERR format, ## args );} \
else if ( dbgmap_info & ( channels ) ) { \
printk( KERN_INFO format, ## args ); } \
else if ( dbgmap_info & ( channels ) ) { \
printk( KERN_DEBUG format, ## args ); } \
}while (0)
static void init_s3c2410_iis_bus_rx ( void );
static void init_s3c2410_iis_bus_tx ( void );
#define fIISPSR_A Fld(5, 5) /* Prescaler Control A */
#define IISPSR_A(x) FInsrt((x), fIISPSR_A)
#define fIISPSR_B Fld(5, 0) /* Prescaler Control B */
#define IISPSR_B(x) FInsrt((x), fIISPSR_B)
typedef struct s3c2410_dma_chan s3c2410_dma_chan_t;
typedef struct s3c2410_dma_client s3c2410_dma_client_t;
typedef enum s3c2410_dmasrc s3c2410_dmasrc_t;
typedef enum s3c2410_dma_buffresult s3c2410_dma_buffresult_t;
#define S3C2410_IISMOD_MASTER ( 0 << 8 )
#define PFX "zhj-alsa: "
#define MAX_DMA_CHANNELS 0
/* The S3C2410 has four internal DMA channels.(real) */
#define MAX_S3C2410_DMA_CHANNELS S3C2410_DMA_CHANNELS
#define DMA_CH1 DMACH_I2S_IN
#define DMA_CH2 DMACH_I2S_OUT
#define DMA_BUF_WR 1
#define DMA_BUF_RD 0
#define dma_wrreg(chan, reg, val) __raw_writel((val), (chan)->regs + (reg))
#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
#define DEF_VOLUME 80
/* UDA1341 Register bits */
#define UDA1341_ADDR 0x14
#define UDA1341_REG_DATA0 (UDA1341_ADDR + 0)
#define UDA1341_REG_DATA1 (UDA1341_ADDR + 1)
#define UDA1341_REG_STATUS (UDA1341_ADDR + 2)
/* status control */
#define STAT0 (0x00)
#define STAT0_RST (1 << 6)
#define STAT0_SC_MASK (3 << 4)
#define STAT0_SC_512FS (0 << 4)
#define STAT0_SC_384FS (1 << 4)
#define STAT0_SC_256FS (2 << 4)
#define STAT0_IF_MASK (7 << 1)
#define STAT0_IF_I2S (0 << 1)
#define STAT0_IF_LSB16 (1 << 1)
#define STAT0_IF_LSB18 (2 << 1)
#define STAT0_IF_LSB20 (3 << 1)
#define STAT0_IF_MSB (4 << 1)
#define STAT0_IF_LSB16MSB (5 << 1)
#define STAT0_IF_LSB18MSB (6 << 1)
#define STAT0_IF_LSB20MSB (7 << 1)
#define STAT0_DC_FILTER (1 << 0)
#define STAT0_DC_NO_FILTER (0 << 0)
#define STAT1 (0x80)
#define STAT1_DAC_GAIN (1 << 6) /* gain of DAC */
#define STAT1_ADC_GAIN (1 << 5) /* gain of ADC */
#define STAT1_ADC_POL (1 << 4) /* polarity of ADC */
#define STAT1_DAC_POL (1 << 3) /* polarity of DAC */
#define STAT1_DBL_SPD (1 << 2) /* double speed playback */
#define STAT1_ADC_ON (1 << 1) /* ADC powered */
#define STAT1_DAC_ON (1 << 0) /* DAC powered */
/* data0 direct control */
#define DATA0 (0x00)
#define DATA0_VOLUME_MASK (0x3f)
#define DATA0_VOLUME(x) (x)
#define DATA1 (0x40)
#define DATA1_BASS(x) ((x) << 2)
#define DATA1_BASS_MASK (15 << 2)
#define DATA1_TREBLE(x) ((x))
#define DATA1_TREBLE_MASK (3)
#define DATA2 (0x80)
#define DATA2_PEAKAFTER (0x1 << 5)
#define DATA2_DEEMP_NONE (0x0 << 3)
#define DATA2_DEEMP_32KHz (0x1 << 3)
#define DATA2_DEEMP_44KHz (0x2 << 3)
#define DATA2_DEEMP_48KHz (0x3 << 3)
#define DATA2_MUTE (0x1 << 2)
#define DATA2_FILTER_FLAT (0x0 << 0)
#define DATA2_FILTER_MIN (0x1 << 0)
#define DATA2_FILTER_MAX (0x3 << 0)
/* data0 extend control */
#define EXTADDR(n) (0xc0 | (n))
#define EXTDATA(d) (0xe0 | (d))
#define EXT2 2
#define EXT2_MIC_GAIN(x) ((x) << 2)
#define EXT2_MIXMODE_CH1 (1)
#define AUDIO_NAME "UDA1341"
#define AUDIO_NAME_VERBOSE "UDA1341 audio driver"
#define AUDIO_RATE_DEFAULT 44100
#define S_CLOCK_FREQ 384
#define PCM_ABS(a) (a < 0 ? -a : a)
#define DMA_BUF_SIZE 8192
/* Mixer Module */
#if MIXER_EN
#define STAT_MASK 0x80
#define DATA0_0 0x00
#define DATA0_1 0x40
#define DATA0_2 0x80
#define DATA_MASK 0xc0
#define IS_DATA0(x) ((x) >= data0_0 && (x) <= data0_2)
#define IS_DATA1(x) ((x) == data1)
#define IS_STATUS(x) ((x) == stat0 || (x) == stat1)
#define IS_EXTEND(x) ((x) >= ext0 && (x) <= ext6)
static const char *peak_names[] = {
"before",
"after",
};
static const char *filter_names[] = {
"flat",
"min",
"min",
"max",
};
static const char *mixer_names[] = {
"double differential",
"input channel 1 (line in)",
"input channel 2 (microphone)",
"digital mixer",
};
static const char *deemp_names[] = {
"none",
"32 kHz",
"44.1 kHz",
"48 kHz",
};
enum uda1341_regs_names {
stat0,
stat1,
data0_0,
data0_1,
data0_2,
data1,
ext0,
ext1,
ext2,
empty,
ext4,
ext5,
ext6,
uda1341_reg_last,
};
enum uda1341_onoff {
OFF=0,
ON,
};
enum uda1341_format {
I2S=0,
LSB16,
LSB18,
LSB20,
MSB,
LSB16MSB,
LSB18MSB,
LSB20MSB,
};
enum uda1341_fs {
F512=0,
F384,
F256,
Funused,
};
enum uda1341_peak {
BEFORE=0,
AFTER,
};
enum uda1341_filter {
FLAT=0,
MIN,
MIN2,
MAX,
};
enum uda1341_mixer {
DOUBLE,
LINE,
MIC,
MIXER,
};
enum uda1341_deemp {
NONE,
D32,
D44,
D48,
};
enum uda1341_config {
CMD_READ_REG = 0,
CMD_RESET,
CMD_FS,
CMD_FORMAT,
CMD_OGAIN,
CMD_IGAIN,
CMD_DAC,
CMD_ADC,
CMD_VOLUME,
CMD_BASS,
CMD_TREBBLE,
CMD_PEAK,
CMD_DEEMP,
CMD_MUTE,
CMD_FILTER,
CMD_CH1,
CMD_CH2,
CMD_MIC,
CMD_MIXER,
CMD_AGC,
CMD_IG,
CMD_AGC_TIME,
CMD_AGC_LEVEL,
#ifdef CONFIG_PM
CMD_SUSPEND,
CMD_RESUME,
#endif
CMD_LAST,
};
enum write_through {
//used in update_bits (write_cfg) to avoid l3_write - just update local copy of regs.
REGS_ONLY=0,
//update local regs and write value to uda1341 - do l3_write
FLUSH,
};
static const char *uda1341_reg_names[] = {
"stat 0 ",
"stat 1 ",
"data 00",
"data 01",
"data 02",
"data 1 ",
"ext 0",
"ext 1",
"ext 2",
"empty",
"ext 4",
"ext 5",
"ext 6",
};
static const int uda1341_enum_items[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, //peak - before/after
4, //deemp - none/32/44.1/48
0,
4, //filter - flat/min/min/max
0, 0, 0,
4, //mixer - differ/line/mic/mixer
0, 0, 0, 0, 0,
};
static const char ** uda1341_enum_names[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
peak_names, //peak - before/after
deemp_names, //deemp - none/32/44.1/48
NULL,
filter_names, //filter - flat/min/min/max
NULL, NULL, NULL,
mixer_names, //mixer - differ/line/mic/mixer
NULL, NULL, NULL, NULL, NULL,
};
typedef int uda1341_cfg[CMD_LAST];
struct s3c24xx_uda1341;
struct uda1341 {
int (*write) (struct s3c24xx_uda1341 *chip, unsigned short reg, unsigned short val);
int (*read) (struct s3c24xx_uda1341 *chip, unsigned short reg);
unsigned char regs[uda1341_reg_last];
int active;
spinlock_t reg_lock;
uda1341_cfg cfg;
#ifdef CONFIG_PM
unsigned char suspend_regs[uda1341_reg_last];
uda1341_cfg suspend_cfg;
#endif
};
#endif
typedef struct audio_stream {
char *id; /* identification string */
int stream_id; /* numeric identification */
dmach_t dma_ch; /* dma channel identification */
unsigned int active:1; /* we are using this stream for transfer now */
int period; /* current transfer period */
int periods; /* current count of periods registerd in the DMA engine */
int tx_spin; /* are we recoding - flag used to do DMA trans. for sync */
unsigned int old_offset;
spinlock_t dma_lock; /* for locking in DMA operations (see dma.c in the kernel) */
struct snd_pcm_substream *stream;
} audio_stream_t;
struct s3c24xx_uda1341 {
struct snd_card *card;
struct snd_pcm *pcm;
struct uda1341 *chip_data;
long samplerate;
struct audio_stream s[2]; /* playback & capture */
};
static unsigned int rates[] = {
8000, 10666, 10985, 14647,
16000, 21970, 22050, 24000,
29400, 32000, 44100, 48000,
};
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static struct clk *iis_clock;
static void __iomem *iis_base;
static struct resource *res = NULL;
static struct s3c2410_dma_client s3c24xxiis_dma_client_out =
{
.name = "I2SSDO",
};
static struct s3c2410_dma_client s3c24xxiis_dma_client_in =
{
.name = "I2SSDI",
};
static void uda1341_l3_address ( u8 data )
{
int i;
unsigned long flags;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -