summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl_sai.c117
-rw-r--r--sound/soc/fsl/fsl_sai.h6
-rw-r--r--sound/soc/fsl/fsl_sai_sysfs.c312
4 files changed, 351 insertions, 86 deletions
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index cec61cc51fcc..827d6b0923b3 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -18,7 +18,7 @@ snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-dsp-objs := fsl_dsp.o fsl_dsp_proxy.o fsl_dsp_pool.o \
fsl_dsp_library_load.o fsl_dsp_xaf_api.o fsl_dsp_cpu.o \
fsl_dsp_platform_compress.o
-snd-soc-fsl-sai-objs := fsl_sai.o
+snd-soc-fsl-sai-objs := fsl_sai.o fsl_sai_sysfs.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
snd-soc-fsl-spdif-objs := fsl_spdif.o
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 305af70660d9..0991e24fa45f 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -991,90 +991,6 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.shutdown = fsl_sai_shutdown,
};
-static const char
- *en_sl[] = { "Disabled", "Enabled", },
- *inc_sl[] = { "On enabled and bitcount increment", "On enabled", };
-
-static const struct soc_enum tstmp_enum[] = {
-SOC_ENUM_SINGLE(FSL_SAI_TTCTL, 0, ARRAY_SIZE(en_sl), en_sl),
-SOC_ENUM_SINGLE(FSL_SAI_RTCTL, 0, ARRAY_SIZE(en_sl), en_sl),
-SOC_ENUM_SINGLE(FSL_SAI_TTCTL, 1, ARRAY_SIZE(inc_sl), inc_sl),
-SOC_ENUM_SINGLE(FSL_SAI_RTCTL, 1, ARRAY_SIZE(inc_sl), inc_sl),
-};
-
-int fsl_sai_get_reg(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- bool pm_active = pm_runtime_active(component->dev);
- unsigned int regval;
- int ret;
-
- if (pm_active)
- regcache_cache_bypass(component->regmap, true);
-
- ret = snd_soc_component_read(component, mc->regbase, &regval);
-
- if (pm_active)
- regcache_cache_bypass(component->regmap, false);
-
- if (ret < 0)
- return ret;
-
- ucontrol->value.integer.value[0] = regval;
-
- return 0;
-}
-
-#define SOC_SINGLE_REG_RO(xname, xreg) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_READ | \
- SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
- .info = snd_soc_info_xr_sx, .get = fsl_sai_get_reg, \
- .private_value = (unsigned long)&(struct soc_mreg_control) \
- { .regbase = xreg, .regcount = 1, .nbits = 32, \
- .invert = 0, .min = 0, .max = 0xffffffff, } }
-
-static const struct snd_kcontrol_new fsl_sai_pb_ctrls[] = {
- SOC_ENUM("Playback Timestamp Control", tstmp_enum[0]),
- SOC_ENUM("Playback Timestamp Increment", tstmp_enum[2]),
- SOC_SINGLE("Playback Timestamp Reset", FSL_SAI_TTCTL, 8, 1, 0),
- SOC_SINGLE("Playback Bit Counter Reset", FSL_SAI_TTCTL, 9, 1, 0),
- SOC_SINGLE_REG_RO("Playback Timestamp Counter", FSL_SAI_TTCTN),
- SOC_SINGLE_REG_RO("Playback Bit Counter", FSL_SAI_TBCTN),
- SOC_SINGLE_REG_RO("Playback Latched Timestamp Counter", FSL_SAI_TTCAP),
-};
-
-static const struct snd_kcontrol_new fsl_sai_cp_ctrls[] = {
- SOC_ENUM("Capture Timestamp Control", tstmp_enum[1]),
- SOC_ENUM("Capture Timestamp Increment", tstmp_enum[3]),
- SOC_SINGLE("Capture Timestamp Reset", FSL_SAI_RTCTL, 8, 1, 0),
- SOC_SINGLE("Capture Bit Counter Reset", FSL_SAI_RTCTL, 9, 1, 0),
- SOC_SINGLE_REG_RO("Capture Timestamp Counter", FSL_SAI_RTCTN),
- SOC_SINGLE_REG_RO("Capture Bit Counter", FSL_SAI_RBCTN),
- SOC_SINGLE_REG_RO("Capture Latched Timestamp Counter", FSL_SAI_RTCAP),
-};
-
-static int fsl_sai_pcm_new(struct snd_soc_pcm_runtime *rtd,
- struct snd_soc_dai *dai)
-{
- struct fsl_sai *sai = dev_get_drvdata(dai->dev);
- struct snd_pcm *pcm = rtd->pcm;
- bool ts_enabled = sai->verid.timestamp_en;
- struct snd_soc_component *comp = dai->component;
-
- if (ts_enabled && pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
- snd_soc_add_component_controls(comp, fsl_sai_pb_ctrls,
- ARRAY_SIZE(fsl_sai_pb_ctrls));
-
- if (ts_enabled && pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
- snd_soc_add_component_controls(comp, fsl_sai_cp_ctrls,
- ARRAY_SIZE(fsl_sai_cp_ctrls));
- return 0;
-}
-
static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
{
struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
@@ -1113,7 +1029,6 @@ static int fsl_sai_dai_resume(struct snd_soc_dai *cpu_dai)
}
static struct snd_soc_dai_driver fsl_sai_dai = {
- .pcm_new = fsl_sai_pcm_new,
.probe = fsl_sai_dai_probe,
.playback = {
.stream_name = "CPU-Playback",
@@ -1276,6 +1191,14 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
case FSL_SAI_RDR5:
case FSL_SAI_RDR6:
case FSL_SAI_RDR7:
+ case FSL_SAI_TTCTN:
+ case FSL_SAI_TTCTL:
+ case FSL_SAI_TBCTN:
+ case FSL_SAI_TTCAP:
+ case FSL_SAI_RTCTN:
+ case FSL_SAI_RTCTL:
+ case FSL_SAI_RBCTN:
+ case FSL_SAI_RTCAP:
return true;
default:
return false;
@@ -1600,6 +1523,25 @@ static int fsl_sai_probe(struct platform_device *pdev)
FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
}
+ if (sai->verid.timestamp_en) {
+ if (of_find_property(np, "fsl,sai-monitor-spdif", NULL) &&
+ of_device_is_compatible(np, "fsl,imx8mm-sai")) {
+ sai->regmap_gpr = syscon_regmap_lookup_by_compatible("fsl,imx8mm-iomuxc-gpr");
+ if (IS_ERR(sai->regmap_gpr))
+ dev_warn(&pdev->dev, "cannot find iomuxc registers\n");
+
+ sai->gpr_idx = of_alias_get_id(np, "sai");
+ if (sai->gpr_idx < 0)
+ dev_warn(&pdev->dev, "cannot find sai alias id\n");
+
+ if (sai->gpr_idx > 0 && !IS_ERR(sai->regmap_gpr))
+ sai->monitor_spdif = true;
+ }
+
+ if (sysfs_create_group(&pdev->dev.kobj, fsl_sai_get_dev_attribute_group(sai->monitor_spdif)))
+ dev_err(&pdev->dev, "fail to create sys group\n");
+ }
+
clk_disable_unprepare(sai->bus_clk);
sai->dma_params_rx.chan_name = "rx";
@@ -1628,8 +1570,13 @@ static int fsl_sai_probe(struct platform_device *pdev)
static int fsl_sai_remove(struct platform_device *pdev)
{
+ struct fsl_sai *sai = dev_get_drvdata(&pdev->dev);
+
pm_runtime_disable(&pdev->dev);
+ if (sai->verid.timestamp_en)
+ sysfs_remove_group(&pdev->dev.kobj, fsl_sai_get_dev_attribute_group(sai->monitor_spdif));
+
return 0;
}
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index ee2eb2fdd03a..81599924c38f 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -249,6 +249,7 @@ struct fsl_sai_dl_cfg {
struct fsl_sai {
struct platform_device *pdev;
struct regmap *regmap;
+ struct regmap *regmap_gpr;
struct clk *bus_clk;
struct clk *mclk_clk[FSL_SAI_MCLK_MAX];
struct clk *pll8k_clk;
@@ -261,7 +262,10 @@ struct fsl_sai {
bool synchronous[2];
bool is_stream_opened[2];
bool is_dsd;
+ bool monitor_spdif;
+ bool monitor_spdif_start;
+ int gpr_idx;
int pcm_dl_cfg_cnt;
int dsd_dl_cfg_cnt;
struct fsl_sai_dl_cfg *pcm_dl_cfg;
@@ -286,6 +290,8 @@ struct fsl_sai {
struct fsl_sai_param param;
};
+const struct attribute_group *fsl_sai_get_dev_attribute_group(bool monitor_spdif);
+
#define TX 1
#define RX 0
diff --git a/sound/soc/fsl/fsl_sai_sysfs.c b/sound/soc/fsl/fsl_sai_sysfs.c
new file mode 100644
index 000000000000..920ebcccc427
--- /dev/null
+++ b/sound/soc/fsl/fsl_sai_sysfs.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2020 NXP Semiconductors.
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/kdev_t.h>
+#include <linux/pm_runtime.h>
+#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
+
+#include "fsl_sai.h"
+
+static ssize_t tx_bitcnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ /* read bitcounter */
+ regmap_read(sai->regmap, FSL_SAI_TBCTN, &val);
+
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(tx_bitcnt);
+
+static ssize_t tx_timestamp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ /* read timestamp */
+ regmap_read(sai->regmap, FSL_SAI_TTCTN, &val);
+
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(tx_timestamp);
+
+static ssize_t tx_bitcnt_latched_timestamp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ /* read timestamp */
+ regmap_read(sai->regmap, FSL_SAI_TTCAP, &val);
+
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(tx_bitcnt_latched_timestamp);
+
+static ssize_t
+tx_timestamp_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_TSEN;
+ regmap_update_bits(sai->regmap, FSL_SAI_TTCTL, FSL_SAI_xTCTL_TSEN, val);
+ return n;
+}
+static DEVICE_ATTR_WO(tx_timestamp_enable);
+
+static ssize_t
+tx_timestamp_increment_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_TSINC;
+ regmap_update_bits(sai->regmap, FSL_SAI_TTCTL, FSL_SAI_xTCTL_TSINC, val);
+ return n;
+}
+static DEVICE_ATTR_WO(tx_timestamp_increment);
+
+static ssize_t
+tx_bitcnt_reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_RBC;
+ regmap_update_bits(sai->regmap, FSL_SAI_TTCTL, FSL_SAI_xTCTL_RBC, val);
+ return n;
+}
+static DEVICE_ATTR_WO(tx_bitcnt_reset);
+
+static ssize_t
+tx_timestamp_reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_RTSC;
+ regmap_update_bits(sai->regmap, FSL_SAI_TTCTL, FSL_SAI_xTCTL_RTSC, val);
+ return n;
+}
+static DEVICE_ATTR_WO(tx_timestamp_reset);
+
+
+static ssize_t rx_bitcnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ /* read bitcounter */
+ regmap_read(sai->regmap, FSL_SAI_RBCTN, &val);
+
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(rx_bitcnt);
+
+static ssize_t rx_timestamp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ /* read timestamp */
+ regmap_read(sai->regmap, FSL_SAI_RTCTN, &val);
+
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(rx_timestamp);
+
+static ssize_t rx_bitcnt_latched_timestamp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ /* read timestamp */
+ regmap_read(sai->regmap, FSL_SAI_RTCAP, &val);
+
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(rx_bitcnt_latched_timestamp);
+
+static ssize_t
+rx_timestamp_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_TSEN;
+ regmap_update_bits(sai->regmap, FSL_SAI_RTCTL, FSL_SAI_xTCTL_TSEN, val);
+ return n;
+}
+static DEVICE_ATTR_WO(rx_timestamp_enable);
+
+static ssize_t
+rx_timestamp_increment_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_TSINC;
+ regmap_update_bits(sai->regmap, FSL_SAI_RTCTL, FSL_SAI_xTCTL_TSINC, val);
+ return n;
+}
+static DEVICE_ATTR_WO(rx_timestamp_increment);
+
+static ssize_t
+rx_bitcnt_reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_RBC;
+ regmap_update_bits(sai->regmap, FSL_SAI_RTCTL, FSL_SAI_xTCTL_RBC, val);
+ return n;
+}
+static DEVICE_ATTR_WO(rx_bitcnt_reset);
+
+static ssize_t
+rx_timestamp_reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned int val = 0;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ val = FSL_SAI_xTCTL_RTSC;
+ regmap_update_bits(sai->regmap, FSL_SAI_RTCTL, FSL_SAI_xTCTL_RTSC, val);
+ return n;
+}
+static DEVICE_ATTR_WO(rx_timestamp_reset);
+
+static ssize_t
+rx_monitor_spdif_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct fsl_sai *sai = dev_get_drvdata(dev);
+ unsigned char offset = sai->soc->reg_offset;
+ unsigned int val = 0;
+ bool enable = false;
+ unsigned int reg;
+ unsigned int shift;
+ unsigned int mask;
+
+ if (kstrtouint(buf, 0, &val))
+ return -EINVAL;
+
+ if (val != 0)
+ enable = true;
+
+ if (pm_runtime_active(&sai->pdev->dev) && enable) {
+ dev_err(dev, "device is busy\n");
+ return -EBUSY;
+ }
+
+ reg = IOMUXC_GPR6 + (sai->gpr_idx - 1) / 2 * 4;
+ shift = ((sai->gpr_idx - 1) % 2) * 16;
+ mask = 0x1F << shift;
+
+ if (enable) {
+ pm_runtime_get_sync(&sai->pdev->dev);
+ /* Fix to MCLK3 */
+ regmap_update_bits(sai->regmap_gpr, reg, mask, 0xF << shift);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(false, offset),
+ FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(0x3));
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(false, offset),
+ FSL_SAI_CR2_DIV_MASK, 0x0);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(false, offset),
+ FSL_SAI_CR2_BCD_MSTR, FSL_SAI_CR2_BCD_MSTR);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR4(false, offset),
+ FSL_SAI_CR4_FSD_MSTR, FSL_SAI_CR4_FSD_MSTR);
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(false, offset),
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+ sai->monitor_spdif_start = true;
+ } else {
+ if (sai->monitor_spdif_start) {
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(false, offset),
+ FSL_SAI_CSR_TERE, 0);
+ pm_runtime_put_sync(&sai->pdev->dev);
+ sai->monitor_spdif_start = false;
+ }
+ }
+
+ return n;
+}
+static DEVICE_ATTR_WO(rx_monitor_spdif);
+
+static struct attribute *fsl_sai_attrs[] = {
+ &dev_attr_tx_bitcnt.attr,
+ &dev_attr_rx_bitcnt.attr,
+ &dev_attr_tx_timestamp.attr,
+ &dev_attr_rx_timestamp.attr,
+ &dev_attr_tx_bitcnt_latched_timestamp.attr,
+ &dev_attr_rx_bitcnt_latched_timestamp.attr,
+ &dev_attr_tx_timestamp_enable.attr,
+ &dev_attr_rx_timestamp_enable.attr,
+ &dev_attr_tx_timestamp_increment.attr,
+ &dev_attr_rx_timestamp_increment.attr,
+ &dev_attr_tx_bitcnt_reset.attr,
+ &dev_attr_rx_bitcnt_reset.attr,
+ &dev_attr_tx_timestamp_reset.attr,
+ &dev_attr_rx_timestamp_reset.attr,
+ NULL,
+ NULL,
+};
+
+static struct attribute_group fsl_sai_attr_group = {
+ .attrs = fsl_sai_attrs,
+};
+
+const struct attribute_group *fsl_sai_get_dev_attribute_group(bool monitor_spdif)
+{
+ if (monitor_spdif)
+ fsl_sai_attrs[ARRAY_SIZE(fsl_sai_attrs) - 2] = &dev_attr_rx_monitor_spdif.attr;
+
+ return &fsl_sai_attr_group;
+}