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

📄 cs8427.c

📁 Linux ARM9 2440 Sound i2C 驱动程序开发
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  Routines for control of the CS8427 via i2c bus *  IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, or *   (at your option) any later version. * *   This 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 * */#include <sound/driver.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/init.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/cs8427.h>#include <sound/asoundef.h>static void snd_cs8427_reset(snd_i2c_device_t *cs8427);MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic");MODULE_LICENSE("GPL");#define CS8427_ADDR			(0x20>>1) /* fixed address */typedef struct {	snd_pcm_substream_t *substream;	char hw_status[24];		/* hardware status */	char def_status[24];		/* default status */	char pcm_status[24];		/* PCM private status */	char hw_udata[32];	snd_kcontrol_t *pcm_ctl;} cs8427_stream_t;typedef struct {	unsigned char regmap[0x14];	/* map of first 1 + 13 registers */	unsigned int rate;	unsigned int reset_timeout;	cs8427_stream_t playback;	cs8427_stream_t capture;} cs8427_t;static unsigned char swapbits(unsigned char val){	int bit;	unsigned char res = 0;	for (bit = 0; bit < 8; bit++) {		res <<= 1;		res |= val & 1;		val >>= 1;	}	return res;}int snd_cs8427_reg_write(snd_i2c_device_t *device, unsigned char reg, unsigned char val){	int err;	unsigned char buf[2];	buf[0] = reg & 0x7f;	buf[1] = val;	if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) {		snd_printk("unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err);		return err < 0 ? err : -EIO;	}	return 0;}static int snd_cs8427_reg_read(snd_i2c_device_t *device, unsigned char reg){	int err;	unsigned char buf;	if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {		snd_printk("unable to send register 0x%x byte to CS8427\n", reg);		return err < 0 ? err : -EIO;	}	if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) {		snd_printk("unable to read register 0x%x byte from CS8427\n", reg);		return err < 0 ? err : -EIO;	}	return buf;}static int snd_cs8427_select_corudata(snd_i2c_device_t *device, int udata){	cs8427_t *chip = device->private_data;	int err;	udata = udata ? CS8427_BSEL : 0;	if (udata != (chip->regmap[CS8427_REG_CSDATABUF] & udata)) {		chip->regmap[CS8427_REG_CSDATABUF] &= ~CS8427_BSEL;		chip->regmap[CS8427_REG_CSDATABUF] |= udata;		err = snd_cs8427_reg_write(device, CS8427_REG_CSDATABUF, chip->regmap[CS8427_REG_CSDATABUF]);		if (err < 0)			return err;	}	return 0;}static int snd_cs8427_send_corudata(snd_i2c_device_t *device,				    int udata,				    unsigned char *ndata,				    int count){	cs8427_t *chip = device->private_data;	char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status;	char data[32];	int err, idx;	if (!memcmp(hw_data, ndata, count))		return 0;	if ((err = snd_cs8427_select_corudata(device, udata)) < 0)		return err;	memcpy(hw_data, ndata, count);	if (udata) {		memset(data, 0, sizeof(data));		if (memcmp(hw_data, data, count) == 0) {			chip->regmap[CS8427_REG_UDATABUF] &= ~CS8427_UBMMASK;			chip->regmap[CS8427_REG_UDATABUF] |= CS8427_UBMZEROS | CS8427_EFTUI;			if ((err = snd_cs8427_reg_write(device, CS8427_REG_UDATABUF, chip->regmap[CS8427_REG_UDATABUF])) < 0)				return err;			return 0;		}	}	data[0] = CS8427_REG_AUTOINC | CS8427_REG_CORU_DATABUF;	for (idx = 0; idx < count; idx++)		data[idx + 1] = swapbits(ndata[idx]);	if (snd_i2c_sendbytes(device, data, count + 1) != count + 1)		return -EIO;	return 1;}static void snd_cs8427_free(snd_i2c_device_t *device){	kfree(device->private_data);}int snd_cs8427_create(snd_i2c_bus_t *bus,		      unsigned char addr,		      unsigned int reset_timeout,		      snd_i2c_device_t **r_cs8427){	static unsigned char initvals1[] = {	  CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC,	  /* CS8427_REG_CONTROL1: RMCK to OMCK, valid PCM audio, disable mutes, TCBL=output */	  CS8427_SWCLK | CS8427_TCBLDIR,	  /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs, normal stereo operation */	  0x00,	  /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial, Rx=>serial */	  CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER,	  /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base =	     recovered input clock, recovered input clock source is ILRCK changed to AES3INPUT (workaround, see snd_cs8427_reset) */	  CS8427_RXDILRCK,	  /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S, 24-bit, 64*Fsi */	  CS8427_SIDEL | CS8427_SILRPOL,	  /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format = I2S, 24-bit, 64*Fsi */	  CS8427_SODEL | CS8427_SOLRPOL,	};	static unsigned char initvals2[] = {	  CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC,	  /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */	  /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR, */	  0xff, /* set everything */	  /* CS8427_REG_CSDATABUF:	     Registers 32-55 window to CS buffer	     Inhibit D->E transfers from overwriting first 5 bytes of CS data.	     Inhibit D->E transfers (all) of CS data.	     Allow E->F transfer of CS data.	     One byte mode; both A/B channels get same written CB data.	     A channel info is output to chip's EMPH* pin. */	  CS8427_CBMR | CS8427_DETCI,	  /* CS8427_REG_UDATABUF:	     Use internal buffer to transmit User (U) data.	     Chip's U pin is an output.	     Transmit all O's for user data.	     Inhibit D->E transfers.	     Inhibit E->F transfers. */	  CS8427_UD | CS8427_EFTUI | CS8427_DETUI,	};	int err;	cs8427_t *chip;	snd_i2c_device_t *device;	unsigned char buf[24];	if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0)		return err;	chip = device->private_data = kcalloc(1, sizeof(*chip), GFP_KERNEL);	if (chip == NULL) {	      	snd_i2c_device_free(device);		return -ENOMEM;	}	device->private_free = snd_cs8427_free;		snd_i2c_lock(bus);	if ((err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER)) != CS8427_VER8427A) {		snd_i2c_unlock(bus);		snd_printk("unable to find CS8427 signature (expected 0x%x, read 0x%x), initialization is not completed\n", CS8427_VER8427A, err);		return -EFAULT;	}	/* turn off run bit while making changes to configuration */	if ((err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00)) < 0)		goto __fail;	/* send initial values */	memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);	if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) {		err = err < 0 ? err : -EIO;		goto __fail;	}	/* Turn off CS8427 interrupt stuff that is not used in hardware */	memset(buf, 0, 7);	/* from address 9 to 15 */	buf[0] = 9;	/* register */	if ((err = snd_i2c_sendbytes(device, buf, 7)) != 7)		goto __fail;	/* send transfer initialization sequence */	memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);	if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) {		err = err < 0 ? err : -EIO;		goto __fail;	}	/* write default channel status bytes */	buf[0] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 0));	buf[1] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 8));	buf[2] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 16));	buf[3] = ((unsigned char)(SNDRV_PCM_DEFAULT_CON_SPDIF >> 24));	memset(buf + 4, 0, 24 - 4);	if (snd_cs8427_send_corudata(device, 0, buf, 24) < 0)		goto __fail;	memcpy(chip->playback.def_status, buf, 24);	memcpy(chip->playback.pcm_status, buf, 24);	snd_i2c_unlock(bus);	/* turn on run bit and rock'n'roll */	if (reset_timeout < 1)		reset_timeout = 1;	chip->reset_timeout = reset_timeout;	snd_cs8427_reset(device);#if 0	// it's nice for read tests	{	char buf[128];	int xx;	buf[0] = 0x81;	snd_i2c_sendbytes(device, buf, 1);	snd_i2c_readbytes(device, buf, 127);	for (xx = 0; xx < 127; xx++)		printk("reg[0x%x] = 0x%x\n", xx+1, buf[xx]);	}#endif		if (r_cs8427)		*r_cs8427 = device;	return 0;      __fail:      	snd_i2c_unlock(bus);      	snd_i2c_device_free(device);      	return err < 0 ? err : -EIO;}/* * Reset the chip using run bit, also lock PLL using ILRCK and * put back AES3INPUT. This workaround is described in latest * CS8427 datasheet, otherwise TXDSERIAL will not work. */static void snd_cs8427_reset(snd_i2c_device_t *cs8427){	cs8427_t *chip;	unsigned long end_time;	int data;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -