[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