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

📄 cs4270.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * CS4270 ALSA SoC (ASoC) codec driver * * Author: Timur Tabi <timur@freescale.com> * * Copyright 2007 Freescale Semiconductor, Inc.  This file is licensed under * the terms of the GNU General Public License version 2.  This program * is licensed "as is" without any warranty of any kind, whether express * or implied. * * This is an ASoC device driver for the Cirrus Logic CS4270 codec. * * Current features/limitations: * * 1) Software mode is supported.  Stand-alone mode is automatically *    selected if I2C is disabled or if a CS4270 is not found on the I2C *    bus.  However, stand-alone mode is only partially implemented because *    there is no mechanism yet for this driver and the machine driver to *    communicate the values of the M0, M1, MCLK1, and MCLK2 pins. * 2) Only I2C is supported, not SPI * 3) Only Master mode is supported, not Slave. * 4) The machine driver's 'startup' function must call *    cs4270_set_dai_sysclk() with the value of MCLK. * 5) Only I2S and left-justified modes are supported * 6) Power management is not supported * 7) The only supported control is volume and hardware mute (if enabled) */#include <linux/module.h>#include <linux/platform_device.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/soc.h>#include <sound/initval.h>#include <linux/i2c.h>#include "cs4270.h"/* If I2C is defined, then we support software mode.  However, if we're   not compiled as module but I2C is, then we can't use I2C calls. */#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))#define USE_I2C#endif/* Private data for the CS4270 */struct cs4270_private {	unsigned int mclk; /* Input frequency of the MCLK pin */	unsigned int mode; /* The mode (I2S or left-justified) */};/* The number of MCLK/LRCK ratios supported by the CS4270 */#define NUM_MCLK_RATIOS		9/* The actual MCLK/LRCK ratios, in increasing numerical order */static unsigned int mclk_ratios[NUM_MCLK_RATIOS] =	{64, 96, 128, 192, 256, 384, 512, 768, 1024};/* * Determine the CS4270 samples rates. * * 'freq' is the input frequency to MCLK.  The other parameters are ignored. * * The value of MCLK is used to determine which sample rates are supported * by the CS4270.  The ratio of MCLK / Fs must be equal to one of nine * support values: 64, 96, 128, 192, 256, 384, 512, 768, and 1024. * * This function calculates the nine ratios and determines which ones match * a standard sample rate.  If there's a match, then it is added to the list * of support sample rates. * * This function must be called by the machine driver's 'startup' function, * otherwise the list of supported sample rates will not be available in * time for ALSA. * * Note that in stand-alone mode, the sample rate is determined by input * pins M0, M1, MDIV1, and MDIV2.  Also in stand-alone mode, divide-by-3 * is not a programmable option.  However, divide-by-3 is not an available * option in stand-alone mode.  This cases two problems: a ratio of 768 is * not available (it requires divide-by-3) and B) ratios 192 and 384 can * only be selected with divide-by-1.5, but there is an errate that make * this selection difficult. * * In addition, there is no mechanism for communicating with the machine * driver what the input settings can be.  This would need to be implemented * for stand-alone mode to work. */static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,				 int clk_id, unsigned int freq, int dir){	struct snd_soc_codec *codec = codec_dai->codec;	struct cs4270_private *cs4270 = codec->private_data;	unsigned int rates = 0;	unsigned int rate_min = -1;	unsigned int rate_max = 0;	unsigned int i;	cs4270->mclk = freq;	for (i = 0; i < NUM_MCLK_RATIOS; i++) {		unsigned int rate = freq / mclk_ratios[i];		rates |= snd_pcm_rate_to_rate_bit(rate);		if (rate < rate_min)			rate_min = rate;		if (rate > rate_max)			rate_max = rate;	}	/* FIXME: soc should support a rate list */	rates &= ~SNDRV_PCM_RATE_KNOT;	if (!rates) {		printk(KERN_ERR "cs4270: could not find a valid sample rate\n");		return -EINVAL;	}	codec_dai->playback.rates = rates;	codec_dai->playback.rate_min = rate_min;	codec_dai->playback.rate_max = rate_max;	codec_dai->capture.rates = rates;	codec_dai->capture.rate_min = rate_min;	codec_dai->capture.rate_max = rate_max;	return 0;}/* * Configure the codec for the selected audio format * * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the * codec accordingly. * * Currently, this function only supports SND_SOC_DAIFMT_I2S and * SND_SOC_DAIFMT_LEFT_J.  The CS4270 codec also supports right-justified * data for playback only, but ASoC currently does not support different * formats for playback vs. record. */static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,			      unsigned int format){	struct snd_soc_codec *codec = codec_dai->codec;	struct cs4270_private *cs4270 = codec->private_data;	int ret = 0;	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {	case SND_SOC_DAIFMT_I2S:	case SND_SOC_DAIFMT_LEFT_J:		cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;		break;	default:		printk(KERN_ERR "cs4270: invalid DAI format\n");		ret = -EINVAL;	}	return ret;}/* * The codec isn't really big-endian or little-endian, since the I2S * interface requires data to be sent serially with the MSbit first. * However, to support BE and LE I2S devices, we specify both here.  That * way, ALSA will always match the bit patterns. */#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \			SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \			SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \			SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \			SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)#ifdef USE_I2C/* CS4270 registers addresses */#define CS4270_CHIPID	0x01	/* Chip ID */#define CS4270_PWRCTL	0x02	/* Power Control */#define CS4270_MODE	0x03	/* Mode Control */#define CS4270_FORMAT	0x04	/* Serial Format, ADC/DAC Control */#define CS4270_TRANS	0x05	/* Transition Control */#define CS4270_MUTE	0x06	/* Mute Control */#define CS4270_VOLA	0x07	/* DAC Channel A Volume Control */#define CS4270_VOLB	0x08	/* DAC Channel B Volume Control */#define CS4270_FIRSTREG	0x01#define CS4270_LASTREG	0x08#define CS4270_NUMREGS	(CS4270_LASTREG - CS4270_FIRSTREG + 1)/* Bit masks for the CS4270 registers */#define CS4270_CHIPID_ID	0xF0#define CS4270_CHIPID_REV	0x0F#define CS4270_PWRCTL_FREEZE	0x80#define CS4270_PWRCTL_PDN_ADC	0x20#define CS4270_PWRCTL_PDN_DAC	0x02#define CS4270_PWRCTL_PDN	0x01#define CS4270_MODE_SPEED_MASK	0x30#define CS4270_MODE_1X		0x00#define CS4270_MODE_2X		0x10#define CS4270_MODE_4X		0x20#define CS4270_MODE_SLAVE	0x30#define CS4270_MODE_DIV_MASK	0x0E#define CS4270_MODE_DIV1	0x00#define CS4270_MODE_DIV15	0x02#define CS4270_MODE_DIV2	0x04#define CS4270_MODE_DIV3	0x06#define CS4270_MODE_DIV4	0x08#define CS4270_MODE_POPGUARD	0x01#define CS4270_FORMAT_FREEZE_A	0x80#define CS4270_FORMAT_FREEZE_B	0x40#define CS4270_FORMAT_LOOPBACK	0x20#define CS4270_FORMAT_DAC_MASK	0x18#define CS4270_FORMAT_DAC_LJ	0x00#define CS4270_FORMAT_DAC_I2S	0x08#define CS4270_FORMAT_DAC_RJ16	0x18#define CS4270_FORMAT_DAC_RJ24	0x10#define CS4270_FORMAT_ADC_MASK	0x01#define CS4270_FORMAT_ADC_LJ	0x00#define CS4270_FORMAT_ADC_I2S	0x01#define CS4270_TRANS_ONE_VOL	0x80#define CS4270_TRANS_SOFT	0x40#define CS4270_TRANS_ZERO	0x20#define CS4270_TRANS_INV_ADC_A	0x08#define CS4270_TRANS_INV_ADC_B	0x10#define CS4270_TRANS_INV_DAC_A	0x02#define CS4270_TRANS_INV_DAC_B	0x04#define CS4270_TRANS_DEEMPH	0x01#define CS4270_MUTE_AUTO	0x20#define CS4270_MUTE_ADC_A	0x08#define CS4270_MUTE_ADC_B	0x10#define CS4270_MUTE_POLARITY	0x04#define CS4270_MUTE_DAC_A	0x01#define CS4270_MUTE_DAC_B	0x02/* * A list of addresses on which this CS4270 could use.  I2C addresses are * 7 bits.  For the CS4270, the upper four bits are always 1001, and the * lower three bits are determined via the AD2, AD1, and AD0 pins * (respectively). */static unsigned short normal_i2c[] = {	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END};I2C_CLIENT_INSMOD;/* * Pre-fill the CS4270 register cache. * * We use the auto-increment feature of the CS4270 to read all registers in * one shot. */static int cs4270_fill_cache(struct snd_soc_codec *codec){	u8 *cache = codec->reg_cache;	struct i2c_client *i2c_client = codec->control_data;	s32 length;	length = i2c_smbus_read_i2c_block_data(i2c_client,		CS4270_FIRSTREG | 0x80, CS4270_NUMREGS, cache);	if (length != CS4270_NUMREGS) {		printk(KERN_ERR "cs4270: I2C read failure, addr=0x%x\n",		       i2c_client->addr);		return -EIO;	}	return 0;}/* * Read from the CS4270 register cache. * * This CS4270 registers are cached to avoid excessive I2C I/O operations. * After the initial read to pre-fill the cache, the CS4270 never updates * the register values, so we won't have a cache coherncy problem. */static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec,	unsigned int reg){	u8 *cache = codec->reg_cache;	if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))		return -EIO;	return cache[reg - CS4270_FIRSTREG];}/* * Write to a CS4270 register via the I2C bus. * * This function writes the given value to the given CS4270 register, and * also updates the register cache. * * Note that we don't use the hw_write function pointer of snd_soc_codec. * That's because it's too clunky: the hw_write_t prototype does not match * i2c_smbus_write_byte_data(), and it's just another layer of overhead. */static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,			    unsigned int value){	u8 *cache = codec->reg_cache;	if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))		return -EIO;	/* Only perform an I2C operation if the new value is different */	if (cache[reg - CS4270_FIRSTREG] != value) {		struct i2c_client *client = codec->control_data;		if (i2c_smbus_write_byte_data(client, reg, value)) {			printk(KERN_ERR "cs4270: I2C write failed\n");			return -EIO;		}		/* We've written to the hardware, so update the cache */		cache[reg - CS4270_FIRSTREG] = value;	}	return 0;}/* * Clock Ratio Selection for Master Mode with I2C enabled * * The data for this chart is taken from Table 5 of the CS4270 reference * manual. * * This table is used to determine how to program the Mode Control register. * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling * rates the CS4270 currently supports. * * Each element in this array corresponds to the ratios in mclk_ratios[]. * These two arrays need to be in sync. * * 'speed_mode' is the corresponding bit pattern to be written to the * MODE bits of the Mode Control Register * * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of * the Mode Control Register. * * In situations where a single ratio is represented by multiple speed * modes, we favor the slowest speed.  E.g, for a ratio of 128, we pick * double-speed instead of quad-speed.  However, the CS4270 errata states * that Divide-By-1.5 can cause failures, so we avoid that mode where * possible. * * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not * work if VD = 3.3V.  If this effects you, select the * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will * never select any sample rates that require divide-by-1.5. */static struct {	u8 speed_mode;	u8 mclk;} cs4270_mode_ratios[NUM_MCLK_RATIOS] = {	{CS4270_MODE_4X, CS4270_MODE_DIV1},	/* 64 */#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA	{CS4270_MODE_4X, CS4270_MODE_DIV15},    /* 96 */#endif	{CS4270_MODE_2X, CS4270_MODE_DIV1},     /* 128 */	{CS4270_MODE_4X, CS4270_MODE_DIV3},     /* 192 */	{CS4270_MODE_1X, CS4270_MODE_DIV1},     /* 256 */	{CS4270_MODE_2X, CS4270_MODE_DIV3},     /* 384 */	{CS4270_MODE_1X, CS4270_MODE_DIV2},     /* 512 */	{CS4270_MODE_1X, CS4270_MODE_DIV3},     /* 768 */	{CS4270_MODE_1X, CS4270_MODE_DIV4}      /* 1024 */};/* * Program the CS4270 with the given hardware parameters. * * The .dai_ops functions are used to provide board-specific data, like * input frequencies, to this driver.  This function takes that information, * combines it with the hardware parameters provided, and programs the * hardware accordingly. */static int cs4270_hw_params(struct snd_pcm_substream *substream,			    struct snd_pcm_hw_params *params){	struct snd_soc_pcm_runtime *rtd = substream->private_data;	struct snd_soc_device *socdev = rtd->socdev;	struct snd_soc_codec *codec = socdev->codec;	struct cs4270_private *cs4270 = codec->private_data;	unsigned int ret = 0;	unsigned int i;	unsigned int rate;	unsigned int ratio;	int reg;	/* Figure out which MCLK/LRCK ratio to use */	rate = params_rate(params);	/* Sampling rate, in Hz */	ratio = cs4270->mclk / rate;	/* MCLK/LRCK ratio */	for (i = 0; i < NUM_MCLK_RATIOS; i++) {		if (mclk_ratios[i] == ratio)			break;	}	if (i == NUM_MCLK_RATIOS) {		/* We did not find a matching ratio */		printk(KERN_ERR "cs4270: could not find matching ratio\n");		return -EINVAL;	}	/* Freeze and power-down the codec */	ret = snd_soc_write(codec, CS4270_PWRCTL, CS4270_PWRCTL_FREEZE |			    CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC |

⌨️ 快捷键说明

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