From patchwork Fri May 22 22:09:21 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dylan Reid X-Patchwork-Id: 6469131 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id CC1BBC0020 for ; Fri, 22 May 2015 22:12:43 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9EBF720532 for ; Fri, 22 May 2015 22:12:42 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id C5AAB20528 for ; Fri, 22 May 2015 22:12:40 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id B4C6E2612CC; Sat, 23 May 2015 00:12:39 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,NO_DNS_FOR_FROM, UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 786A626057A; Sat, 23 May 2015 00:11:12 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 2170F260557; Sat, 23 May 2015 00:11:09 +0200 (CEST) Received: from mail-vn0-f73.google.com (mail-vn0-f73.google.com [209.85.216.73]) by alsa0.perex.cz (Postfix) with ESMTP id CD0B126042F for ; Sat, 23 May 2015 00:10:55 +0200 (CEST) Received: by vnbf129 with SMTP id f129so1014789vnb.1 for ; Fri, 22 May 2015 15:10:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=iAGxYaMaveCLbflmm+Vgiqj5oW99cJ6wU4OKn4EO00U=; b=K1YPtyAIajQILo+bnUoJl6BX3TIinUlVQHb9pLJRKz8TH4O876yHGL70NsKnvrwNv+ p1WhYnng/OqhhB7Fif1JvHxSq5B4inBnSXu0ht7IqUP0FLmBziVc4bIXRlTWCVK/hYkV 3iSEOYQnwISr2v2SffQTU456xiEkazexV+jFSzMmL9pKK72HRANmwVAfWpPmAX8n9Z9i skId//ElcP3buusOY26eiVpnml9L9gdiy3Umr7t10qDHKeVcX3m/G1chRcPdqnv4dJT5 ulAlUGfydHiu6mGrlx1vWWcHtor//+02Gqzwp0b0Qq5PuWEqVPY6j38WSjZ0h8piu+W5 tdQw== X-Gm-Message-State: ALoCoQniDBJYJFXSgubD/thwyRtBTr0+tWSSSQQnNvjZaM2f+awHIC7CLy0d6PVfar9FNM9vwhY22kwfAbGx2AltVa8FbD2OvtEb0Z9Bejj6aGJfO/NS/HBY5tAZRgkiAQSeBjM4dmiN79z6PNRnIEgs7FmfC8NaBSAelFGrfBVvH359Q44AsV+5jQXUUe4GMKU9QiWimROu X-Received: by 10.52.100.102 with SMTP id ex6mr12772402vdb.0.1432332654571; Fri, 22 May 2015 15:10:54 -0700 (PDT) Received: from corpmail-nozzle1-1.hot.corp.google.com ([100.108.1.104]) by gmr-mx.google.com with ESMTPS id v23si178418yhv.4.2015.05.22.15.10.53 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 22 May 2015 15:10:54 -0700 (PDT) Received: from hojo20.mtv.corp.google.com ([172.22.65.103]) by corpmail-nozzle1-1.hot.corp.google.com with ESMTP id o9jeuwK6.1; Fri, 22 May 2015 15:10:54 -0700 Received: by hojo20.mtv.corp.google.com (Postfix, from userid 123195) id 773251C2FE4; Fri, 22 May 2015 15:10:53 -0700 (PDT) From: Dylan Reid To: alsa-devel@alsa-project.org Date: Fri, 22 May 2015 15:09:21 -0700 Message-Id: <1432332563-15447-4-git-send-email-dgreid@chromium.org> X-Mailer: git-send-email 2.2.1.62.g3f15098 In-Reply-To: <1432332563-15447-1-git-send-email-dgreid@chromium.org> References: <1432332563-15447-1-git-send-email-dgreid@chromium.org> Cc: lars@metafoo.de, zhengxing@rock-chips.com, tiwai@suse.de, lgirdwood@gmail.com, broonie@kernel.org, Dylan Reid Subject: [alsa-devel] [RFC 3/5] ASoC: Add GPIO based jack device X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP Add a jack device that allows for separate headphone and mic detect GPIOs. This device will be used as an aux device and will registered the jack with the card at init time. The gpios are each optional allowing for a headphone, microphone, or combination devices to be created depending on the board configuration. A board will be able to have several of these jacks, one for each physical connection. Signed-off-by: Dylan Reid --- .../devicetree/bindings/sound/gpio-audio-jack.txt | 39 +++++ sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/gpio-audio-jack.c | 191 +++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/gpio-audio-jack.txt create mode 100644 sound/soc/codecs/gpio-audio-jack.c diff --git a/Documentation/devicetree/bindings/sound/gpio-audio-jack.txt b/Documentation/devicetree/bindings/sound/gpio-audio-jack.txt new file mode 100644 index 0000000..17a4501 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/gpio-audio-jack.txt @@ -0,0 +1,39 @@ +Gpio-Audio-Jack: + +GPIO based audio jacks. This can represent a headphone, mic, or combo jack. + +Required properties: + +- compatible : "gpio-audio-jack" + +Optional properties: + +- gpio-audio-jack,jack-name : Name of the jack. Normally + 'Headphone Jack', 'Mic Jack', or + 'Headset Jack' +- gpio-audio-jack,sw-det-gpios : Reference to GPIOs that signals when + a jack is plugged. +- gpio-audio-jack,gpio-names : Names of the GPIOs. Must provide one + per sw-det-gpio entry. +- gpio-audio-jack,report-masks : Jack report mask from + dt-bindings/sound/audio-jack-events.h. + Must have one per sw-det-gpio entry. +- gpio-audio-jack,debounce-times : Debounce time for each sw-det-gpio + entry. + +Example of Headphone/Mic combo jack: + +audio_jack: gpio-audio-jack { + compatible = "gpio-audio-jack"; + gpio-audio-jack,jack-name = "Headset Jack"; + gpio-audio-jack,sw-det-gpios = + <&gpio TEGRA_GPIO(I, 7) GPIO_ACTIVE_HIGH>, + <&gpio TEGRA_GPIO(R, 7) GPIO_ACTIVE_LOW>; + gpio-audio-jack,gpio-names = "Headphones", "Mic Jack"; + gpio-audio-jack,report-masks = , ; + gpio-audio-jack,debounce-times = <150>, <150>; +}; + +sound { + nvidia,headset-dev = <&audio_jack>; + ... diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061c465..c9e96a7 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -62,6 +62,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_BT_SCO select SND_SOC_ES8328_SPI if SPI_MASTER select SND_SOC_ES8328_I2C if I2C + select SND_SOC_GPIO_AUDIO_JACK select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C @@ -444,6 +445,9 @@ config SND_SOC_ES8328_SPI tristate select SND_SOC_ES8328 +config SND_SOC_GPIO_AUDIO_JACK + tristate "GPIO based audio jack detection" + config SND_SOC_ISABELLE tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index abe2d7e..ae570f5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -55,6 +55,7 @@ snd-soc-dmic-objs := dmic.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o +snd-soc-gpio-audio-jack-objs := gpio-audio-jack.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o @@ -240,6 +241,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o +obj-$(CONFIG_SND_SOC_GPIO_AUDIO_JACK) += snd-soc-gpio-audio-jack.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o diff --git a/sound/soc/codecs/gpio-audio-jack.c b/sound/soc/codecs/gpio-audio-jack.c new file mode 100644 index 0000000..61662cf --- /dev/null +++ b/sound/soc/codecs/gpio-audio-jack.c @@ -0,0 +1,191 @@ +/* + * GPIO based audio jack detection + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define JACK_NAME_CON_ID "gpio-audio-jack,jack-name" +#define GPIO_CON_ID "gpio-audio-jack,sw-det" +#define NAME_CON_ID "gpio-audio-jack,gpio-names" +#define REPORT_MASK_CON_ID "gpio-audio-jack,report-masks" +#define DEBOUNCE_CON_ID "gpio-audio-jack,debounce-times" + +struct gpio_audio_jack { + struct snd_soc_component_driver component_drv; + struct snd_soc_jack jack; + struct snd_soc_jack_gpio *gpios; + int gpio_count; +}; + +static int dt_jack_to_alsa_report(int dt_jack) +{ + switch (dt_jack) { + case JACK_HEADPHONE: + return SND_JACK_HEADPHONE; + case JACK_MICROPHONE: + return SND_JACK_MICROPHONE; + case JACK_LINEOUT: + return SND_JACK_LINEOUT; + case JACK_LINEIN: + return SND_JACK_LINEIN; + default: + return 0; + } +} + +static int gpio_audio_component_probe(struct snd_soc_component *component) +{ + struct device *dev = component->dev; + struct gpio_audio_jack *priv = container_of(component->driver, + struct gpio_audio_jack, component_drv); + const char *jack_name; + const char **gpio_names; + u32 *debounce; + u32 *gpio_report; + int report_mask = 0; + int ret; + int i; + + ret = device_property_read_string(dev, JACK_NAME_CON_ID, &jack_name); + if (ret < 0) { + dev_err(dev, "Failed to read jack name %d\n", ret); + return ret; + } + + priv->gpio_count = gpiod_count(dev, GPIO_CON_ID); + if (priv->gpio_count <= 0) + return priv->gpio_count; + + gpio_names = devm_kzalloc(dev, sizeof(*gpio_names) * priv->gpio_count, + GFP_KERNEL); + if (!gpio_names) + return -ENOMEM; + gpio_report = devm_kzalloc(dev, sizeof(*gpio_report) * priv->gpio_count, + GFP_KERNEL); + if (!gpio_report) + return -ENOMEM; + debounce = devm_kzalloc(dev, sizeof(*debounce) * priv->gpio_count, + GFP_KERNEL); + if (!debounce) + return -ENOMEM; + priv->gpios = devm_kzalloc(dev, + sizeof(*priv->gpios) * priv->gpio_count, GFP_KERNEL); + if (!priv->gpios) + return -ENOMEM; + + ret = device_property_read_string_array(dev, NAME_CON_ID, gpio_names, + priv->gpio_count); + if (ret < 0) { + dev_err(dev, "Failed to read gpio names %d\n", ret); + return ret; + } + + ret = device_property_read_u32_array(dev, REPORT_MASK_CON_ID, + gpio_report, priv->gpio_count); + if (ret < 0) { + dev_err(dev, "Failed to read gpio masks %d\n", ret); + return ret; + } + + ret = device_property_read_u32_array(dev, DEBOUNCE_CON_ID, + debounce, priv->gpio_count); + if (ret < 0) { + dev_err(dev, "Failed to read gpio debounce times %d\n", ret); + return ret; + } + + for (i = 0; i < priv->gpio_count; i++) { + priv->gpios[i].desc = gpiod_get_index(dev, GPIO_CON_ID, + i, GPIOD_IN); + if (IS_ERR(priv->gpios[i].desc)) { + ret = PTR_ERR(priv->gpios[i].desc); + dev_err(priv->gpios[i].gpiod_dev, + "Cannot get jack gpio at index %d: %d", + i, ret); + goto gpio_err; + } + + priv->gpios[i].name = gpio_names[i]; + priv->gpios[i].report = dt_jack_to_alsa_report(gpio_report[i]); + priv->gpios[i].debounce_time = debounce[i]; + report_mask |= gpio_report[i]; + } + + snd_soc_card_jack_new(component->card, jack_name, report_mask, + &priv->jack, NULL, 0); + + devm_kfree(dev, gpio_names); + devm_kfree(dev, gpio_report); + devm_kfree(dev, debounce); + + return snd_soc_jack_add_gpiods(dev, &priv->jack, + priv->gpio_count, priv->gpios); + +gpio_err: + for (i = 0; i < priv->gpio_count; i++) { + if (!IS_ERR(priv->gpios[i].desc)) + gpiod_put(priv->gpios[i].desc); + } + return ret; +} + +static void gpio_audio_component_remove(struct snd_soc_component *component) +{ + struct gpio_audio_jack *priv = container_of(component->driver, + struct gpio_audio_jack, component_drv); + int i; + + for (i = 0; i < priv->gpio_count; i++) { + if (!IS_ERR(priv->gpios[i].desc)) + gpiod_put(priv->gpios[i].desc); + } +} + +static int gpio_audio_jack_probe(struct platform_device *pdev) +{ + struct gpio_audio_jack *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, priv); + + priv->component_drv.probe = gpio_audio_component_probe; + priv->component_drv.remove = gpio_audio_component_remove; + + return devm_snd_soc_register_component(&pdev->dev, &priv->component_drv, + NULL, 0); +} + +static const struct of_device_id gpio_audio_of_match[] = { + { .compatible = "gpio-audio-jack", }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_audio_of_match); + +static struct platform_driver gpio_audio_jack = { + .driver = { + .name = "gpio-audio-jack", + .of_match_table = gpio_audio_of_match, + }, + .probe = gpio_audio_jack_probe, +}; +module_platform_driver(gpio_audio_jack); + +MODULE_DESCRIPTION("ASoC GPIO audio jack driver"); +MODULE_AUTHOR("Dylan Reid "); +MODULE_LICENSE("GPL v2");