From patchwork Sat Feb 18 14:26:31 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: simon.ho.cnxt@gmail.com X-Patchwork-Id: 9581109 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 54EF46042F for ; Sat, 18 Feb 2017 14:27:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4099627FA8 for ; Sat, 18 Feb 2017 14:27:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 352622848D; Sat, 18 Feb 2017 14:27:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, T_DKIM_INVALID autolearn=no version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BC46E28551 for ; Sat, 18 Feb 2017 14:27:25 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 65FD62668CA; Sat, 18 Feb 2017 15:26:47 +0100 (CET) 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 927D2266838; Sat, 18 Feb 2017 15:26:44 +0100 (CET) Received: from mail-pf0-f195.google.com (mail-pf0-f195.google.com [209.85.192.195]) by alsa0.perex.cz (Postfix) with ESMTP id B88B7266860 for ; Sat, 18 Feb 2017 15:26:40 +0100 (CET) Received: by mail-pf0-f195.google.com with SMTP id 68so6266665pfx.2 for ; Sat, 18 Feb 2017 06:26:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=eb9j7BRRJNZLnqbA8VfXMHxPu2c24TLk69Bi5LIx190=; b=lT3qkEkYb1dJ5u4fwAzkOZWd8gadP3xGNpHfzRsT4AKGrznd6O+fKT1hmex/fs3Ylz CkHkuOhuhbRAxO5GNyTWDhNWc/nQIahPrBf6lKYO8EKaWMrFTE0MLnlI/PIW5Tzlas+T IhFkg3MnDJp+PkWdvYlORTr7QtGaa4zR9aapwwjXHANrP7T6eFyDXFsAq8CRYlg5KLOx Qb2aWnoANkusNzezF114phqcc4/ZrZuPKBhzRWtR/L+bMLbmcrPXJ4leWG/Z/ZfxEXPD fExnmCnfjoCawrmmMK+rc7O9h9qYpOlKkwqZY6mot6fk2G2dLWCrTPMkFlz5LO3UlF+I SgHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=eb9j7BRRJNZLnqbA8VfXMHxPu2c24TLk69Bi5LIx190=; b=EkdUswCCNPf4X84TViVAKTLioLNNdiS11ov3m4B7eEItv82JbRedacC+NlZk/kimdZ zCZH6KtR/szRS+AvU79b+n+d0W2kEA3xXpqN+It0+1XhxmphdIKaJzeJqcOFbP1gEcxi 7y3BCano3e7Znm++6UWMo34cWIKojobyO52NyVoS8UXTIt9jwSNu6ByhTDW+pZgzZpiw 9RGdBGwCGSVhOfS6Qc9KukTSPxh4BldtsMAen54DmRuzBCZkSQ2Naz2kS5e3T0OoMkgW xZ/rb5/2zc9NkpTlmBhkZS/aKIiKzjqG8QTMhpab5Nbo1sR07luTZC62syQH59miSpNQ b1eA== X-Gm-Message-State: AMke39mmJLYYkFmKgNl/8U/vkds6KqynMYrkGBFt+hzjBg8NUOA5LMrHh1badAaxAOcrww== X-Received: by 10.98.19.145 with SMTP id 17mr15353394pft.26.1487427999649; Sat, 18 Feb 2017 06:26:39 -0800 (PST) Received: from localhost.localdomain (114-42-202-32.dynamic.hinet.net. [114.42.202.32]) by smtp.gmail.com with ESMTPSA id l12sm25744620pfj.37.2017.02.18.06.26.37 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 18 Feb 2017 06:26:39 -0800 (PST) From: simon.ho.cnxt@gmail.com X-Google-Original-From: simon.ho@conexant.com To: alsa-devel@alsa-project.org Date: Sat, 18 Feb 2017 22:26:31 +0800 Message-Id: <1487427991-18174-3-git-send-email-simon.ho@conexant.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1487427991-18174-1-git-send-email-simon.ho@conexant.com> References: <1487427991-18174-1-git-send-email-simon.ho@conexant.com> Cc: Simon Ho , tiwai@suse.com, broonie@kernel.org, lgirdwood@gmail.com Subject: [alsa-devel] [PATCH 2/2] ASoC: Add driver for CX2092X I2S Audio DSP 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 From: Simon Ho Initial commit of the Conexant CX20921/CX20924 I2S audio DSP driver The CX2092X is a far field voice input processor SoC with ADC. Signed-off-by: Simon Ho --- sound/soc/codecs/Kconfig | 17 ++ sound/soc/codecs/Makefile | 6 + sound/soc/codecs/cx2092x-i2c.c | 54 ++++++ sound/soc/codecs/cx2092x-spi.c | 57 ++++++ sound/soc/codecs/cx2092x.c | 419 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cx2092x.h | 26 +++ 6 files changed, 579 insertions(+) create mode 100644 sound/soc/codecs/cx2092x-i2c.c create mode 100644 sound/soc/codecs/cx2092x-spi.c create mode 100644 sound/soc/codecs/cx2092x.c create mode 100644 sound/soc/codecs/cx2092x.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 37a4a43..79b3512 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -63,6 +63,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS47L24 if MFD_CS47L24 select SND_SOC_CS53L30 if I2C select SND_SOC_CX20442 if TTY + select SND_SOC_CX2092X_I2C if I2C + select SND_SOC_CX2092X_SPI if SPI_MASTER select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C select SND_SOC_DA7218 if I2C @@ -491,6 +493,21 @@ config SND_SOC_CX20442 tristate depends on TTY +config SND_SOC_CX2092X + tristate + +config SND_SOC_CX2092X_I2C + tristate "Conexant CX20921/CX2094 CODEC (I2C)" + depends on I2C + select SND_SOC_CX2092X + select REGMAP_I2C + +config SND_SOC_CX2092X_SPI + tristate "Conexant CX20921/CX2094 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CX2092X + select REGMAP_SPI + config SND_SOC_JZ4740_CODEC select REGMAP_MMIO tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1796cb9..e30a398 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -56,6 +56,9 @@ snd-soc-cs4349-objs := cs4349.o snd-soc-cs47l24-objs := cs47l24.o snd-soc-cs53l30-objs := cs53l30.o snd-soc-cx20442-objs := cx20442.o +snd-soc-cx2092x-objs := cx2092x.o +snd-soc-cx2092x-i2c-objs := cx2092x-i2c.o +snd-soc-cx2092x-spi-objs := cx2092x-spi.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o snd-soc-da7218-objs := da7218.o @@ -286,6 +289,9 @@ obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o +obj-$(CONFIG_SND_SOC_CX2092X) += snd-soc-cx2092x.o +obj-$(CONFIG_SND_SOC_CX2092X_I2C) += snd-soc-cx2092x-i2c.o +obj-$(CONFIG_SND_SOC_CX2092X_SPI) += snd-soc-cx2092x-spi.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o diff --git a/sound/soc/codecs/cx2092x-i2c.c b/sound/soc/codecs/cx2092x-i2c.c new file mode 100644 index 0000000..a07800e --- /dev/null +++ b/sound/soc/codecs/cx2092x-i2c.c @@ -0,0 +1,54 @@ +/* + * cx2092x-i2c.c -- CX20921 and CX20924 I2C Audio driver + * + * Copyright: (C) 2017 Conexant Systems, Inc. + * + * This is based on Alexander Sverdlin's CS4271 driver code. + * + * 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 "cx2092x.h" + +static int cx2092x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return cx2092x_dev_probe(&i2c->dev, + devm_regmap_init_i2c(i2c, &cx2092x_regmap_config)); +} +static int cx2092x_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id cx2092x_i2c_id[] = { + {"cx20921", 0}, + {"cx20924", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cx2092x_i2c_id); + +static struct i2c_driver cx2092x_i2c_driver = { + .driver = { + .name = "cx2092x", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cx2092x_dt_ids), + }, + .id_table = cx2092x_i2c_id, + .probe = cx2092x_i2c_probe, + .remove = cx2092x_i2c_remove, +}; +module_i2c_driver(cx2092x_i2c_driver); + +MODULE_DESCRIPTION("ASoC CX2092X I2C Driver"); +MODULE_AUTHOR("Simon Ho "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cx2092x-spi.c b/sound/soc/codecs/cx2092x-spi.c new file mode 100644 index 0000000..3723909 --- /dev/null +++ b/sound/soc/codecs/cx2092x-spi.c @@ -0,0 +1,57 @@ +/* + * cx2092x-spi.c -- CX20921 and CX20924 SPI Audio driver + * + * Copyright: (C) 2017 Conexant Systems, Inc. + * + * This is based on Alexander Sverdlin's CS4271 driver code. + * + * 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 "cx2092x.h" + +static int cx2092x_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = cx2092x_regmap_config; + config.write_flag_mask = 0x81; + + return cx2092x_dev_probe(&spi->dev, + devm_regmap_init_spi(spi, &cx2092x_regmap_config)); +} + +static int cx2092x_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static const struct spi_device_id cx2092x_spi_id[] = { + {"cx20921", 0}, + {"cx20924", 0}, + {} +}; + +static struct spi_driver cx2092x_spi_driver = { + .driver = { + .name = "cx2092x", + .of_match_table = of_match_ptr(cx2092x_dt_ids), + }, + .id_table = cx2092x_spi_id, + .probe = cx2092x_spi_probe, + .remove = cx2092x_spi_remove, +}; + +module_spi_driver(cx2092x_spi_driver); + +MODULE_DESCRIPTION("ASoC CX2092X SPI Driver"); +MODULE_AUTHOR("Simon Ho "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cx2092x.c b/sound/soc/codecs/cx2092x.c new file mode 100644 index 0000000..a231ee1 --- /dev/null +++ b/sound/soc/codecs/cx2092x.c @@ -0,0 +1,419 @@ +/* + * cx2092x.c -- CX20921 and CX20924 ALSA SoC Audio driver + * + * Copyright: (C) 2017 Conexant Systems, Inc. + * + * This is based on Alexander Sverdlin's CS4271 driver code. + * + * 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 +#include +#include +#include "cx2092x.h" + +#define CX2092X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define CX2092X_CAPE_ID(a, b, c, d) (((a) - 0x20) << 8 | \ + ((b) - 0x20) << 14| \ + ((c) - 0x20) << 20| \ + ((d) - 0x20) << 26) + +#define CX2092X_ID2CH_A(id) (((((unsigned int)(id)) >> 8) & 0x3f) + 0x20) +#define CX2092X_ID2CH_B(id) (((((unsigned int)(id)) >> 14) & 0x3f) + 0x20) +#define CX2092X_ID2CH_C(id) (((((unsigned int)(id)) >> 20) & 0x3f) + 0x20) +#define CX2092X_ID2CH_D(id) (((((unsigned int)(id)) >> 26) & 0x3f) + 0x20) + +#define CX2092X_CONTROL(xname, xinfo, xget, xput, xaccess) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = xaccess, .info = xinfo, .get = xget, .put = xput, \ + } + +#define CX2092X_CMD_GET(item) ((item) | 0x0100) +#define CX2092X_CMD_SIZE 13 + +/* + * Defines the command format which is used to communicate with cx2092x device. + */ +struct cx2092x_cmd { + int num_32b_words:16; /* Indicates how many data to be sent. + * If operation is successful, this will + * be updated with the number of returned + * data in word. one word == 4 bytes. + */ + + u32 command_id:15; + u32 reply:1; /* The device will set this flag once + * the operation is complete. + */ + u32 app_module_id; + u32 data[CX2092X_CMD_SIZE]; /* Used for storing parameters and + * receiving the returned data from + * device. + */ +}; + +/* codec private data*/ +struct cx2092x_priv { + struct device *dev; + struct regmap *regmap; + int gpio_reset; + struct cx2092x_cmd cmd; + int cmd_res; +}; + +/* + * This functions takes cx2092x_cmd structure as input and output parameters + * to communicate CX2092X. If operation is successfully, it returns number of + * returned data and stored the returned data in "cmd->data" array. + * Otherwise, it returns the error code. + */ +static int cx2092x_sendcmd(struct snd_soc_codec *codec, + struct cx2092x_cmd *cmd) +{ + struct cx2092x_priv *cx2092x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + int num_32b_words = cmd->num_32b_words; + unsigned long time_out; + u32 *i2c_data = (u32 *)cmd; + int size = num_32b_words + 2; + + /* calculate how many WORD that will be wrote to device*/ + cmd->num_32b_words = cmd->command_id & CX2092X_CMD_GET(0) ? + CX2092X_CMD_SIZE : num_32b_words; + + + /* write all command data except fo frist 4 bytes*/ + ret = regmap_bulk_write(cx2092x->regmap, 4, &i2c_data[1], size - 1); + if (ret < 0) { + dev_err(cx2092x->dev, "Failed to write command data\n"); + goto LEAVE; + } + + /* write first 4 bytes command data*/ + ret = regmap_bulk_write(cx2092x->regmap, 0, i2c_data, 1); + if (ret < 0) { + dev_err(cx2092x->dev, "Failed to write command\n"); + goto LEAVE; + } + + /* continuously read the first bytes data from device until + * either timeout or the flag 'reply' is set. + */ + time_out = msecs_to_jiffies(2000); + time_out += jiffies; + do { + regmap_bulk_read(cx2092x->regmap, 0, &i2c_data[0], 1); + if (cmd->reply == 1) + break; + mdelay(10); + + } while (!time_after(jiffies, time_out)); + + if (cmd->reply == 1) { + /* check if there is returned data. If yes copy the returned + * data to cmd->data array + */ + if (cmd->num_32b_words > 0) + regmap_bulk_read(cx2092x->regmap, 8, &i2c_data[2], + cmd->num_32b_words); + /* return error code if operation is not successful.*/ + else if (cmd->num_32b_words < 0) + dev_err(cx2092x->dev, "SendCmd failed, err = %d\n", + cmd->num_32b_words); + + ret = cmd->num_32b_words; + } else { + dev_err(cx2092x->dev, "SendCmd timeout\n"); + ret = -EBUSY; + } + +LEAVE: + return ret; +} + + +static int cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct cx2092x_cmd); + + return 0; +} + +static int cmd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cx2092x_priv *cx2092x = + snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.bytes.data, &cx2092x->cmd, + sizeof(cx2092x->cmd)); + + return 0; +} + +static int cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cx2092x_priv *cx2092x = snd_soc_component_get_drvdata(component); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + + memcpy(&cx2092x->cmd, ucontrol->value.bytes.data, + sizeof(cx2092x->cmd)); + + cx2092x->cmd_res = cx2092x_sendcmd(codec, &cx2092x->cmd); + + if (cx2092x->cmd_res < 0) + dev_err(codec->dev, "Failed to send cmd, ret = %d\n", + cx2092x->cmd_res); + + return cx2092x->cmd_res < 0 ? cx2092x->cmd_res : 0; +} + + +static int mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 4; + + return 0; +} + +static int mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct cx2092x_cmd cmd; + int ret = 0; + + cmd.command_id = 0x12f; /*CX2092X_CMD_GET(SOS_RESOURCE);*/ + cmd.reply = 0; + cmd.app_module_id = CX2092X_CAPE_ID('S', 'O', 'S', ' '); + cmd.num_32b_words = 1; + cmd.data[0] = CX2092X_CAPE_ID('C', 'T', 'R', 'L'); + + ret = cx2092x_sendcmd(codec, &cmd); + if (ret <= 0) + dev_err(codec->dev, "Failed to get current mode, ret = %d\n", + ret); + else { + ucontrol->value.bytes.data[0] = CX2092X_ID2CH_A(cmd.data[0]); + ucontrol->value.bytes.data[1] = CX2092X_ID2CH_B(cmd.data[0]); + ucontrol->value.bytes.data[2] = CX2092X_ID2CH_C(cmd.data[0]); + ucontrol->value.bytes.data[3] = CX2092X_ID2CH_D(cmd.data[0]); + + dev_dbg(codec->dev, "Current mode = %c%c%c%c\n", + ucontrol->value.bytes.data[0], + ucontrol->value.bytes.data[1], + ucontrol->value.bytes.data[2], + ucontrol->value.bytes.data[3]); + + ret = 0; + } + + return ret; +} + +static int mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); + struct cx2092x_cmd cmd; + int ret = -1; + + cmd.command_id = 4; + cmd.reply = 0; + cmd.app_module_id = CX2092X_CAPE_ID('C', 'T', 'R', 'L'); + cmd.num_32b_words = 1; + cmd.data[0] = CX2092X_CAPE_ID(ucontrol->value.bytes.data[0], + ucontrol->value.bytes.data[1], + ucontrol->value.bytes.data[2], + ucontrol->value.bytes.data[3]); + + ret = cx2092x_sendcmd(codec, &cmd); + if (ret < 0) { + dev_err(codec->dev, "Failed to set mode, ret =%d\n", ret); + } else { + dev_dbg(codec->dev, "Set mode successfully, ret = %d\n", ret); + ret = 0; + } + + return ret; +} + +static const struct snd_kcontrol_new cx2092x_snd_controls[] = { + CX2092X_CONTROL("SendCmd", cmd_info, cmd_get, cmd_put, + SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE| + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + CX2092X_CONTROL("Mode", mode_info, mode_get, mode_put, + SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE| + SNDRV_CTL_ELEM_ACCESS_VOLATILE), +}; + + +static const struct snd_soc_dapm_widget cx2092x_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("Mic AIF", "Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("MIC"), +}; + +static const struct snd_soc_dapm_route cx2092x_intercon[] = { + {"Mic AIF", NULL, "MIC"}, +}; + + +static struct snd_soc_dai_driver soc_codec_cx2092x_dai[] = { + { + .name = "cx2092x-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = CX2092X_FORMATS, + }, + }, + { + .name = "cx2092x-dsp", + .capture = { + .stream_name = "AEC Ref", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = CX2092X_FORMATS, + }, + }, +}; + +static int cx2092x_reset(struct snd_soc_codec *codec) +{ + struct cx2092x_priv *cx2092x = snd_soc_codec_get_drvdata(codec); + + if (gpio_is_valid(cx2092x->gpio_reset)) { + gpio_set_value(cx2092x->gpio_reset, 0); + mdelay(10); + gpio_set_value(cx2092x->gpio_reset, 1); + } + + return 0; +} + +const struct of_device_id cx2092x_dt_ids[] = { + { .compatible = "cnxt,cx20921", }, + { .compatible = "cnxt,cx20924", }, + { } +}; +EXPORT_SYMBOL_GPL(cx2092x_dt_ids); +MODULE_DEVICE_TABLE(of, cx2092x_dt_ids); + +static int cx2092x_probe(struct snd_soc_codec *codec) +{ + return cx2092x_reset(codec); +} + +static int cx2092x_remove(struct snd_soc_codec *codec) +{ + struct cx2092x_priv *cx2092x = snd_soc_codec_get_drvdata(codec); + + if (gpio_is_valid(cx2092x->gpio_reset)) + /* Set codec to the reset state */ + gpio_set_value(cx2092x->gpio_reset, 0); + + return 0; +} + + +static struct snd_soc_codec_driver soc_codec_driver_cx2092x = { + .probe = cx2092x_probe, + .remove = cx2092x_remove, + + .component_driver = { + .controls = cx2092x_snd_controls, + .num_controls = ARRAY_SIZE(cx2092x_snd_controls), + .dapm_widgets = cx2092x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cx2092x_dapm_widgets), + .dapm_routes = cx2092x_intercon, + .num_dapm_routes = ARRAY_SIZE(cx2092x_intercon), + }, +}; +EXPORT_SYMBOL_GPL(soc_codec_driver_cx2092x); + +static bool cx2092x_volatile_register(struct device *dev, unsigned int reg) +{ + return true; /*all register are volatile*/ +} + +const struct regmap_config cx2092x_regmap_config = { + .reg_bits = 16, + .val_bits = 32, + .reg_stride = 4, + .max_register = CX2092X_REG_MAX, + .cache_type = REGCACHE_NONE, + .volatile_reg = cx2092x_volatile_register, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; +EXPORT_SYMBOL_GPL(cx2092x_regmap_config); + +int cx2092x_dev_probe(struct device *dev, struct regmap *regmap) +{ + struct cx2092x_priv *cx2092x; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cx2092x = devm_kzalloc(dev, sizeof(*cx2092x), GFP_KERNEL); + if (!cx2092x) + return -ENOMEM; + + if (of_match_device(cx2092x_dt_ids, dev)) + cx2092x->gpio_reset = + of_get_named_gpio(dev->of_node, "reset-gpio", 0); + + if (gpio_is_valid(cx2092x->gpio_reset)) { + int ret; + + ret = devm_gpio_request(dev, cx2092x->gpio_reset, + "CX2092X Reset"); + if (ret < 0) + return ret; + } + + dev_set_drvdata(dev, cx2092x); + cx2092x->regmap = regmap; + cx2092x->dev = dev; + + ret = snd_soc_register_codec(cx2092x->dev, &soc_codec_driver_cx2092x, + soc_codec_cx2092x_dai, + ARRAY_SIZE(soc_codec_cx2092x_dai)); + if (ret < 0) + dev_err(dev, "Failed to register codec: %d\n", ret); + else + dev_dbg(dev, "%s: Register codec.\n", __func__); + + return ret; +} +EXPORT_SYMBOL_GPL(cx2092x_dev_probe); + +MODULE_DESCRIPTION("ASoC CX2092X ALSA SoC Driver"); +MODULE_AUTHOR("Simon Ho "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cx2092x.h b/sound/soc/codecs/cx2092x.h new file mode 100644 index 0000000..9577be8 --- /dev/null +++ b/sound/soc/codecs/cx2092x.h @@ -0,0 +1,26 @@ +/* + * cx2092x.h -- CX20921 and CX20924 Audio driver + * + * Copyright: (C) 2017 Conexant Systems, Inc. + * + * This is based on Alexander Sverdlin's CS4271 driver code. + * + * 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. + * + */ + +#ifndef __CX2092X_PRIV_H__ +#define __CX2092X_PRIV_H__ + +#include + +extern const struct of_device_id cx2092x_dt_ids[]; +extern const struct regmap_config cx2092x_regmap_config; + +int cx2092x_dev_probe(struct device *dev, struct regmap *regmap); + +#define CX2092X_REG_MAX 0x2000 + +#endif