Skip to content
Snippets Groups Projects
Commit f27d6d09 authored by Yuri Mazzuoli's avatar Yuri Mazzuoli
Browse files

[SOUND][TLV320] add seco-tlv320 driver for ssi interface

parent 55276fc3
No related branches found
No related tags found
1 merge request!93[SOUND][TLV320] add seco-tlv320 driver for ssi interface
......@@ -83,6 +83,7 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o
obj-$(CONFIG_SND_SOC_IMX_HDMI_DMA) += imx-hdmi-dma.o hdmi_pcm.o
# i.MX Machine Support
snd-soc-seco-tlv320-objs := seco-tlv320.o
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
......@@ -107,6 +108,7 @@ snd-soc-imx-si476x-objs := imx-si476x.o
snd-soc-imx-hdmi-objs := imx-hdmi.o
snd-soc-imx-cdnhdmi-objs := imx-cdnhdmi.o
obj-$(CONFIG_SND_SOC_SECO_TLV320) += snd-soc-seco-tlv320.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
......
/*
* seco-tlv320.c -- SoC audio for seco_cpuimxXX in I2S mode
*
* Copyright 2017 Michele Cirinei, Seco srl <michele.cirinei@seco.com>
*
* based on sound/soc/fsl/eukrea-tlv320.c which is
* Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "../codecs/tlv320aic32x4.h"
#include "imx-ssi.h"
#include "imx-audmux.h"
#define CODEC_CLOCK 24000000
static int seco_tlv320_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_card *card = rtd->card;
struct device *dev = card->dev;
unsigned int fmt;
int ret = 0;
fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS;
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
if (ret) {
dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0, 2,
params_physical_width(params));
if (ret) {
dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
return ret;
}
static struct snd_soc_ops seco_tlv320_snd_ops = {
.hw_params = seco_tlv320_hw_params,
};
static struct snd_soc_dai_link seco_tlv320_dai = {
.name = "tlv320aic32x4",
.stream_name = "TLV320AIC32X4",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &seco_tlv320_snd_ops,
};
static struct snd_soc_card seco_tlv320 = {
.owner = THIS_MODULE,
.dai_link = &seco_tlv320_dai,
.num_links = 1,
};
static int seco_tlv320_audmux_init(struct device_node *np,
struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
u32 int_ptcr = 0, ext_ptcr = 0;
int int_port, ext_port;
int ret;
ret = of_property_read_u32(np, "mux-int-port", &int_port);
if (ret) {
dev_err(dev, "mux-int-port missing or invalid\n");
return ret;
}
ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
if (ret) {
dev_err(dev, "mux-ext-port missing or invalid\n");
return ret;
}
/*
* The port numbering in the hardware manual starts at 1, while
* the AUDMUX API expects it starts at 0.
*/
int_port--;
ext_port--;
ext_ptcr =
IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
IMX_AUDMUX_V2_PTCR_RFSDIR |
IMX_AUDMUX_V2_PTCR_RCLKDIR |
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
/* Asynchronous mode can not be set along with RCLKDIR */
ret = imx_audmux_v2_configure_port(int_port, 0,
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
if (ret) {
dev_err(dev, "audmux internal port setup failed\n");
return ret;
}
ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
if (ret) {
dev_err(dev, "audmux internal port setup failed\n");
return ret;
}
ret = imx_audmux_v2_configure_port(ext_port, 0,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {
dev_err(dev, "audmux external port setup failed\n");
return ret;
}
ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {
dev_err(dev, "audmux external port setup failed\n");
return ret;
}
return 0;
}
static int seco_tlv320_probe(struct platform_device *pdev)
{
int ret;
struct device_node *cpu_np = NULL, *codec_np = NULL;
struct platform_device *cpu_pdev;
struct i2c_client *codec_dev;
struct snd_soc_dai_link_component *comp;
bool pmicbias_en = false;
seco_tlv320.dev = &pdev->dev;
cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
ret = -EINVAL;
goto err;
}
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
if (!codec_np) {
dev_err(&pdev->dev, "phandle missing or invalid\n");
ret = -EINVAL;
goto err;
}
cpu_pdev = of_find_device_by_node(cpu_np);
if (!cpu_pdev) {
dev_err(&pdev->dev, "failed to find SAI platform device\n");
ret = -EINVAL;
goto err;
}
codec_dev = of_find_i2c_device_by_node(codec_np);
if (!codec_dev || !codec_dev->dev.driver) {
dev_err(&pdev->dev, "failed to find codec platform device\n");
ret = -EINVAL;
goto err;
}
pmicbias_en = of_property_read_bool(pdev->dev.of_node, "enable-mic-bias");
ret=seco_tlv320_audmux_init(pdev->dev.of_node, pdev);
if(ret)
goto err;
comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
if (!comp) {
ret = -ENOMEM;
goto err;
}
seco_tlv320_dai.cpus = &comp[0];
seco_tlv320_dai.codecs = &comp[1];
seco_tlv320_dai.platforms = &comp[2];
seco_tlv320_dai.num_cpus = 1;
seco_tlv320_dai.num_codecs = 1;
seco_tlv320_dai.num_platforms = 1;
seco_tlv320_dai.name = "seco-tlv320";
seco_tlv320_dai.stream_name = "seco-tlv320-analog";
seco_tlv320_dai.codecs->dai_name = "tlv320aic32x4-hifi";
seco_tlv320_dai.cpus->of_node = cpu_np;
seco_tlv320_dai.platforms->of_node = cpu_np;
seco_tlv320_dai.codecs->of_node = codec_np;
ret = snd_soc_of_parse_card_name(&seco_tlv320,"model");
ret = snd_soc_register_card(&seco_tlv320);
if(pmicbias_en){
i2c_smbus_write_byte_data(codec_dev, 0x00, 0x01);
mdelay(20);
i2c_smbus_write_byte_data(codec_dev, 0x33, 0x78);
dev_err(&pdev->dev, "tlv320aic32x4: Mic-bias-enabled\n");
}
err:
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
if (cpu_np)
of_node_put(cpu_np);
if (codec_np)
of_node_put(codec_np);
if (comp)
kfree (comp);
return ret;
}
static int seco_tlv320_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&seco_tlv320);
return 0;
}
static const struct of_device_id imx_tlv320_dt_ids[] = {
{ .compatible = "seco,asoc-tlv320"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_tlv320_dt_ids);
static struct platform_driver seco_tlv320_driver = {
.driver = {
.name = "seco_tlv320",
.of_match_table = imx_tlv320_dt_ids,
},
.probe = seco_tlv320_probe,
.remove = seco_tlv320_remove,
};
module_platform_driver(seco_tlv320_driver);
MODULE_AUTHOR("Michele Cirinei <michele.cirinei@seco.com>");
MODULE_DESCRIPTION("AIC32x4 ALSA SoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:seco_tlv320");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment