summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDong Aisheng <aisheng.dong@nxp.com>2019-12-02 18:00:41 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2019-12-02 18:00:41 +0800
commit25d4beddff1fa677609f340b84ca2ea45f20e3fe (patch)
tree27c9679f53e4b1588995bfc84eb5ba2e23254ddd
parent845f67dda29211eb740d031b4f889d6e70ab5bd9 (diff)
parentfcfc4be154e14741ab631710a85b0cdc891a8747 (diff)
Merge remote-tracking branch 'origin/audio/fm' into audio/next
* origin/audio/fm: (8 commits) MLK-11429-21: ASoC: fsl: port si476x machine driver from imx_3.10.y MLK-11305 radio-si476x: support set V4L2_CID_AUDIO_MUTE CTRL MLK-22355: mfd: si476x: Use system_freezable_wq instead of system_wq MLK-10055-2: mfd: si476x-i2c: sound is registered when no FM module attached MLK-10038-1: mfd: si476x-i2c: Add support of si476x-rev4.0 board ...
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-si476x.txt24
-rw-r--r--drivers/media/radio/radio-si476x.c18
-rw-r--r--drivers/mfd/si476x-i2c.c24
-rw-r--r--include/linux/mfd/si476x-core.h2
-rw-r--r--sound/soc/fsl/Kconfig12
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/imx-si476x.c194
7 files changed, 271 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt b/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt
new file mode 100644
index 000000000000..53cd34afe6b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt
@@ -0,0 +1,24 @@
+Freescale i.MX audio complex with si476x codec
+
+Required properties:
+- compatible : "fsl,imx-audio-si476x"
+- model : The user-visible name of this sound complex
+- ssi-controller : The phandle of the i.MX SSI controller
+
+- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
+- mux-ext-port : The external port of the i.MX audio muxer
+
+Note: The AUDMUX port numbering should start at 1, which is consistent with
+hardware manual.
+
+Example:
+
+sound {
+ compatible = "fsl,imx-audio-si476x",
+ "fsl,imx-tuner-si476x";
+ model = "imx-radio-si476x";
+
+ ssi-controller = <&ssi1>;
+ mux-int-port = <2>;
+ mux-ext-port = <5>;
+};
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index b203296de977..c2490267f254 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -988,6 +988,14 @@ static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl)
}
break;
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->val)
+ retval = regmap_write(radio->core->regmap,
+ SI476X_PROP_AUDIO_MUTE, 3);
+ else
+ retval = regmap_write(radio->core->regmap,
+ SI476X_PROP_AUDIO_MUTE, 0);
+ break;
default:
retval = -EINVAL;
break;
@@ -1515,6 +1523,16 @@ static int si476x_radio_probe(struct platform_device *pdev)
goto exit;
}
+ ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ rval = radio->ctrl_handler.error;
+ if (ctrl == NULL && rval) {
+ dev_err(&pdev->dev, "Could not initialize V4L2_CID_AUDIO_MUTE control %d\n",
+ rval);
+ goto exit;
+ }
+
if (si476x_core_has_diversity(radio->core)) {
si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def =
si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode);
diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c
index c8d28b844def..accdcdc7f23f 100644
--- a/drivers/mfd/si476x-i2c.c
+++ b/drivers/mfd/si476x-i2c.c
@@ -97,7 +97,7 @@ static int si476x_core_config_pinmux(struct si476x_core *core)
static inline void si476x_core_schedule_polling_work(struct si476x_core *core)
{
- schedule_delayed_work(&core->status_monitor,
+ queue_delayed_work(system_freezable_wq, &core->status_monitor,
usecs_to_jiffies(SI476X_STATUS_POLL_US));
}
@@ -294,7 +294,7 @@ int si476x_core_set_power_state(struct si476x_core *core,
*/
udelay(100);
- err = si476x_core_start(core, false);
+ err = si476x_core_start(core, true);
if (err < 0)
goto disable_regulators;
@@ -303,7 +303,7 @@ int si476x_core_set_power_state(struct si476x_core *core,
case SI476X_POWER_DOWN:
core->power_state = next_state;
- err = si476x_core_stop(core, false);
+ err = si476x_core_stop(core, true);
if (err < 0)
core->power_state = SI476X_POWER_INCONSISTENT;
disable_regulators:
@@ -729,8 +729,15 @@ static int si476x_core_probe(struct i2c_client *client,
memcpy(&core->pinmux, &pdata->pinmux,
sizeof(struct si476x_pinmux));
} else {
- dev_err(&client->dev, "No platform data provided\n");
- return -EINVAL;
+ dev_warn(&client->dev, "Using default platform data.\n");
+ core->power_up_parameters.xcload = 0x28;
+ core->power_up_parameters.func = SI476X_FUNC_FM_RECEIVER;
+ core->power_up_parameters.freq = SI476X_FREQ_37P209375_MHZ;
+ core->diversity_mode = SI476X_PHDIV_DISABLED;
+ core->pinmux.dclk = SI476X_DCLK_DAUDIO;
+ core->pinmux.dfs = SI476X_DFS_DAUDIO;
+ core->pinmux.dout = SI476X_DOUT_I2S_OUTPUT;
+ core->pinmux.xout = SI476X_XOUT_TRISTATE;
}
core->supplies[0].supply = "vd";
@@ -789,12 +796,18 @@ static int si476x_core_probe(struct i2c_client *client,
core->chip_id = id->driver_data;
+ /* Power down si476x first */
+ si476x_core_stop(core, true);
+
rval = si476x_core_get_revision_info(core);
if (rval < 0) {
rval = -ENODEV;
goto free_kfifo;
}
+ if (of_property_read_bool(client->dev.of_node, "revision-a10"))
+ core->revision = SI476X_REVISION_A10;
+
cell_num = 0;
cell = &core->cells[SI476X_RADIO_CELL];
@@ -810,6 +823,7 @@ static int si476x_core_probe(struct i2c_client *client,
core->pinmux.xout == SI476X_XOUT_TRISTATE) {
cell = &core->cells[SI476X_CODEC_CELL];
cell->name = "si476x-codec";
+ cell->of_compatible = "si476x-codec";
cell_num++;
}
#endif
diff --git a/include/linux/mfd/si476x-core.h b/include/linux/mfd/si476x-core.h
index 4708c2b8512a..e591c050dce1 100644
--- a/include/linux/mfd/si476x-core.h
+++ b/include/linux/mfd/si476x-core.h
@@ -484,6 +484,8 @@ enum si476x_common_receiver_properties {
SI476X_PROP_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202,
SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT = 0x0203,
+ SI476X_PROP_AUDIO_MUTE = 0x0301,
+
SI476X_PROP_SEEK_BAND_BOTTOM = 0x1100,
SI476X_PROP_SEEK_BAND_TOP = 0x1101,
SI476X_PROP_SEEK_FREQUENCY_SPACING = 0x1102,
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 208250dcd876..5832934c526f 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -489,6 +489,18 @@ config SND_SOC_IMX_DSP
Say Y if you want to add support for SoC audio on an i.MX board with
IMX DSP.
+config SND_SOC_IMX_SI476X
+ tristate "SoC Audio support for i.MX boards with si476x"
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_IMX_AUDMUX
+ select SND_SOC_FSL_SSI
+ select SND_SOC_FSL_UTILS
+ select SND_SOC_SI476X
+ help
+ SoC Audio support for i.MX boards with SI476x
+ Say Y if you want to add support for Soc audio for the AMFM Tuner chip
+ SI476x module.
+
endif # SND_IMX_SOC
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8be44b869a8f..de1bb892c7aa 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -82,6 +82,7 @@ snd-soc-imx-ak5558-objs := imx-ak5558.o
snd-soc-imx-ak4497-objs := imx-ak4497.o
snd-soc-imx-micfil-objs := imx-micfil.o
snd-soc-imx-dsp-objs := imx-dsp.o
+snd-soc-imx-si476x-objs := imx-si476x.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
@@ -105,3 +106,4 @@ obj-$(CONFIG_SND_SOC_IMX_AK5558) += snd-soc-imx-ak5558.o
obj-$(CONFIG_SND_SOC_IMX_AK4497) += snd-soc-imx-ak4497.o
obj-$(CONFIG_SND_SOC_IMX_MICFIL) += snd-soc-imx-micfil.o
obj-$(CONFIG_SND_SOC_IMX_DSP) += snd-soc-imx-dsp.o
+obj-$(CONFIG_SND_SOC_IMX_SI476X) += snd-soc-imx-si476x.o
diff --git a/sound/soc/fsl/imx-si476x.c b/sound/soc/fsl/imx-si476x.c
new file mode 100644
index 000000000000..a5112a7d8d5a
--- /dev/null
+++ b/sound/soc/fsl/imx-si476x.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2008-2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+
+#include "imx-audmux.h"
+
+static int imx_audmux_config(int slave, int master)
+{
+ unsigned int ptcr, pdcr;
+ slave = slave - 1;
+ master = master - 1;
+
+ ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+ IMX_AUDMUX_V2_PTCR_TFSDIR |
+ IMX_AUDMUX_V2_PTCR_TFSEL(slave) |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR |
+ IMX_AUDMUX_V2_PTCR_TCSEL(slave);
+ pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(slave);
+ imx_audmux_v2_configure_port(master, ptcr, pdcr);
+
+ /*
+ * According to RM, RCLKDIR and SYN should not be changed at same time.
+ * So separate to two step for configuring this port.
+ */
+ ptcr |= IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_RFSEL(slave) |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR |
+ IMX_AUDMUX_V2_PTCR_RCSEL(slave);
+ imx_audmux_v2_configure_port(master, ptcr, pdcr);
+
+ ptcr = IMX_AUDMUX_V2_PTCR_SYN;
+ pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(master);
+ imx_audmux_v2_configure_port(slave, ptcr, pdcr);
+
+ return 0;
+}
+
+static int imx_si476x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret) {
+ dev_err(cpu_dai->dev, "failed to set dai fmt\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops imx_si476x_ops = {
+ .hw_params = imx_si476x_hw_params,
+};
+
+SND_SOC_DAILINK_DEFS(hifi,
+ DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "si476x-codec")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link imx_dai = {
+ .name = "imx-si476x",
+ .stream_name = "imx-si476x",
+ .ops = &imx_si476x_ops,
+ SND_SOC_DAILINK_REG(hifi),
+};
+
+static struct snd_soc_card snd_soc_card_imx_3stack = {
+ .name = "imx-audio-si476x",
+ .dai_link = &imx_dai,
+ .num_links = 1,
+ .owner = THIS_MODULE,
+};
+
+static int imx_si476x_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_card_imx_3stack;
+ struct device_node *ssi_np, *np = pdev->dev.of_node;
+ struct platform_device *ssi_pdev;
+ struct i2c_client *fm_dev;
+ struct device_node *fm_np = NULL;
+ int int_port, ext_port, ret;
+
+ ret = of_property_read_u32(np, "mux-int-port", &int_port);
+ if (ret) {
+ dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+ if (ret) {
+ dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
+ return ret;
+ }
+
+ imx_audmux_config(int_port, ext_port);
+
+ ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
+ if (!ssi_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ return -EINVAL;
+ }
+
+ ssi_pdev = of_find_device_by_node(ssi_np);
+ if (!ssi_pdev) {
+ dev_err(&pdev->dev, "failed to find SSI platform device\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ fm_np = of_parse_phandle(pdev->dev.of_node, "fm-controller", 0);
+ if (!fm_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ fm_dev = of_find_i2c_device_by_node(fm_np->parent);
+ if (!fm_dev || !fm_dev->dev.driver) {
+ dev_err(&pdev->dev, "failed to find FM platform device\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ card->dev = &pdev->dev;
+ card->dai_link->cpus->dai_name = dev_name(&ssi_pdev->dev);
+ card->dai_link->platforms->of_node = ssi_np;
+ card->dai_link->codecs->of_node = fm_np;
+
+ platform_set_drvdata(pdev, card);
+
+ ret = snd_soc_register_card(card);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to register card: %d\n", ret);
+
+end:
+ if (ssi_np)
+ of_node_put(ssi_np);
+ if (fm_np)
+ of_node_put(fm_np);
+
+ return ret;
+}
+
+static int imx_si476x_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_card_imx_3stack;
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static const struct of_device_id imx_si476x_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-si476x", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_si476x_dt_ids);
+
+static struct platform_driver imx_si476x_driver = {
+ .driver = {
+ .name = "imx-tuner-si476x",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = imx_si476x_dt_ids,
+ },
+ .probe = imx_si476x_probe,
+ .remove = imx_si476x_remove,
+};
+
+module_platform_driver(imx_si476x_driver);
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("ALSA SoC i.MX si476x");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-tuner-si476x");