📄 wm8753.c
字号:
.set_fmt = wm8753_mode1v_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, },},/* DAI HiFi mode 2 - dummy */{ .name = "WM8753 HiFi", .id = 2,},/* DAI Voice mode 2 */{ .name = "WM8753 Voice", .id = 2, .playback = { .stream_name = "Voice Playback", .channels_min = 1, .channels_max = 1, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .ops = { .hw_params = wm8753_pcm_hw_params,}, .dai_ops = { .digital_mute = wm8753_mute, .set_fmt = wm8753_mode2_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, },},/* DAI HiFi mode 3 */{ .name = "WM8753 HiFi", .id = 3, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .ops = { .hw_params = wm8753_i2s_hw_params,}, .dai_ops = { .digital_mute = wm8753_mute, .set_fmt = wm8753_mode3_4_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, },},/* DAI Voice mode 3 - dummy */{ .name = "WM8753 Voice", .id = 3,},/* DAI HiFi mode 4 */{ .name = "WM8753 HiFi", .id = 4, .playback = { .stream_name = "HiFi Playback", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, .formats = WM8753_FORMATS,}, .ops = { .hw_params = wm8753_i2s_hw_params,}, .dai_ops = { .digital_mute = wm8753_mute, .set_fmt = wm8753_mode3_4_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, },},/* DAI Voice mode 4 - dummy */{ .name = "WM8753 Voice", .id = 4,},};struct snd_soc_codec_dai wm8753_dai[2];EXPORT_SYMBOL_GPL(wm8753_dai);static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode){ if (mode < 4) { int playback_active, capture_active, codec_active, pop_wait; void *private_data; playback_active = wm8753_dai[0].playback.active; capture_active = wm8753_dai[0].capture.active; codec_active = wm8753_dai[0].active; private_data = wm8753_dai[0].private_data; pop_wait = wm8753_dai[0].pop_wait; wm8753_dai[0] = wm8753_all_dai[mode << 1]; wm8753_dai[0].playback.active = playback_active; wm8753_dai[0].capture.active = capture_active; wm8753_dai[0].active = codec_active; wm8753_dai[0].private_data = private_data; wm8753_dai[0].pop_wait = pop_wait; playback_active = wm8753_dai[1].playback.active; capture_active = wm8753_dai[1].capture.active; codec_active = wm8753_dai[1].active; private_data = wm8753_dai[1].private_data; pop_wait = wm8753_dai[1].pop_wait; wm8753_dai[1] = wm8753_all_dai[(mode << 1) + 1]; wm8753_dai[1].playback.active = playback_active; wm8753_dai[1].capture.active = capture_active; wm8753_dai[1].active = codec_active; wm8753_dai[1].private_data = private_data; wm8753_dai[1].pop_wait = pop_wait; } wm8753_dai[0].codec = codec; wm8753_dai[1].codec = codec;}static void wm8753_work(struct work_struct *work){ struct snd_soc_codec *codec = container_of(work, struct snd_soc_codec, delayed_work.work); wm8753_dapm_event(codec, codec->dapm_state);}static int wm8753_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; /* we only need to suspend if we are a valid card */ if(!codec->card) return 0; wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold); return 0;}static int wm8753_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; /* we only need to resume if we are a valid card */ if(!codec->card) return 0; /* Sync reg_cache with the hardware */ for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) { if (i + 1 == WM8753_RESET) continue; data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001); data[1] = cache[i] & 0x00ff; codec->hw_write(codec->control_data, data, 2); } wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot); /* charge wm8753 caps */ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D0; schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(caps_charge)); } return 0;}/* * initialise the WM8753 driver * register the mixer and dsp interfaces with the kernel */static int wm8753_init(struct snd_soc_device *socdev){ struct snd_soc_codec *codec = socdev->codec; int reg, ret = 0; codec->name = "WM8753"; codec->owner = THIS_MODULE; codec->read = wm8753_read_reg_cache; codec->write = wm8753_write; codec->dapm_event = wm8753_dapm_event; codec->dai = wm8753_dai; codec->num_dai = 2; codec->reg_cache_size = sizeof(wm8753_reg); codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL); if (codec->reg_cache == NULL) return -ENOMEM; wm8753_set_dai_mode(codec, 0); wm8753_reset(codec); /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { printk(KERN_ERR "wm8753: failed to create pcms\n"); goto pcm_err; } /* charge output caps */ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D3hot; schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(caps_charge)); /* set the update bits */ reg = wm8753_read_reg_cache(codec, WM8753_LDAC); wm8753_write(codec, WM8753_LDAC, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_RDAC); wm8753_write(codec, WM8753_RDAC, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_LADC); wm8753_write(codec, WM8753_LADC, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_RADC); wm8753_write(codec, WM8753_RADC, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V); wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V); wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V); wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V); wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_LINVOL); wm8753_write(codec, WM8753_LINVOL, reg | 0x0100); reg = wm8753_read_reg_cache(codec, WM8753_RINVOL); wm8753_write(codec, WM8753_RINVOL, reg | 0x0100); wm8753_add_controls(codec); wm8753_add_widgets(codec); ret = snd_soc_register_card(socdev); if (ret < 0) { printk(KERN_ERR "wm8753: 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 *wm8753_socdev;#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)/* * WM8753 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 wm8753_i2c_driver;static struct i2c_client client_template;static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind){ struct snd_soc_device *socdev = wm8753_socdev; struct wm8753_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 = wm8753_init(socdev); if (ret < 0) { err("failed to initialise WM8753\n"); goto err; } return ret;err: kfree(codec); kfree(i2c); return ret;}static int wm8753_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 wm8753_i2c_attach(struct i2c_adapter *adap){ return i2c_probe(adap, &addr_data, wm8753_codec_probe);}/* corgi i2c codec control layer */static struct i2c_driver wm8753_i2c_driver = { .driver = { .name = "WM8753 I2C Codec", .owner = THIS_MODULE, }, .id = I2C_DRIVERID_WM8753, .attach_adapter = wm8753_i2c_attach, .detach_client = wm8753_i2c_detach, .command = NULL,};static struct i2c_client client_template = { .name = "WM8753", .driver = &wm8753_i2c_driver,};#endifstatic int wm8753_probe(struct platform_device *pdev){ struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct wm8753_setup_data *setup; struct snd_soc_codec *codec; struct wm8753_priv *wm8753; int ret = 0; info("WM8753 Audio Codec %s", WM8753_VERSION); setup = socdev->codec_data; codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL); if (wm8753 == NULL) { kfree(codec); return -ENOMEM; } codec->private_data = wm8753; socdev->codec = codec; mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); wm8753_socdev = socdev; INIT_DELAYED_WORK(&codec->delayed_work, wm8753_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(&wm8753_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 wm8753_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) wm8753_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(&wm8753_i2c_driver);#endif kfree(codec->private_data); kfree(codec); return 0;}struct snd_soc_codec_device soc_codec_dev_wm8753 = { .probe = wm8753_probe, .remove = wm8753_remove, .suspend = wm8753_suspend, .resume = wm8753_resume,};EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);MODULE_DESCRIPTION("ASoC WM8753 driver");MODULE_AUTHOR("Liam Girdwood");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -