Merge branch 'topic/fsl' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-fsl-sai

This commit is contained in:
Mark Brown 2014-09-01 16:36:34 +01:00
commit 025b78b809
24 changed files with 2339 additions and 34 deletions

View File

@ -0,0 +1,47 @@
Device-Tree binding for regmap
The endianness mode of CPU & Device scenarios:
Index Device Endianness properties
---------------------------------------------------
1 BE 'big-endian'
2 LE 'little-endian'
For one device driver, which will run in different scenarios above
on different SoCs using the devicetree, we need one way to simplify
this.
Required properties:
- {big,little}-endian: these are boolean properties, if absent
meaning that the CPU and the Device are in the same endianness mode,
these properties are for register values and all the buffers only.
Examples:
Scenario 1 : CPU in LE mode & device in LE mode.
dev: dev@40031000 {
compatible = "name";
reg = <0x40031000 0x1000>;
...
};
Scenario 2 : CPU in LE mode & device in BE mode.
dev: dev@40031000 {
compatible = "name";
reg = <0x40031000 0x1000>;
...
big-endian;
};
Scenario 3 : CPU in BE mode & device in BE mode.
dev: dev@40031000 {
compatible = "name";
reg = <0x40031000 0x1000>;
...
};
Scenario 4 : CPU in BE mode & device in LE mode.
dev: dev@40031000 {
compatible = "name";
reg = <0x40031000 0x1000>;
...
little-endian;
};

View File

@ -0,0 +1,38 @@
Everest ES8328 audio CODEC
This device supports both I2C and SPI.
Required properties:
- compatible : "everest,es8328"
- DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V
- AVDD-supply : Regulator providing analog supply voltage 3.3V
- PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V
- IPVDD-supply : Regulator providing analog output voltage 3.3V
- clocks : A 22.5792 or 11.2896 MHz clock
- reg : the I2C address of the device for I2C, the chip select number for SPI
Pins on the device (for linking into audio routes):
* LOUT1
* LOUT2
* ROUT1
* ROUT2
* LINPUT1
* RINPUT1
* LINPUT2
* RINPUT2
* Mic Bias
Example:
codec: es8328@11 {
compatible = "everest,es8328";
DVDD-supply = <&reg_3p3v>;
AVDD-supply = <&reg_3p3v>;
PVDD-supply = <&reg_3p3v>;
HPVDD-supply = <&reg_3p3v>;
clocks = <&clks 169>;
reg = <0x11>;
};

View File

