summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/codecs/twl6040.c20
-rw-r--r--sound/soc/codecs/uda1380.c42
-rw-r--r--sound/soc/soc-utils.c2
-rw-r--r--sound/soc/uniphier/Kconfig19
-rw-r--r--sound/soc/uniphier/Makefile3
-rw-r--r--sound/soc/uniphier/evea.c567
-rw-r--r--sound/soc/ux500/mop500.c4
-rw-r--r--sound/soc/ux500/ux500_pcm.c5
10 files changed, 634 insertions, 30 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index d227581..84c3582 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -71,6 +71,7 @@ source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
+source "sound/soc/uniphier/Kconfig"
source "sound/soc/ux500/Kconfig"
source "sound/soc/xtensa/Kconfig"
source "sound/soc/zte/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 5327f4d..74cd185 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
+obj-$(CONFIG_SND_SOC) += uniphier/
obj-$(CONFIG_SND_SOC) += ux500/
obj-$(CONFIG_SND_SOC) += xtensa/
obj-$(CONFIG_SND_SOC) += zte/
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 1773ff8..3b895b4 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -106,10 +106,12 @@ static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
{ .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
};
+#define to_twl6040(codec) dev_get_drvdata((codec)->dev->parent)
+
static unsigned int twl6040_read(struct snd_soc_codec *codec, unsigned int reg)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(codec);
u8 value;
if (reg >= TWL6040_CACHEREGNUM)
@@ -171,7 +173,7 @@ static inline void twl6040_update_dl12_cache(struct snd_soc_codec *codec,
static int twl6040_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(codec);
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
@@ -541,7 +543,7 @@ int twl6040_get_dl1_gain(struct snd_soc_codec *codec)
if (snd_soc_dapm_get_pin_status(dapm, "HSOR") ||
snd_soc_dapm_get_pin_status(dapm, "HSOL")) {
- u8 val = snd_soc_read(codec, TWL6040_REG_HSLCTL);
+ u8 val = twl6040_read(codec, TWL6040_REG_HSLCTL);
if (val & TWL6040_HSDACMODE)
/* HSDACL in LP mode */
return -8; /* -8dB */
@@ -572,7 +574,7 @@ EXPORT_SYMBOL_GPL(twl6040_get_trim_value);
int twl6040_get_hs_step_size(struct snd_soc_codec *codec)
{
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(codec);
if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3)
/* For ES under ES_1.3 HS step is 2 mV */
@@ -830,7 +832,7 @@ static const struct snd_soc_dapm_route intercon[] = {
static int twl6040_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int ret = 0;
@@ -922,7 +924,7 @@ static int twl6040_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int ret;
@@ -964,7 +966,7 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
int mute)
{
- struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040 *twl6040 = to_twl6040(codec);
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int hslctl, hsrctl, earctl;
int hflctl, hfrctl;
@@ -1108,7 +1110,6 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
static int twl6040_probe(struct snd_soc_codec *codec)
{
struct twl6040_data *priv;
- struct twl6040 *twl6040 = dev_get_drvdata(codec->dev->parent);
struct platform_device *pdev = to_platform_device(codec->dev);
int ret = 0;
@@ -1119,7 +1120,6 @@ static int twl6040_probe(struct snd_soc_codec *codec)
snd_soc_codec_set_drvdata(codec, priv);
priv->codec = codec;
- codec->control_data = twl6040;
priv->plug_irq = platform_get_irq(pdev, 0);
if (priv->plug_irq < 0) {
@@ -1158,8 +1158,6 @@ static int twl6040_remove(struct snd_soc_codec *codec)
static const struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
.probe = twl6040_probe,
.remove = twl6040_remove,
- .read = twl6040_read,
- .write = twl6040_write,
.set_bias_level = twl6040_set_bias_level,
.suspend_bias_off = true,
.ignore_pmdown_time = true,
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 926c81a..c73e6a1 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -37,7 +37,8 @@ struct uda1380_priv {
struct snd_soc_codec *codec;
unsigned int dac_clk;
struct work_struct work;
- void *control_data;
+ struct i2c_client *i2c;
+ u16 *reg_cache;
};
/*
@@ -63,7 +64,9 @@ static unsigned long uda1380_cache_dirty;
static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
- u16 *cache = codec->reg_cache;
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
+ u16 *cache = uda1380->reg_cache;
+
if (reg == UDA1380_RESET)
return 0;
if (reg >= UDA1380_CACHEREGNUM)
@@ -77,7 +80,8 @@ static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, unsigned int value)
{
- u16 *cache = codec->reg_cache;
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
+ u16 *cache = uda1380->reg_cache;
if (reg >= UDA1380_CACHEREGNUM)
return;
@@ -92,6 +96,7 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
u8 data[3];
/* data is
@@ -111,10 +116,10 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL))
return 0;
pr_debug("uda1380: hw write %x val %x\n", reg, value);
- if (codec->hw_write(codec->control_data, data, 3) == 3) {
+ if (i2c_master_send(uda1380->i2c, data, 3) == 3) {
unsigned int val;
- i2c_master_send(codec->control_data, data, 1);
- i2c_master_recv(codec->control_data, data, 2);
+ i2c_master_send(uda1380->i2c, data, 1);
+ i2c_master_recv(uda1380->i2c, data, 2);
val = (data[0]<<8) | data[1];
if (val != value) {
pr_debug("uda1380: READ BACK VAL %x\n",
@@ -130,16 +135,17 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
static void uda1380_sync_cache(struct snd_soc_codec *codec)
{
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int reg;
u8 data[3];
- u16 *cache = codec->reg_cache;
+ u16 *cache = uda1380->reg_cache;
/* Sync reg_cache with the hardware */
for (reg = 0; reg < UDA1380_MVOL; reg++) {
data[0] = reg;
data[1] = (cache[reg] & 0xff00) >> 8;
data[2] = cache[reg] & 0x00ff;
- if (codec->hw_write(codec->control_data, data, 3) != 3)
+ if (i2c_master_send(uda1380->i2c, data, 3) != 3)
dev_err(codec->dev, "%s: write to reg 0x%x failed\n",
__func__, reg);
}
@@ -148,6 +154,7 @@ static void uda1380_sync_cache(struct snd_soc_codec *codec)
static int uda1380_reset(struct snd_soc_codec *codec)
{
struct uda1380_platform_data *pdata = codec->dev->platform_data;
+ struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(pdata->gpio_reset)) {
gpio_set_value(pdata->gpio_reset, 1);
@@ -160,7 +167,7 @@ static int uda1380_reset(struct snd_soc_codec *codec)
data[1] = 0;
data[2] = 0;
- if (codec->hw_write(codec->control_data, data, 3) != 3) {
+ if (i2c_master_send(uda1380->i2c, data, 3) != 3) {
dev_err(codec->dev, "%s: failed\n", __func__);
return -EIO;
}
@@ -695,9 +702,6 @@ static int uda1380_probe(struct snd_soc_codec *codec)
uda1380->codec = codec;
- codec->hw_write = (hw_write_t)i2c_master_send;
- codec->control_data = uda1380->control_data;
-
if (!gpio_is_valid(pdata->gpio_power)) {
ret = uda1380_reset(codec);
if (ret)
@@ -727,11 +731,6 @@ static const struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
.set_bias_level = uda1380_set_bias_level,
.suspend_bias_off = true,
- .reg_cache_size = ARRAY_SIZE(uda1380_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = uda1380_reg,
- .reg_cache_step = 1,
-
.component_driver = {
.controls = uda1380_snd_controls,
.num_controls = ARRAY_SIZE(uda1380_snd_controls),
@@ -771,8 +770,15 @@ static int uda1380_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ uda1380->reg_cache = devm_kmemdup(&i2c->dev,
+ uda1380_reg,
+ ARRAY_SIZE(uda1380_reg) * sizeof(u16),
+ GFP_KERNEL);
+ if (!uda1380->reg_cache)
+ return -ENOMEM;
+
i2c_set_clientdata(i2c, uda1380);
- uda1380->control_data = i2c;
+ uda1380->i2c = i2c;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index e30aacb..bcd3da2 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -288,7 +288,7 @@ static const struct snd_soc_platform_driver dummy_platform = {
.ops = &dummy_dma_ops,
};
-static struct snd_soc_codec_driver dummy_codec;
+static const struct snd_soc_codec_driver dummy_codec;
#define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig
new file mode 100644
index 0000000..02886a4
--- /dev/null
+++ b/sound/soc/uniphier/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+config SND_SOC_UNIPHIER
+ tristate "ASoC support for UniPhier"
+ depends on (ARCH_UNIPHIER || COMPILE_TEST)
+ help
+ Say Y or M if you want to add support for the Socionext
+ UniPhier SoC audio interfaces. You will also need to select the
+ audio interfaces to support below.
+ If unsure select "N".
+
+config SND_SOC_UNIPHIER_EVEA_CODEC
+ tristate "UniPhier SoC internal audio codec"
+ depends on SND_SOC_UNIPHIER
+ select REGMAP_MMIO
+ help
+ This adds Codec driver for Socionext UniPhier LD11/20 SoC
+ internal DAC. This driver supports Line In / Out and HeadPhone.
+ Select Y if you use such device.
+ If unsure select "N".
diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile
new file mode 100644
index 0000000..3be00d7
--- /dev/null
+++ b/sound/soc/uniphier/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-uniphier-evea-objs := evea.o
+obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o
diff --git a/sound/soc/uniphier/evea.c b/sound/soc/uniphier/evea.c
new file mode 100644
index 0000000..0cc9eff
--- /dev/null
+++ b/sound/soc/uniphier/evea.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Socionext UniPhier EVEA ADC/DAC codec driver.
+ *
+ * Copyright (c) 2016-2017 Socionext Inc.
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "evea"
+#define EVEA_RATES SNDRV_PCM_RATE_48000
+#define EVEA_FORMATS SNDRV_PCM_FMTBIT_S32_LE
+
+#define AADCPOW(n) (0x0078 + 0x04 * (n))
+#define AADCPOW_AADC_POWD BIT(0)
+#define AHPOUTPOW 0x0098
+#define AHPOUTPOW_HP_ON BIT(4)
+#define ALINEPOW 0x009c
+#define ALINEPOW_LIN2_POWD BIT(3)
+#define ALINEPOW_LIN1_POWD BIT(4)
+#define ALO1OUTPOW 0x00a8
+#define ALO1OUTPOW_LO1_ON BIT(4)
+#define ALO2OUTPOW 0x00ac
+#define ALO2OUTPOW_ADAC2_MUTE BIT(0)
+#define ALO2OUTPOW_LO2_ON BIT(4)
+#define AANAPOW 0x00b8
+#define AANAPOW_A_POWD BIT(4)
+#define ADACSEQ1(n) (0x0144 + 0x40 * (n))
+#define ADACSEQ1_MMUTE BIT(1)
+#define ADACSEQ2(n) (0x0160 + 0x40 * (n))
+#define ADACSEQ2_ADACIN_FIX BIT(0)
+#define ADAC1ODC 0x0200
+#define ADAC1ODC_HP_DIS_RES_MASK GENMASK(2, 1)
+#define ADAC1ODC_HP_DIS_RES_OFF (0x0 << 1)
+#define ADAC1ODC_HP_DIS_RES_ON (0x3 << 1)
+#define ADAC1ODC_ADAC_RAMPCLT_MASK GENMASK(8, 7)
+#define ADAC1ODC_ADAC_RAMPCLT_NORMAL (0x0 << 7)
+#define ADAC1ODC_ADAC_RAMPCLT_REDUCE (0x1 << 7)
+
+struct evea_priv {
+ struct clk *clk, *clk_exiv;
+ struct reset_control *rst, *rst_exiv, *rst_adamv;
+ struct regmap *regmap;
+
+ int switch_lin;
+ int switch_lo;
+ int switch_hp;
+};
+
+static const struct snd_soc_dapm_widget evea_widgets[] = {
+ SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("LIN1_LP"),
+ SND_SOC_DAPM_INPUT("LIN1_RP"),
+ SND_SOC_DAPM_INPUT("LIN2_LP"),
+ SND_SOC_DAPM_INPUT("LIN2_RP"),
+ SND_SOC_DAPM_INPUT("LIN3_LP"),
+ SND_SOC_DAPM_INPUT("LIN3_RP"),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("HP1_L"),
+ SND_SOC_DAPM_OUTPUT("HP1_R"),
+ SND_SOC_DAPM_OUTPUT("LO2_L"),
+ SND_SOC_DAPM_OUTPUT("LO2_R"),
+};
+
+static const struct snd_soc_dapm_route evea_routes[] = {
+ { "ADC", NULL, "LIN1_LP" },
+ { "ADC", NULL, "LIN1_RP" },
+ { "ADC", NULL, "LIN2_LP" },
+ { "ADC", NULL, "LIN2_RP" },
+ { "ADC", NULL, "LIN3_LP" },
+ { "ADC", NULL, "LIN3_RP" },
+
+ { "HP1_L", NULL, "DAC" },
+ { "HP1_R", NULL, "DAC" },
+ { "LO2_L", NULL, "DAC" },
+ { "LO2_R", NULL, "DAC" },
+};
+
+static void evea_set_power_state_on(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ regmap_update_bits(map, AANAPOW, AANAPOW_A_POWD,
+ AANAPOW_A_POWD);
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_ON);
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_ADAC_RAMPCLT_MASK,
+ ADAC1ODC_ADAC_RAMPCLT_REDUCE);
+
+ regmap_update_bits(map, ADACSEQ2(0), ADACSEQ2_ADACIN_FIX, 0);
+ regmap_update_bits(map, ADACSEQ2(1), ADACSEQ2_ADACIN_FIX, 0);
+ regmap_update_bits(map, ADACSEQ2(2), ADACSEQ2_ADACIN_FIX, 0);
+}
+
+static void evea_set_power_state_off(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_ON);
+
+ regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+ regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+ regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+
+ regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);
+ regmap_update_bits(map, ALO2OUTPOW, ALO2OUTPOW_LO2_ON, 0);
+ regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);
+}
+
+static int evea_update_switch_lin(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ if (evea->switch_lin) {
+ regmap_update_bits(map, ALINEPOW,
+ ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD,
+ ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD);
+
+ regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD,
+ AADCPOW_AADC_POWD);
+ regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD,
+ AADCPOW_AADC_POWD);
+ } else {
+ regmap_update_bits(map, AADCPOW(0), AADCPOW_AADC_POWD, 0);
+ regmap_update_bits(map, AADCPOW(1), AADCPOW_AADC_POWD, 0);
+
+ regmap_update_bits(map, ALINEPOW,
+ ALINEPOW_LIN2_POWD | ALINEPOW_LIN1_POWD, 0);
+ }
+
+ return 0;
+}
+
+static int evea_update_switch_lo(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ if (evea->switch_lo) {
+ regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE, 0);
+ regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE, 0);
+
+ regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON,
+ ALO1OUTPOW_LO1_ON);
+ regmap_update_bits(map, ALO2OUTPOW,
+ ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,
+ ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON);
+ } else {
+ regmap_update_bits(map, ADACSEQ1(0), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+ regmap_update_bits(map, ADACSEQ1(2), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+
+ regmap_update_bits(map, ALO1OUTPOW, ALO1OUTPOW_LO1_ON, 0);
+ regmap_update_bits(map, ALO2OUTPOW,
+ ALO2OUTPOW_ADAC2_MUTE | ALO2OUTPOW_LO2_ON,
+ 0);
+ }
+
+ return 0;
+}
+
+static int evea_update_switch_hp(struct evea_priv *evea)
+{
+ struct regmap *map = evea->regmap;
+
+ if (evea->switch_hp) {
+ regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE, 0);
+
+ regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON,
+ AHPOUTPOW_HP_ON);
+
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_OFF);
+ } else {
+ regmap_update_bits(map, ADAC1ODC, ADAC1ODC_HP_DIS_RES_MASK,
+ ADAC1ODC_HP_DIS_RES_ON);
+
+ regmap_update_bits(map, ADACSEQ1(1), ADACSEQ1_MMUTE,
+ ADACSEQ1_MMUTE);
+
+ regmap_update_bits(map, AHPOUTPOW, AHPOUTPOW_HP_ON, 0);
+ }
+
+ return 0;
+}
+
+static void evea_update_switch_all(struct evea_priv *evea)
+{
+ evea_update_switch_lin(evea);
+ evea_update_switch_lo(evea);
+ evea_update_switch_hp(evea);
+}
+
+static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = evea->switch_lin;
+
+ return 0;
+}
+
+static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ if (evea->switch_lin == ucontrol->value.integer.value[0])
+ return 0;
+
+ evea->switch_lin = ucontrol->value.integer.value[0];
+
+ return evea_update_switch_lin(evea);
+}
+
+static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = evea->switch_lo;
+
+ return 0;
+}
+
+static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ if (evea->switch_lo == ucontrol->value.integer.value[0])
+ return 0;
+
+ evea->switch_lo = ucontrol->value.integer.value[0];
+
+ return evea_update_switch_lo(evea);
+}
+
+static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = evea->switch_hp;
+
+ return 0;
+}
+
+static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ if (evea->switch_hp == ucontrol->value.integer.value[0])
+ return 0;
+
+ evea->switch_hp = ucontrol->value.integer.value[0];
+
+ return evea_update_switch_hp(evea);
+}
+
+static const struct snd_kcontrol_new eva_controls[] = {
+ SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0,
+ evea_get_switch_lin, evea_set_switch_lin),
+ SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0,
+ evea_get_switch_lo, evea_set_switch_lo),
+ SOC_SINGLE_BOOL_EXT("Headphone Playback Switch", 0,
+ evea_get_switch_hp, evea_set_switch_hp),
+};
+
+static int evea_codec_probe(struct snd_soc_codec *codec)
+{
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ evea->switch_lin = 1;
+ evea->switch_lo = 1;
+ evea->switch_hp = 1;
+
+ evea_set_power_state_on(evea);
+ evea_update_switch_all(evea);
+
+ return 0;
+}
+
+static int evea_codec_suspend(struct snd_soc_codec *codec)
+{
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+
+ evea_set_power_state_off(evea);
+
+ reset_control_assert(evea->rst_adamv);
+ reset_control_assert(evea->rst_exiv);
+ reset_control_assert(evea->rst);
+
+ clk_disable_unprepare(evea->clk_exiv);
+ clk_disable_unprepare(evea->clk);
+
+ return 0;
+}
+
+static int evea_codec_resume(struct snd_soc_codec *codec)
+{
+ struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = clk_prepare_enable(evea->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(evea->clk_exiv);
+ if (ret)
+ goto err_out_clock;
+
+ ret = reset_control_deassert(evea->rst);
+ if (ret)
+ goto err_out_clock_exiv;
+
+ ret = reset_control_deassert(evea->rst_exiv);
+ if (ret)
+ goto err_out_reset;
+
+ ret = reset_control_deassert(evea->rst_adamv);
+ if (ret)
+ goto err_out_reset_exiv;
+
+ evea_set_power_state_on(evea);
+ evea_update_switch_all(evea);
+
+ return 0;
+
+err_out_reset_exiv:
+ reset_control_assert(evea->rst_exiv);
+
+err_out_reset:
+ reset_control_assert(evea->rst);
+
+err_out_clock_exiv:
+ clk_disable_unprepare(evea->clk_exiv);
+
+err_out_clock:
+ clk_disable_unprepare(evea->clk);
+
+ return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_evea = {
+ .probe = evea_codec_probe,
+ .suspend = evea_codec_suspend,
+ .resume = evea_codec_resume,
+
+ .component_driver = {
+ .dapm_widgets = evea_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(evea_widgets),
+ .dapm_routes = evea_routes,
+ .num_dapm_routes = ARRAY_SIZE(evea_routes),
+ .controls = eva_controls,
+ .num_controls = ARRAY_SIZE(eva_controls),
+ },
+};
+
+static struct snd_soc_dai_driver soc_dai_evea[] = {
+ {
+ .name = DRV_NAME "-line1",
+ .playback = {
+ .stream_name = "Line Out 1",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .stream_name = "Line In 1",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ },
+ {
+ .name = DRV_NAME "-hp1",
+ .playback = {
+ .stream_name = "Headphone 1",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ },
+ {
+ .name = DRV_NAME "-lo2",
+ .playback = {
+ .stream_name = "Line Out 2",
+ .formats = EVEA_FORMATS,
+ .rates = EVEA_RATES,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ },
+};
+
+static const struct regmap_config evea_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xffc,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int evea_probe(struct platform_device *pdev)
+{
+ struct evea_priv *evea;
+ struct resource *res;
+ void __iomem *preg;
+ int ret;
+
+ evea = devm_kzalloc(&pdev->dev, sizeof(struct evea_priv), GFP_KERNEL);
+ if (!evea)
+ return -ENOMEM;
+
+ evea->clk = devm_clk_get(&pdev->dev, "evea");
+ if (IS_ERR(evea->clk))
+ return PTR_ERR(evea->clk);
+
+ evea->clk_exiv = devm_clk_get(&pdev->dev, "exiv");
+ if (IS_ERR(evea->clk_exiv))
+ return PTR_ERR(evea->clk_exiv);
+
+ evea->rst = devm_reset_control_get_shared(&pdev->dev, "evea");
+ if (IS_ERR(evea->rst))
+ return PTR_ERR(evea->rst);
+
+ evea->rst_exiv = devm_reset_control_get_shared(&pdev->dev, "exiv");
+ if (IS_ERR(evea->rst_exiv))
+ return PTR_ERR(evea->rst_exiv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ preg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(preg))
+ return PTR_ERR(preg);
+
+ evea->regmap = devm_regmap_init_mmio(&pdev->dev, preg,
+ &evea_regmap_config);
+ if (IS_ERR(evea->regmap))
+ return PTR_ERR(evea->regmap);
+
+ ret = clk_prepare_enable(evea->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(evea->clk_exiv);
+ if (ret)
+ goto err_out_clock;
+
+ ret = reset_control_deassert(evea->rst);
+ if (ret)
+ goto err_out_clock_exiv;
+
+ ret = reset_control_deassert(evea->rst_exiv);
+ if (ret)
+ goto err_out_reset;
+
+ /* ADAMV will hangup if EXIV reset is asserted */
+ evea->rst_adamv = devm_reset_control_get_shared(&pdev->dev, "adamv");
+ if (IS_ERR(evea->rst_adamv)) {
+ ret = PTR_ERR(evea->rst_adamv);
+ goto err_out_reset_exiv;
+ }
+
+ ret = reset_control_deassert(evea->rst_adamv);
+ if (ret)
+ goto err_out_reset_exiv;
+
+ platform_set_drvdata(pdev, evea);
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea,
+ soc_dai_evea, ARRAY_SIZE(soc_dai_evea));
+ if (ret)
+ goto err_out_reset_adamv;
+
+ return 0;
+
+err_out_reset_adamv:
+ reset_control_assert(evea->rst_adamv);
+
+err_out_reset_exiv:
+ reset_control_assert(evea->rst_exiv);
+
+err_out_reset:
+ reset_control_assert(evea->rst);
+
+err_out_clock_exiv:
+ clk_disable_unprepare(evea->clk_exiv);
+
+err_out_clock:
+ clk_disable_unprepare(evea->clk);
+
+ return ret;
+}
+
+static int evea_remove(struct platform_device *pdev)
+{
+ struct evea_priv *evea = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ reset_control_assert(evea->rst_adamv);
+ reset_control_assert(evea->rst_exiv);
+ reset_control_assert(evea->rst);
+
+ clk_disable_unprepare(evea->clk_exiv);
+ clk_disable_unprepare(evea->clk);
+
+ return 0;
+}
+
+static const struct of_device_id evea_of_match[] = {
+ { .compatible = "socionext,uniphier-evea", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, evea_of_match);
+
+static struct platform_driver evea_codec_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(evea_of_match),
+ },
+ .probe = evea_probe,
+ .remove = evea_remove,
+};
+module_platform_driver(evea_codec_driver);
+
+MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
+MODULE_DESCRIPTION("UniPhier EVEA codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 070a688..c60a577 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -163,3 +163,7 @@ static struct platform_driver snd_soc_mop500_driver = {
};
module_platform_driver(snd_soc_mop500_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC MOP500 board driver");
+MODULE_AUTHOR("Ola Lilja");
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index f12c01d..d35ba77 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -165,3 +165,8 @@ int ux500_pcm_unregister_platform(struct platform_device *pdev)
return 0;
}
EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform);
+
+MODULE_AUTHOR("Ola Lilja");
+MODULE_AUTHOR("Roger Nilsson");
+MODULE_DESCRIPTION("ASoC UX500 driver");
+MODULE_LICENSE("GPL v2");
OpenPOWER on IntegriCloud