diff options
-rw-r--r-- | sound/soc/fsl/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_sai.c | 117 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_sai.h | 6 | ||||
-rw-r--r-- | sound/soc/fsl/fsl_sai_sysfs.c | 312 |
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, ®val); - - 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; +} |