summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Kconfig5
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c4
-rw-r--r--sound/soc/codecs/spdif_transmitter.c2
-rw-r--r--sound/soc/codecs/tas2552.c544
-rw-r--r--sound/soc/codecs/tas2552.h129
-rw-r--r--sound/soc/codecs/tas5086.c67
-rw-r--r--sound/soc/sirf/Kconfig6
-rw-r--r--sound/soc/sirf/Makefile2
-rw-r--r--sound/soc/sirf/sirf-usp.c415
-rw-r--r--sound/soc/sirf/sirf-usp.h293
11 files changed, 1457 insertions, 12 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a445b448d41d..1090135de0ba 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_STA350 if I2C
select SND_SOC_STA529 if I2C
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
+ select SND_SOC_TAS2552 if I2C
select SND_SOC_TAS5086 if I2C
select SND_SOC_TLV320AIC23_I2C if I2C
select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
@@ -539,6 +540,10 @@ config SND_SOC_STA529
config SND_SOC_STAC9766
tristate
+config SND_SOC_TAS2552
+ tristate "Texas Instruments TAS2552 Mono Audio amplifier"
+ depends on I2C
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a29da79a4359..20afe0f0c5be 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -165,6 +165,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
+snd-soc-tas2552-objs := tas2552.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@@ -261,6 +262,7 @@ obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o
obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
+obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
index d90cb0fafcb2..06ba4923fd5a 100644
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ b/sound/soc/codecs/sirf-audio-codec.c
@@ -471,8 +471,8 @@ static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem_res);
- if (base == NULL)
- return -ENOMEM;
+ if (IS_ERR(base))
+ return PTR_ERR(base);
sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sirf_audio_codec_regmap_config);
diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c
index a078aa31052a..e0df537dd4b7 100644
--- a/sound/soc/codecs/spdif_transmitter.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -24,7 +24,7 @@
#define DRV_NAME "spdif-dit"
-#define STUB_RATES SNDRV_PCM_RATE_8000_96000
+#define STUB_RATES SNDRV_PCM_RATE_8000_192000
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
new file mode 100644
index 000000000000..23b32960ff1d
--- /dev/null
+++ b/sound/soc/codecs/tas2552.c
@@ -0,0 +1,544 @@
+/*
+ * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Dan Murphy <dmurphy@ti.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.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/tas2552-plat.h>
+
+#include "tas2552.h"
+
+static struct reg_default tas2552_reg_defs[] = {
+ {TAS2552_CFG_1, 0x22},
+ {TAS2552_CFG_3, 0x80},
+ {TAS2552_DOUT, 0x00},
+ {TAS2552_OUTPUT_DATA, 0xc0},
+ {TAS2552_PDM_CFG, 0x01},
+ {TAS2552_PGA_GAIN, 0x00},
+ {TAS2552_BOOST_PT_CTRL, 0x0f},
+ {TAS2552_RESERVED_0D, 0x00},
+ {TAS2552_LIMIT_RATE_HYS, 0x08},
+ {TAS2552_CFG_2, 0xef},
+ {TAS2552_SER_CTRL_1, 0x00},
+ {TAS2552_SER_CTRL_2, 0x00},
+ {TAS2552_PLL_CTRL_1, 0x10},
+ {TAS2552_PLL_CTRL_2, 0x00},
+ {TAS2552_PLL_CTRL_3, 0x00},
+ {TAS2552_BTIP, 0x8f},
+ {TAS2552_BTS_CTRL, 0x80},
+ {TAS2552_LIMIT_RELEASE, 0x04},
+ {TAS2552_LIMIT_INT_COUNT, 0x00},
+ {TAS2552_EDGE_RATE_CTRL, 0x40},
+ {TAS2552_VBAT_DATA, 0x00},
+};
+
+#define TAS2552_NUM_SUPPLIES 3
+static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
+ "vbat", /* vbat voltage */
+ "iovdd", /* I/O Voltage */
+ "avdd", /* Analog DAC Voltage */
+};
+
+struct tas2552_data {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct i2c_client *tas2552_client;
+ struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES];
+ struct gpio_desc *enable_gpio;
+ unsigned char regs[TAS2552_VBAT_DATA];
+ unsigned int mclk;
+};
+
+static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
+{
+ u8 cfg1_reg;
+
+ if (sw_shutdown)
+ cfg1_reg = 0;
+ else
+ cfg1_reg = TAS2552_SWS_MASK;
+
+ snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
+ TAS2552_SWS_MASK, cfg1_reg);
+}
+
+static int tas2552_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 tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ int sample_rate, pll_clk;
+ int d;
+ u8 p, j;
+
+ /* Turn on Class D amplifier */
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK,
+ TAS2552_CLASSD_EN);
+
+ if (!tas2552->mclk)
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
+
+ if (tas2552->mclk == TAS2552_245MHZ_CLK ||
+ tas2552->mclk == TAS2552_225MHZ_CLK) {
+ /* By pass the PLL configuration */
+ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
+ TAS2552_PLL_BYPASS_MASK,
+ TAS2552_PLL_BYPASS);
+ } else {
+ /* Fill in the PLL control registers for J & D
+ * PLL_CLK = (.5 * freq * J.D) / 2^p
+ * Need to fill in J and D here based on incoming freq
+ */
+ p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
+ p = (p >> 7);
+ sample_rate = params_rate(params);
+
+ if (sample_rate == 48000)
+ pll_clk = TAS2552_245MHZ_CLK;
+ else if (sample_rate == 44100)
+ pll_clk = TAS2552_225MHZ_CLK;
+ else {
+ dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ j = (pll_clk * 2 * (1 << p)) / tas2552->mclk;
+ d = (pll_clk * 2 * (1 << p)) % tas2552->mclk;
+
+ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
+ TAS2552_PLL_J_MASK, j);
+ snd_soc_write(codec, TAS2552_PLL_CTRL_2,
+ (d >> 7) & TAS2552_PLL_D_UPPER_MASK);
+ snd_soc_write(codec, TAS2552_PLL_CTRL_3,
+ d & TAS2552_PLL_D_LOWER_MASK);
+
+ }
+
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
+ TAS2552_PLL_ENABLE);
+
+ return 0;
+}
+
+static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 serial_format;
+ u8 serial_control_mask;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ serial_format = 0x00;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ serial_format = TAS2552_WORD_CLK_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ serial_format = TAS2552_BIT_CLK_MASK;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK);
+ break;
+ default:
+ dev_vdbg(codec->dev, "DAI Format master is not found\n");
+ return -EINVAL;
+ }
+
+ serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ serial_format &= TAS2552_DAIFMT_I2S_MASK;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ serial_format |= TAS2552_DAIFMT_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ serial_format |= TAS2552_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ serial_format |= TAS2552_DAIFMT_LEFT_J;
+ break;
+ default:
+ dev_vdbg(codec->dev, "DAI Format is not found\n");
+ return -EINVAL;
+ }
+
+ if (fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ serial_control_mask |= TAS2552_DATA_FORMAT_MASK;
+
+ snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask,
+ serial_format);
+
+ return 0;
+}
+
+static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+
+ tas2552->mclk = freq;
+
+ return 0;
+}
+
+static int tas2552_mute(struct snd_soc_dai *dai, int mute)
+{
+ u8 cfg1_reg;
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute)
+ cfg1_reg = TAS2552_MUTE_MASK;
+ else
+ cfg1_reg = ~TAS2552_MUTE_MASK;
+
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int tas2552_runtime_suspend(struct device *dev)
+{
+ struct tas2552_data *tas2552 = dev_get_drvdata(dev);
+
+ tas2552_sw_shutdown(tas2552, 0);
+
+ regcache_cache_only(tas2552->regmap, true);
+ regcache_mark_dirty(tas2552->regmap);
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 0);
+
+ return 0;
+}
+
+static int tas2552_runtime_resume(struct device *dev)
+{
+ struct tas2552_data *tas2552 = dev_get_drvdata(dev);
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 1);
+
+ tas2552_sw_shutdown(tas2552, 1);
+
+ regcache_cache_only(tas2552->regmap, false);
+ regcache_sync(tas2552->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops tas2552_pm = {
+ SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume,
+ NULL)
+};
+
+static void tas2552_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
+}
+
+static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
+ .hw_params = tas2552_hw_params,
+ .set_sysclk = tas2552_set_dai_sysclk,
+ .set_fmt = tas2552_set_dai_fmt,
+ .shutdown = tas2552_shutdown,
+ .digital_mute = tas2552_mute,
+};
+
+/* Formats supported by TAS2552 driver. */
+#define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* TAS2552 dai structure. */
+static struct snd_soc_dai_driver tas2552_dai[] = {
+ {
+ .name = "tas2552-amplifier",
+ .playback = {
+ .stream_name = "Speaker",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2552_FORMATS,
+ },
+ .ops = &tas2552_speaker_dai_ops,
+ },
+};
+
+/*
+ * DAC digital volumes. From -7 to 24 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
+
+static const struct snd_kcontrol_new tas2552_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Driver Playback Volume",
+ TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
+};
+
+static const struct reg_default tas2552_init_regs[] = {
+ { TAS2552_RESERVED_0D, 0xc0 },
+};
+
+static int tas2552_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ tas2552->codec = codec;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
+ tas2552->supplies);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 1);
+
+ ret = pm_runtime_get_sync(codec->dev);
+ if (ret < 0) {
+ dev_err(codec->dev, "Enabling device failed: %d\n",
+ ret);
+ goto probe_fail;
+ }
+
+ snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK |
+ TAS2552_PLL_SRC_BCLK);
+ snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
+ TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ);
+ snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
+ snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
+ snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL);
+ snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
+ TAS2552_APT_THRESH_2_1_7);
+
+ ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs,
+ ARRAY_SIZE(tas2552_init_regs));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to write init registers: %d\n",
+ ret);
+ goto patch_fail;
+ }
+
+ snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN |
+ TAS2552_BOOST_EN | TAS2552_APT_EN |
+ TAS2552_LIM_EN);
+ return 0;
+
+patch_fail:
+ pm_runtime_put(codec->dev);
+probe_fail:
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
+ tas2552->supplies);
+ return -EIO;
+}
+
+static int tas2552_codec_remove(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+
+ pm_runtime_put(codec->dev);
+
+ if (tas2552->enable_gpio)
+ gpiod_set_value(tas2552->enable_gpio, 0);
+
+ return 0;
+};
+
+#ifdef CONFIG_PM
+static int tas2552_suspend(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
+ tas2552->supplies);
+
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to disable supplies: %d\n",
+ ret);
+ return 0;
+}
+
+static int tas2552_resume(struct snd_soc_codec *codec)
+{
+ struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
+ tas2552->supplies);
+
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to enable supplies: %d\n",
+ ret);
+ }
+
+ return 0;
+}
+#else
+#define tas2552_suspend NULL
+#define tas2552_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
+ .probe = tas2552_codec_probe,
+ .remove = tas2552_codec_remove,
+ .suspend = tas2552_suspend,
+ .resume = tas2552_resume,
+ .controls = tas2552_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2552_snd_controls),
+};
+
+static const struct regmap_config tas2552_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS2552_MAX_REG,
+ .reg_defaults = tas2552_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tas2552_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev;
+ struct tas2552_data *data;
+ int ret;
+ int i;
+
+ dev = &client->dev;
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->enable_gpio = devm_gpiod_get(dev, "enable");
+ if (IS_ERR(data->enable_gpio)) {
+ ret = PTR_ERR(data->enable_gpio);
+ if (ret != -ENOENT && ret != -ENOSYS)
+ return ret;
+
+ data->enable_gpio = NULL;
+ } else {
+ gpiod_direction_output(data->enable_gpio, 0);
+ }
+
+ data->tas2552_client = client;
+ data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ ret = PTR_ERR(data->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+ data->supplies[i].supply = tas2552_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+ data->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_sync_autosuspend(&client->dev);
+
+ dev_set_drvdata(&client->dev, data);
+
+ ret = snd_soc_register_codec(&client->dev,
+ &soc_codec_dev_tas2552,
+ tas2552_dai, ARRAY_SIZE(tas2552_dai));
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int tas2552_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id tas2552_id[] = {
+ { "tas2552", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2552_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tas2552_of_match[] = {
+ { .compatible = "ti,tas2552", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2552_of_match);
+#endif
+
+static struct i2c_driver tas2552_i2c_driver = {
+ .driver = {
+ .name = "tas2552",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tas2552_of_match),
+ .pm = &tas2552_pm,
+ },
+ .probe = tas2552_probe,
+ .remove = tas2552_i2c_remove,
+ .id_table = tas2552_id,
+};
+
+module_i2c_driver(tas2552_i2c_driver);
+
+MODULE_AUTHOR("Dan Muprhy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("TAS2552 Audio amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
new file mode 100644
index 000000000000..6cea8f31bf88
--- /dev/null
+++ b/sound/soc/codecs/tas2552.h
@@ -0,0 +1,129 @@
+/*
+ * tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Dan Murphy <dmurphy@ti.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.
+ *
+ * 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.
+ */
+
+#ifndef __TAS2552_H__
+#define __TAS2552_H__
+
+/* Register Address Map */
+#define TAS2552_DEVICE_STATUS 0x00
+#define TAS2552_CFG_1 0x01
+#define TAS2552_CFG_2 0x02
+#define TAS2552_CFG_3 0x03
+#define TAS2552_DOUT 0x04
+#define TAS2552_SER_CTRL_1 0x05
+#define TAS2552_SER_CTRL_2 0x06
+#define TAS2552_OUTPUT_DATA 0x07
+#define TAS2552_PLL_CTRL_1 0x08
+#define TAS2552_PLL_CTRL_2 0x09
+#define TAS2552_PLL_CTRL_3 0x0a
+#define TAS2552_BTIP 0x0b
+#define TAS2552_BTS_CTRL 0x0c
+#define TAS2552_RESERVED_0D 0x0d
+#define TAS2552_LIMIT_RATE_HYS 0x0e
+#define TAS2552_LIMIT_RELEASE 0x0f
+#define TAS2552_LIMIT_INT_COUNT 0x10
+#define TAS2552_PDM_CFG 0x11
+#define TAS2552_PGA_GAIN 0x12
+#define TAS2552_EDGE_RATE_CTRL 0x13
+#define TAS2552_BOOST_PT_CTRL 0x14
+#define TAS2552_VER_NUM 0x16
+#define TAS2552_VBAT_DATA 0x19
+#define TAS2552_MAX_REG 0x20
+
+/* CFG1 Register Masks */
+#define TAS2552_MUTE_MASK (1 << 2)
+#define TAS2552_SWS_MASK (1 << 1)
+#define TAS2552_WCLK_MASK 0x07
+#define TAS2552_CLASSD_EN_MASK (1 << 7)
+
+/* CFG2 Register Masks */
+#define TAS2552_CLASSD_EN (1 << 7)
+#define TAS2552_BOOST_EN (1 << 6)
+#define TAS2552_APT_EN (1 << 5)
+#define TAS2552_PLL_ENABLE (1 << 3)
+#define TAS2552_LIM_EN (1 << 2)
+#define TAS2552_IVSENSE_EN (1 << 1)
+
+/* CFG3 Register Masks */
+#define TAS2552_WORD_CLK_MASK (1 << 7)
+#define TAS2552_BIT_CLK_MASK (1 << 6)
+#define TAS2552_DATA_FORMAT_MASK (0x11 << 2)
+
+#define TAS2552_DAIFMT_I2S_MASK 0xf3
+#define TAS2552_DAIFMT_DSP (1 << 3)
+#define TAS2552_DAIFMT_RIGHT_J (1 << 4)
+#define TAS2552_DAIFMT_LEFT_J (0x11 << 3)
+
+#define TAS2552_PLL_SRC_MCLK 0x00
+#define TAS2552_PLL_SRC_BCLK (1 << 3)
+#define TAS2552_PLL_SRC_IVCLKIN (1 << 4)
+#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3)
+
+#define TAS2552_DIN_SRC_SEL_MUTED 0x00
+#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4)
+#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5)
+#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4)
+
+#define TAS2552_PDM_IN_SEL (1 << 5)
+#define TAS2552_I2S_OUT_SEL (1 << 6)
+#define TAS2552_ANALOG_IN_SEL (1 << 7)
+
+/* CFG3 WCLK Dividers */
+#define TAS2552_8KHZ 0x00
+#define TAS2552_11_12KHZ (1 << 1)
+#define TAS2552_16KHZ (1 << 2)
+#define TAS2552_22_24KHZ (1 << 3)
+#define TAS2552_32KHZ (1 << 4)
+#define TAS2552_44_48KHZ (1 << 5)
+#define TAS2552_88_96KHZ (1 << 6)
+#define TAS2552_176_192KHZ (1 << 7)
+
+/* OUTPUT_DATA register */
+#define TAS2552_PDM_DATA_I 0x00
+#define TAS2552_PDM_DATA_V (1 << 6)
+#define TAS2552_PDM_DATA_I_V (1 << 7)
+#define TAS2552_PDM_DATA_V_I (0x11 << 6)
+
+/* PDM CFG Register */
+#define TAS2552_PDM_DATA_ES_RISE 0x4
+
+#define TAS2552_PDM_PLL_CLK_SEL 0x00
+#define TAS2552_PDM_IV_CLK_SEL (1 << 1)
+#define TAS2552_PDM_BCLK_SEL (1 << 2)
+#define TAS2552_PDM_MCLK_SEL (1 << 3)
+
+/* Boost pass-through register */
+#define TAS2552_APT_DELAY_50 0x00
+#define TAS2552_APT_DELAY_75 (1 << 1)
+#define TAS2552_APT_DELAY_125 (1 << 2)
+#define TAS2552_APT_DELAY_200 (1 << 3)
+
+#define TAS2552_APT_THRESH_2_5 0x00
+#define TAS2552_APT_THRESH_1_7 (1 << 3)
+#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4)
+#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2)
+
+/* PLL Control Register */
+#define TAS2552_245MHZ_CLK 24576000
+#define TAS2552_225MHZ_CLK 22579200
+#define TAS2552_PLL_J_MASK 0x7f
+#define TAS2552_PLL_D_UPPER_MASK 0x3f
+#define TAS2552_PLL_D_LOWER_MASK 0xff
+#define TAS2552_PLL_BYPASS_MASK 0x80
+#define TAS2552_PLL_BYPASS 0x80
+
+#endif
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index d48491a4a19d..be7194b43b7a 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -36,6 +36,7 @@
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -240,6 +241,10 @@ static int tas5086_reg_read(void *context, unsigned int reg,
return 0;
}
+static const char * const supply_names[] = {
+ "dvdd", "avdd"
+};
+
struct tas5086_private {
struct regmap *regmap;
unsigned int mclk, sclk;
@@ -251,6 +256,7 @@ struct tas5086_private {
int rate;
/* GPIO driving Reset pin, if any */
int gpio_nreset;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static int tas5086_deemph[] = { 0, 32000, 44100, 48000 };
@@ -773,6 +779,8 @@ static int tas5086_soc_suspend(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
return 0;
}
@@ -781,6 +789,10 @@ static int tas5086_soc_resume(struct snd_soc_codec *codec)
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
int ret;
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (ret < 0)
+ return ret;
+
tas5086_reset(priv);
regcache_mark_dirty(priv->regmap);
@@ -812,6 +824,12 @@ static int tas5086_probe(struct snd_soc_codec *codec)
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
int i, ret;
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
priv->pwm_start_mid_z = 0;
priv->charge_period = 1300000; /* hardware default is 1300 ms */
@@ -832,16 +850,22 @@ static int tas5086_probe(struct snd_soc_codec *codec)
}
}
+ tas5086_reset(priv);
ret = tas5086_init(codec->dev, priv);
if (ret < 0)
- return ret;
+ goto exit_disable_regulators;
/* set master volume to 0 dB */
ret = regmap_write(priv->regmap, TAS5086_MASTER_VOL, 0x30);
if (ret < 0)
- return ret;
+ goto exit_disable_regulators;
return 0;
+
+exit_disable_regulators:
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
+ return ret;
}
static int tas5086_remove(struct snd_soc_codec *codec)
@@ -852,6 +876,8 @@ static int tas5086_remove(struct snd_soc_codec *codec)
/* Set codec to the reset state */
gpio_set_value(priv->gpio_nreset, 0);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
return 0;
};
@@ -900,6 +926,16 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
if (!priv)
return -ENOMEM;
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
+ }
+
priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
@@ -919,21 +955,34 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
gpio_nreset = -EINVAL;
priv->gpio_nreset = gpio_nreset;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
tas5086_reset(priv);
/* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */
ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i);
- if (ret < 0)
- return ret;
-
- if (i != 0x3) {
+ if (ret == 0 && i != 0x3) {
dev_err(dev,
"Failed to identify TAS5086 codec (got %02x)\n", i);
- return -ENODEV;
+ ret = -ENODEV;
}
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086,
- &tas5086_dai, 1);
+ /*
+ * The chip has been identified, so we can turn off the power
+ * again until the dai link is set up.
+ */
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+
+ if (ret == 0)
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086,
+ &tas5086_dai, 1);
+
+ return ret;
}
static int tas5086_i2c_remove(struct i2c_client *i2c)
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index 89e89429b04a..840058dcad09 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -12,3 +12,9 @@ config SND_SOC_SIRF_AUDIO
config SND_SOC_SIRF_AUDIO_PORT
select REGMAP_MMIO
tristate
+
+config SND_SOC_SIRF_USP
+ tristate "SoC Audio (I2S protocol) for SiRF SoC USP interface"
+ depends on SND_SOC_SIRF
+ select REGMAP_MMIO
+ tristate
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 913b93231d4e..dd917f20f12f 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,5 +1,7 @@
snd-soc-sirf-audio-objs := sirf-audio.o
snd-soc-sirf-audio-port-objs := sirf-audio-port.o
+snd-soc-sirf-usp-objs := sirf-usp.o
obj-$(CONFIG_SND_SOC_SIRF_AUDIO) += snd-soc-sirf-audio.o
obj-$(CONFIG_SND_SOC_SIRF_AUDIO_PORT) += snd-soc-sirf-audio-port.o
+obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c
new file mode 100644
index 000000000000..3a730374e259
--- /dev/null
+++ b/sound/soc/sirf/sirf-usp.c
@@ -0,0 +1,415 @@
+/*
+ * SiRF USP in I2S/DSP mode
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "sirf-usp.h"
+
+struct sirf_usp {
+ struct regmap *regmap;
+ struct clk *clk;
+ u32 mode1_reg;
+ u32 mode2_reg;
+ int daifmt_format;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+};
+
+static void sirf_usp_tx_enable(struct sirf_usp *usp)
+{
+ regmap_update_bits(usp->regmap, USP_TX_FIFO_OP,
+ USP_TX_FIFO_RESET, USP_TX_FIFO_RESET);
+ regmap_write(usp->regmap, USP_TX_FIFO_OP, 0);
+
+ regmap_update_bits(usp->regmap, USP_TX_FIFO_OP,
+ USP_TX_FIFO_START, USP_TX_FIFO_START);
+
+ regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
+ USP_TX_ENA, USP_TX_ENA);
+}
+
+static void sirf_usp_tx_disable(struct sirf_usp *usp)
+{
+ regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
+ USP_TX_ENA, ~USP_TX_ENA);
+ /* FIFO stop */
+ regmap_write(usp->regmap, USP_TX_FIFO_OP, 0);
+}
+
+static void sirf_usp_rx_enable(struct sirf_usp *usp)
+{
+ regmap_update_bits(usp->regmap, USP_RX_FIFO_OP,
+ USP_RX_FIFO_RESET, USP_RX_FIFO_RESET);
+ regmap_write(usp->regmap, USP_RX_FIFO_OP, 0);
+
+ regmap_update_bits(usp->regmap, USP_RX_FIFO_OP,
+ USP_RX_FIFO_START, USP_RX_FIFO_START);
+
+ regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
+ USP_RX_ENA, USP_RX_ENA);
+}
+
+static void sirf_usp_rx_disable(struct sirf_usp *usp)
+{
+ regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE,
+ USP_RX_ENA, ~USP_RX_ENA);
+ /* FIFO stop */
+ regmap_write(usp->regmap, USP_RX_FIFO_OP, 0);
+}
+
+static int sirf_usp_pcm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
+ snd_soc_dai_init_dma_data(dai, &usp->playback_dma_data,
+ &usp->capture_dma_data);
+ return 0;
+}
+
+static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ default:
+ dev_err(dai->dev, "Only CBM and CFM supported\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ usp->daifmt_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ break;
+ default:
+ dev_err(dai->dev, "Only I2S and DSP_A format supported\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void sirf_usp_i2s_init(struct sirf_usp *usp)
+{
+ /* Configure RISC mode */
+ regmap_update_bits(usp->regmap, USP_RISC_DSP_MODE,
+ USP_RISC_DSP_SEL, ~USP_RISC_DSP_SEL);
+
+ /*
+ * Configure DMA IO Length register
+ * Set no limit, USP can receive data continuously until it is diabled
+ */
+ regmap_write(usp->regmap, USP_TX_DMA_IO_LEN, 0);
+ regmap_write(usp->regmap, USP_RX_DMA_IO_LEN, 0);
+
+ /* Configure Mode2 register */
+ regmap_write(usp->regmap, USP_MODE2, (1 << USP_RXD_DELAY_LEN_OFFSET) |
+ (0 << USP_TXD_DELAY_LEN_OFFSET) |
+ USP_TFS_CLK_SLAVE_MODE | USP_RFS_CLK_SLAVE_MODE);
+
+ /* Configure Mode1 register */
+ regmap_write(usp->regmap, USP_MODE1,
+ USP_SYNC_MODE | USP_EN | USP_TXD_ACT_EDGE_FALLING |
+ USP_RFS_ACT_LEVEL_LOGIC1 | USP_TFS_ACT_LEVEL_LOGIC1 |
+ USP_TX_UFLOW_REPEAT_ZERO | USP_CLOCK_MODE_SLAVE);
+
+ /* Configure RX DMA IO Control register */
+ regmap_write(usp->regmap, USP_RX_DMA_IO_CTRL, 0);
+
+ /* Congiure RX FIFO Control register */
+ regmap_write(usp->regmap, USP_RX_FIFO_CTRL,
+ (USP_RX_FIFO_THRESHOLD << USP_RX_FIFO_THD_OFFSET) |
+ (USP_TX_RX_FIFO_WIDTH_DWORD << USP_RX_FIFO_WIDTH_OFFSET));
+
+ /* Congiure RX FIFO Level Check register */
+ regmap_write(usp->regmap, USP_RX_FIFO_LEVEL_CHK,
+ RX_FIFO_SC(0x04) | RX_FIFO_LC(0x0E) | RX_FIFO_HC(0x1B));
+
+ /* Configure TX DMA IO Control register*/
+ regmap_write(usp->regmap, USP_TX_DMA_IO_CTRL, 0);
+
+ /* Configure TX FIFO Control register */
+ regmap_write(usp->regmap, USP_TX_FIFO_CTRL,
+ (USP_TX_FIFO_THRESHOLD << USP_TX_FIFO_THD_OFFSET) |
+ (USP_TX_RX_FIFO_WIDTH_DWORD << USP_TX_FIFO_WIDTH_OFFSET));
+ /* Congiure TX FIFO Level Check register */
+ regmap_write(usp->regmap, USP_TX_FIFO_LEVEL_CHK,
+ TX_FIFO_SC(0x1B) | TX_FIFO_LC(0x0E) | TX_FIFO_HC(0x04));
+}
+
+static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
+ u32 data_len, frame_len, shifter_len;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ data_len = 16;
+ frame_len = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ data_len = 24;
+ frame_len = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ data_len = 24;
+ frame_len = 24;
+ break;
+ default:
+ dev_err(dai->dev, "Format unsupported\n");
+ return -EINVAL;
+ }
+
+ shifter_len = data_len;
+
+ switch (usp->daifmt_format) {
+ case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
+ USP_I2S_SYNC_CHG, USP_I2S_SYNC_CHG);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
+ USP_I2S_SYNC_CHG, 0);
+ frame_len = data_len * params_channels(params);
+ data_len = frame_len;
+ break;
+ default:
+ dev_err(dai->dev, "Only support I2S and DSP_A mode\n");
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(usp->regmap, USP_TX_FRAME_CTRL,
+ USP_TXC_DATA_LEN_MASK | USP_TXC_FRAME_LEN_MASK
+ | USP_TXC_SHIFTER_LEN_MASK | USP_TXC_SLAVE_CLK_SAMPLE,
+ ((data_len - 1) << USP_TXC_DATA_LEN_OFFSET)
+ | ((frame_len - 1) << USP_TXC_FRAME_LEN_OFFSET)
+ | ((shifter_len - 1) << USP_TXC_SHIFTER_LEN_OFFSET)
+ | USP_TXC_SLAVE_CLK_SAMPLE);
+ else
+ regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL,
+ USP_RXC_DATA_LEN_MASK | USP_RXC_FRAME_LEN_MASK
+ | USP_RXC_SHIFTER_LEN_MASK | USP_SINGLE_SYNC_MODE,
+ ((data_len - 1) << USP_RXC_DATA_LEN_OFFSET)
+ | ((frame_len - 1) << USP_RXC_FRAME_LEN_OFFSET)
+ | ((shifter_len - 1) << USP_RXC_SHIFTER_LEN_OFFSET)
+ | USP_SINGLE_SYNC_MODE);
+
+ return 0;
+}
+
+static int sirf_usp_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sirf_usp_tx_enable(usp);
+ else
+ sirf_usp_rx_enable(usp);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sirf_usp_tx_disable(usp);
+ else
+ sirf_usp_rx_disable(usp);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sirf_usp_pcm_dai_ops = {
+ .trigger = sirf_usp_pcm_trigger,
+ .set_fmt = sirf_usp_pcm_set_dai_fmt,
+ .hw_params = sirf_usp_pcm_hw_params,
+};
+
+static struct snd_soc_dai_driver sirf_usp_pcm_dai = {
+ .probe = sirf_usp_pcm_dai_probe,
+ .name = "sirf-usp-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "SiRF USP PCM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ },
+ .capture = {
+ .stream_name = "SiRF USP PCM Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
+ },
+ .ops = &sirf_usp_pcm_dai_ops,
+};
+
+static int sirf_usp_pcm_runtime_suspend(struct device *dev)
+{
+ struct sirf_usp *usp = dev_get_drvdata(dev);
+ clk_disable_unprepare(usp->clk);
+ return 0;
+}
+
+static int sirf_usp_pcm_runtime_resume(struct device *dev)
+{
+ struct sirf_usp *usp = dev_get_drvdata(dev);
+ int ret;
+ ret = clk_prepare_enable(usp->clk);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+ sirf_usp_i2s_init(usp);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_usp_pcm_suspend(struct device *dev)
+{
+ struct sirf_usp *usp = dev_get_drvdata(dev);
+
+ if (!pm_runtime_status_suspended(dev)) {
+ regmap_read(usp->regmap, USP_MODE1, &usp->mode1_reg);
+ regmap_read(usp->regmap, USP_MODE2, &usp->mode2_reg);
+ sirf_usp_pcm_runtime_suspend(dev);
+ }
+ return 0;
+}
+
+static int sirf_usp_pcm_resume(struct device *dev)
+{
+ struct sirf_usp *usp = dev_get_drvdata(dev);
+ int ret;
+
+ if (!pm_runtime_status_suspended(dev)) {
+ ret = sirf_usp_pcm_runtime_resume(dev);
+ if (ret)
+ return ret;
+ regmap_write(usp->regmap, USP_MODE1, usp->mode1_reg);
+ regmap_write(usp->regmap, USP_MODE2, usp->mode2_reg);
+ }
+ return 0;
+}
+#endif
+
+static const struct snd_soc_component_driver sirf_usp_component = {
+ .name = "sirf-usp",
+};
+
+static const struct regmap_config sirf_usp_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = USP_RX_FIFO_DATA,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int sirf_usp_pcm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct sirf_usp *usp;
+ void __iomem *base;
+ struct resource *mem_res;
+
+ usp = devm_kzalloc(&pdev->dev, sizeof(struct sirf_usp),
+ GFP_KERNEL);
+ if (!usp)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, usp);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap(&pdev->dev, mem_res->start,
+ resource_size(mem_res));
+ if (base == NULL)
+ return -ENOMEM;
+ usp->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sirf_usp_regmap_config);
+ if (IS_ERR(usp->regmap))
+ return PTR_ERR(usp->regmap);
+
+ usp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(usp->clk)) {
+ dev_err(&pdev->dev, "Get clock failed.\n");
+ return PTR_ERR(usp->clk);
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = sirf_usp_pcm_runtime_resume(&pdev->dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &sirf_usp_component,
+ &sirf_usp_pcm_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Register Audio SoC dai failed.\n");
+ return ret;
+ }
+ return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+}
+
+static int sirf_usp_pcm_remove(struct platform_device *pdev)
+{
+ if (!pm_runtime_enabled(&pdev->dev))
+ sirf_usp_pcm_runtime_suspend(&pdev->dev);
+ else
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id sirf_usp_pcm_of_match[] = {
+ { .compatible = "sirf,prima2-usp-pcm", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sirf_usp_pcm_of_match);
+
+static const struct dev_pm_ops sirf_usp_pcm_pm_ops = {
+ SET_RUNTIME_PM_OPS(sirf_usp_pcm_runtime_suspend,
+ sirf_usp_pcm_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(sirf_usp_pcm_suspend, sirf_usp_pcm_resume)
+};
+
+static struct platform_driver sirf_usp_pcm_driver = {
+ .driver = {
+ .name = "sirf-usp-pcm",
+ .owner = THIS_MODULE,
+ .of_match_table = sirf_usp_pcm_of_match,
+ .pm = &sirf_usp_pcm_pm_ops,
+ },
+ .probe = sirf_usp_pcm_probe,
+ .remove = sirf_usp_pcm_remove,
+};
+
+module_platform_driver(sirf_usp_pcm_driver);
+
+MODULE_DESCRIPTION("SiRF SoC USP PCM bus driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sirf/sirf-usp.h b/sound/soc/sirf/sirf-usp.h
new file mode 100644
index 000000000000..bf0201cb15bc
--- /dev/null
+++ b/sound/soc/sirf/sirf-usp.h
@@ -0,0 +1,293 @@
+/*
+ * arch/arm/mach-prima2/include/mach/sirfsoc_usp.h
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef _SIRF_USP_H
+#define _SIRF_USP_H
+
+/* USP Registers */
+#define USP_MODE1 0x00
+#define USP_MODE2 0x04
+#define USP_TX_FRAME_CTRL 0x08
+#define USP_RX_FRAME_CTRL 0x0C
+#define USP_TX_RX_ENABLE 0x10
+#define USP_INT_ENABLE 0x14
+#define USP_INT_STATUS 0x18
+#define USP_PIN_IO_DATA 0x1C
+#define USP_RISC_DSP_MODE 0x20
+#define USP_AYSNC_PARAM_REG 0x24
+#define USP_IRDA_X_MODE_DIV 0x28
+#define USP_SM_CFG 0x2C
+#define USP_TX_DMA_IO_CTRL 0x100
+#define USP_TX_DMA_IO_LEN 0x104
+#define USP_TX_FIFO_CTRL 0x108
+#define USP_TX_FIFO_LEVEL_CHK 0x10C
+#define USP_TX_FIFO_OP 0x110
+#define USP_TX_FIFO_STATUS 0x114
+#define USP_TX_FIFO_DATA 0x118
+#define USP_RX_DMA_IO_CTRL 0x120
+#define USP_RX_DMA_IO_LEN 0x124
+#define USP_RX_FIFO_CTRL 0x128
+#define USP_RX_FIFO_LEVEL_CHK 0x12C
+#define USP_RX_FIFO_OP 0x130
+#define USP_RX_FIFO_STATUS 0x134
+#define USP_RX_FIFO_DATA 0x138
+
+/* USP MODE register-1 */
+#define USP_SYNC_MODE 0x00000001
+#define USP_CLOCK_MODE_SLAVE 0x00000002
+#define USP_LOOP_BACK_EN 0x00000004
+#define USP_HPSIR_EN 0x00000008
+#define USP_ENDIAN_CTRL_LSBF 0x00000010
+#define USP_EN 0x00000020
+#define USP_RXD_ACT_EDGE_FALLING 0x00000040
+#define USP_TXD_ACT_EDGE_FALLING 0x00000080
+#define USP_RFS_ACT_LEVEL_LOGIC1 0x00000100
+#define USP_TFS_ACT_LEVEL_LOGIC1 0x00000200
+#define USP_SCLK_IDLE_MODE_TOGGLE 0x00000400
+#define USP_SCLK_IDLE_LEVEL_LOGIC1 0x00000800
+#define USP_SCLK_PIN_MODE_IO 0x00001000
+#define USP_RFS_PIN_MODE_IO 0x00002000
+#define USP_TFS_PIN_MODE_IO 0x00004000
+#define USP_RXD_PIN_MODE_IO 0x00008000
+#define USP_TXD_PIN_MODE_IO 0x00010000
+#define USP_SCLK_IO_MODE_INPUT 0x00020000
+#define USP_RFS_IO_MODE_INPUT 0x00040000
+#define USP_TFS_IO_MODE_INPUT 0x00080000
+#define USP_RXD_IO_MODE_INPUT 0x00100000
+#define USP_TXD_IO_MODE_INPUT 0x00200000
+#define USP_IRDA_WIDTH_DIV_MASK 0x3FC00000
+#define USP_IRDA_WIDTH_DIV_OFFSET 0
+#define USP_IRDA_IDLE_LEVEL_HIGH 0x40000000
+#define USP_TX_UFLOW_REPEAT_ZERO 0x80000000
+#define USP_TX_ENDIAN_MODE 0x00000020
+#define USP_RX_ENDIAN_MODE 0x00000020
+
+/* USP Mode Register-2 */
+#define USP_RXD_DELAY_LEN_MASK 0x000000FF
+#define USP_RXD_DELAY_LEN_OFFSET 0
+
+#define USP_TXD_DELAY_LEN_MASK 0x0000FF00
+#define USP_TXD_DELAY_LEN_OFFSET 8
+
+#define USP_ENA_CTRL_MODE 0x00010000
+#define USP_FRAME_CTRL_MODE 0x00020000
+#define USP_TFS_SOURCE_MODE 0x00040000
+#define USP_TFS_MS_MODE 0x00080000
+#define USP_CLK_DIVISOR_MASK 0x7FE00000
+#define USP_CLK_DIVISOR_OFFSET 21
+
+#define USP_TFS_CLK_SLAVE_MODE (1<<20)
+#define USP_RFS_CLK_SLAVE_MODE (1<<19)
+
+#define USP_IRDA_DATA_WIDTH 0x80000000
+
+/* USP Transmit Frame Control Register */
+
+#define USP_TXC_DATA_LEN_MASK 0x000000FF
+#define USP_TXC_DATA_LEN_OFFSET 0
+
+#define USP_TXC_SYNC_LEN_MASK 0x0000FF00
+#define USP_TXC_SYNC_LEN_OFFSET 8
+
+#define USP_TXC_FRAME_LEN_MASK 0x00FF0000
+#define USP_TXC_FRAME_LEN_OFFSET 16
+
+#define USP_TXC_SHIFTER_LEN_MASK 0x1F000000
+#define USP_TXC_SHIFTER_LEN_OFFSET 24
+
+#define USP_TXC_SLAVE_CLK_SAMPLE 0x20000000
+
+#define USP_TXC_CLK_DIVISOR_MASK 0xC0000000
+#define USP_TXC_CLK_DIVISOR_OFFSET 30
+
+/* USP Receive Frame Control Register */
+
+#define USP_RXC_DATA_LEN_MASK 0x000000FF
+#define USP_RXC_DATA_LEN_OFFSET 0
+
+#define USP_RXC_FRAME_LEN_MASK 0x0000FF00
+#define USP_RXC_FRAME_LEN_OFFSET 8
+
+#define USP_RXC_SHIFTER_LEN_MASK 0x001F0000
+#define USP_RXC_SHIFTER_LEN_OFFSET 16
+
+#define USP_START_EDGE_MODE 0x00800000
+#define USP_I2S_SYNC_CHG 0x00200000
+
+#define USP_RXC_CLK_DIVISOR_MASK 0x0F000000
+#define USP_RXC_CLK_DIVISOR_OFFSET 24
+#define USP_SINGLE_SYNC_MODE 0x00400000
+
+/* Tx - RX Enable Register */
+
+#define USP_RX_ENA 0x00000001
+#define USP_TX_ENA 0x00000002
+
+/* USP Interrupt Enable and status Register */
+#define USP_RX_DONE_INT 0x00000001
+#define USP_TX_DONE_INT 0x00000002
+#define USP_RX_OFLOW_INT 0x00000004
+#define USP_TX_UFLOW_INT 0x00000008
+#define USP_RX_IO_DMA_INT 0x00000010
+#define USP_TX_IO_DMA_INT 0x00000020
+#define USP_RXFIFO_FULL_INT 0x00000040
+#define USP_TXFIFO_EMPTY_INT 0x00000080
+#define USP_RXFIFO_THD_INT 0x00000100
+#define USP_TXFIFO_THD_INT 0x00000200
+#define USP_UART_FRM_ERR_INT 0x00000400
+#define USP_RX_TIMEOUT_INT 0x00000800
+#define USP_TX_ALLOUT_INT 0x00001000
+#define USP_RXD_BREAK_INT 0x00008000
+
+/* All possible TX interruots */
+#define USP_TX_INTERRUPT (USP_TX_DONE_INT|USP_TX_UFLOW_INT|\
+ USP_TX_IO_DMA_INT|\
+ USP_TXFIFO_EMPTY_INT|\
+ USP_TXFIFO_THD_INT)
+/* All possible RX interruots */
+#define USP_RX_INTERRUPT (USP_RX_DONE_INT|USP_RX_OFLOW_INT|\
+ USP_RX_IO_DMA_INT|\
+ USP_RXFIFO_FULL_INT|\
+ USP_RXFIFO_THD_INT|\
+ USP_RXFIFO_THD_INT|USP_RX_TIMEOUT_INT)
+
+#define USP_INT_ALL 0x1FFF
+
+/* USP Pin I/O Data Register */
+
+#define USP_RFS_PIN_VALUE_MASK 0x00000001
+#define USP_TFS_PIN_VALUE_MASK 0x00000002
+#define USP_RXD_PIN_VALUE_MASK 0x00000004
+#define USP_TXD_PIN_VALUE_MASK 0x00000008
+#define USP_SCLK_PIN_VALUE_MASK 0x00000010
+
+/* USP RISC/DSP Mode Register */
+#define USP_RISC_DSP_SEL 0x00000001
+
+/* USP ASYNC PARAMETER Register*/
+
+#define USP_ASYNC_TIMEOUT_MASK 0x0000FFFF
+#define USP_ASYNC_TIMEOUT_OFFSET 0
+#define USP_ASYNC_TIMEOUT(x) (((x)&USP_ASYNC_TIMEOUT_MASK) \
+ <<USP_ASYNC_TIMEOUT_OFFSET)
+
+#define USP_ASYNC_DIV2_MASK 0x003F0000
+#define USP_ASYNC_DIV2_OFFSET 16
+
+/* USP TX DMA I/O MODE Register */
+#define USP_TX_MODE_IO 0x00000001
+
+/* USP TX DMA I/O Length Register */
+#define USP_TX_DATA_LEN_MASK 0xFFFFFFFF
+#define USP_TX_DATA_LEN_OFFSET 0
+
+/* USP TX FIFO Control Register */
+#define USP_TX_FIFO_WIDTH_MASK 0x00000003
+#define USP_TX_FIFO_WIDTH_OFFSET 0
+
+#define USP_TX_FIFO_THD_MASK 0x000001FC
+#define USP_TX_FIFO_THD_OFFSET 2
+
+/* USP TX FIFO Level Check Register */
+#define USP_TX_FIFO_LEVEL_CHECK_MASK 0x1F
+#define USP_TX_FIFO_SC_OFFSET 0
+#define USP_TX_FIFO_LC_OFFSET 10
+#define USP_TX_FIFO_HC_OFFSET 20
+
+#define TX_FIFO_SC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \
+ << USP_TX_FIFO_SC_OFFSET)
+#define TX_FIFO_LC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \
+ << USP_TX_FIFO_LC_OFFSET)
+#define TX_FIFO_HC(x) (((x) & USP_TX_FIFO_LEVEL_CHECK_MASK) \
+ << USP_TX_FIFO_HC_OFFSET)
+
+/* USP TX FIFO Operation Register */
+#define USP_TX_FIFO_RESET 0x00000001
+#define USP_TX_FIFO_START 0x00000002
+
+/* USP TX FIFO Status Register */
+#define USP_TX_FIFO_LEVEL_MASK 0x0000007F
+#define USP_TX_FIFO_LEVEL_OFFSET 0
+
+#define USP_TX_FIFO_FULL 0x00000080
+#define USP_TX_FIFO_EMPTY 0x00000100
+
+/* USP TX FIFO Data Register */
+#define USP_TX_FIFO_DATA_MASK 0xFFFFFFFF
+#define USP_TX_FIFO_DATA_OFFSET 0
+
+/* USP RX DMA I/O MODE Register */
+#define USP_RX_MODE_IO 0x00000001
+#define USP_RX_DMA_FLUSH 0x00000004
+
+/* USP RX DMA I/O Length Register */
+#define USP_RX_DATA_LEN_MASK 0xFFFFFFFF
+#define USP_RX_DATA_LEN_OFFSET 0
+
+/* USP RX FIFO Control Register */
+#define USP_RX_FIFO_WIDTH_MASK 0x00000003
+#define USP_RX_FIFO_WIDTH_OFFSET 0
+
+#define USP_RX_FIFO_THD_MASK 0x000001FC
+#define USP_RX_FIFO_THD_OFFSET 2
+
+/* USP RX FIFO Level Check Register */
+
+#define USP_RX_FIFO_LEVEL_CHECK_MASK 0x1F
+#define USP_RX_FIFO_SC_OFFSET 0
+#define USP_RX_FIFO_LC_OFFSET 10
+#define USP_RX_FIFO_HC_OFFSET 20
+
+#define RX_FIFO_SC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \
+ << USP_RX_FIFO_SC_OFFSET)
+#define RX_FIFO_LC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \
+ << USP_RX_FIFO_LC_OFFSET)
+#define RX_FIFO_HC(x) (((x) & USP_RX_FIFO_LEVEL_CHECK_MASK) \
+ << USP_RX_FIFO_HC_OFFSET)
+
+/* USP RX FIFO Operation Register */
+#define USP_RX_FIFO_RESET 0x00000001
+#define USP_RX_FIFO_START 0x00000002
+
+/* USP RX FIFO Status Register */
+
+#define USP_RX_FIFO_LEVEL_MASK 0x0000007F
+#define USP_RX_FIFO_LEVEL_OFFSET 0
+
+#define USP_RX_FIFO_FULL 0x00000080
+#define USP_RX_FIFO_EMPTY 0x00000100
+
+/* USP RX FIFO Data Register */
+
+#define USP_RX_FIFO_DATA_MASK 0xFFFFFFFF
+#define USP_RX_FIFO_DATA_OFFSET 0
+
+/*
+ * When rx thd irq occur, sender just disable tx empty irq,
+ * Remaining data in tx fifo wil also be sent out.
+ */
+#define USP_FIFO_SIZE 128
+#define USP_TX_FIFO_THRESHOLD (USP_FIFO_SIZE/2)
+#define USP_RX_FIFO_THRESHOLD (USP_FIFO_SIZE/2)
+
+/* FIFO_WIDTH for the USP_TX_FIFO_CTRL and USP_RX_FIFO_CTRL registers */
+#define USP_FIFO_WIDTH_BYTE 0x00
+#define USP_FIFO_WIDTH_WORD 0x01
+#define USP_FIFO_WIDTH_DWORD 0x02
+
+#define USP_ASYNC_DIV2 16
+
+#define USP_PLUGOUT_RETRY_CNT 2
+
+#define USP_TX_RX_FIFO_WIDTH_DWORD 2
+
+#define SIRF_USP_DIV_MCLK 0
+
+#define SIRF_USP_I2S_TFS_SYNC 0
+#define SIRF_USP_I2S_RFS_SYNC 1
+#endif