[PATCH v2 5/6] ASoC: sirf-inner: add mach driver for SiRFSoC internal codec
Barry Song
21cnbao at gmail.com
Mon Oct 28 19:15:59 EDT 2013
From: Rongjun Ying <Rongjun.Ying at csr.com>
This connects DMA, CPU DAI and Codec DAI together and works
as a mach driver.
Signed-off-by: Rongjun Ying <Rongjun.Ying at csr.com>
Signed-off-by: Barry Song <Baohua.Song at csr.com>
---
-v2: use extcon for both Linux and Android support;
use DAPM to replace legacy kcontrol
sound/soc/sirf/Kconfig | 5 +
sound/soc/sirf/Makefile | 2 +
sound/soc/sirf/sirf-inner.c | 237 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 244 insertions(+)
create mode 100644 sound/soc/sirf/sirf-inner.c
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig
index afa3952..564b0ec 100644
--- a/sound/soc/sirf/Kconfig
+++ b/sound/soc/sirf/Kconfig
@@ -11,3 +11,8 @@ config SND_SIRF_SOC_INNER
config SND_SOC_SIRF_USP
tristate
+
+config SND_SIRF_INNER
+ tristate "SoC Audio support for SiRF inner codec of SiRF EVB"
+ depends on SND_SIRF_SOC
+ select SND_SIRF_SOC_INNER
diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile
index 8517c67..80abdf6 100644
--- a/sound/soc/sirf/Makefile
+++ b/sound/soc/sirf/Makefile
@@ -1,9 +1,11 @@
snd-soc-sirf-objs := sirf-pcm.o
+snd-soc-sirf-inner-objs := sirf-inner.o
snd-soc-sirf-soc-inner-objs := sirf-soc-inner.o
snd-soc-sirf-i2s-objs := sirf-i2s.o
snd-soc-sirf-usp-objs := sirf-usp.o
obj-$(CONFIG_SND_SIRF_SOC) += snd-soc-sirf.o
+obj-$(CONFIG_SND_SIRF_INNER) += snd-soc-sirf-inner.o
obj-$(CONFIG_SND_SIRF_SOC_INNER) += snd-soc-sirf-soc-inner.o
obj-$(CONFIG_SND_SOC_SIRF_I2S) += snd-soc-sirf-i2s.o
obj-$(CONFIG_SND_SOC_SIRF_USP) += snd-soc-sirf-usp.o
diff --git a/sound/soc/sirf/sirf-inner.c b/sound/soc/sirf/sirf-inner.c
new file mode 100644
index 0000000..e36ac5e
--- /dev/null
+++ b/sound/soc/sirf/sirf-inner.c
@@ -0,0 +1,237 @@
+/*
+ * SiRF inner audio device driver
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <linux/extcon.h>
+#include <linux/extcon/extcon-gpio.h>
+
+#define SIRF_JACK_GPIO_DEBOUNCE_TIME 200 /* in ms */
+struct sirf_inner_extcon_info {
+ struct platform_device pdev;
+ struct gpio_extcon_platform_data extcon_data;
+ int last_state;
+ int state_changed;
+};
+
+struct sirf_inner_card {
+ unsigned int gpio_hp_pa;
+ unsigned int gpio_spk_pa;
+ struct platform_device *sirf_inner_device;
+ struct sirf_inner_extcon_info extcon_info;
+};
+
+static int sirf_inner_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *ctrl, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+ int on = !SND_SOC_DAPM_EVENT_OFF(event);
+ if (gpio_is_valid(sinner_card->gpio_hp_pa))
+ gpio_direction_output(sinner_card->gpio_hp_pa, on);
+ return 0;
+}
+
+static int sirf_inner_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *ctrl, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+ int on = !SND_SOC_DAPM_EVENT_OFF(event);
+
+ if (sinner_card->extcon_info.state_changed) {
+ sinner_card->extcon_info.state_changed = 0;
+ if (!sinner_card->extcon_info.last_state)
+ gpio_direction_output(sinner_card->gpio_spk_pa, 0);
+ else
+ gpio_direction_output(sinner_card->gpio_spk_pa, 1);
+ } else {
+ if (gpio_is_valid(sinner_card->gpio_spk_pa))
+ gpio_direction_output(sinner_card->gpio_spk_pa, on);
+ }
+
+ return 0;
+}
+static const struct snd_soc_dapm_widget sirf_inner_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Hp", sirf_inner_hp_event),
+ SND_SOC_DAPM_SPK("Ext Spk", sirf_inner_spk_event),
+ SND_SOC_DAPM_MIC("Ext Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"Hp", NULL, "HPOUTL"},
+ {"Hp", NULL, "HPOUTR"},
+ {"Ext Spk", NULL, "SPKOUT"},
+ {"MICIN1", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Ext Mic"},
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sirf_inner_dai_links[] = {
+ {
+ .name = "SiRF inner",
+ .stream_name = "SiRF inner",
+ .codec_dai_name = "sirf-soc-inner",
+ .platform_name = "sirf-pcm-audio.1",
+ },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_sirf_inner_card = {
+ .name = "SiRF inner",
+ .owner = THIS_MODULE,
+ .dai_link = sirf_inner_dai_links,
+ .num_links = ARRAY_SIZE(sirf_inner_dai_links),
+ .dapm_widgets = sirf_inner_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sirf_inner_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+};
+
+static int sirf_inner_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_sirf_inner_card;
+ struct sirf_inner_card *sinner_card;
+ int ret;
+
+ sinner_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_inner_card),
+ GFP_KERNEL);
+ if (sinner_card == NULL)
+ return -ENOMEM;
+
+ sinner_card->sirf_inner_device = platform_device_alloc("soc-audio", -1);
+ if (!sinner_card->sirf_inner_device)
+ return -ENOMEM;
+
+ sirf_inner_dai_links[0].cpu_of_node =
+ of_parse_phandle(pdev->dev.of_node, "sirf,inner-platform", 0);
+ sirf_inner_dai_links[0].codec_of_node =
+ of_parse_phandle(pdev->dev.of_node, "sirf,inner-codec", 0);
+ sinner_card->gpio_spk_pa = of_get_named_gpio(pdev->dev.of_node,
+ "spk-pa-gpios", 0);
+ sinner_card->gpio_hp_pa = of_get_named_gpio(pdev->dev.of_node,
+ "hp-pa-gpios", 0);
+ if (gpio_is_valid(sinner_card->gpio_spk_pa))
+ gpio_request(sinner_card->gpio_spk_pa, "SPA_PA_SD");
+ if (gpio_is_valid(sinner_card->gpio_hp_pa))
+ gpio_request(sinner_card->gpio_hp_pa, "HP_PA_SD");
+
+ card->dev = &pdev->dev;
+ snd_soc_card_set_drvdata(card, sinner_card);
+ platform_set_drvdata(pdev, card);
+ if (gpio_is_valid(sinner_card->gpio_hp_pa))
+ gpio_direction_output(sinner_card->gpio_hp_pa, 0);
+ if (gpio_is_valid(sinner_card->gpio_spk_pa))
+ gpio_direction_output(sinner_card->gpio_spk_pa, 0);
+
+ platform_set_drvdata(sinner_card->sirf_inner_device,
+ &snd_soc_sirf_inner_card);
+
+ ret = platform_device_add(sinner_card->sirf_inner_device);
+ if (ret) {
+ platform_device_put(sinner_card->sirf_inner_device);
+ return ret;
+ }
+
+ sinner_card->extcon_info.extcon_data.name = "h2w";
+ sinner_card->extcon_info.extcon_data.debounce =
+ SIRF_JACK_GPIO_DEBOUNCE_TIME;
+ sinner_card->extcon_info.extcon_data.irq_flags =
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED;
+ sinner_card->extcon_info.extcon_data.state_on = "0";
+ sinner_card->extcon_info.extcon_data.state_off = "1";
+ sinner_card->extcon_info.extcon_data.gpio =
+ of_get_named_gpio(pdev->dev.of_node,
+ "hp-switch-gpios", 0);
+
+ sinner_card->extcon_info.pdev.name = "extcon-gpio";
+ sinner_card->extcon_info.pdev.id = pdev->id;
+ sinner_card->extcon_info.pdev.dev.platform_data =
+ &sinner_card->extcon_info.extcon_data;
+
+ return platform_device_register(&sinner_card->extcon_info.pdev);
+}
+
+static int sirf_inner_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+
+ if (gpio_is_valid(sinner_card->gpio_hp_pa))
+ gpio_free(sinner_card->gpio_hp_pa);
+ if (gpio_is_valid(sinner_card->gpio_spk_pa))
+ gpio_free(sinner_card->gpio_spk_pa);
+
+ platform_device_put(sinner_card->sirf_inner_device);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_inner_resume(struct device *dev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+ struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+ struct extcon_dev *edev;
+ int state;
+
+ edev = extcon_get_extcon_dev(sinner_card->extcon_info.extcon_data.name);
+ state = gpio_get_value(sinner_card->extcon_info.extcon_data.gpio);
+ if (state != sinner_card->extcon_info.last_state) {
+ sinner_card->extcon_info.state_changed = 1;
+ sinner_card->extcon_info.last_state = state;
+ extcon_set_state(edev, state);
+ }
+ return 0;
+}
+
+static int sirf_inner_suspend(struct device *dev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(dev);
+ struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card);
+ sinner_card->extcon_info.last_state = gpio_get_value(sinner_card->extcon_info.extcon_data.gpio);
+ if (gpio_is_valid(sinner_card->gpio_spk_pa))
+ gpio_direction_output(sinner_card->gpio_spk_pa, 0);
+ return 0;
+}
+#endif
+
+static const struct of_device_id sirf_inner_of_match[] = {
+ {.compatible = "sirf,sirf-inner", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sirf_inner_of_match);
+
+static const struct dev_pm_ops sirf_inner_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sirf_inner_suspend, sirf_inner_resume)
+};
+
+static struct platform_driver sirf_inner_driver = {
+ .driver = {
+ .name = "sirf-inner",
+ .owner = THIS_MODULE,
+ .pm = &sirf_inner_pm_ops,
+ .of_match_table = sirf_inner_of_match,
+ },
+ .probe = sirf_inner_probe,
+ .remove = sirf_inner_remove,
+};
+module_platform_driver(sirf_inner_driver);
+
+MODULE_AUTHOR("RongJun Ying <RongJun.Ying at csr.com>");
+MODULE_DESCRIPTION("ALSA SoC SIRF inner AUDIO driver");
+MODULE_LICENSE("GPL v2");
--
1.8.2.3
More information about the linux-arm-kernel
mailing list