⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c24xx-uda1341.c

📁 扬创2440平台上的ALSA音频驱动程序( for Linux-2.6.26.5 )。驱动使用说明和ALSA Lib的编译过程都在压缩包内
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 *  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 + -