@ -0,0 +1,82 @@
Freescale Generic ASoC Sound Card with ASRC support
The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
SoCs connecting with external CODECs.
The idea of this generic sound card is a bit like ASoC Simple Card. However,
for Freescale SoCs (especially those released in recent years), most of them
have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
this is a specific feature that might be painstakingly controlled and merged
into the Simple Card.
So having this generic sound card allows all Freescale SoC users to benefit
from the simplification of a new card support and the capability of the wide
sample rates support through ASRC.
Note: The card is initially designed for those sound cards who use I2S and
PCM DAI formats. However, it'll be also possible to support those non
I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
as the driver has been properly upgraded.
The compatible list for this generic sound card currently:
"fsl,imx-audio-cs42888"
"fsl,imx-audio-wm8962"
(compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
"fsl,imx-audio-sgtl5000"
(compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
Required properties:
- compatible : Contains one of entries in the compatible list.
- model : The user-visible name of this sound complex
- audio-cpu : The phandle of an CPU DAI controller
- audio-codec : The phandle of an audio codec
- audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the
connection's sink, the second being the connection's
source. There're a few pre-designed board connectors:
* Line Out Jack
* Line In Jack
* Headphone Jack
* Mic Jack
* Ext Spk
* AMIC (stands for Analog Microphone Jack)
* DMIC (stands for Digital Microphone Jack)
Note: The "Mic Jack" and "AMIC" are redundant while
coexsiting in order to support the old bindings
of wm8962 and sgtl5000.
Optional properties:
- audio-asrc : The phandle of ASRC. It can be absent if there's no
need to add ASRC support via DPCM.
Example:
sound-cs42888 {
compatible = "fsl,imx-audio-cs42888";
model = "cs42888-audio";
audio-cpu = <&esai>;
audio-asrc = <&asrc>;
audio-codec = <&cs42888>;
audio-routing =
"Line Out Jack", "AOUT1L",
"Line Out Jack", "AOUT1R",
"Line Out Jack", "AOUT2L",
"Line Out Jack", "AOUT2R",
"Line Out Jack", "AOUT3L",
"Line Out Jack", "AOUT3R",
"Line Out Jack", "AOUT4L",
"Line Out Jack", "AOUT4R",
"AIN1L", "Line In Jack",
"AIN1R", "Line In Jack",
"AIN2L", "Line In Jack",
"AIN2R", "Line In Jack";
};

View File

@ -18,9 +18,8 @@ Required properties:
- pinctrl-names: Must contain a "default" entry.
- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
See ../pinctrl/pinctrl-bindings.txt for details of the property values.
- big-endian-regs: If this property is absent, the little endian mode will
be in use as default, or the big endian mode will be in use for all the
device registers.
- big-endian: Boolean property, required if all the FTM_PWM registers
are big-endian rather than little-endian.
- big-endian-data: If this property is absent, the little endian mode will
be in use as default, or the big endian mode will be in use for all the
fifo data.
@ -53,6 +52,6 @@ sai2: sai@40031000 {
dma-names = "tx", "rx";
dmas = <&edma0 0 VF610_EDMA_MUXID0_SAI2_TX>,
<&edma0 0 VF610_EDMA_MUXID0_SAI2_RX>;
big-endian-regs;
big-endian;
big-endian-data;
};

View File

@ -0,0 +1,60 @@
Freescale i.MX audio complex with ES8328 codec
Required properties:
- compatible : "fsl,imx-audio-es8328"
- model : The user-visible name of this sound complex
- ssi-controller : The phandle of the i.MX SSI controller
- jack-gpio : Optional GPIO for headphone jack
- audio-amp-supply : Power regulator for speaker amps
- audio-codec : The phandle of the ES8328 audio codec
- audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the
connection's sink, the second being the connection's
source. Valid names could be power supplies, ES8328
pins, and the jacks on the board:
Power supplies:
* audio-amp
ES8328 pins:
* LOUT1
* LOUT2
* ROUT1
* ROUT2
* LINPUT1
* LINPUT2
* RINPUT1
* RINPUT2
* Mic PGA
Board connectors:
* Headphone
* Speaker
* Mic Jack
- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
- mux-ext-port : The external port of the i.MX audio muxer (AUDMIX)
Note: The AUDMUX port numbering should start at 1, which is consistent with
hardware manual.
Example:
sound {
compatible = "fsl,imx-audio-es8328";
model = "imx-audio-es8328";
ssi-controller = <&ssi1>;
audio-codec = <&codec>;
jack-gpio = <&gpio5 15 0>;
audio-amp-supply = <&reg_audio_amp>;
audio-routing =
"Speaker", "LOUT2",
"Speaker", "ROUT2",
"Speaker", "audio-amp",
"Headphone", "ROUT1",
"Headphone", "LOUT1",
"LINPUT1", "Mic Jack",
"RINPUT1", "Mic Jack",
"Mic Jack", "Mic Bias";
mux-int-port = <1>;
mux-ext-port = <3>;
};

View File

@ -48,6 +48,7 @@ epfl Ecole Polytechnique Fédérale de Lausanne
epson Seiko Epson Corp.
est ESTeem Wireless Modems
eukrea Eukréa Electromatique
everest Everest Semiconductor Co. Ltd.
excito Excito
fsl Freescale Semiconductor
GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc.

View File

@ -168,6 +168,8 @@ static struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,

View File

@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = {
.async_alloc = regmap_spi_async_alloc,
.read = regmap_spi_read,
.read_flag_mask = 0x80,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
/**

View File

@ -15,6 +15,7 @@
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
@ -448,6 +449,66 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
}
EXPORT_SYMBOL_GPL(regmap_attach_dev);
static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
const struct regmap_config *config)
{
enum regmap_endian endian;
/* Retrieve the endianness specification from the regmap config */
endian = config->reg_format_endian;
/* If the regmap config specified a non-default value, use that */
if (endian != REGMAP_ENDIAN_DEFAULT)
return endian;
/* Retrieve the endianness specification from the bus config */
if (bus && bus->reg_format_endian_default)
endian = bus->reg_format_endian_default;
/* If the bus specified a non-default value, use that */
if (endian != REGMAP_ENDIAN_DEFAULT)
return endian;
/* Use this if no other value was found */
return REGMAP_ENDIAN_BIG;
}
static enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config)
{
struct device_node *np = dev->of_node;
enum regmap_endian endian;
/* Retrieve the endianness specification from the regmap config */
endian = config->val_format_endian;
/* If the regmap config specified a non-default value, use that */
if (endian != REGMAP_ENDIAN_DEFAULT)
return endian;
/* Parse the device's DT node for an endianness specification */
if (of_property_read_bool(np, "big-endian"))
endian = REGMAP_ENDIAN_BIG;
else if (of_property_read_bool(np, "little-endian"))
endian = REGMAP_ENDIAN_LITTLE;
/* If the endianness was specified in DT, use that */
if (endian != REGMAP_ENDIAN_DEFAULT)
return endian;
/* Retrieve the endianness specification from the bus config */
if (bus && bus->val_format_endian_default)
endian = bus->val_format_endian_default;
/* If the bus specified a non-default value, use that */
if (endian != REGMAP_ENDIAN_DEFAULT)
return endian;
/* Use this if no other value was found */
return REGMAP_ENDIAN_BIG;
}
/**
* regmap_init(): Initialise register map
*
@ -551,17 +612,8 @@ struct regmap *regmap_init(struct device *dev,
map->reg_read = _regmap_bus_read;
}
reg_endian = config->reg_format_endian;
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
reg_endian = bus->reg_format_endian_default;
if (reg_endian == REGMAP_ENDIAN_DEFAULT)
reg_endian = REGMAP_ENDIAN_BIG;
val_endian = config->val_format_endian;
if (val_endian == REGMAP_ENDIAN_DEFAULT)
val_endian = bus->val_format_endian_default;
if (val_endian == REGMAP_ENDIAN_DEFAULT)
val_endian = REGMAP_ENDIAN_BIG;
reg_endian = regmap_get_reg_endian(bus, config);
val_endian = regmap_get_val_endian(dev, bus, config);
switch (config->reg_bits + map->reg_shift) {
case 2:

View File

@ -57,6 +57,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
select SND_SOC_BT_SCO
select SND_SOC_ES8328_SPI if SPI_MASTER
select SND_SOC_ES8328_I2C if I2C
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@ -405,6 +407,17 @@ config SND_SOC_DMIC
config SND_SOC_HDMI_CODEC
tristate "HDMI stub CODEC"
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
config SND_SOC_ES8328_I2C
tristate
select SND_SOC_ES8328
config SND_SOC_ES8328_SPI
tristate
select SND_SOC_ES8328
config SND_SOC_ISABELLE
tristate

View File

@ -49,6 +49,9 @@ snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-dmic-objs := dmic.o
snd-soc-es8328-objs := es8328.o
snd-soc-es8328-i2c-objs := es8328-i2c.o
snd-soc-es8328-spi-objs := es8328-spi.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-l3-objs := l3.o
@ -220,6 +223,9 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o

View File

@ -0,0 +1,60 @@
/*
* es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
*
* 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/i2c.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
{ "everest,es8328", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
static const struct of_device_id es8328_of_match[] = {
{ .compatible = "everest,es8328", },
{ }
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
static int es8328_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
return es8328_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
}
static int es8328_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static struct i2c_driver es8328_i2c_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_i2c_probe,
.remove = es8328_i2c_remove,
.id_table = es8328_id,
};
module_i2c_driver(es8328_i2c_driver);
MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver");
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,49 @@
/*
* es8328.c -- ES8328 ALSA SoC SPI Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
*
* 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/regmap.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "es8328.h"
static const struct of_device_id es8328_of_match[] = {
{ .compatible = "everest,es8328", },
{ }
};
MODULE_DEVICE_TABLE(of, es8328_of_match);
static int es8328_spi_probe(struct spi_device *spi)
{
return es8328_probe(&spi->dev,
devm_regmap_init_spi(spi, &es8328_regmap_config));
}
static int es8328_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
return 0;
}
static struct spi_driver es8328_spi_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_spi_probe,
.remove = es8328_spi_remove,
};
module_spi_driver(es8328_spi_driver);
MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver");
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_LICENSE("GPL");

756
sound/soc/codecs/es8328.c Normal file
View File

@ -0,0 +1,756 @@
/*
* es8328.c -- ES8328 ALSA SoC Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
*
* 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/clk.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "es8328.h"
#define ES8328_SYSCLK_RATE_1X 11289600
#define ES8328_SYSCLK_RATE_2X 22579200
/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
static struct {
int rate;
u8 ratio;
} mclk_ratios[] = {
{ 8000, 9 },
{11025, 7 },
{22050, 4 },
{44100, 2 },
};
/* regulator supplies for sgtl5000, VDDD is an optional external supply */
enum sgtl5000_regulator_supplies {
DVDD,
AVDD,
PVDD,
HPVDD,
ES8328_SUPPLY_NUM
};
/* vddd is optional supply */
static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"DVDD",
"AVDD",
"PVDD",
"HPVDD",
};
#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_11025)
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
struct es8328_priv {
struct regmap *regmap;
struct clk *clk;
int playback_fs;
bool deemph;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
};
/*
* ES8328 Controls
*/
static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert",
"L + R Invert"};
static SOC_ENUM_SINGLE_DECL(adcpol,
ES8328_ADCCONTROL6, 6, adcpol_txt);
static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0);
static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
static int es8328_set_deemph(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int val, i, best;
/*
* If we're using deemphasis select the nearest available sample
* rate.
*/
if (es8328->deemph) {
best = 1;
for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
if (abs(deemph_settings[i] - es8328->playback_fs) <
abs(deemph_settings[best] - es8328->playback_fs))
best = i;
}
val = best << 1;
} else {
val = 0;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
}
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
ucontrol->value.enumerated.item[0] = es8328->deemph;
return 0;
}
static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int deemph = ucontrol->value.enumerated.item[0];
int ret;
if (deemph > 1)
return -EINVAL;
ret = es8328_set_deemph(codec);
if (ret < 0)
return ret;
es8328->deemph = deemph;
return 0;
}
static const struct snd_kcontrol_new es8328_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Digital Volume",
ES8328_ADCCONTROL8, ES8328_ADCCONTROL9,
0, 0xc0, 1, dac_adc_tlv),
SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0),
SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
es8328_get_deemph, es8328_put_deemph),
SOC_ENUM("Capture Polarity", adcpol),
SOC_SINGLE_TLV("Left Mixer Left Bypass Volume",
ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Left Mixer Right Bypass Volume",
ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Right Mixer Left Bypass Volume",
ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv),
SOC_SINGLE_TLV("Right Mixer Right Bypass Volume",
ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv),
SOC_DOUBLE_R_TLV("PCM Volume",
ES8328_LDACVOL, ES8328_RDACVOL,
0, ES8328_DACVOL_MAX, 1, dac_adc_tlv),
SOC_DOUBLE_R_TLV("Output 1 Playback Volume",
ES8328_LOUT1VOL, ES8328_ROUT1VOL,
0, ES8328_OUT1VOL_MAX, 0, play_tlv),
SOC_DOUBLE_R_TLV("Output 2 Playback Volume",
ES8328_LOUT2VOL, ES8328_ROUT2VOL,
0, ES8328_OUT2VOL_MAX, 0, play_tlv),
SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1,
4, 0, 8, 0, mic_tlv),
};
/*
* DAPM Controls
*/
static const char * const es8328_line_texts[] = {
"Line 1", "Line 2", "PGA", "Differential"};
static const struct soc_enum es8328_lline_enum =
SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3,
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_left_line_controls =
SOC_DAPM_ENUM("Route", es8328_lline_enum);
static const struct soc_enum es8328_rline_enum =
SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0,
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_right_line_controls =
SOC_DAPM_ENUM("Route", es8328_lline_enum);
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
};
static const char * const es8328_pga_sel[] = {
"Line 1", "Line 2", "Line 3", "Differential"};
/* Left PGA Mux */
static const struct soc_enum es8328_lpga_enum =
SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6,
ARRAY_SIZE(es8328_pga_sel),
es8328_pga_sel);
static const struct snd_kcontrol_new es8328_left_pga_controls =
SOC_DAPM_ENUM("Route", es8328_lpga_enum);
/* Right PGA Mux */
static const struct soc_enum es8328_rpga_enum =
SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4,
ARRAY_SIZE(es8328_pga_sel),
es8328_pga_sel);
static const struct snd_kcontrol_new es8328_right_pga_controls =
SOC_DAPM_ENUM("Route", es8328_rpga_enum);
/* Differential Mux */
static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"};
static SOC_ENUM_SINGLE_DECL(diffmux,
ES8328_ADCCONTROL3, 7, es8328_diff_sel);
static const struct snd_kcontrol_new es8328_diffmux_controls =
SOC_DAPM_ENUM("Route", diffmux);
/* Mono ADC Mux */
static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)",
"Mono (Right)", "Digital Mono"};
static SOC_ENUM_SINGLE_DECL(monomux,
ES8328_ADCCONTROL3, 3, es8328_mono_mux);
static const struct snd_kcontrol_new es8328_monomux_controls =
SOC_DAPM_ENUM("Route", monomux);
static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
&es8328_diffmux_controls),
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
&es8328_monomux_controls),
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
&es8328_monomux_controls),
SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER,
ES8328_ADCPOWER_AINL_OFF, 1,
&es8328_left_pga_controls),
SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER,
ES8328_ADCPOWER_AINR_OFF, 1,
&es8328_right_pga_controls),
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
&es8328_left_line_controls),
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
&es8328_right_line_controls),
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER,
ES8328_ADCPOWER_ADCR_OFF, 1),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER,
ES8328_ADCPOWER_ADCL_OFF, 1),
SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER,
ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER,
ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER,
ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER,
ES8328_DACPOWER_RDAC_OFF, 1),
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER,
ES8328_DACPOWER_LDAC_OFF, 1),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&es8328_left_mixer_controls[0],
ARRAY_SIZE(es8328_left_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
&es8328_right_mixer_controls[0],
ARRAY_SIZE(es8328_right_mixer_controls)),
SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER,
ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER,
ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER,
ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER,
ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
SND_SOC_DAPM_OUTPUT("LOUT2"),
SND_SOC_DAPM_OUTPUT("ROUT2"),
SND_SOC_DAPM_INPUT("LINPUT1"),
SND_SOC_DAPM_INPUT("LINPUT2"),
SND_SOC_DAPM_INPUT("RINPUT1"),
SND_SOC_DAPM_INPUT("RINPUT2"),
};
static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "Left Line Mux", "Line 1", "LINPUT1" },
{ "Left Line Mux", "Line 2", "LINPUT2" },
{ "Left Line Mux", "PGA", "Left PGA Mux" },
{ "Left Line Mux", "Differential", "Differential Mux" },
{ "Right Line Mux", "Line 1", "RINPUT1" },
{ "Right Line Mux", "Line 2", "RINPUT2" },
{ "Right Line Mux", "PGA", "Right PGA Mux" },
{ "Right Line Mux", "Differential", "Differential Mux" },
{ "Left PGA Mux", "Line 1", "LINPUT1" },
{ "Left PGA Mux", "Line 2", "LINPUT2" },
{ "Left PGA Mux", "Differential", "Differential Mux" },
{ "Right PGA Mux", "Line 1", "RINPUT1" },
{ "Right PGA Mux", "Line 2", "RINPUT2" },
{ "Right PGA Mux", "Differential", "Differential Mux" },
{ "Differential Mux", "Line 1", "LINPUT1" },
{ "Differential Mux", "Line 1", "RINPUT1" },
{ "Differential Mux", "Line 2", "LINPUT2" },
{ "Differential Mux", "Line 2", "RINPUT2" },
{ "Left ADC Mux", "Stereo", "Left PGA Mux" },
{ "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
{ "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
{ "Right ADC Mux", "Stereo", "Right PGA Mux" },
{ "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
{ "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
{ "Left ADC", NULL, "Left ADC Mux" },
{ "Right ADC", NULL, "Right ADC Mux" },
{ "ADC DIG", NULL, "ADC STM" },
{ "ADC DIG", NULL, "ADC Vref" },
{ "ADC DIG", NULL, "ADC DLL" },
{ "Left ADC", NULL, "ADC DIG" },
{ "Right ADC", NULL, "ADC DIG" },
{ "Mic Bias", NULL, "Mic Bias Gen" },
{ "Left Line Mux", "Line 1", "LINPUT1" },
{ "Left Line Mux", "Line 2", "LINPUT2" },
{ "Left Line Mux", "PGA", "Left PGA Mux" },
{ "Left Line Mux", "Differential", "Differential Mux" },
{ "Right Line Mux", "Line 1", "RINPUT1" },
{ "Right Line Mux", "Line 2", "RINPUT2" },
{ "Right Line Mux", "PGA", "Right PGA Mux" },
{ "Right Line Mux", "Differential", "Differential Mux" },
{ "Left Out 1", NULL, "Left DAC" },
{ "Right Out 1", NULL, "Right DAC" },
{ "Left Out 2", NULL, "Left DAC" },
{ "Right Out 2", NULL, "Right DAC" },
{ "Left Mixer", "Playback Switch", "Left DAC" },
{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Left Mixer", "Right Playback Switch", "Right DAC" },
{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "Right Mixer", "Left Playback Switch", "Left DAC" },
{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
{ "Right Mixer", "Playback Switch", "Right DAC" },
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "DAC DIG", NULL, "DAC STM" },
{ "DAC DIG", NULL, "DAC Vref" },
{ "DAC DIG", NULL, "DAC DLL" },
{ "Left DAC", NULL, "DAC DIG" },
{ "Right DAC", NULL, "DAC DIG" },
{ "Left Out 1", NULL, "Left Mixer" },
{ "LOUT1", NULL, "Left Out 1" },
{ "Right Out 1", NULL, "Right Mixer" },
{ "ROUT1", NULL, "Right Out 1" },
{ "Left Out 2", NULL, "Left Mixer" },
{ "LOUT2", NULL, "Left Out 2" },
{ "Right Out 2", NULL, "Right Mixer" },
{ "ROUT2", NULL, "Right Out 2" },
};
static int es8328_mute(struct snd_soc_dai *dai, int mute)
{
return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
ES8328_DACCONTROL3_DACMUTE,
mute ? ES8328_DACCONTROL3_DACMUTE : 0);
}
static int es8328_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int clk_rate;
int i;
int reg;
u8 ratio;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2;
else
reg = ES8328_ADCCONTROL5;
clk_rate = clk_get_rate(es8328->clk);
if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
(clk_rate != ES8328_SYSCLK_RATE_2X)) {
dev_err(codec->dev,
"%s: clock is running at %d Hz, not %d or %d Hz\n",
__func__, clk_rate,
ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
return -EINVAL;
}
/* find master mode MCLK to sampling frequency ratio */
ratio = mclk_ratios[0].rate;
for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
if (params_rate(params) <= mclk_ratios[i].rate)
ratio = mclk_ratios[i].ratio;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
es8328->playback_fs = params_rate(params);
es8328_set_deemph(codec);
}
return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
}
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
int clk_rate;
u8 mode = ES8328_DACCONTROL1_DACWL_16;
/* set master/slave audio interface */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
return -EINVAL;
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
break;
default:
return -EINVAL;
}
/* clock inversion */
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
snd_soc_write(codec, ES8328_DACCONTROL1, mode);
snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
/* Master serial port mode, with BCLK generated automatically */
clk_rate = clk_get_rate(es8328->clk);
if (clk_rate == ES8328_SYSCLK_RATE_1X)
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC);
else
snd_soc_write(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MCLKDIV2 |
ES8328_MASTERMODE_MSC);
return 0;
}
static int es8328_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
snd_soc_write(codec, ES8328_CHIPPOWER, 0);
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_50k |
ES8328_CONTROL1_ENREF);
break;
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_5k |
ES8328_CONTROL1_ENREF);
/* Charge caps */
msleep(100);
}
snd_soc_write(codec, ES8328_CONTROL2,
ES8328_CONTROL2_OVERCURRENT_ON |
ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
/* VREF, VMID=2*500k, digital stopped */
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_500k |
ES8328_CONTROL1_ENREF);
break;
case SND_SOC_BIAS_OFF:
snd_soc_update_bits(codec, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
0);
break;
}
codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops es8328_dai_ops = {
.hw_params = es8328_hw_params,
.digital_mute = es8328_mute,
.set_fmt = es8328_set_dai_fmt,
};
static struct snd_soc_dai_driver es8328_dai = {
.name = "es8328-hifi-analog",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = ES8328_RATES,
.formats = ES8328_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = ES8328_RATES,
.formats = ES8328_FORMATS,
},
.ops = &es8328_dai_ops,
};
static int es8328_suspend(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328;
int ret;
es8328 = snd_soc_codec_get_drvdata(codec);
es8328_set_bias_level(codec, SND_SOC_BIAS_OFF);
clk_disable_unprepare(es8328->clk);
ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(codec->dev, "unable to disable regulators\n");
return ret;
}
return 0;
}
static int es8328_resume(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
struct es8328_priv *es8328;
int ret;
es8328 = snd_soc_codec_get_drvdata(codec);
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(codec->dev, "unable to enable clock\n");
return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(codec->dev, "unable to enable regulators\n");
return ret;
}
regcache_mark_dirty(regmap);
ret = regcache_sync(regmap);
if (ret) {
dev_err(codec->dev, "unable to sync regcache\n");
return ret;
}
es8328_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static int es8328_codec_probe(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328;
int ret;
es8328 = snd_soc_codec_get_drvdata(codec);
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(codec->dev, "unable to enable regulators\n");
return ret;
}
/* Setup clocks */
es8328->clk = devm_clk_get(codec->dev, NULL);
if (IS_ERR(es8328->clk)) {
dev_err(codec->dev, "codec clock missing or invalid\n");
goto clk_fail;
}
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(codec->dev, "unable to prepare codec clk\n");
goto clk_fail;
}
return 0;
clk_fail:
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
return ret;
}
static int es8328_remove(struct snd_soc_codec *codec)
{
struct es8328_priv *es8328;
es8328 = snd_soc_codec_get_drvdata(codec);
if (es8328->clk)
clk_disable_unprepare(es8328->clk);
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
return 0;
}
const struct regmap_config es8328_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ES8328_REG_MAX,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL_GPL(es8328_regmap_config);
static struct snd_soc_codec_driver es8328_codec_driver = {
.probe = es8328_codec_probe,
.suspend = es8328_suspend,
.resume = es8328_resume,
.remove = es8328_remove,
.set_bias_level = es8328_set_bias_level,
.controls = es8328_snd_controls,
.num_controls = ARRAY_SIZE(es8328_snd_controls),
.dapm_widgets = es8328_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
.dapm_routes = es8328_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
};
int es8328_probe(struct device *dev, struct regmap *regmap)
{
struct es8328_priv *es8328;
int ret;
int i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL);
if (es8328 == NULL)
return -ENOMEM;
es8328->regmap = regmap;
for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
es8328->supplies[i].supply = supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
dev_err(dev, "unable to get regulators\n");
return ret;
}
dev_set_drvdata(dev, es8328);
return snd_soc_register_codec(dev,
&es8328_codec_driver, &es8328_dai, 1);
}
EXPORT_SYMBOL_GPL(es8328_probe);
MODULE_DESCRIPTION("ASoC ES8328 driver");
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_LICENSE("GPL");

314
sound/soc/codecs/es8328.h Normal file
View File

@ -0,0 +1,314 @@
/*
* es8328.h -- ES8328 ALSA SoC Audio driver
*/
#ifndef _ES8328_H
#define _ES8328_H
#include <linux/regmap.h>
struct device;
extern const struct regmap_config es8328_regmap_config;
int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_DACLVOL 46
#define ES8328_DACRVOL 47
#define ES8328_DACCTL 28
#define ES8328_RATEMASK (0x1f << 0)
#define ES8328_CONTROL1 0x00
#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
#define ES8328_CONTROL1_ENREF (1 << 2)
#define ES8328_CONTROL1_SEQEN (1 << 3)
#define ES8328_CONTROL1_SAMEFS (1 << 4)
#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
#define ES8328_CONTROL1_LRCM (1 << 6)
#define ES8328_CONTROL1_SCP_RESET (1 << 7)
#define ES8328_CONTROL2 0x01
#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
#define ES8328_CHIPPOWER 0x02
#define ES8328_CHIPPOWER_DACVREF_OFF 0
#define ES8328_CHIPPOWER_ADCVREF_OFF 1
#define ES8328_CHIPPOWER_DACDLL_OFF 2
#define ES8328_CHIPPOWER_ADCDLL_OFF 3
#define ES8328_CHIPPOWER_DACSTM_RESET 4
#define ES8328_CHIPPOWER_ADCSTM_RESET 5
#define ES8328_CHIPPOWER_DACDIG_OFF 6
#define ES8328_CHIPPOWER_ADCDIG_OFF 7
#define ES8328_ADCPOWER 0x03
#define ES8328_ADCPOWER_INT1_LOWPOWER 0
#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1
#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2
#define ES8328_ADCPOWER_MIC_BIAS_OFF 3
#define ES8328_ADCPOWER_ADCR_OFF 4
#define ES8328_ADCPOWER_ADCL_OFF 5
#define ES8328_ADCPOWER_AINR_OFF 6
#define ES8328_ADCPOWER_AINL_OFF 7
#define ES8328_DACPOWER 0x04
#define ES8328_DACPOWER_OUT3_ON 0
#define ES8328_DACPOWER_MONO_ON 1
#define ES8328_DACPOWER_ROUT2_ON 2
#define ES8328_DACPOWER_LOUT2_ON 3
#define ES8328_DACPOWER_ROUT1_ON 4
#define ES8328_DACPOWER_LOUT1_ON 5
#define ES8328_DACPOWER_RDAC_OFF 6
#define ES8328_DACPOWER_LDAC_OFF 7
#define ES8328_CHIPLOPOW1 0x05
#define ES8328_CHIPLOPOW2 0x06
#define ES8328_ANAVOLMANAG 0x07
#define ES8328_MASTERMODE 0x08
#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
#define ES8328_MASTERMODE_MSC (1 << 7)
#define ES8328_ADCCONTROL1 0x09
#define ES8328_ADCCONTROL2 0x0a
#define ES8328_ADCCONTROL3 0x0b
#define ES8328_ADCCONTROL4 0x0c
#define ES8328_ADCCONTROL5 0x0d
#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
#define ES8328_ADCCONTROL6 0x0e
#define ES8328_ADCCONTROL7 0x0f
#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
#define ES8328_ADCCONTROL8 0x10
#define ES8328_ADCCONTROL9 0x11
#define ES8328_ADCCONTROL10 0x12
#define ES8328_ADCCONTROL11 0x13
#define ES8328_ADCCONTROL12 0x14
#define ES8328_ADCCONTROL13 0x15
#define ES8328_ADCCONTROL14 0x16
#define ES8328_DACCONTROL1 0x17
#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6)
#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
#define ES8328_DACCONTROL2 0x18
#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5)
#define ES8328_DACCONTROL3 0x19
#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
#define ES8328_LDACVOL 0x1a
#define ES8328_LDACVOL_MASK (0 << 0)
#define ES8328_LDACVOL_MAX (0xc0)
#define ES8328_RDACVOL 0x1b
#define ES8328_RDACVOL_MASK (0 << 0)
#define ES8328_RDACVOL_MAX (0xc0)
#define ES8328_DACVOL_MAX (0xc0)
#define ES8328_DACCONTROL4 0x1a
#define ES8328_DACCONTROL5 0x1b
#define ES8328_DACCONTROL6 0x1c
#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
#define ES8328_DACCONTROL7 0x1d
#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0)
#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0)
#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0)
#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0)
#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */
#define ES8328_DACCONTROL7_MONO (1 << 5)
#define ES8328_DACCONTROL7_ZEROR (1 << 6)
#define ES8328_DACCONTROL7_ZEROL (1 << 7)
/* Shelving filter */
#define ES8328_DACCONTROL8 0x1e
#define ES8328_DACCONTROL9 0x1f
#define ES8328_DACCONTROL10 0x20
#define ES8328_DACCONTROL11 0x21
#define ES8328_DACCONTROL12 0x22
#define ES8328_DACCONTROL13 0x23
#define ES8328_DACCONTROL14 0x24
#define ES8328_DACCONTROL15 0x25
#define ES8328_DACCONTROL16 0x26
#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
#define ES8328_DACCONTROL17 0x27
#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
#define ES8328_DACCONTROL17_LI2LO (1 << 6)
#define ES8328_DACCONTROL17_LD2LO (1 << 7)
#define ES8328_DACCONTROL18 0x28
#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
#define ES8328_DACCONTROL18_RI2LO (1 << 6)
#define ES8328_DACCONTROL18_RD2LO (1 << 7)
#define ES8328_DACCONTROL19 0x29
#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
#define ES8328_DACCONTROL19_LI2RO (1 << 6)
#define ES8328_DACCONTROL19_LD2RO (1 << 7)
#define ES8328_DACCONTROL20 0x2a
#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
#define ES8328_DACCONTROL20_RI2RO (1 << 6)
#define ES8328_DACCONTROL20_RD2RO (1 << 7)
#define ES8328_DACCONTROL21 0x2b
#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3)
#define ES8328_DACCONTROL21_LI2MO (1 << 6)
#define ES8328_DACCONTROL21_LD2MO (1 << 7)
#define ES8328_DACCONTROL22 0x2c
#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3)
#define ES8328_DACCONTROL22_RI2MO (1 << 6)
#define ES8328_DACCONTROL22_RD2MO (1 << 7)
#define ES8328_DACCONTROL23 0x2d
#define ES8328_DACCONTROL23_MOUTINV (1 << 1)
#define ES8328_DACCONTROL23_HPSWPOL (1 << 2)
#define ES8328_DACCONTROL23_HPSWEN (1 << 3)
#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4)
#define ES8328_DACCONTROL23_VROI_40k (1 << 4)
#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5)
#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5)
#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5)
#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5)
#define ES8328_DACCONTROL23_ROUT2INV (1 << 7)
/* LOUT1 Amplifier */
#define ES8328_LOUT1VOL 0x2e
#define ES8328_LOUT1VOL_MASK (0 << 5)
#define ES8328_LOUT1VOL_MAX (0x24)
/* ROUT1 Amplifier */
#define ES8328_ROUT1VOL 0x2f
#define ES8328_ROUT1VOL_MASK (0 << 5)
#define ES8328_ROUT1VOL_MAX (0x24)
#define ES8328_OUT1VOL_MAX (0x24)
/* LOUT2 Amplifier */
#define ES8328_LOUT2VOL 0x30
#define ES8328_LOUT2VOL_MASK (0 << 5)
#define ES8328_LOUT2VOL_MAX (0x24)
/* ROUT2 Amplifier */
#define ES8328_ROUT2VOL 0x31
#define ES8328_ROUT2VOL_MASK (0 << 5)
#define ES8328_ROUT2VOL_MAX (0x24)
#define ES8328_OUT2VOL_MAX (0x24)
/* Mono Out Amplifier */
#define ES8328_MONOOUTVOL 0x32
#define ES8328_MONOOUTVOL_MASK (0 << 5)
#define ES8328_MONOOUTVOL_MAX (0x24)
#define ES8328_DACCONTROL29 0x33
#define ES8328_DACCONTROL30 0x34
#define ES8328_SYSCLK 0
#define ES8328_REG_MAX 0x35
#define ES8328_PLL1 0
#define ES8328_PLL2 1
/* clock inputs */
#define ES8328_MCLK 0
#define ES8328_PCMCLK 1
/* clock divider id's */
#define ES8328_PCMDIV 0
#define ES8328_BCLKDIV 1
#define ES8328_VXCLKDIV 2
/* PCM clock dividers */
#define ES8328_PCM_DIV_1 (0 << 6)
#define ES8328_PCM_DIV_3 (2 << 6)
#define ES8328_PCM_DIV_5_5 (3 << 6)
#define ES8328_PCM_DIV_2 (4 << 6)
#define ES8328_PCM_DIV_4 (5 << 6)
#define ES8328_PCM_DIV_6 (6 << 6)
#define ES8328_PCM_DIV_8 (7 << 6)
/* BCLK clock dividers */
#define ES8328_BCLK_DIV_1 (0 << 7)
#define ES8328_BCLK_DIV_2 (1 << 7)
#define ES8328_BCLK_DIV_4 (2 << 7)
#define ES8328_BCLK_DIV_8 (3 << 7)
/* VXCLK clock dividers */
#define ES8328_VXCLK_DIV_1 (0 << 6)
#define ES8328_VXCLK_DIV_2 (1 << 6)
#define ES8328_VXCLK_DIV_4 (2 << 6)
#define ES8328_VXCLK_DIV_8 (3 << 6)
#define ES8328_VXCLK_DIV_16 (4 << 6)
#define ES8328_DAI_HIFI 0
#define ES8328_DAI_VOICE 1
#define ES8328_1536FS 1536
#define ES8328_1024FS 1024
#define ES8328_768FS 768
#define ES8328_512FS 512
#define ES8328_384FS 384
#define ES8328_256FS 256
#define ES8328_128FS 128
#endif

View File

@ -241,6 +241,18 @@ config SND_SOC_IMX_WM8962
Say Y if you want to add support for SoC audio on an i.MX board with
a wm8962 codec.
config SND_SOC_IMX_ES8328
tristate "SoC Audio support for i.MX boards with the ES8328 codec"
depends on OF && (I2C || SPI)
select SND_SOC_ES8328_I2C if I2C
select SND_SOC_ES8328_SPI if SPI_MASTER
select SND_SOC_IMX_PCM_DMA
select SND_SOC_IMX_AUDMUX
select SND_SOC_FSL_SSI
help
Say Y if you want to add support for the ES8328 audio codec connected
via SSI/I2S over either SPI or I2C.
config SND_SOC_IMX_SGTL5000
tristate "SoC Audio support for i.MX boards with sgtl5000"
depends on OF && I2C
@ -269,6 +281,23 @@ config SND_SOC_IMX_MC13783
select SND_SOC_MC13783
select SND_SOC_IMX_PCM_DMA
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
depends on OF && I2C
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_ESAI
select SND_SOC_FSL_SAI
select SND_SOC_FSL_SSI
select SND_SOC_CS42XX8_I2C
select SND_SOC_SGTL5000
select SND_SOC_WM8962
help
ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
and SGTL5000.
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
endif # SND_IMX_SOC
endmenu

