📄 wm8731.c
字号:
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; } /* set iface */ wm8731_write(codec, WM8731_IFACE, iface); return 0;}static int wm8731_dapm_event(struct snd_soc_codec *codec, int event){ u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f; switch (event) { case SNDRV_CTL_POWER_D0: /* full On */ /* vref/mid, osc on, dac unmute */ wm8731_write(codec, WM8731_PWR, reg); break; case SNDRV_CTL_POWER_D1: /* partial On */ case SNDRV_CTL_POWER_D2: /* partial On */ break; case SNDRV_CTL_POWER_D3hot: /* Off, with power */ /* everything off except vref/vmid, */ wm8731_write(codec, WM8731_PWR, reg | 0x0040); break; case SNDRV_CTL_POWER_D3cold: /* Off, without power */ /* everything off, dac mute, inactive */ wm8731_write(codec, WM8731_ACTIVE, 0x0); wm8731_write(codec, WM8731_PWR, 0xffff); break; } codec->dapm_state = event; return 0;}#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000)#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE)struct snd_soc_codec_dai wm8731_dai = { .name = "WM8731", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, .ops = { .prepare = wm8731_pcm_prepare, .hw_params = wm8731_hw_params, .shutdown = wm8731_shutdown, }, .dai_ops = { .digital_mute = wm8731_mute, .set_sysclk = wm8731_set_dai_sysclk, .set_fmt = wm8731_set_dai_fmt, }};EXPORT_SYMBOL_GPL(wm8731_dai);static int wm8731_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; wm8731_write(codec, WM8731_ACTIVE, 0x0); wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); return 0;}static int wm8731_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(wm8731_reg); i++) { data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); data[1] = cache[i] & 0x00ff; codec->hw_write(codec->control_data, data, 2); } wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); wm8731_dapm_event(codec, codec->suspend_dapm_state); return 0;}/* * initialise the WM8731 driver * register the mixer and dsp interfaces with the kernel */static int wm8731_init(struct snd_soc_device *socdev){ struct snd_soc_codec *codec = socdev->codec; int reg, ret = 0; codec->name = "WM8731"; codec->owner = THIS_MODULE; codec->read = wm8731_read_reg_cache; codec->write = wm8731_write; codec->dapm_event = wm8731_dapm_event; codec->dai = &wm8731_dai; codec->num_dai = 1; codec->reg_cache_size = sizeof(wm8731_reg); codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL); if (codec->reg_cache == NULL) return -ENOMEM; wm8731_reset(codec); /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { printk(KERN_ERR "wm8731: failed to create pcms\n"); goto pcm_err; } /* power on device */ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); /* set the update bits */ reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100); reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V); wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100); reg = wm8731_read_reg_cache(codec, WM8731_LINVOL); wm8731_write(codec, WM8731_LINVOL, reg | 0x0100); reg = wm8731_read_reg_cache(codec, WM8731_RINVOL); wm8731_write(codec, WM8731_RINVOL, reg | 0x0100); wm8731_add_controls(codec); wm8731_add_widgets(codec); ret = snd_soc_register_card(socdev); if (ret < 0) { printk(KERN_ERR "wm8731: 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;}static struct snd_soc_device *wm8731_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 wm8731_i2c_driver;static struct i2c_client client_template;/* If the i2c layer weren't so broken, we could pass this kind of data around */static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind){ struct snd_soc_device *socdev = wm8731_socdev; struct wm8731_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 = wm8731_init(socdev); if (ret < 0) { err("failed to initialise WM8731\n"); goto err; } return ret;err: kfree(codec); kfree(i2c); return ret;}static int wm8731_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 wm8731_i2c_attach(struct i2c_adapter *adap){ return i2c_probe(adap, &addr_data, wm8731_codec_probe);}/* corgi i2c codec control layer */static struct i2c_driver wm8731_i2c_driver = { .driver = { .name = "WM8731 I2C Codec", .owner = THIS_MODULE, }, .id = I2C_DRIVERID_WM8731, .attach_adapter = wm8731_i2c_attach, .detach_client = wm8731_i2c_detach, .command = NULL,};static struct i2c_client client_template = { .name = "WM8731", .driver = &wm8731_i2c_driver,};#endifstatic int wm8731_probe(struct platform_device *pdev){ struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct wm8731_setup_data *setup; struct snd_soc_codec *codec; struct wm8731_priv *wm8731; int ret = 0; info("WM8731 Audio Codec %s", WM8731_VERSION); setup = socdev->codec_data; codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL); if (wm8731 == NULL) { kfree(codec); return -ENOMEM; } codec->private_data = wm8731; socdev->codec = codec; mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); wm8731_socdev = socdev;#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(&wm8731_i2c_driver); if (ret != 0) printk(KERN_ERR "can't add i2c driver"); }#else /* Add other interfaces here */#endif return ret;}/* power down chip */static int wm8731_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) wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev);#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) i2c_del_driver(&wm8731_i2c_driver);#endif kfree(codec->private_data); kfree(codec); return 0;}struct snd_soc_codec_device soc_codec_dev_wm8731 = { .probe = wm8731_probe, .remove = wm8731_remove, .suspend = wm8731_suspend, .resume = wm8731_resume,};EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);MODULE_DESCRIPTION("ASoC WM8731 driver");MODULE_AUTHOR("Richard Purdie");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -