原理图:
tlv320aic3x音频芯片
功率放大器
移植分为三步:
1.codec 驱动 sound/soc/codecs/tlv320aic3x.c
2.平台驱动 sound/soc/imx/imx-tlv320aic3x.c
3.添加板文件
arch/arm/mach-mx6/board-mx6q_sabresd.c 和 arch/arm/mach-mx6/board-mx6q_sabresd.c
一、codec驱动是linux内核自带的,在sound/soc/codecs/下面有不同的音频芯片,根据开发的音频芯片选择或者修改。
在tlv320aic3x.c文件中有几个重要的结构体需要关注:
- static struct snd_soc_dai_ops aic3x_dai_ops = {
- .hw_params = aic3x_hw_params, //硬件参数设置
- .digital_mute = aic3x_mute, //静音设置
- .set_sysclk = aic3x_set_dai_sysclk, //时钟设置
- .set_fmt = aic3x_set_dai_fmt, //格式设置
- };
- static struct snd_soc_dai_driver aic3x_dai = {
- .name = "tlv320aic3x-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AIC3X_RATES,
- .formats = AIC3X_FORMATS,},
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AIC3X_RATES,
- .formats = AIC3X_FORMATS,},
- .ops = &aic3x_dai_ops,
- .symmetric_rates = 1,
- };
- static struct snd_soc_dai_link imx_tlv320aic3x_dai[] = {
- {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.0-0018", //I2C-0
- .cpu_dai_name = "imx-ssi.1",
- .platform_name = "imx-pcm-audio.1",
- .init =imx_3stack_tlv320aic3x_init,
- .ops = &imx_tlv320aic3x_hifi_ops,
- },
- };
- /* machine i2c codec control layer */
- static struct i2c_driver aic3x_i2c_driver = {
- .driver = {
- .name = "tlv320aic3x-codec",
- .owner = THIS_MODULE,
- },
- .probe = aic3x_i2c_probe,
- .remove = aic3x_i2c_remove,
- .id_table = aic3x_i2c_id,
- };
- static struct snd_soc_dai_link imx_tlv320aic3x_dai[] = {
- {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.0-0018", //I2C-0
- .cpu_dai_name = "imx-ssi.1",
- .platform_name = "imx-pcm-audio.1",
- .init =imx_3stack_tlv320aic3x_init,
- .ops = &imx_tlv320aic3x_hifi_ops,
- },
- };
因为是平台设备驱动,所以就要将平台驱动和平台设备进行关联。
平台驱动在sound/soc/imx/imx-tlv320aic3x.c文件中:
- static struct platform_driver imx_tlv320aic3x_audio_driver = {
- .probe = imx_tlv320aic3x_probe,
- .remove = imx_tlv320aic3x_remove,
- .driver = {
- .name = "imx-tlv320",
- },
- };
- static struct platform_device mx6_audio_tlv320_device = {
- .name = "imx-tlv320",
- };
在板文件中,要进行i2c0的注册所和用到的引脚的配置。
在arch/arm/mach-mx6/board-mx6q_sabresd.c中:
- static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
- {
- I2C_BOARD_INFO("ds1338", 0x68),
- },
- {
- I2C_BOARD_INFO("ov5640_mipi", 0x3c),
- .platform_data = (void *)&mipi_csi2_data,
- },
- {
- I2C_BOARD_INFO("tlv320aic3x", 0x18),
- },
- };
- imx6q_add_imx_i2c(0, &mx6q_qy_imx6s_i2c_data);
- i2c_register_board_info(0, mxc_i2c0_board_info,
- ARRAY_SIZE(mxc_i2c0_board_info));
- static iomux_v3_cfg_t mx6q_qy_imx6s_i2c_pads[] =
- {
- MX6Q_PAD_CSI0_DAT8__I2C1_SDA,//i2c
- MX6Q_PAD_CSI0_DAT9__I2C1_SCL,,
- };
- static iomux_v3_cfg_t mx6q_qy_imx6s_audio_pads[] = {
- /*MX6Q_PAD_CSI0_DAT4__AUDMUX_AUD3_TXC,
- MX6Q_PAD_CSI0_DAT5__AUDMUX_AUD3_TXD,
- MX6Q_PAD_CSI0_DAT6__AUDMUX_AUD3_TXFS,
- MX6Q_PAD_CSI0_DAT7__AUDMUX_AUD3_RXD,*/
- MX6Q_PAD_GPIO_0__CCM_CLKO, //CLK ouput
- MX6Q_PAD_SD2_CMD__GPIO_1_11, //AUDIO SD
- MX6Q_PAD_SD2_CLK__GPIO_1_10, //AUDIO REST
- MX6Q_PAD_SD2_DAT0__AUDMUX_AUD4_RXD,
- MX6Q_PAD_SD2_DAT1__AUDMUX_AUD4_TXFS,
- MX6Q_PAD_SD2_DAT2__AUDMUX_AUD4_TXD,
- MX6Q_PAD_SD2_DAT3__AUDMUX_AUD4_TXC,
- };
在arch/arm/mach-mx6/board-mx6q_sabresd.c写功能函数:
1.定义功放和复位
- //audio
- #define AUDIO_REST IMX_GPIO_NR(1 , 10)
- #define AUDIO_SD IMX_GPIO_NR(1 , 11)
- static void tlv320_gpio_set(void)
- {
- static void __iomem *audio_gpio_base = IO_ADDRESS(0x020e0740);//配置引脚寄存器第13位为 0=keeper
- writel(0x90B0,audio_gpio_base);
- gpio_request(AUDIO_SD, "audio_sd");
- gpio_direction_output(AUDIO_SD, 0); //低电平有效
- msleep(1);
- gpio_set_value(AUDIO_SD, 0);
- gpio_request(AUDIO_REST, "audio_rest");
- gpio_direction_output(AUDIO_REST, 1);
- msleep(1);
- gpio_set_value(AUDIO_REST, 1);
- }
- static int mxc_tlv320_init(void)
- {
- //struct clk *clko;
- //struct clk *new_parent;
- int rate;
- clko = clk_get(NULL, "clko_clk");
- if (IS_ERR(clko)) {
- pr_err("can't get CLKO clock.\n");
- return PTR_ERR(clko);
- }
- /* both audio codec and comera use CLKO clk*/
- rate = clk_round_rate(clko, 24000000);
- clk_set_rate(clko, rate);
- tlv320_data.sysclk = rate;
- clk_enable(clko);
- tlv320_gpio_set();
- return 0;
- }
下面放上板文件和imx-tlv320aic3x.c的代码片段
arch/arm/mach-mx6/board-mx6q_sabresd.c:
- static struct regulator_consumer_supply qy_imx6s_vmmc_consumers[] = {
- REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx.1"),
- REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx.2"),
- REGULATOR_SUPPLY("vmmc", "sdhci-esdhc-imx.3"),
- REGULATOR_SUPPLY("vcc", "spi4.1"),
- REGULATOR_SUPPLY("IOVDD", "0-0018"),
- REGULATOR_SUPPLY("AVDD", "0-0018"),
- REGULATOR_SUPPLY("DRVDD", "0-0018"),
- };
- static struct regulator_init_data qy_imx6s_vmmc_init = {
- .num_consumer_supplies = ARRAY_SIZE(qy_imx6s_vmmc_consumers),
- .consumer_supplies = qy_imx6s_vmmc_consumers,
- };
- static struct fixed_voltage_config qy_imx6s_vmmc_reg_config = {
- .supply_name = "vmmc",
- .microvolts = 3300000,
- .gpio = -1,
- .init_data = &qy_imx6s_vmmc_init,
- };
- static struct platform_device qy_imx6s_vmmc_reg_devices = {
- .name = "reg-fixed-voltage",
- .id = 3,
- .dev = {
- .platform_data = &qy_imx6s_vmmc_reg_config,
- },
- };
- static struct platform_device mx6_audio_tlv320_device = {
- .name = "imx-tlv320",
- };
- static int tlv320_clk_enable(int enable)
- {
- if (enable)
- clk_enable(clko);
- else
- clk_disable(clko);
- return 0;
- }
- static struct mxc_audio_platform_data tlv320_data;
- static void tlv320_gpio_set(void)
- {
- static void __iomem *audio_gpio_base = IO_ADDRESS(0x020e0740);//13位 0=keeper
- writel(0x90B0,audio_gpio_base);
- gpio_request(AUDIO_SD, "audio_sd");
- gpio_direction_output(AUDIO_SD, 0);
- msleep(1);
- gpio_set_value(AUDIO_SD, 0);
- gpio_request(AUDIO_REST, "audio_rest");
- gpio_direction_output(AUDIO_REST, 1);
- msleep(1);
- gpio_set_value(AUDIO_REST, 1);
- }
- static int mxc_tlv320_init(void)
- {
- //struct clk *clko;
- //struct clk *new_parent;
- int rate;
- clko = clk_get(NULL, "clko_clk");
- if (IS_ERR(clko)) {
- pr_err("can't get CLKO clock.\n");
- return PTR_ERR(clko);
- }
- /* both audio codec and comera use CLKO clk*/
- rate = clk_round_rate(clko, 24000000);
- clk_set_rate(clko, rate);
- tlv320_data.sysclk = rate;
- clk_enable(clko);
- tlv320_gpio_set();
- return 0;
- }
- static struct mxc_audio_platform_data tlv320_data = {
- .ssi_num = 1,
- .src_port = 2,
- .ext_port = 4,//control playing stop
- .init = mxc_tlv320_init,
- .clock_enable = tlv320_clk_enable,
- };
- static struct imx_ssi_platform_data mx6_ssi_pdata = {
- .flags = IMX_SSI_DMA | IMX_SSI_SYN,
- };
- static struct regulator_consumer_supply qy_imx6s_tlv320_consumers[] = {
- REGULATOR_SUPPLY("DVDD", "0-0018"),
- };
- static struct regulator_init_data qy_imx6s_tlv320_init = {
- .num_consumer_supplies = ARRAY_SIZE(qy_imx6s_tlv320_consumers),
- .consumer_supplies = qy_imx6s_tlv320_consumers,
- };
- static struct fixed_voltage_config qy_imx6s_tlv320_reg_config = {
- .supply_name = "DVDD",
- .microvolts = 1800000,
- .gpio = -1, //if changed no soundcard exist
- .init_data = &qy_imx6s_tlv320_init,
- };
- static struct platform_device qy_imx6s_tlv320_reg_devices = {
- .name = "reg-fixed-voltage",
- .id = 4,
- .dev = {
- .platform_data = &qy_imx6s_tlv320_reg_config,
- },
- };
- static int __init imx6q_init_audio(void)
- {
- platform_device_register(&qy_imx6s_tlv320_reg_devices);
- mxc_register_device(&mx6_audio_tlv320_device,
- &tlv320_data);
- imx6q_add_imx_ssi(1, &mx6_ssi_pdata);
- mxc_tlv320_init();
- return 0;
- }
- static void __init mx6_qy_imx6s_board_init(void)
- {
- ……………
- ……………
- imx6q_init_audio();
- ……………
- ……………
- }
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/device.h>
- #include <linux/i2c.h>
- #include <linux/fsl_devices.h>
- #include <linux/gpio.h>
- #include <sound/core.h>
- #include <sound/pcm.h>
- #include <sound/soc.h>
- #include <sound/jack.h>
- #include <sound/soc-dapm.h>
- #include <asm/mach-types.h>
- #include <mach/audmux.h>
- #include "../codecs/tlv320aic23.h"
- #include "imx-ssi.h"
- #define CODEC_CLOCK 24000000
- static int qiyang_tlv320_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_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret) {
- pr_err("%s: failed set cpu dai format\n", __func__);
- return ret;
- }
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret) {
- pr_err("%s: failed set codec dai format\n", __func__);
- return ret;
- }
- ret = snd_soc_dai_set_sysclk(codec_dai, 0,
- CODEC_CLOCK, SND_SOC_CLOCK_OUT);
- if (ret) {
- pr_err("%s: failed setting codec sysclk\n", __func__);
- return ret;
- }
- snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
- ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
- SND_SOC_CLOCK_IN);
- if (ret) {
- pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
- return ret;
- }
- return 0;
- }
- static struct snd_soc_ops qiyang_tlv320_snd_ops = {
- .hw_params = qiyang_tlv320_hw_params,
- };
- static struct snd_soc_dai_link qiyang_tlv320_dai = {
- .name = "tlv320aic3x",
- .stream_name = "TLV320AIC3X",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.0-0018",//i2c0
- .platform_name = "imx-pcm-audio.1",
- .cpu_dai_name = "imx-ssi.1",
- .ops = &qiyang_tlv320_snd_ops,
- };
- static struct snd_soc_card qiyang_tlv320 = {
- .name = "tlv320-audio",
- .dai_link = &qiyang_tlv320_dai,
- .num_links = 1,
- };
- static int imx_audmux_config(int slave, int master)
- {
- unsigned int ptcr, pdcr;
- slave = slave - 1;
- master = master - 1;
- // SSI0 mastered by port 4
- ptcr = MXC_AUDMUX_V2_PTCR_SYN |
- MXC_AUDMUX_V2_PTCR_TFSDIR |
- MXC_AUDMUX_V2_PTCR_TFSEL(master) |
- MXC_AUDMUX_V2_PTCR_TCLKDIR |
- MXC_AUDMUX_V2_PTCR_TCSEL(master);
- pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master);
- mxc_audmux_v2_configure_port(slave, ptcr, pdcr);
- ptcr = MXC_AUDMUX_V2_PTCR_SYN;
- pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(slave);
- mxc_audmux_v2_configure_port(master, ptcr, pdcr);
- return 0;
- }
- static int __devinit imx_tlv320_probe(struct platform_device *pdev)
- {
- //printk("*****************888imx_tlv320_probe\n");
- struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
- int ret = 0;
- imx_audmux_config(plat->src_port, plat->ext_port);
- ret = -EINVAL;
- if (plat->init && plat->init())
- return ret;
- return 0;
- }
- static int imx_tlv320_remove(struct platform_device *pdev)
- {
- struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
- if (plat->finit)
- plat->finit();
- return 0;
- }
- static struct platform_driver imx_tlv320_audio_driver = {
- .probe = imx_tlv320_probe,
- .remove = imx_tlv320_remove,
- .driver = {
- .name = "imx-tlv320",
- },
- };
- static struct platform_device *qiyang_tlv320_snd_device;
- static int __init qiyang_tlv320_init(void)
- {
- int ret;
- //printk("********************************************************11111!%d\n",machine_arch_type);
- ret = platform_driver_register(&imx_tlv320_audio_driver);
- if (ret)
- return -ENOMEM;
- qiyang_tlv320_snd_device = platform_device_alloc("soc-audio", 6);
- if (!qiyang_tlv320_snd_device)
- return -ENOMEM;
- platform_set_drvdata(qiyang_tlv320_snd_device, &qiyang_tlv320);
- ret = platform_device_add(qiyang_tlv320_snd_device);
- if (ret) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- platform_device_put(qiyang_tlv320_snd_device);
- }
- //printk("********************************************************2222222!\n");
- return ret;
- }
- static void __exit qiyang_tlv320_exit(void)
- {
- platform_driver_unregister(&imx_tlv320_audio_driver);
- platform_device_unregister(qiyang_tlv320_snd_device);
- }
- module_init(qiyang_tlv320_init);
- module_exit(qiyang_tlv320_exit);
- MODULE_AUTHOR("allen young <yangcenhao@gmail.com>");
- MODULE_DESCRIPTION("FUCK ALSA SoC driver");
- <p>ODULE_LICENSE("GPL");</p><p>调试</p><p>/usr/test/test.wav</p><p>1、录音arecord -f dat test.wav</p><p>2、播放录音aplay test.wav</p>