View File

@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
@ -19,6 +20,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
@ -50,6 +52,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
snd-soc-phycore-ac97-objs := phycore-ac97.o
snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-es8328-objs := imx-es8328.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-wm8962-objs := imx-wm8962.o
snd-soc-imx-spdif-objs := imx-spdif.o
@ -59,6 +62,7 @@ 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
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o

View File

@ -0,0 +1,574 @@
/*
* Freescale Generic ASoC Sound Card driver with ASRC
*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* Author: Nicolin Chen <nicoleotsuka@gmail.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "fsl_esai.h"
#include "fsl_sai.h"
#include "imx-audmux.h"
#include "../codecs/sgtl5000.h"
#include "../codecs/wm8962.h"
#define RX 0
#define TX 1
/* Default DAI format without Master and Slave flag */
#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
/**
* CODEC private data
*
* @mclk_freq: Clock rate of MCLK
* @mclk_id: MCLK (or main clock) id for set_sysclk()
* @fll_id: FLL (or secordary clock) id for set_sysclk()
* @pll_id: PLL id for set_pll()
*/
struct codec_priv {
unsigned long mclk_freq;
u32 mclk_id;
u32 fll_id;
u32 pll_id;
};
/**
* CPU private data
*
* @sysclk_freq[2]: SYSCLK rates for set_sysclk()
* @sysclk_dir[2]: SYSCLK directions for set_sysclk()
* @sysclk_id[2]: SYSCLK ids for set_sysclk()
*
* Note: [1] for tx and [0] for rx
*/
struct cpu_priv {
unsigned long sysclk_freq[2];
u32 sysclk_dir[2];
u32 sysclk_id[2];
};
/**
* Freescale Generic ASOC card private data
*
* @dai_link[3]: DAI link structure including normal one and DPCM link
* @pdev: platform device pointer
* @codec_priv: CODEC private data
* @cpu_priv: CPU private data
* @card: ASoC card structure
* @sample_rate: Current sample rate
* @sample_format: Current sample format
* @asrc_rate: ASRC sample rate used by Back-Ends
* @asrc_format: ASRC sample format used by Back-Ends
* @dai_fmt: DAI format between CPU and CODEC
* @name: Card name
*/
struct fsl_asoc_card_priv {
struct snd_soc_dai_link dai_link[3];
struct platform_device *pdev;
struct codec_priv codec_priv;
struct cpu_priv cpu_priv;
struct snd_soc_card card;
u32 sample_rate;
u32 sample_format;
u32 asrc_rate;
u32 asrc_format;
u32 dai_fmt;
char name[32];
};
/**
* This dapm route map exsits for DPCM link only.
* The other routes shall go through Device Tree.
*/
static const struct snd_soc_dapm_route audio_map[] = {
{"CPU-Playback", NULL, "ASRC-Playback"},
{"Playback", NULL, "CPU-Playback"},
{"ASRC-Capture", NULL, "CPU-Capture"},
{"CPU-Capture", NULL, "Capture"},
};
/* Add all possible widgets into here without being redundant */
static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
SND_SOC_DAPM_LINE("Line In Jack", NULL),
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
SND_SOC_DAPM_MIC("DMIC", NULL),
};
static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct cpu_priv *cpu_priv = &priv->cpu_priv;
struct device *dev = rtd->card->dev;
int ret;
priv->sample_rate = params_rate(params);
priv->sample_format = params_format(params);
if (priv->card.set_bias_level)
return 0;
/* Specific configurations of DAIs starts from here */
ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
cpu_priv->sysclk_freq[tx],
cpu_priv->sysclk_dir[tx]);
if (ret) {
dev_err(dev, "failed to set sysclk for cpu dai\n");
return ret;
}
return 0;
}
static struct snd_soc_ops fsl_asoc_card_ops = {
.hw_params = fsl_asoc_card_hw_params,
};
static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_interval *rate;
struct snd_mask *mask;
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
rate->max = rate->min = priv->asrc_rate;
mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_none(mask);
snd_mask_set(mask, priv->asrc_format);
return 0;
}
static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
/* Default ASoC DAI Link*/
{
.name = "HiFi",
.stream_name = "HiFi",
.ops = &fsl_asoc_card_ops,
},
/* DPCM Link between Front-End and Back-End (Optional) */
{
.name = "HiFi-ASRC-FE",
.stream_name = "HiFi-ASRC-FE",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dpcm_playback = 1,
.dpcm_capture = 1,
.dynamic = 1,
},
{
.name = "HiFi-ASRC-BE",
.stream_name = "HiFi-ASRC-BE",
.platform_name = "snd-soc-dummy",
.be_hw_params_fixup = be_hw_params_fixup,
.ops = &fsl_asoc_card_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
.no_pcm = 1,
},
};
static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
struct device *dev = card->dev;
unsigned int pll_out;
int ret;
if (dapm->dev != codec_dai->dev)
return 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
break;
if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
pll_out = priv->sample_rate * 384;
else
pll_out = priv->sample_rate * 256;
ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
codec_priv->mclk_id,
codec_priv->mclk_freq, pll_out);
if (ret) {
dev_err(dev, "failed to start FLL: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
pll_out, SND_SOC_CLOCK_IN);
if (ret) {
dev_err(dev, "failed to set SYSCLK: %d\n", ret);
return ret;
}
break;
case SND_SOC_BIAS_STANDBY:
if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
break;
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq,
SND_SOC_CLOCK_IN);
if (ret) {
dev_err(dev, "failed to switch away from FLL: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
if (ret) {
dev_err(dev, "failed to stop FLL: %d\n", ret);
return ret;
}
break;
default:
break;
}
return 0;
}
static int fsl_asoc_card_audmux_init(struct device_node *np,
struct fsl_asoc_card_priv *priv)
{
struct device *dev = &priv->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--;
/*
* Use asynchronous mode (6 wires) for all cases.
* If only 4 wires are needed, just set SSI into
* synchronous mode and enable 4 PADs in IOMUX.
*/
switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RFSDIR |
IMX_AUDMUX_V2_PTCR_RCLKDIR |
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
case SND_SOC_DAIFMT_CBM_CFS:
int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RCLKDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
IMX_AUDMUX_V2_PTCR_RFSDIR |
IMX_AUDMUX_V2_PTCR_TFSDIR;
break;
case SND_SOC_DAIFMT_CBS_CFM:
int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_RFSDIR |
IMX_AUDMUX_V2_PTCR_TFSDIR;
ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
IMX_AUDMUX_V2_PTCR_RCLKDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR;
break;
case SND_SOC_DAIFMT_CBS_CFS:
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;
break;
default:
return -EINVAL;
}
/* 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 fsl_asoc_card_late_probe(struct snd_soc_card *card)
{
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
struct codec_priv *codec_priv = &priv->codec_priv;
struct device *dev = card->dev;
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
if (ret) {
dev_err(dev, "failed to set sysclk in %s\n", __func__);
return ret;
}
return 0;
}
static int fsl_asoc_card_probe(struct platform_device *pdev)
{
struct device_node *cpu_np, *codec_np, *asrc_np;
struct device_node *np = pdev->dev.of_node;
struct platform_device *asrc_pdev = NULL;
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
struct i2c_client *codec_dev;
struct clk *codec_clk;
u32 width;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
cpu_np = of_parse_phandle(np, "audio-cpu", 0);
/* Give a chance to old DT binding */
if (!cpu_np)
cpu_np = of_parse_phandle(np, "ssi-controller", 0);
codec_np = of_parse_phandle(np, "audio-codec", 0);
if (!cpu_np || !codec_np) {
dev_err(&pdev->dev, "phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
cpu_pdev = of_find_device_by_node(cpu_np);
if (!cpu_pdev) {
dev_err(&pdev->dev, "failed to find CPU DAI device\n");
ret = -EINVAL;
goto fail;
}
codec_dev = of_find_i2c_device_by_node(codec_np);
if (!codec_dev) {
dev_err(&pdev->dev, "failed to find codec platform device\n");
ret = -EINVAL;
goto fail;
}
asrc_np = of_parse_phandle(np, "audio-asrc", 0);
if (asrc_np)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
codec_clk = clk_get(&codec_dev->dev, NULL);
if (!IS_ERR(codec_clk)) {
priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
clk_put(codec_clk);
}
/* Default sample rate and format, will be updated in hw_params() */
priv->sample_rate = 44100;
priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
/* Assign a default DAI format, and allow each card to overwrite it */
priv->dai_fmt = DAI_FMT_BASE;
/* Diversify the card configurations */
if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
priv->card.set_bias_level = NULL;
priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
priv->codec_priv.pll_id = WM8962_FLL;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
return -EINVAL;
}
/* Common settings for corresponding Freescale CPU DAI driver */
if (strstr(cpu_np->name, "ssi")) {
/* Only SSI needs to configure AUDMUX */
ret = fsl_asoc_card_audmux_init(np, priv);
if (ret) {
dev_err(&pdev->dev, "failed to init audmux\n");
goto asrc_fail;
}
} else if (strstr(cpu_np->name, "esai")) {
priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
} else if (strstr(cpu_np->name, "sai")) {
priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
}
sprintf(priv->name, "%s-audio", codec_dev->name);
/* Initialize sound card */
priv->pdev = pdev;
priv->card.dev = &pdev->dev;
priv->card.name = priv->name;
priv->card.dai_link = priv->dai_link;
priv->card.dapm_routes = audio_map;
priv->card.late_probe = fsl_asoc_card_late_probe;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
memcpy(priv->dai_link, fsl_asoc_card_dai,
sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
/* Normal DAI Link */
priv->dai_link[0].cpu_of_node = cpu_np;
priv->dai_link[0].codec_of_node = codec_np;
priv->dai_link[0].codec_dai_name = codec_dev->name;
priv->dai_link[0].platform_of_node = cpu_np;
priv->dai_link[0].dai_fmt = priv->dai_fmt;
priv->card.num_links = 1;
if (asrc_pdev) {
/* DPCM DAI Links only if ASRC exsits */
priv->dai_link[1].cpu_of_node = asrc_np;
priv->dai_link[1].platform_of_node = asrc_np;
priv->dai_link[2].codec_dai_name = codec_dev->name;
priv->dai_link[2].codec_of_node = codec_np;
priv->dai_link[2].cpu_of_node = cpu_np;
priv->dai_link[2].dai_fmt = priv->dai_fmt;
priv->card.num_links = 3;
ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
&priv->asrc_rate);
if (ret) {
dev_err(&pdev->dev, "failed to get output rate\n");
ret = -EINVAL;
goto asrc_fail;
}
ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
if (ret) {
dev_err(&pdev->dev, "failed to get output rate\n");
ret = -EINVAL;
goto asrc_fail;
}
if (width == 24)
priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
else
priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
}
/* Finish card registering */
platform_set_drvdata(pdev, priv);
snd_soc_card_set_drvdata(&priv->card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
asrc_fail:
of_node_put(asrc_np);
fail:
of_node_put(codec_np);
of_node_put(cpu_np);
return ret;
}
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
{ .compatible = "fsl,imx-audio-cs42888", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
{}
};
static struct platform_driver fsl_asoc_card_driver = {
.probe = fsl_asoc_card_probe,
.driver = {
.name = "fsl-asoc-card",
.pm = &snd_soc_pm_ops,
.of_match_table = fsl_asoc_card_dt_ids,
},
};
module_platform_driver(fsl_asoc_card_driver);
MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
MODULE_ALIAS("platform:fsl-asoc-card");
MODULE_LICENSE("GPL");

View File

@ -684,7 +684,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
}
}
static struct regmap_config fsl_asrc_regmap_config = {
static const struct regmap_config fsl_asrc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@ -802,10 +802,6 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc_priv->paddr = res->start;
/* Register regmap and let it prepare core clock */
if (of_property_read_bool(np, "big-endian"))
fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
&fsl_asrc_regmap_config);
if (IS_ERR(asrc_priv->regmap)) {

View File

@ -707,7 +707,7 @@ static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
}
}
static struct regmap_config fsl_esai_regmap_config = {
static const struct regmap_config fsl_esai_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@ -733,9 +733,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
esai_priv->pdev = pdev;
strcpy(esai_priv->name, np->name);
if (of_property_read_bool(np, "big-endian"))
fsl_esai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);

