summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShengjiu Wang <shengjiu.wang@nxp.com>2020-04-15 13:14:26 +0800
committerShengjiu Wang <shengjiu.wang@nxp.com>2020-04-17 12:06:39 +0800
commit02d362f8d6dac7bd2357edf30420db8648860c77 (patch)
treed66812ad6a7215cfa6e78a86f2e4b01bb55c5da0
parentec7568ce2f85e3f86119cbedc482dc280b8151fa (diff)
MLK-23792-1: ASoC: fsl_sai: Monitor spdif rx clock in imx8mm
As we use one spare sai instance to monitor the spdif rx clock, there isn't belong to a sound card, we can't access the registers by amixer controls. So remove the amixer controls, replace them with the device attribute. And add an additional device attribute for enablement of monitorring spdif. This feature only be supported on imx8mm. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Reviewed-by: Viorel Suman <viorel.suman@nxp.com>
-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;
+}