From patchwork Thu May 12 15:35:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Rosin X-Patchwork-Id: 9083761 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 7106E9F1C3 for ; Thu, 12 May 2016 15:48:56 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 56EEC20256 for ; Thu, 12 May 2016 15:48:54 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id EA90A2024F for ; Thu, 12 May 2016 15:48:50 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id EC2112604A9; Thu, 12 May 2016 17:48:47 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Status: No, score=-1.8 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, DKIM_SIGNED, RCVD_IN_DNSWL_NONE, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 44597265357; Thu, 12 May 2016 17:45: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 4CC1A265357; Thu, 12 May 2016 17:45:11 +0200 (CEST) Received: from emea01-am1-obe.outbound.protection.outlook.com (mail-am1on0129.outbound.protection.outlook.com [157.56.112.129]) by alsa0.perex.cz (Postfix) with ESMTP id 7FBCF266A99 for ; Thu, 12 May 2016 17:35:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=axentiatech.onmicrosoft.com; s=selector1-axentia-se; h=From:To:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=UhLSwhsUklpyX7PX0Vp3Um4wnHgYsRM0zb3pW1mc7lo=; b=LRxNjgYS1I0MDF8HFHGa6jFn3DKvROgGd6+R8ZTNnvknZ9Doo9l8X4sz4gqzV2G22sEwce7FUDJs1F9cYb6eygQf4VaK9L2fR9hCkLHN+TtZX10sl62qP0PC1WK3guPEY2e7szxjIQRz6TajIibLtE0Vo+UvvghGedFCEYQ8kYg= Authentication-Results: vger.kernel.org; dkim=none (message not signed) header.d=none; vger.kernel.org; dmarc=none action=none header.from=axentia.se; Received: from localhost.localdomain (217.210.101.82) by DB5PR02MB1304.eurprd02.prod.outlook.com (10.164.177.150) with Microsoft SMTP Server (TLS) id 15.1.492.11; Thu, 12 May 2016 15:35:10 +0000 From: Peter Rosin To: Date: Thu, 12 May 2016 17:35:01 +0200 Message-ID: <1463067301-6047-1-git-send-email-peda@axentia.se> X-Mailer: git-send-email 2.1.4 MIME-Version: 1.0 X-Originating-IP: [217.210.101.82] X-ClientProxiedBy: VI1PR05CA0034.eurprd05.prod.outlook.com (10.162.33.172) To DB5PR02MB1304.eurprd02.prod.outlook.com (10.164.177.150) X-MS-Office365-Filtering-Correlation-Id: 4848d9af-0878-495e-34f0-08d37a7b01fc X-Microsoft-Exchange-Diagnostics: 1; DB5PR02MB1304; 2:TCG88zNwDMRdEk2VhuFMuIQSeB3AhPEkLozafBUoCa8Wjtt7WHG3Qxtn6dyYxmsHIfsweoC2+r1tkVTL+TEyKHWDkkgjWN/oxqv6gHTL1isX0v/nWuxQkFMtR5lw6p+5moaVic4DShCOl1XmVVOzHYEMI+UYrGmMunRFxRb+EfWleSy+If6IPqM3EXxCVW8y; 3:JYzhYtpZYPgMwR3DLYmxV5+PZlK3AOSptLgO5O4YADzDRhlelkBceZNeKIfWe/QbSXX3xbq0C0N6hRfKL4bxztBQSEU2TKKddCPUAa5Dx9bvamiq5dh5+Zfpz7Vw+jq0; 25:XPPEJSf1kOsHFaYLUiSHxVNH91ahCtzi/iFmWaB9zQvQOIzv1X1v3pwhbwp2OV9W1weFkd2RHZjKVctJRzXQR42eDFqdmtmb+JySFLMWkHLS/6ZWo7RqOaGVLuxnPOR4iJ11HKJw8zGRNec71GuXr032IbwyO3u602r5nf6Gvu+nT5RPVR6HCp9FSIqJeKcgsKLN8/PeZoxa05qnTUSPnfImLhD07Z5LvAjznI39nYsrQMyqW8hdThtPE6jZIdNtGMZdC9JjMPPSVGIR3fxvMpWQPwFibSyxTig6gL9RCFAqlQvXowrdLMdq5xaiU3R6b+EicDaVMlHHSVhCVYvZ/MrjHm/WPlHmEkXx9UQ1uFT+NOTvum+xzvajkHu/p/Kx/FfwtdUHVAY/FGfDxZIkr0lFYEcX1xpEmcUq7/eqbcg= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DB5PR02MB1304; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040130)(601004)(2401047)(8121501046)(5005006)(10201501046)(3002001)(6041072)(6043046); SRVR:DB5PR02MB1304; BCL:0; PCL:0; RULEID:; SRVR:DB5PR02MB1304; X-Microsoft-Exchange-Diagnostics: 1; DB5PR02MB1304; 4:e26znKLj1cEPDOy7PZfx+NZ/rEdVVLscT9p40SQDdUN7KAyPee4Fjh9Ud7KiXbnvXB3Do/zyr9f7G9NwjebeLuv040N1IZt5iI1Um23vX5y5hzu7manYrVyD/PG/51Yr/XW5EXXWRATtVUfmXqsY8CIURyVZbuYbIbkYgs88uKtnBEkVugi2paJIR7UZ9D+/sRiFRfRzB5OUjul6uYzIX/981RUgW8z6gcJf38vTasy6JJkLZ/zyxggFhe5XEMz89vmz14JgeSmZoNqXuV8MEJMxNeARzEpWkEKUjenczJ7rJKXtbFPfvfLZo8Awgpe9Bk/9/hRcHGPKkxSET4Me0G5cWh6rMRkOj2yCfQIAWHqZZPaAfZdTczoYkIEM4bSctzbDU4dHHFgySG8FgUzbX2OElVx9x4IWWZs9RwpT2vI= X-Forefront-PRVS: 0940A19703 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(979002)(6069001)(6009001)(48376002)(586003)(36756003)(110136002)(5004730100002)(33646002)(229853001)(50466002)(97736004)(2351001)(189998001)(66066001)(42186005)(106356001)(5008740100001)(50986999)(575784001)(15975445007)(47776003)(2906002)(81166006)(4326007)(19580405001)(77096005)(19580395003)(5003940100001)(86362001)(92566002)(50226002)(3846002)(6116002)(74482002)(2004002)(42262002)(357404004)(969003)(989001)(999001)(1009001)(1019001); DIR:OUT; SFP:1102; SCL:1; SRVR:DB5PR02MB1304; H:localhost.localdomain; FPR:; SPF:None; MLV:ovrnspm; PTR:InfoNoRecords; LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; DB5PR02MB1304; 23:87Stib05h0wG/Y97Uxpp/dyNg9TrUb0J51TM6DPDV?= =?us-ascii?Q?wvisU116JIpUK+Xzh257hO5VmAZ8mZMhQB4V9HkwLoYyj3bj8oCrX2R4UkVA?= =?us-ascii?Q?x1wXSviAifTQDOsPfWfhiFU2FCcVI+FKO3OzSMe5ko/P4wB30++Ys+e2Wzwe?= =?us-ascii?Q?SuskwM+WJS5CPZULKXFzBlnlksdpWemg9y0O8vDwf8d+/3sQzULG1ixCXKYA?= =?us-ascii?Q?xiDmPktS66idi6ZHiWoGi+nDPQ0QqxpSwpGP20QvsxXAwdUh6Nfh8aOCyv7F?= =?us-ascii?Q?862wZrIFo1FMvpbKK2ow9jtcaSneknB0KeojSBfnVRn2HDrugJ4+hG5zFkvr?= =?us-ascii?Q?w10Gpqnee4pGY4Trt93DmONr1bqD9JqaLHZ9tVN5S/vpZXqFv7ydf0+uoQ3o?= =?us-ascii?Q?UhjE/2Th+CtCNkuYTF5hjSX0WiZ1IwSqwGBoc5NKxR595P8UpfLp339rw6hG?= =?us-ascii?Q?YLCGgVxfqSrfDsdYja0sXJlfQfLizN48ZlkFF0pSuh/uM0dwDLrwHgFIU6YG?= =?us-ascii?Q?pJx4I0UK8F9t6A0QMnNxmNsYbnNUUSRy/klDFTVBFK08mhim4fZyh5pwl6Wp?= =?us-ascii?Q?P+6lm3RE8AmA+eGgfbW46KiblickgCr4j2fn40bl/j2SFymU7P8uA9sHsxOj?= =?us-ascii?Q?vlAQXKxaapnXkF1qnW7i3Co6Ws5KqLQsChGtmBGD6mv9ugPpDHXUVPqvmgPH?= =?us-ascii?Q?R8b/5+FZLDy5xU66a+qY1bg/6djR2gxEywxUidY18GoP/TwMKxk+iJro5PIz?= =?us-ascii?Q?HBKMv/j9Tn8CG0c3Ts9ZC9nh7ptLoTn0nRdiaGnL+GmRJeWu1G1u5S+VJLDf?= =?us-ascii?Q?A5eBmP99AbOAvSxoC051MmkR3NF4IzJYMJ/lqNap14/Ix8/Cg8eEbOGR62Jq?= =?us-ascii?Q?lROEhikiwXOTkLF310dJ4c9vMxivzgWPELk3U+7D77MTxfTs6f3iRFRJJ46O?= =?us-ascii?Q?tO7O58nuFqNw0pyqXxeoQ7OhWxCCrb4Z8U7nQutmrXBVKE/5tX0T1xAQJaEY?= =?us-ascii?Q?LThcLa89D2BT96V7MbeB7Px4uyKoH0k5YTJQAGnh9E34/RBaVGHMpNydGBdb?= =?us-ascii?Q?qjmrU4yNvpCRJpWbZoYgUzO6DMFN1G1UrBzVCEcf3DTVdN4NmitNpV39YaMz?= =?us-ascii?Q?0ZD8pK5cxE=3D?= X-Microsoft-Exchange-Diagnostics: 1; DB5PR02MB1304; 5:CxMipsZQ+SJ9PrZHselkB8WAuGBRSnZuJ9fk6SRzbaa6m8psQGgDZEFYWwoqaQxTsMKajAgGslheF4EpojJChBxv8QXnqtSbgWu/JdkWgwepLEvMQwgKK587aj1iOvAzKf8kh4QrRZan1J0pCEGZ+w==; 24:Z6lOWAbv6OVgUrAt9hrybO99sfAzv7U47B0a3CsA581iqtXvtCGIU6BYoAbnoYnjh0iza67HVEwMlnUnEnB8RKlLe5c0Z4tCrz4BLpl0OJg=; 7:Kknpio3TknbJkU4+KxXl9izSoXv92wGUmvI91lpCPyj1GtCorR/asAQ8/LTvRNbclk9d7pNNB+wdnpsDtUWW8umzZkm6TXS8MDYhQK2oqJMB1t71DQjxc9zagYNh2Ip9qKB5dW4Y1YaBnPAQcSQCcs2MhinE7u03ZEgpzYYuz8irYKIN5jwTbbryYSjkneRP SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: axentia.se X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 May 2016 15:35:10.1149 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB5PR02MB1304 Cc: Mark Rutland , devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Pawel Moll , Ian Campbell , Mark Brown , Takashi Iwai , Liam Girdwood , Rob Herring , Misbah Ullah Khan , Kumar Gala , Peter Rosin Subject: [alsa-devel] [PATCH v2] ASoC: MAX9860: new driver 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: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This is a driver for the MAX9860 Mono Audio Voice Codec. https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf This driver does not support sidetone since the DVST register field is backwards with the mute near the maximum level instead of the minimum. Signed-off-by: Peter Rosin --- MAINTAINERS | 7 + sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/max9860.c | 761 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/max9860.h | 162 ++++++++++ 5 files changed, 938 insertions(+) create mode 100644 sound/soc/codecs/max9860.c create mode 100644 sound/soc/codecs/max9860.h I have yet to find a good spot to add scanning for a mute TLV value needed for the backwards sidetone register field (DVST). Hence, still no sidetone support... New in v2: - Add comment about fall through when Integer Clock Mode is not possible. - Drop export of max9860_probe. - Ignore clk docs and read the mclk rate w/o enabling the clock. - Manage the DVDDIO supply. Cheers, Peter diff --git a/MAINTAINERS b/MAINTAINERS index a727d9959ecd..0803396b10f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7000,6 +7000,13 @@ F: Documentation/devicetree/bindings/i2c/max6697.txt F: drivers/hwmon/max6697.c F: include/linux/platform_data/max6697.h +MAX9860 MONO AUDIO VOICE CODEC DRIVER +M: Peter Rosin +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/max9860.txt +F: sound/soc/codecs/max9860* + MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS M: Krzysztof Kozlowski L: linux-pm@vger.kernel.org diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7ef3a0c16478..241a23c9aa9f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -83,6 +83,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C select SND_SOC_MAX9850 if I2C + select SND_SOC_MAX9860 if I2C select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX @@ -534,6 +535,11 @@ config SND_SOC_MAX98926 config SND_SOC_MAX9850 tristate +config SND_SOC_MAX9860 + tristate "Maxim MAX9860 Mono Audio Voice Codec" + depends on I2C + select REGMAP_I2C + config SND_SOC_PCM1681 tristate "Texas Instruments PCM1681 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 185a712a7fe7..e435f4df1787 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -78,6 +78,7 @@ snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o snd-soc-max9850-objs := max9850.o +snd-soc-max9860-objs := max9860.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-nau8825-objs := nau8825.o @@ -287,6 +288,7 @@ obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o +obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c new file mode 100644 index 000000000000..ee30832b0afb --- /dev/null +++ b/sound/soc/codecs/max9860.c @@ -0,0 +1,761 @@ +/* + * Driver for the MAX9860 Mono Audio Voice Codec + * + * https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf + * + * The driver does not support sidetone since the DVST register field is + * backwards with the mute near the maximum level instead of the minimum. + * + * Author: Peter Rosin + * Copyright 2016 Axentia Technologies + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max9860.h" + +struct max9860_priv { + struct regmap *regmap; + struct regulator *dvddio; + struct notifier_block dvddio_nb; + u8 psclk; + unsigned long pclk_rate; + int fmt; +}; + +static int max9860_dvddio_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct max9860_priv *max9860 = container_of(nb, struct max9860_priv, + dvddio_nb); + if (event & REGULATOR_EVENT_DISABLE) { + regcache_mark_dirty(max9860->regmap); + regcache_cache_only(max9860->regmap, true); + } + + return 0; +} + +static const struct reg_default max9860_reg_defaults[] = { + { MAX9860_PWRMAN, 0x00 }, + { MAX9860_INTEN, 0x00 }, + { MAX9860_SYSCLK, 0x00 }, + { MAX9860_AUDIOCLKHIGH, 0x00 }, + { MAX9860_AUDIOCLKLOW, 0x00 }, + { MAX9860_IFC1A, 0x00 }, + { MAX9860_IFC1B, 0x00 }, + { MAX9860_VOICEFLTR, 0x00 }, + { MAX9860_DACATTN, 0x00 }, + { MAX9860_ADCLEVEL, 0x00 }, + { MAX9860_DACGAIN, 0x00 }, + { MAX9860_MICGAIN, 0x00 }, + { MAX9860_MICADC, 0x00 }, + { MAX9860_NOISEGATE, 0x00 }, +}; + +static bool max9860_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTRSTATUS ... MAX9860_MICGAIN: + case MAX9860_MICADC ... MAX9860_PWRMAN: + case MAX9860_REVISION: + return true; + } + + return false; +} + +static bool max9860_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTEN ... MAX9860_MICGAIN: + case MAX9860_MICADC ... MAX9860_PWRMAN: + return true; + } + + return false; +} + +static bool max9860_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTRSTATUS: + case MAX9860_MICREADBACK: + return true; + } + + return false; +} + +static bool max9860_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTRSTATUS: + return true; + } + + return false; +} + +const struct regmap_config max9860_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = max9860_readable, + .writeable_reg = max9860_writeable, + .volatile_reg = max9860_volatile, + .precious_reg = max9860_precious, + + .max_register = MAX9860_MAX_REGISTER, + .reg_defaults = max9860_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(max9860_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1); +static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_RANGE(pam_tlv, + 0, MAX9860_PAM_MAX - 1, TLV_DB_SCALE_ITEM(-2000, 2000, 1), + MAX9860_PAM_MAX, MAX9860_PAM_MAX, TLV_DB_SCALE_ITEM(3000, 0, 0)); +static const DECLARE_TLV_DB_SCALE(pgam_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(anth_tlv, -7600, 400, 1); +static const DECLARE_TLV_DB_SCALE(agcth_tlv, -1800, 100, 0); + +static const char * const agchld_text[] = { + "AGC Disabled", "50ms", "100ms", "400ms" +}; + +static SOC_ENUM_SINGLE_DECL(agchld_enum, MAX9860_MICADC, + MAX9860_AGCHLD_SHIFT, agchld_text); + +static const char * const agcsrc_text[] = { + "Left ADC", "Left/Right ADC" +}; + +static SOC_ENUM_SINGLE_DECL(agcsrc_enum, MAX9860_MICADC, + MAX9860_AGCSRC_SHIFT, agcsrc_text); + +static const char * const agcatk_text[] = { + "3ms", "12ms", "50ms", "200ms" +}; + +static SOC_ENUM_SINGLE_DECL(agcatk_enum, MAX9860_MICADC, + MAX9860_AGCATK_SHIFT, agcatk_text); + +static const char * const agcrls_text[] = { + "78ms", "156ms", "312ms", "625ms", + "1.25s", "2.5s", "5s", "10s" +}; + +static SOC_ENUM_SINGLE_DECL(agcrls_enum, MAX9860_MICADC, + MAX9860_AGCRLS_SHIFT, agcrls_text); + +static const char * const filter_text[] = { + "Disabled", + "Elliptical HP 217Hz notch (16kHz)", + "Butterworth HP 500Hz (16kHz)", + "Elliptical HP 217Hz notch (8kHz)", + "Butterworth HP 500Hz (8kHz)", + "Butterworth HP 200Hz (48kHz)" +}; + +static SOC_ENUM_SINGLE_DECL(avflt_enum, MAX9860_VOICEFLTR, + MAX9860_AVFLT_SHIFT, filter_text); + +static SOC_ENUM_SINGLE_DECL(dvflt_enum, MAX9860_VOICEFLTR, + MAX9860_DVFLT_SHIFT, filter_text); + +static const struct snd_kcontrol_new max9860_controls[] = { +SOC_SINGLE_TLV("Master Playback Volume", MAX9860_DACATTN, + MAX9860_DVA_SHIFT, MAX9860_DVA_MUTE, 1, dva_tlv), +SOC_SINGLE_TLV("DAC Gain Volume", MAX9860_DACGAIN, + MAX9860_DVG_SHIFT, MAX9860_DVG_MAX, 0, dvg_tlv), +SOC_DOUBLE_TLV("Line Capture Volume", MAX9860_ADCLEVEL, + MAX9860_ADCLL_SHIFT, MAX9860_ADCRL_SHIFT, MAX9860_ADCxL_MIN, 1, + adc_tlv), + +SOC_ENUM("AGC Hold Time", agchld_enum), +SOC_ENUM("AGC/Noise Gate Source", agcsrc_enum), +SOC_ENUM("AGC Attack Time", agcatk_enum), +SOC_ENUM("AGC Release Time", agcrls_enum), + +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MAX9860_NOISEGATE, + MAX9860_ANTH_SHIFT, MAX9860_ANTH_MAX, 0, anth_tlv), +SOC_SINGLE_TLV("AGC Signal Threshold Volume", MAX9860_NOISEGATE, + MAX9860_AGCTH_SHIFT, MAX9860_AGCTH_MIN, 1, agcth_tlv), + +SOC_SINGLE_TLV("Mic PGA Volume", MAX9860_MICGAIN, + MAX9860_PGAM_SHIFT, MAX9860_PGAM_MIN, 1, pgam_tlv), +SOC_SINGLE_TLV("Mic Preamp Volume", MAX9860_MICGAIN, + MAX9860_PAM_SHIFT, MAX9860_PAM_MAX, 0, pam_tlv), + +SOC_ENUM("ADC Filter", avflt_enum), +SOC_ENUM("DAC Filter", dvflt_enum), +}; + +static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MICL"), +SND_SOC_DAPM_INPUT("MICR"), + +SND_SOC_DAPM_ADC("ADCL", NULL, MAX9860_PWRMAN, MAX9860_ADCLEN_SHIFT, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_DAC("DAC", NULL, MAX9860_PWRMAN, MAX9860_DACEN_SHIFT, 0), + +SND_SOC_DAPM_OUTPUT("OUT"), + +SND_SOC_DAPM_SUPPLY("Supply", SND_SOC_NOPM, 0, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DVDD", 0, 0), +SND_SOC_DAPM_CLOCK_SUPPLY("mclk"), +}; + +static const struct snd_soc_dapm_route max9860_dapm_routes[] = { + { "ADCL", NULL, "MICL" }, + { "ADCR", NULL, "MICR" }, + { "AIFOUTL", NULL, "ADCL" }, + { "AIFOUTR", NULL, "ADCR" }, + + { "DAC", NULL, "AIFINL" }, + { "DAC", NULL, "AIFINR" }, + { "OUT", NULL, "DAC" }, + + { "Supply", NULL, "AVDD" }, + { "Supply", NULL, "DVDD" }, + { "Supply", NULL, "mclk" }, + + { "DAC", NULL, "Supply" }, + { "ADCL", NULL, "Supply" }, + { "ADCR", NULL, "Supply" }, +}; + +static int max9860_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec); + + switch (max9860->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBS_CFS: + return 0; + + default: + return -EINVAL; + } +} + +static int max9860_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec); + u8 master; + u8 ifc1a = 0; + u8 ifc1b = 0; + u8 sysclk = 0; + unsigned long n; + int ret; + + dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n", + params_rate(params), + params_channels(params)); + + if (params_channels(params) == 2) + ifc1b |= MAX9860_ST; + + switch (max9860->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + master = MAX9860_MASTER; + break; + default: + return -EINVAL; + } + ifc1a |= master; + + if (master) { + if (params_width(params) * params_channels(params) > 48) + ifc1b |= MAX9860_BSEL_64X; + else + ifc1b |= MAX9860_BSEL_48X; + } + + switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ifc1a |= MAX9860_DDLY; + ifc1b |= MAX9860_ADLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + ifc1a |= MAX9860_WCI; + break; + case SND_SOC_DAIFMT_DSP_A: + if (params_width(params) != 16) { + dev_err(codec->dev, + "DSP_A works for 16 bits per sample only.\n"); + return -EINVAL; + } + ifc1a |= MAX9860_DDLY | MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM; + ifc1b |= MAX9860_ADLY; + break; + case SND_SOC_DAIFMT_DSP_B: + if (params_width(params) != 16) { + dev_err(codec->dev, + "DSP_B works for 16 bits per sample only.\n"); + return -EINVAL; + } + ifc1a |= MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM; + break; + default: + return -EINVAL; + } + + switch (max9860->fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + return -EINVAL; + } + ifc1a ^= MAX9860_WCI; + break; + case SND_SOC_DAIFMT_IB_IF: + switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + return -EINVAL; + } + ifc1a ^= MAX9860_WCI; + /* fall through */ + case SND_SOC_DAIFMT_IB_NF: + ifc1a ^= MAX9860_DBCI; + ifc1b ^= MAX9860_ABCI; + break; + default: + return -EINVAL; + } + + dev_dbg(codec->dev, "IFC1A %02x\n", ifc1a); + ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a); + if (ret) { + dev_err(codec->dev, "Failed to set IFC1A: %d\n", ret); + return ret; + } + dev_dbg(codec->dev, "IFC1B %02x\n", ifc1b); + ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b); + if (ret) { + dev_err(codec->dev, "Failed to set IFC1B: %d\n", ret); + return ret; + } + + /* + * Check if Integer Clock Mode is possible, but avoid it in slave mode + * since we then do not know if lrclk is derived from pclk and the + * datasheet mentions that the frequencies have to match exactly in + * order for this to work. + */ + if (params_rate(params) == 8000 || params_rate(params) == 16000) { + if (master) { + switch (max9860->pclk_rate) { + case 12000000: + sysclk = MAX9860_FREQ_12MHZ; + break; + case 13000000: + sysclk = MAX9860_FREQ_13MHZ; + break; + case 19200000: + sysclk = MAX9860_FREQ_19_2MHZ; + break; + default: + /* + * Integer Clock Mode not possible. Leave + * sysclk at zero and fall through to the + * code below for PLL mode. + */ + break; + } + + if (sysclk && params_rate(params) == 16000) + sysclk |= MAX9860_16KHZ; + } + } + + /* + * Largest possible n: + * 65536 * 96 * 48kHz / 10MHz -> 30199 + * Smallest possible n: + * 65536 * 96 * 8kHz / 20MHz -> 2517 + * Both fit nicely in the available 15 bits, no need to apply any mask. + */ + n = DIV_ROUND_CLOSEST_ULL(65536ULL * 96 * params_rate(params), + max9860->pclk_rate); + + if (!sysclk) { + /* PLL mode */ + if (params_rate(params) > 24000) + sysclk |= MAX9860_16KHZ; + + if (!master) + n |= 1; /* trigger rapid pll lock mode */ + } + + sysclk |= max9860->psclk; + dev_dbg(codec->dev, "SYSCLK %02x\n", sysclk); + ret = regmap_write(max9860->regmap, + MAX9860_SYSCLK, sysclk); + if (ret) { + dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret); + return ret; + } + dev_dbg(codec->dev, "N %lu\n", n); + ret = regmap_write(max9860->regmap, + MAX9860_AUDIOCLKHIGH, n >> 8); + if (ret) { + dev_err(codec->dev, "Failed to set NHI: %d\n", ret); + return ret; + } + ret = regmap_write(max9860->regmap, + MAX9860_AUDIOCLKLOW, n & 0xff); + if (ret) { + dev_err(codec->dev, "Failed to set NLO: %d\n", ret); + return ret; + } + + if (!master) { + dev_dbg(codec->dev, "Enable PLL\n"); + ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH, + MAX9860_PLL, MAX9860_PLL); + if (ret) { + dev_err(codec->dev, "Failed to enable PLL: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec); + + max9860->fmt = fmt; + return 0; +} + +static const struct snd_soc_dai_ops max9860_dai_ops = { + .startup = max9860_dai_startup, + .hw_params = max9860_hw_params, + .set_fmt = max9860_set_fmt, +}; + +static struct snd_soc_dai_driver max9860_dai = { + .name = "max9860-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &max9860_dai_ops, + .symmetric_rates = 1, +}; + +static int max9860_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct max9860_priv *max9860 = dev_get_drvdata(codec->dev); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN, + MAX9860_SHDN, MAX9860_SHDN); + if (ret) { + dev_err(codec->dev, "Failed to remove SHDN: %d\n", ret); + return ret; + } + break; + + case SND_SOC_BIAS_OFF: + ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN, + MAX9860_SHDN, 0); + if (ret) { + dev_err(codec->dev, "Failed to request SHDN: %d\n", + ret); + return ret; + } + break; + } + + return 0; +} + +static struct snd_soc_codec_driver max9860_codec_driver = { + .set_bias_level = max9860_set_bias_level, + .idle_bias_off = true, + + .controls = max9860_controls, + .num_controls = ARRAY_SIZE(max9860_controls), + .dapm_widgets = max9860_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets), + .dapm_routes = max9860_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes), +}; + +#ifdef CONFIG_PM +static int max9860_suspend(struct device *dev) +{ + struct max9860_priv *max9860 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK, + MAX9860_PSCLK, MAX9860_PSCLK_OFF); + if (ret) { + dev_err(dev, "Failed to disable clock: %d\n", ret); + return ret; + } + + regulator_disable(max9860->dvddio); + + return 0; +} + +static int max9860_resume(struct device *dev) +{ + struct max9860_priv *max9860 = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(max9860->dvddio); + if (ret) { + dev_err(dev, "Failed to enable DVDDIO: %d\n", ret); + return ret; + } + + regcache_cache_only(max9860->regmap, false); + ret = regcache_sync(max9860->regmap); + if (ret) { + dev_err(dev, "Failed to sync cache: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK, + MAX9860_PSCLK, max9860->psclk); + if (ret) { + dev_err(dev, "Failed to enable clock: %d\n", ret); + return ret; + } + + return 0; +} +#endif + +const struct dev_pm_ops max9860_pm_ops = { + SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL) +}; + +static int max9860_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct max9860_priv *max9860; + int ret; + struct clk *mclk; + unsigned long mclk_rate; + int i; + int intr; + + max9860 = devm_kzalloc(dev, sizeof(struct max9860_priv), GFP_KERNEL); + if (!max9860) + return -ENOMEM; + + max9860->dvddio = devm_regulator_get(dev, "DVDDIO"); + if (IS_ERR(max9860->dvddio)) { + ret = PTR_ERR(max9860->dvddio); + dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret); + return ret; + } + + max9860->dvddio_nb.notifier_call = max9860_dvddio_event; + + ret = regulator_register_notifier(max9860->dvddio, &max9860->dvddio_nb); + if (ret) + dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret); + + ret = regulator_enable(max9860->dvddio); + if (ret != 0) { + dev_err(dev, "Failed to enable DVDDIO: %d\n", ret); + return ret; + } + + max9860->regmap = devm_regmap_init_i2c(i2c, &max9860_regmap); + if (IS_ERR(max9860->regmap)) { + ret = PTR_ERR(max9860->regmap); + goto err_regulator; + } + + dev_set_drvdata(dev, max9860); + + /* + * mclk has to be in the 10MHz to 60MHz range. + * psclk is used to scale mclk into pclk so that + * pclk is in the 10MHz to 20MHz range. + */ + mclk = clk_get(dev, "mclk"); + + if (IS_ERR(mclk)) { + ret = PTR_ERR(mclk); + dev_err(dev, "Failed to get MCLK: %d\n", ret); + goto err_regulator; + } + + mclk_rate = clk_get_rate(mclk); + clk_put(mclk); + + if (mclk_rate > 60000000 || mclk_rate < 10000000) { + dev_err(dev, "Bad mclk %luHz (needs 10MHz - 60MHz)\n", + mclk_rate); + ret = -EINVAL; + goto err_regulator; + } + if (mclk_rate >= 40000000) + max9860->psclk = 3; + else if (mclk_rate >= 20000000) + max9860->psclk = 2; + else + max9860->psclk = 1; + max9860->pclk_rate = mclk_rate >> (max9860->psclk - 1); + max9860->psclk <<= MAX9860_PSCLK_SHIFT; + dev_dbg(dev, "mclk %lu pclk %lu\n", mclk_rate, max9860->pclk_rate); + + regcache_cache_bypass(max9860->regmap, true); + for (i = 0; i < max9860_regmap.num_reg_defaults; ++i) { + ret = regmap_write(max9860->regmap, + max9860_regmap.reg_defaults[i].reg, + max9860_regmap.reg_defaults[i].def); + if (ret) { + dev_err(dev, "Failed to initialize register %u: %d\n", + max9860_regmap.reg_defaults[i].reg, ret); + goto err_regulator; + } + } + regcache_cache_bypass(max9860->regmap, false); + + ret = regmap_read(max9860->regmap, MAX9860_INTRSTATUS, &intr); + if (ret) { + dev_err(dev, "Failed to clear INTRSTATUS: %d\n", ret); + goto err_regulator; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = snd_soc_register_codec(dev, &max9860_codec_driver, + &max9860_dai, 1); + if (ret) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_pm; + } + + return 0; + +err_pm: + pm_runtime_disable(dev); +err_regulator: + regulator_disable(max9860->dvddio); + return ret; +} + +static int max9860_remove(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct max9860_priv *max9860 = dev_get_drvdata(dev); + + snd_soc_unregister_codec(dev); + pm_runtime_disable(dev); + regulator_disable(max9860->dvddio); + return 0; +} + +static const struct i2c_device_id max9860_i2c_id[] = { + { "max9860", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9860_i2c_id); + +static const struct of_device_id max9860_of_match[] = { + { .compatible = "maxim,max9860", }, + { } +}; +MODULE_DEVICE_TABLE(of, max9860_of_match); + +static struct i2c_driver max9860_i2c_driver = { + .probe = max9860_probe, + .remove = max9860_remove, + .id_table = max9860_i2c_id, + .driver = { + .name = "max9860", + .of_match_table = max9860_of_match, + .pm = &max9860_pm_ops, + }, +}; + +module_i2c_driver(max9860_i2c_driver); + +MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h new file mode 100644 index 000000000000..22041bd67a7d --- /dev/null +++ b/sound/soc/codecs/max9860.h @@ -0,0 +1,162 @@ +/* + * Driver for the MAX9860 Mono Audio Voice Codec + * + * Author: Peter Rosin + * Copyright 2016 Axentia Technologies + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _SND_SOC_MAX9860 +#define _SND_SOC_MAX9860 + +#define MAX9860_INTRSTATUS 0x00 +#define MAX9860_MICREADBACK 0x01 +#define MAX9860_INTEN 0x02 +#define MAX9860_SYSCLK 0x03 +#define MAX9860_AUDIOCLKHIGH 0x04 +#define MAX9860_AUDIOCLKLOW 0x05 +#define MAX9860_IFC1A 0x06 +#define MAX9860_IFC1B 0x07 +#define MAX9860_VOICEFLTR 0x08 +#define MAX9860_DACATTN 0x09 +#define MAX9860_ADCLEVEL 0x0a +#define MAX9860_DACGAIN 0x0b +#define MAX9860_MICGAIN 0x0c +#define MAX9860_RESERVED 0x0d +#define MAX9860_MICADC 0x0e +#define MAX9860_NOISEGATE 0x0f +#define MAX9860_PWRMAN 0x10 +#define MAX9860_REVISION 0xff + +#define MAX9860_MAX_REGISTER 0xff + +/* INTRSTATUS */ +#define MAX9860_CLD 0x80 +#define MAX9860_SLD 0x40 +#define MAX9860_ULK 0x20 + +/* MICREADBACK */ +#define MAX9860_NG 0xe0 +#define MAX9860_AGC 0x1f + +/* INTEN */ +#define MAX9860_ICLD 0x80 +#define MAX9860_ISLD 0x40 +#define MAX9860_IULK 0x20 + +/* SYSCLK */ +#define MAX9860_PSCLK 0x30 +#define MAX9860_PSCLK_OFF 0x00 +#define MAX9860_PSCLK_SHIFT 4 +#define MAX9860_FREQ 0x06 +#define MAX9860_FREQ_NORMAL 0x00 +#define MAX9860_FREQ_12MHZ 0x02 +#define MAX9860_FREQ_13MHZ 0x04 +#define MAX9860_FREQ_19_2MHZ 0x06 +#define MAX9860_16KHZ 0x01 + +/* AUDIOCLKHIGH */ +#define MAX9860_PLL 0x80 +#define MAX9860_NHI 0x7f + +/* AUDIOCLKLOW */ +#define MAX9860_NLO 0xff + +/* IFC1A */ +#define MAX9860_MASTER 0x80 +#define MAX9860_WCI 0x40 +#define MAX9860_DBCI 0x20 +#define MAX9860_DDLY 0x10 +#define MAX9860_HIZ 0x08 +#define MAX9860_TDM 0x04 + +/* IFC1B */ +#define MAX9860_ABCI 0x20 +#define MAX9860_ADLY 0x10 +#define MAX9860_ST 0x08 +#define MAX9860_BSEL 0x07 +#define MAX9860_BSEL_OFF 0x00 +#define MAX9860_BSEL_64X 0x01 +#define MAX9860_BSEL_48X 0x02 +#define MAX9860_BSEL_PCLK_2 0x04 +#define MAX9860_BSEL_PCLK_4 0x05 +#define MAX9860_BSEL_PCLK_8 0x06 +#define MAX9860_BSEL_PCLK_16 0x07 + +/* VOICEFLTR */ +#define MAX9860_AVFLT 0xf0 +#define MAX9860_AVFLT_SHIFT 4 +#define MAX9860_AVFLT_COUNT 6 +#define MAX9860_DVFLT 0x0f +#define MAX9860_DVFLT_SHIFT 0 +#define MAX9860_DVFLT_COUNT 6 + +/* DACATTN */ +#define MAX9860_DVA 0xfe +#define MAX9860_DVA_SHIFT 1 +#define MAX9860_DVA_MUTE 0x5e + +/* ADCLEVEL */ +#define MAX9860_ADCRL 0xf0 +#define MAX9860_ADCRL_SHIFT 4 +#define MAX9860_ADCLL 0x0f +#define MAX9860_ADCLL_SHIFT 0 +#define MAX9860_ADCxL_MIN 15 + +/* DACGAIN */ +#define MAX9860_DVG 0x60 +#define MAX9860_DVG_SHIFT 5 +#define MAX9860_DVG_MAX 3 +#define MAX9860_DVST 0x1f +#define MAX9860_DVST_SHIFT 0 +#define MAX9860_DVST_MIN 31 + +/* MICGAIN */ +#define MAX9860_PAM 0x60 +#define MAX9860_PAM_SHIFT 5 +#define MAX9860_PAM_MAX 3 +#define MAX9860_PGAM 0x1f +#define MAX9860_PGAM_SHIFT 0 +#define MAX9860_PGAM_MIN 20 + +/* MICADC */ +#define MAX9860_AGCSRC 0x80 +#define MAX9860_AGCSRC_SHIFT 7 +#define MAX9860_AGCSRC_COUNT 2 +#define MAX9860_AGCRLS 0x70 +#define MAX9860_AGCRLS_SHIFT 4 +#define MAX9860_AGCRLS_COUNT 8 +#define MAX9860_AGCATK 0x0c +#define MAX9860_AGCATK_SHIFT 2 +#define MAX9860_AGCATK_COUNT 4 +#define MAX9860_AGCHLD 0x03 +#define MAX9860_AGCHLD_OFF 0x00 +#define MAX9860_AGCHLD_SHIFT 0 +#define MAX9860_AGCHLD_COUNT 4 + +/* NOISEGATE */ +#define MAX9860_ANTH 0xf0 +#define MAX9860_ANTH_SHIFT 4 +#define MAX9860_ANTH_MAX 15 +#define MAX9860_AGCTH 0x0f +#define MAX9860_AGCTH_SHIFT 0 +#define MAX9860_AGCTH_MIN 15 + +/* PWRMAN */ +#define MAX9860_SHDN 0x80 +#define MAX9860_DACEN 0x08 +#define MAX9860_DACEN_SHIFT 3 +#define MAX9860_ADCLEN 0x02 +#define MAX9860_ADCLEN_SHIFT 1 +#define MAX9860_ADCREN 0x01 +#define MAX9860_ADCREN_SHIFT 0 + +#endif /* _SND_SOC_MAX9860 */