View File

@ -544,7 +544,7 @@ static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
}
}
static struct regmap_config fsl_sai_regmap_config = {
static const struct regmap_config fsl_sai_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@ -573,10 +573,6 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
sai->sai_on_imx = true;
sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
if (sai->big_endian_regs)
fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

View File

@ -132,7 +132,6 @@ struct fsl_sai {
struct clk *bus_clk;
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
bool big_endian_regs;
bool big_endian_data;
bool is_dsp_mode;
bool sai_on_imx;

View File

@ -1040,7 +1040,7 @@ static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
}
}
static struct regmap_config fsl_spdif_regmap_config = {
static const struct regmap_config fsl_spdif_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@ -1184,9 +1184,6 @@ static int fsl_spdif_probe(struct platform_device *pdev)
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
spdif_priv->cpu_dai_drv.name = spdif_priv->name;
if (of_property_read_bool(np, "big-endian"))
fsl_spdif_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);

232
sound/soc/fsl/imx-es8328.c Normal file
View File

@ -0,0 +1,232 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2012 Linaro Ltd.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include "imx-audmux.h"
#define DAI_NAME_SIZE 32
#define MUX_PORT_MAX 7
struct imx_es8328_data {
struct device *dev;
struct snd_soc_dai_link dai;
struct snd_soc_card card;
char codec_dai_name[DAI_NAME_SIZE];
char platform_name[DAI_NAME_SIZE];
int jack_gpio;
};
static struct snd_soc_jack_gpio headset_jack_gpios[] = {
{
.gpio = -1,
.name = "headset-gpio",
.report = SND_JACK_HEADSET,
.invert = 0,
.debounce_time = 200,
},
};
static struct snd_soc_jack headset_jack;
static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct imx_es8328_data *data = container_of(rtd->card,
struct imx_es8328_data, card);
int ret = 0;
/* Headphone jack detection */
if (gpio_is_valid(data->jack_gpio)) {
ret = snd_soc_jack_new(rtd->codec, "Headphone",
SND_JACK_HEADPHONE | SND_JACK_BTN_0,
&headset_jack);
if (ret)
return ret;
headset_jack_gpios[0].gpio = data->jack_gpio;
ret = snd_soc_jack_add_gpios(&headset_jack,
ARRAY_SIZE(headset_jack_gpios),
headset_jack_gpios);
}
return ret;
}
static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0),
};
static int imx_es8328_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *ssi_np, *codec_np;
struct platform_device *ssi_pdev;
struct imx_es8328_data *data;
u32 int_port, ext_port;
int ret;
struct device *dev = &pdev->dev;
ret = of_property_read_u32(np, "mux-int-port", &int_port);
if (ret) {
dev_err(dev, "mux-int-port missing or invalid\n");
goto fail;
}
if (int_port > MUX_PORT_MAX || int_port == 0) {
dev_err(dev, "mux-int-port: hardware only has %d mux ports\n",
MUX_PORT_MAX);
goto fail;
}
ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
if (ret) {
dev_err(dev, "mux-ext-port missing or invalid\n");
goto fail;
}
if (ext_port > MUX_PORT_MAX || ext_port == 0) {
dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n",
MUX_PORT_MAX);
goto fail;
}
/*
* The port numbering in the hardware manual starts at 1, while
* the audmux API expects it starts at 0.
*/
int_port--;
ext_port--;
ret = imx_audmux_v2_configure_port(int_port,
IMX_AUDMUX_V2_PTCR_SYN |
IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR,
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,
IMX_AUDMUX_V2_PTCR_SYN,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {
dev_err(dev, "audmux external port setup failed\n");
return ret;
}
ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
if (!ssi_np || !codec_np) {
dev_err(dev, "phandle missing or invalid\n");
ret = -EINVAL;
goto fail;
}
ssi_pdev = of_find_device_by_node(ssi_np);
if (!ssi_pdev) {
dev_err(dev, "failed to find SSI platform device\n");
ret = -EINVAL;
goto fail;
}
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto fail;
}
data->dev = dev;
data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0);
data->dai.name = "hifi";
data->dai.stream_name = "hifi";
data->dai.codec_dai_name = "es8328-hifi-analog";
data->dai.codec_of_node = codec_np;
data->dai.cpu_of_node = ssi_np;
data->dai.platform_of_node = ssi_np;
data->dai.init = &imx_es8328_dai_init;
data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM;
data->card.dev = dev;
data->card.dapm_widgets = imx_es8328_dapm_widgets;
data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets);
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret) {
dev_err(dev, "Unable to parse card name\n");
goto fail;
}
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret) {
dev_err(dev, "Unable to parse routing: %d\n", ret);
goto fail;
}
data->card.num_links = 1;
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
ret = snd_soc_register_card(&data->card);
if (ret) {
dev_err(dev, "Unable to register: %d\n", ret);
goto fail;
}
platform_set_drvdata(pdev, data);
fail:
of_node_put(ssi_np);
of_node_put(codec_np);
return ret;
}
static int imx_es8328_remove(struct platform_device *pdev)
{
struct imx_es8328_data *data = platform_get_drvdata(pdev);
snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
headset_jack_gpios);
snd_soc_unregister_card(&data->card);
return 0;
}
static const struct of_device_id imx_es8328_dt_ids[] = {
{ .compatible = "fsl,imx-audio-es8328", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids);
static struct platform_driver imx_es8328_driver = {
.driver = {
.name = "imx-es8328",
.of_match_table = imx_es8328_dt_ids,
},
.probe = imx_es8328_probe,
.remove = imx_es8328_remove,
};
module_platform_driver(imx_es8328_driver);
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:imx-audio-es8328");