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

📄 wm8750.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	{12000000, 22050, 544, 0x1b, 0x1},	/* 32k */	{12288000, 32000, 384, 0xc, 0x0},	{18432000, 32000, 576, 0xd, 0x0},	{12000000, 32000, 375, 0xa, 0x1},	/* 44.1k */	{11289600, 44100, 256, 0x10, 0x0},	{16934400, 44100, 384, 0x11, 0x0},	{12000000, 44100, 272, 0x11, 0x1},	/* 48k */	{12288000, 48000, 256, 0x0, 0x0},	{18432000, 48000, 384, 0x1, 0x0},	{12000000, 48000, 250, 0x0, 0x1},	/* 88.2k */	{11289600, 88200, 128, 0x1e, 0x0},	{16934400, 88200, 192, 0x1f, 0x0},	{12000000, 88200, 136, 0x1f, 0x1},	/* 96k */	{12288000, 96000, 128, 0xe, 0x0},	{18432000, 96000, 192, 0xf, 0x0},	{12000000, 96000, 125, 0xe, 0x1},};static inline int get_coeff(int mclk, int rate){	int i;	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)			return i;	}	printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",		mclk, rate);	return -EINVAL;}static int wm8750_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 wm8750_priv *wm8750 = codec->private_data;	switch (freq) {	case 11289600:	case 12000000:	case 12288000:	case 16934400:	case 18432000:		wm8750->sysclk = freq;		return 0;	}	return -EINVAL;}static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,		unsigned int fmt){	struct snd_soc_codec *codec = codec_dai->codec;	u16 iface = 0;	/* set master/slave audio interface */	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {	case SND_SOC_DAIFMT_CBM_CFM:		iface = 0x0040;		break;	case SND_SOC_DAIFMT_CBS_CFS:		break;	default:		return -EINVAL;	}	/* interface format */	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {	case SND_SOC_DAIFMT_I2S:		iface |= 0x0002;		break;	case SND_SOC_DAIFMT_RIGHT_J:		break;	case SND_SOC_DAIFMT_LEFT_J:		iface |= 0x0001;		break;	case SND_SOC_DAIFMT_DSP_A:		iface |= 0x0003;		break;	case SND_SOC_DAIFMT_DSP_B:		iface |= 0x0013;		break;	default:		return -EINVAL;	}	/* clock inversion */	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {	case SND_SOC_DAIFMT_NB_NF:		break;	case SND_SOC_DAIFMT_IB_IF:		iface |= 0x0090;		break;	case SND_SOC_DAIFMT_IB_NF:		iface |= 0x0080;		break;	case SND_SOC_DAIFMT_NB_IF:		iface |= 0x0010;		break;	default:		return -EINVAL;	}	wm8750_write(codec, WM8750_IFACE, iface);	return 0;}static int wm8750_pcm_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 wm8750_priv *wm8750 = codec->private_data;	u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;	u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;	int coeff = get_coeff(wm8750->sysclk, params_rate(params));	/* bit size */	switch (params_format(params)) {	case SNDRV_PCM_FORMAT_S16_LE:		break;	case SNDRV_PCM_FORMAT_S20_3LE:		iface |= 0x0004;		break;	case SNDRV_PCM_FORMAT_S24_LE:		iface |= 0x0008;		break;	case SNDRV_PCM_FORMAT_S32_LE:		iface |= 0x000c;		break;	}	/* set iface & srate */	wm8750_write(codec, WM8750_IFACE, iface);	if (coeff >= 0)		wm8750_write(codec, WM8750_SRATE, srate |			(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);	return 0;}static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute){	struct snd_soc_codec *codec = dai->codec;	u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;	if (mute)		wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);	else		wm8750_write(codec, WM8750_ADCDAC, mute_reg);	return 0;}static int wm8750_dapm_event(struct snd_soc_codec *codec, int event){	u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;	switch (event) {	case SNDRV_CTL_POWER_D0: /* full On */		/* set vmid to 50k and unmute dac */		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);		break;	case SNDRV_CTL_POWER_D1: /* partial On */	case SNDRV_CTL_POWER_D2: /* partial On */		/* set vmid to 5k for quick power up */		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);		break;	case SNDRV_CTL_POWER_D3hot: /* Off, with power */		/* mute dac and set vmid to 500k, enable VREF */		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);		break;	case SNDRV_CTL_POWER_D3cold: /* Off, without power */		wm8750_write(codec, WM8750_PWR1, 0x0001);		break;	}	codec->dapm_state = event;	return 0;}#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\	SNDRV_PCM_FMTBIT_S24_LE)struct snd_soc_codec_dai wm8750_dai = {	.name = "WM8750",	.playback = {		.stream_name = "Playback",		.channels_min = 1,		.channels_max = 2,		.rates = WM8750_RATES,		.formats = WM8750_FORMATS,},	.capture = {		.stream_name = "Capture",		.channels_min = 1,		.channels_max = 2,		.rates = WM8750_RATES,		.formats = WM8750_FORMATS,},	.ops = {		.hw_params = wm8750_pcm_hw_params,	},	.dai_ops = {		.digital_mute = wm8750_mute,		.set_fmt = wm8750_set_dai_fmt,		.set_sysclk = wm8750_set_dai_sysclk,	},};EXPORT_SYMBOL_GPL(wm8750_dai);static void wm8750_work(struct work_struct *work){	struct snd_soc_codec *codec =		container_of(work, struct snd_soc_codec, delayed_work.work);	wm8750_dapm_event(codec, codec->dapm_state);}static int wm8750_suspend(struct platform_device *pdev, pm_message_t state){	struct snd_soc_device *socdev = platform_get_drvdata(pdev);	struct snd_soc_codec *codec = socdev->codec;	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);	return 0;}static int wm8750_resume(struct platform_device *pdev){	struct snd_soc_device *socdev = platform_get_drvdata(pdev);	struct snd_soc_codec *codec = socdev->codec;	int i;	u8 data[2];	u16 *cache = codec->reg_cache;	/* Sync reg_cache with the hardware */	for (i = 0; i < ARRAY_SIZE(wm8750_reg); i++) {		if (i == WM8750_RESET)			continue;		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);		data[1] = cache[i] & 0x00ff;		codec->hw_write(codec->control_data, data, 2);	}	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);	/* charge wm8750 caps */	if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {		wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);		codec->dapm_state = SNDRV_CTL_POWER_D0;		schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));	}	return 0;}/* * initialise the WM8750 driver * register the mixer and dsp interfaces with the kernel */static int wm8750_init(struct snd_soc_device *socdev){	struct snd_soc_codec *codec = socdev->codec;	int reg, ret = 0;	codec->name = "WM8750";	codec->owner = THIS_MODULE;	codec->read = wm8750_read_reg_cache;	codec->write = wm8750_write;	codec->dapm_event = wm8750_dapm_event;	codec->dai = &wm8750_dai;	codec->num_dai = 1;	codec->reg_cache_size = sizeof(wm8750_reg);	codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);	if (codec->reg_cache == NULL)		return -ENOMEM;	wm8750_reset(codec);	/* register pcms */	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);	if (ret < 0) {		printk(KERN_ERR "wm8750: failed to create pcms\n");		goto pcm_err;	}	/* charge output caps */	wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);	codec->dapm_state = SNDRV_CTL_POWER_D3hot;	schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));	/* set the update bits */	reg = wm8750_read_reg_cache(codec, WM8750_LDAC);	wm8750_write(codec, WM8750_LDAC, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_RDAC);	wm8750_write(codec, WM8750_RDAC, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);	wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);	wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);	wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);	wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);	wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);	reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);	wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);	wm8750_add_controls(codec);	wm8750_add_widgets(codec);	ret = snd_soc_register_card(socdev);	if (ret < 0) {		printk(KERN_ERR "wm8750: failed to register card\n");		goto card_err;	}	return ret;card_err:	snd_soc_free_pcms(socdev);	snd_soc_dapm_free(socdev);pcm_err:	kfree(codec->reg_cache);	return ret;}/* If the i2c layer weren't so broken, we could pass this kind of data   around */static struct snd_soc_device *wm8750_socdev;#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)/* * WM8731 2 wire address is determined by GPIO5 * state during powerup. *    low  = 0x1a *    high = 0x1b */static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };/* Magic definition of all other variables and things */I2C_CLIENT_INSMOD;static struct i2c_driver wm8750_i2c_driver;static struct i2c_client client_template;static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind){	struct snd_soc_device *socdev = wm8750_socdev;	struct wm8750_setup_data *setup = socdev->codec_data;	struct snd_soc_codec *codec = socdev->codec;	struct i2c_client *i2c;	int ret;	if (addr != setup->i2c_address)		return -ENODEV;	client_template.adapter = adap;	client_template.addr = addr;	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);	if (i2c == NULL) {		kfree(codec);		return -ENOMEM;	}	i2c_set_clientdata(i2c, codec);	codec->control_data = i2c;	ret = i2c_attach_client(i2c);	if (ret < 0) {		err("failed to attach codec at addr %x\n", addr);		goto err;	}	ret = wm8750_init(socdev);	if (ret < 0) {	err("failed to initialise WM8750\n");		goto err;	}	return ret;err:	kfree(codec);	kfree(i2c);	return ret;}static int wm8750_i2c_detach(struct i2c_client *client){	struct snd_soc_codec *codec = i2c_get_clientdata(client);	i2c_detach_client(client);	kfree(codec->reg_cache);	kfree(client);	return 0;}static int wm8750_i2c_attach(struct i2c_adapter *adap){	return i2c_probe(adap, &addr_data, wm8750_codec_probe);}/* corgi i2c codec control layer */static struct i2c_driver wm8750_i2c_driver = {	.driver = {		.name = "WM8750 I2C Codec",		.owner = THIS_MODULE,	},	.id =             I2C_DRIVERID_WM8750,	.attach_adapter = wm8750_i2c_attach,	.detach_client =  wm8750_i2c_detach,	.command =        NULL,};static struct i2c_client client_template = {	.name =   "WM8750",	.driver = &wm8750_i2c_driver,};#endifstatic int wm8750_probe(struct platform_device *pdev){	struct snd_soc_device *socdev = platform_get_drvdata(pdev);	struct wm8750_setup_data *setup = socdev->codec_data;	struct snd_soc_codec *codec;	struct wm8750_priv *wm8750;	int ret = 0;	info("WM8750 Audio Codec %s", WM8750_VERSION);	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);	if (codec == NULL)		return -ENOMEM;	wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);	if (wm8750 == NULL) {		kfree(codec);		return -ENOMEM;	}	codec->private_data = wm8750;	socdev->codec = codec;	mutex_init(&codec->mutex);	INIT_LIST_HEAD(&codec->dapm_widgets);	INIT_LIST_HEAD(&codec->dapm_paths);	wm8750_socdev = socdev;	INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);	#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)	if (setup->i2c_address) {		normal_i2c[0] = setup->i2c_address;		codec->hw_write = (hw_write_t)i2c_master_send;		ret = i2c_add_driver(&wm8750_i2c_driver);		if (ret != 0)			printk(KERN_ERR "can't add i2c driver");	}#else		/* Add other interfaces here */#endif	return ret;}/* * This function forces any delayed work to be queued and run. */static int run_delayed_work(struct delayed_work *dwork){	int ret;	/* cancel any work waiting to be queued. */	ret = cancel_delayed_work(dwork);	/* if there was any work waiting then we run it now and	 * wait for it's completion */	if (ret) {		schedule_delayed_work(dwork, 0);		flush_scheduled_work();	}	return ret;}/* power down chip */static int wm8750_remove(struct platform_device *pdev){	struct snd_soc_device *socdev = platform_get_drvdata(pdev);	struct snd_soc_codec *codec = socdev->codec;	if (codec->control_data)		wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);	run_delayed_work(&codec->delayed_work);	snd_soc_free_pcms(socdev);	snd_soc_dapm_free(socdev);#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)	i2c_del_driver(&wm8750_i2c_driver);#endif	kfree(codec->private_data);	kfree(codec);	return 0;}struct snd_soc_codec_device soc_codec_dev_wm8750 = {	.probe = 	wm8750_probe,	.remove = 	wm8750_remove,	.suspend = 	wm8750_suspend,	.resume =	wm8750_resume,};EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);MODULE_DESCRIPTION("ASoC WM8750 driver");MODULE_AUTHOR("Liam Girdwood");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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