📄 linux-at91rm9200-i2s.c
字号:
+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
+
+ snd_soc_dapm_sync_endpoints(codec);
+
+ return 0;
+}
+
+unsigned int eti_b1_config_sysclk(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_clock_info *info)
+{
+ if(info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) {
+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12000000);
+ }
+ return 0;
+}
+
+static struct snd_soc_dai_link eti_b1_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731",
+ .cpu_dai = &at91_i2s_dai[1],
+ .codec_dai = &wm8731_dai,
+ .init = eti_b1_wm8731_init,
+ .config_sysclk = eti_b1_config_sysclk,
+};
+
+static struct snd_soc_machine snd_soc_machine_eti_b1 = {
+ .name = "ETI_B1",
+ .dai_link = &eti_b1_dai,
+ .num_links = 1,
+ .ops = &eti_b1_ops,
+};
+
+static struct wm8731_setup_data eti_b1_wm8731_setup = {
+ .i2c_address = 0x1a,
+};
+
+static struct snd_soc_device eti_b1_snd_devdata = {
+ .machine = &snd_soc_machine_eti_b1,
+ .platform = &at91_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8731,
+ .codec_data = &eti_b1_wm8731_setup,
+};
+
+static struct platform_device *eti_b1_snd_device;
+
+static int __init eti_b1_init(void)
+{
+ int ret;
+ u32 ssc_pio_lines;
+ struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
+
+ if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) {
+ DBG("SSC1 memory region is busy\n");
+ return -EBUSY;
+ }
+
+ ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K);
+ if (!ssc->base) {
+ DBG("SSC1 memory ioremap failed\n");
+ ret = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ ssc->pid = AT91RM9200_ID_SSC1;
+
+ eti_b1_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!eti_b1_snd_device) {
+ DBG("platform device allocation failed\n");
+ ret = -ENOMEM;
+ goto fail_io_unmap;
+ }
+
+ platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata);
+ eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev;
+
+ ret = platform_device_add(eti_b1_snd_device);
+ if (ret) {
+ DBG("platform device add failed\n");
+ platform_device_put(eti_b1_snd_device);
+ goto fail_io_unmap;
+ }
+
+ ssc_pio_lines = AT91_PIO_TF1 | AT91_PIO_TK1 | AT91_PIO_TD1
+ | AT91_PIO_RD1 /* | AT91_PIO_RK1 | AT91_PIO_RF1 */;
+
+ /* Reset all PIO registers and assign lines to peripheral A */
+ at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_ODR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_IFDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_CODR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_IDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_MDDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_PUDR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_ASR, ssc_pio_lines);
+ at91_sys_write(AT91_PIOB + PIO_OWDR, ssc_pio_lines);
+
+ /*
+ * Set PCK1 parent to PLLB and its rate to 12 Mhz.
+ */
+ pllb_clk = clk_get(NULL, "pllb");
+ pck1_clk = clk_get(NULL, "pck1");
+
+ clk_set_parent(pck1_clk, pllb_clk);
+ clk_set_rate(pck1_clk, 12000000);
+
+ DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk));
+
+ /* assign the GPIO pin to PCK1 */
+ at91_set_B_periph(AT91_PIN_PA24, 0);
+
+ return ret;
+
+fail_io_unmap:
+ iounmap(ssc->base);
+fail_release_mem:
+ release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
+ return ret;
+}
+
+static void __exit eti_b1_exit(void)
+{
+ struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
+
+ clk_put(pck1_clk);
+ clk_put(pllb_clk);
+
+ platform_device_unregister(eti_b1_snd_device);
+
+ iounmap(ssc->base);
+ release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
+}
+
+module_init(eti_b1_init);
+module_exit(eti_b1_exit);
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
new file mode 100644
index 0000000..8151b45
--- /dev/null
+++ b/sound/soc/codecs/wm8731.c
@@ -0,0 +1,886 @@
+/*
+ * wm8731.c -- WM8731 ALSA SoC Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8731.h"
+
+#define AUDIO_NAME "wm8731"
+#define WM8731_VERSION "0.12"
+
+/*
+ * Debug
+ */
+
+#define WM8731_DEBUG 0
+
+#ifdef WM8731_DEBUG
+#define dbg(format, arg...) \
+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) \
+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) \
+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) \
+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
+
+struct snd_soc_codec_device soc_codec_dev_wm8731;
+
+/*
+ * wm8731 register cache
+ * We can't read the WM8731 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
+ 0x0000, 0x0000
+};
+
+#define WM8731_DAIFMT \
+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
+ SND_SOC_DAIFMT_IB_IF)
+
+#define WM8731_DIR \
+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#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_HIFI_BITS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_mode wm8731_modes[] = {
+ /* codec frame and clock master modes */
+ /* 8k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_8000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 1536,
+ .bfs = 64,
+ },
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_8000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 2304,
+ .bfs = 64,
+ },
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_8000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 1408,
+ .bfs = 64,
+ },
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_8000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 2112,
+ .bfs = 64,
+ },
+
+ /* 32k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_32000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 384,
+ .bfs = 64,
+ },
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_32000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 576,
+ .bfs = 64,
+ },
+
+ /* 44.1k & 48k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 256,
+ .bfs = 64,
+ },
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 384,
+ .bfs = 64,
+ },
+
+ /* 88.2 & 96k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 128,
+ .bfs = 64,
+ },
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_RATE,
+ .fs = 192,
+ .bfs = 64,
+ },
+
+ /* USB codec frame and clock master modes */
+ /* 8k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_8000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_DIV,
+ .fs = 1500,
+ .bfs = SND_SOC_FSBD(1),
+ },
+
+ /* 44.1k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_44100,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_DIV,
+ .fs = 272,
+ .bfs = SND_SOC_FSBD(1),
+ },
+
+ /* 48k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_48000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_DIV,
+ .fs = 250,
+ .bfs = SND_SOC_FSBD(1),
+ },
+
+ /* 88.2k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_88200,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_DIV,
+ .fs = 136,
+ .bfs = SND_SOC_FSBD(1),
+ },
+
+ /* 96k */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = SNDRV_PCM_RATE_96000,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_DIV,
+ .fs = 125,
+ .bfs = SND_SOC_FSBD(1),
+ },
+
+ /* codec frame and clock slave modes */
+ {
+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
+ .pcmfmt = WM8731_HIFI_BITS,
+ .pcmrate = WM8731_RATES,
+ .pcmdir = WM8731_DIR,
+ .flags = SND_SOC_DAI_BFS_DIV,
+ .fs = SND_SOC_FS_ALL,
+ .bfs = SND_SOC_FSB_ALL,
+ },
+};
+
+/*
+ * read wm8731 register cache
+ */
+static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg == WM8731_RESET)
+ return 0;
+ if (reg >= WM8731_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write wm8731 register cache
+ */
+static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= WM8731_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8731 register space
+ */
+static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8731 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8731_write_reg_cache (codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
+
+static const char *wm8731_input_select[] = {"Line In", "Mic"};
+static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum wm8731_enum[] = {
+ SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select),
+ SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
+};
+
+static const struct snd_kcontrol_new wm8731_snd_controls[] = {
+
+SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+ 0, 127, 0),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
+ 7, 1, 0),
+
+SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
+SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
+
+SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
+};
+
+/* add non dapm controls */
+static int wm8731_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
+ if ((err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -