From: Rongjun Ying Rongjun.Ying@csr.com
This connects DMA, CPU DAI and Codec DAI together and works as a mach driver.
Signed-off-by: Rongjun Ying Rongjun.Ying@csr.com Signed-off-by: Barry Song Baohua.Song@csr.com --- sound/soc/sirf/Kconfig | 7 +- sound/soc/sirf/Makefile | 2 + sound/soc/sirf/sirf-inner.c | 267 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sirf/sirf-inner.c
diff --git a/sound/soc/sirf/Kconfig b/sound/soc/sirf/Kconfig index 60b8857..ce3c025 100644 --- a/sound/soc/sirf/Kconfig +++ b/sound/soc/sirf/Kconfig @@ -2,7 +2,7 @@ config SND_SIRF_SOC tristate "Platform DMA driver for the SiRF SoC chips" depends on ARCH_SIRF && SND_SOC select SND_SOC_DMAENGINE_PCM - + config SND_SOC_SIRF_I2S tristate
@@ -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..a8f2765 --- /dev/null +++ b/sound/soc/sirf/sirf-inner.c @@ -0,0 +1,267 @@ +/* + * 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 <sound/jack.h> + +struct sirf_inner_card { + unsigned int gpio_hp_pa; + unsigned int gpio_spk_pa; + /* + * Android platform uses switch gpio instead of jack. + */ +#ifndef CONFIG_ANDROID + unsigned int gpio_hp_detect; + struct snd_soc_jack hp_jack; +#endif +}; + +#ifndef CONFIG_ANDROID +static int sirf_inner_jack_status_check(void); + +static struct snd_soc_jack_gpio hp_jack_gpios[] = { + { + .name = "hpdet-gpio", + .report = SND_JACK_HEADPHONE, + .debounce_time = 200, + .jack_status_check = sirf_inner_jack_status_check, + }, +}; + +static int sirf_inner_jack_status_check(void) +{ + int spk_out = 0; + struct snd_soc_codec *codec = hp_jack_gpios[0].jack->codec; + struct snd_soc_card *card = codec->card; + struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card); + int hp_report = 0; + + if (gpio_is_valid(sinner_card->gpio_hp_detect)) + spk_out = gpio_get_value(sinner_card->gpio_hp_detect); + + if (gpio_is_valid(sinner_card->gpio_hp_pa)) + gpio_direction_output(sinner_card->gpio_hp_pa, !spk_out); + + if (gpio_is_valid(sinner_card->gpio_spk_pa)) + gpio_direction_output(sinner_card->gpio_spk_pa, spk_out); + + if (!spk_out) + hp_report |= SND_JACK_HEADPHONE; + + return hp_report; +} + +static int sirf_inner_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card); + int ret; + hp_jack_gpios[0].gpio = sinner_card->gpio_hp_detect; + ret = snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &sinner_card->hp_jack); + if (ret) + return ret; + return snd_soc_jack_add_gpios(&sinner_card->hp_jack, + ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); +} +#endif + +/* 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", +#ifndef CONFIG_ANDROID + .init = sirf_inner_init, +#endif + }, +}; + +static int sirf_inner_headphone_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int hp_out = 0; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = codec->card; + struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(sinner_card->gpio_hp_pa)) + hp_out = gpio_get_value(sinner_card->gpio_hp_pa); + + *ucontrol->value.integer.value = hp_out; + return 0; +} + +static int sirf_inner_headphone_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int is_hp_out = ucontrol->value.integer.value[0]; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = codec->card; + struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(sinner_card->gpio_hp_pa)) + gpio_direction_output(sinner_card->gpio_hp_pa, is_hp_out); + + return 0; +} + +static int sirf_inner_speaker_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int spk_out = 0; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = codec->card; + struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(sinner_card->gpio_spk_pa)) + spk_out = gpio_get_value(sinner_card->gpio_spk_pa); + + *ucontrol->value.integer.value = spk_out; + return 0; +} + +static int sirf_inner_speaker_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int is_spk_out = ucontrol->value.integer.value[0]; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = codec->card; + struct sirf_inner_card *sinner_card = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(sinner_card->gpio_spk_pa)) + gpio_direction_output(sinner_card->gpio_spk_pa, is_spk_out); + return 0; +} + +static int sirf_inner_speaker_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return 0; +} + +static int sirf_inner_headphone_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return 0; +} + +static struct snd_kcontrol_new snd_sirf_inner_out_route_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Speaker Out", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = sirf_inner_speaker_out_info, + .get = sirf_inner_speaker_out_get, + .put = sirf_inner_speaker_out_put, + }, { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Out", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = sirf_inner_headphone_out_info, + .get = sirf_inner_headphone_out_get, + .put = sirf_inner_headphone_out_put, + }, +}; + +/* 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), + .controls = snd_sirf_inner_out_route_controls, + .num_controls = ARRAY_SIZE(snd_sirf_inner_out_route_controls), +}; + +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; + sinner_card = devm_kzalloc(&pdev->dev, sizeof(struct sirf_inner_card), + GFP_KERNEL); + if (sinner_card == NULL) + return -ENOMEM; + + sirf_inner_dai_links[0].platform_of_node = + of_find_compatible_node(NULL, NULL, "sirf,pcm-audio"); + 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); +#ifndef CONFIG_ANDROID + sinner_card->gpio_hp_detect = of_get_named_gpio(pdev->dev.of_node, + "hp-switch-gpios", 0); +#endif + 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); + + return snd_soc_register_card(card); +} + +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); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id sirf_inner_of_match[] = { + {.compatible = "sirf,sirf-inner-audio", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sirf_inner_of_match); + +static struct platform_driver sirf_inner_driver = { + .driver = { + .name = "sirf-inner-audio", + .owner = THIS_MODULE, + .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@csr.com"); +MODULE_DESCRIPTION("ALSA SoC SIRF inner AUDIO driver"); +MODULE_LICENSE("GPL v2");