From patchwork Tue Sep 15 08:26:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuninori Morimoto X-Patchwork-Id: 7180851 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 9FF44BEEC1 for ; Tue, 15 Sep 2015 08:27:06 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 242C52073F for ; Tue, 15 Sep 2015 08:27:05 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 135A32072F for ; Tue, 15 Sep 2015 08:27:03 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 1053A2608BB; Tue, 15 Sep 2015 10:27:00 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Status: No, score=-2.6 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, NO_DNS_FOR_FROM, RCVD_IN_DNSWL_LOW, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id A6FA22606BC; Tue, 15 Sep 2015 10:26:53 +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 87E4A2606D6; Tue, 15 Sep 2015 10:26:51 +0200 (CEST) Received: from relmlie3.idc.renesas.com (relmlor4.renesas.com [210.160.252.174]) by alsa0.perex.cz (Postfix) with ESMTP id B1678260502 for ; Tue, 15 Sep 2015 10:26:42 +0200 (CEST) Received: from unknown (HELO relmlir2.idc.renesas.com) ([10.200.68.152]) by relmlie3.idc.renesas.com with ESMTP; 15 Sep 2015 17:26:39 +0900 Received: from relmlac1.idc.renesas.com (relmlac1.idc.renesas.com [10.200.69.21]) by relmlir2.idc.renesas.com (Postfix) with ESMTP id E180E42694; Tue, 15 Sep 2015 17:26:39 +0900 (JST) Received: by relmlac1.idc.renesas.com (Postfix, from userid 0) id CC1B18002D; Tue, 15 Sep 2015 17:26:39 +0900 (JST) Received: from relmlac1.idc.renesas.com (localhost [127.0.0.1]) by relmlac1.idc.renesas.com (Postfix) with ESMTP id CA83F8002F; Tue, 15 Sep 2015 17:26:39 +0900 (JST) Received: from relmlii2.idc.renesas.com [10.200.68.66] by relmlac1.idc.renesas.com with ESMTP id TAB11671; Tue, 15 Sep 2015 17:26:39 +0900 X-IronPort-AV: E=Sophos;i="5.17,534,1437404400"; d="scan'";a="195665143" Received: from mail-sg2apc01lp0245.outbound.protection.outlook.com (HELO APC01-SG2-obe.outbound.protection.outlook.com) ([65.55.88.245]) by relmlii2.idc.renesas.com with ESMTP/TLS/AES256-SHA; 15 Sep 2015 17:26:38 +0900 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=kuninori.morimoto.gx@renesas.com; Received: from morimoto-PC.renesas.com (211.11.155.144) by SIXPR06MB0607.apcprd06.prod.outlook.com (10.160.237.149) with Microsoft SMTP Server (TLS) id 15.1.268.17; Tue, 15 Sep 2015 08:26:36 +0000 Message-ID: <8737yg3xdr.wl%kuninori.morimoto.gx@renesas.com> From: Kuninori Morimoto User-Agent: Wanderlust/2.15.9 Emacs/24.3 Mule/6.0 To: Mark Brown MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Date: Tue, 15 Sep 2015 08:26:36 +0000 X-Originating-IP: [211.11.155.144] X-ClientProxiedBy: TY1PR06CA0018.apcprd06.prod.outlook.com (25.164.91.28) To SIXPR06MB0607.apcprd06.prod.outlook.com (25.160.237.149) X-Microsoft-Exchange-Diagnostics: 1; SIXPR06MB0607; 2:ykB198gQuaHcVESlLoP618dTcWXhYmW+GyC8wWBxXto3Q7iJXMO6BseQmDXMAlDVlZtlzP6pPDnhmcy0rRpv1ZZOUckeUpphAzlXLFFIqrugHDFg/cYD4idvYZzaUGmzlNnWOW+9HjdQdQQ5APhiqya6yF+LaU14AYDcqUIeGJU=; 3:LIxK5w0K5TzrUrS3TZsbQMyDxt18ztJ0sDyIFxs5y4cYY6ndTT+7Andmy8yPTiaEsaFp7f5vRCEeJwnCKN5cmEGcahRQQzMkMwcjxaTITl9EMj6KLk65SgmabNTE2j7AoKju/XCVlrs/w9OBiXDgVg==; 25:PhmdjblEK0F5dDOuQr+NyLrcX7jBMKM+nWzpQ8X9PnJTFyTo9NYv32rcP249n7/RKsHeNAFlVPqXoR3m37vp7bJ1HYtW6R93DVGot04PZV2FFsQ2mlbhS1y+gQP/h/pufsOP6IJ+ticcyXspdw185U6ZuDYR+ukPJHHHZQe81qUsih1vwDRvFCZcSM1Zl+uf6lkBHX+yAhksdhUlMMnkM90XecOvFXN7ALjrpYitRhMasZVvxh0RS98lot7J2icaRF+3O3YxMrUQc2EWBST+aw== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:SIXPR06MB0607; X-Microsoft-Exchange-Diagnostics: 1; SIXPR06MB0607; 20:4CgOLeh3g2xOjXIWqxxFbeIGmIhNKZAqGZyAHwv9kdB++jLGbATGI9SaGcUd89hv3hKtjwchbWKMMH16ngzA+6i0pQUkwP93x6LmWMhoLBuN4o5K48/qLzlh3tEpkM3KvZm/PpzQzDAyYOxVd1vr6BWlBaASw9NCyHzPhPGr28htfkyIUSkOjar8RupGpDG7u5r+b+XuOPh8nMd6Ga+BmFt2AL490iGl6HZDix/Ag8mCdYRp+rbXyNQr+fxIJCvfI2iZ5y+fPe2dv6TpaPQ4YXxEnhW94Ne22F4f4fZ4GoZ1GrP3EoDI5Q+vg4iJumNyl8nt8NBCwMuPQZHnl1sgYxNqM0Jb4/TU5HtncoOTInAkdLpkLHtxJPS0/5uvwiZJ4MVntubkmeaKYebDyStijOINQzKv+wvdWsusYeWTp2Z1jDJvOXXWHanLxtN7seSBHLCxX/9rQf9FM1nj7+JUjBUhMI2EGJQiZbi3anU+DTMgPEafx1ahlkXUoCpUPjpd; 4:HZ6WLok84lCjymbFnxo7LvZC5UKQ2RdY8w79e7YUFcX3Z7gHWzQh3M0lyFN5W+GVWriSHMYdYfIjlRVZqNgfnu30f+jjRwwLXpHaEp3isUjYphwHIHJa9/YX4JNhlfgG5TBQiP7sa2NUiQo69Qpmi6aRjtSih4K5FBLg+gO/Va0n2IVnEofepJBWzZHO4BtMrWFHtNQWrlotWVpNMrGeOJo7+cOfDUiInBQ8QGDOO1Vo84VxpyHYkIB2delxwrBsVqZGU6/20PEr9JucpGYSJq7ChZWMd+2eiONs40CheJV+xKSoQf06V65Add8Jl5e+tvhpsr8rQIuA1HNmc6xrWMVx/rQpswsw199cyvnM1eU= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(5005006)(520075)(8121501046)(520078)(3002001); SRVR:SIXPR06MB0607; BCL:0; PCL:0; RULEID:; SRVR:SIXPR06MB0607; X-Forefront-PRVS: 070092A9D3 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(6009001)(199003)(189002)(40100003)(19580405001)(189998001)(77156002)(101416001)(83506001)(87976001)(92566002)(19580395003)(4001540100001)(53416004)(81156007)(69596002)(33646002)(66066001)(64706001)(68736005)(86362001)(77096005)(42186005)(4001350100001)(62966003)(47776003)(575784001)(46406003)(5001960100002)(5007970100001)(5004730100002)(5001860100001)(23726002)(110136002)(5001920100001)(36756003)(50466002)(105586002)(46102003)(229853001)(122386002)(50986999)(54356999)(5001830100001)(106356001)(97736004)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:SIXPR06MB0607; H:morimoto-PC.renesas.com; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; SIXPR06MB0607; 23:8oRdK7nf+5faQ9SPQDAHrILEt7HgsZCOZ2JPoKRxP?= =?us-ascii?Q?OJW8MWLHzGbHLj9jem7iQzqZaNL8t6XlwGROdaQw5faySDUfcFfWHB7U1mDb?= =?us-ascii?Q?o2PHGTh16xbElS1PGFqcYEzDiJjdZbINcnnWcvj+rJCWEFV5Sv3Arn/0NPDL?= =?us-ascii?Q?GulvDAnCq3Oh8mcY/Sk5QWGOICk1DdNuKpTydg8anR+6GaZ0TG6DyeDxB547?= =?us-ascii?Q?+Y1/J7JABn+FxMI8AOJj9Mi1Db6HUllxeegspOg5KSdLqF72d2eMaKzhxBo0?= =?us-ascii?Q?oTfVqUtWA3kULj63k6kzQWs3tBkiwMabVyDqwTv5AzTA+eEmQ4nMQnB+9H9+?= =?us-ascii?Q?GqB2uwSNxbc3DB2c1ozyqfVfEOaGtsrxX4+H43TOrCeLOMO6rRvHyAVdOW69?= =?us-ascii?Q?yzRwHW5T2Bb8KfBqDAFotMjeHm3idVM158dBjQoWUsbMXCsTCO7YN8AKUMeK?= =?us-ascii?Q?Md+MP76fiIQQz11Cje4ggHPdXUH74QVYmWvslvT9xjdftsTwz6tNjBDDWmpp?= =?us-ascii?Q?0rCx9KuiHcSyLfBNCWKs/RCNHD3zb6KMW3uqnp01Z8bEjApVdp44irVxnP6a?= =?us-ascii?Q?78G0g7+esP0Xfu34H9GnOp+607DGKnt+3nsqsdSIkOhCTrHwSuL0RH3XvY33?= =?us-ascii?Q?MdwrEGbaEetQDHilTKTEmD/wpKPrh/yzyGLjxBtSEO9sswx3M3q4bkyYLufC?= =?us-ascii?Q?4/SXjNYk7mHS3k2BF9j19idPm3SgJWLv8U/vUo+m5PMDRDB1VR3ffo+DFFI3?= =?us-ascii?Q?ZicYtAze2Jtm2Ha2Uyss61sPsoJVEATB2bg5ZaLCrBcVsjRMZ936csv0uRWO?= =?us-ascii?Q?Mi0F+9LWxh/5KSNfjBSO8D5YCe8YV7xRfxqGR0lDF6bt2L42I7dvEozB12dt?= =?us-ascii?Q?71QfqiXqhEO8Xpdef4tHJA92fh/biY4L+BiFO2OGDkvZt6X5d7i0b2bzB56i?= =?us-ascii?Q?C/JWPPf9VFOW09scsz1MrChaUEyhM/28Gx4FW0CGjsDEawZHEbf6NER5+z0f?= =?us-ascii?Q?hSqTwXI3Z8amo/ErKeFBW382vQ01ZvKoPy3g4uodyN6dmMJ3sBKox1ZylaEr?= =?us-ascii?Q?iTgp52hqG7CWudnIwRTV3Jdb/+EfHTLxISZUKSt4YclbpD+BjrWx6wT+Q/UU?= =?us-ascii?Q?e6oDn1DV3PZLjQ2UtFc42LrQA6u7gUB8/JJKF0Vd4S0xi5QnS7sl9ns/d0j4?= =?us-ascii?Q?0Ql5JHQw2bAIBJDPyORIsrR+HKtkbeUL/v4NMHgx6qc2qNbU+CJzdrto0cZR?= =?us-ascii?Q?PTsaxqRcYNXTXMCaL75zCIw/K5eq8fv31GJintX?= X-Microsoft-Exchange-Diagnostics: 1; SIXPR06MB0607; 5:BsBijFhF8lon4gL4m8Q8Nde1U0qCZXFqx2NSpTYZWffLHNkLN4ptxruOhr4wtdtz2dWnkXVDRe1qfJMjvWwL0LxpgYOo+eca0r2MvqjChE9WFTNXJroDYg6U/1RfZ2WoelQDjrdFkf+Bsnyku25fUg==; 24:wnGXAlS2FA66KlUk0BOOIOwewFhVrmyRv6ImQYI60FM42Xxi2XWZZ3d4yRb/THyunAdmSdYm/g3UCNmfN4muu7MS2aN4fCXdgexKRBYgk+0=; 20:4az2pDlx2RVCsSzy47o7omZfiB5R/U+bRVVpqaPrxzVserKlOOXW5SQ2/TJgv6WZ6zOQ67QSnk82iX5rZwwMJJ8p7f45eT77D0qy2Fx/h4sUCIc1rHZFSrOsE2Y3AnTRBNHaqFCd5YN9ChVHya9Ql6lCgy4PimpVGjE+xqivGWY= SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: renesas.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Sep 2015 08:26:36.5334 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: SIXPR06MB0607 Cc: Linux-ALSA , Simon , Liam Girdwood Subject: [alsa-devel] [PATCH v2] ASoC: add ak4613 support 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 From: Kuninori Morimoto Signed-off-by: Kuninori Morimoto --- v1 -> v2 - fix for checkpatch - adds ak4613 in SND_SOC_ALL_CODECS - ak4613_write() -> snd_soc_write() - ak4613_read() was removed - spin lock -> mutex - ak4613_resume() returns regcache_sync()'s result Documentation/devicetree/bindings/sound/ak4613.txt | 17 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4613.c | 469 +++++++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ak4613.txt create mode 100644 sound/soc/codecs/ak4613.c diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt new file mode 100644 index 0000000..15a9195 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4613.txt @@ -0,0 +1,17 @@ +AK4613 I2C transmitter + +This device supports I2C mode only. + +Required properties: + +- compatible : "asahi-kasei,ak4613" +- reg : The chip select number on the I2C bus + +Example: + +&i2c { + ak4613: ak4613@0x10 { + compatible = "asahi-kasei,ak4613"; + reg = <0x10>; + }; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c9733e..a92e4d4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C select SND_SOC_AK4554 + select SND_SOC_AK4613 if I2C select SND_SOC_AK4641 if I2C select SND_SOC_AK4642 if I2C select SND_SOC_AK4671 if I2C @@ -319,6 +320,10 @@ config SND_SOC_AK4535 config SND_SOC_AK4554 tristate "AKM AK4554 CODEC" +config SND_SOC_AK4613 + tristate "AKM AK4613 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 4a32077..5b6c8af 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o +snd-soc-ak4613-objs := ak4613.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o @@ -216,6 +217,7 @@ obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o +obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c new file mode 100644 index 0000000..fd96a8f --- /dev/null +++ b/sound/soc/codecs/ak4613.c @@ -0,0 +1,469 @@ +/* + * ak4613.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * Kuninori Morimoto + * + * Based on ak4642.c by Kuninori Morimoto + * Based on wm8731.c by Richard Purdie + * Based on ak4535.c by Richard Purdie + * Based on wm8753.c by Liam Girdwood + * + * 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 + +#define PW_MGMT1 0x00 /* Power Management 1 */ +#define PW_MGMT2 0x01 /* Power Management 2 */ +#define PW_MGMT3 0x02 /* Power Management 3 */ +#define CTRL1 0x03 /* Control 1 */ +#define CTRL2 0x04 /* Control 2 */ +#define DEMP1 0x05 /* De-emphasis1 */ +#define DEMP2 0x06 /* De-emphasis2 */ +#define OFD 0x07 /* Overflow Detect */ +#define ZRD 0x08 /* Zero Detect */ +#define ICTRL 0x09 /* Input Control */ +#define OCTRL 0x0a /* Output Control */ +#define LOUT1 0x0b /* LOUT1 Volume Control */ +#define ROUT1 0x0c /* ROUT1 Volume Control */ +#define LOUT2 0x0d /* LOUT2 Volume Control */ +#define ROUT2 0x0e /* ROUT2 Volume Control */ +#define LOUT3 0x0f /* LOUT3 Volume Control */ +#define ROUT3 0x10 /* ROUT3 Volume Control */ +#define LOUT4 0x11 /* LOUT4 Volume Control */ +#define ROUT4 0x12 /* ROUT4 Volume Control */ +#define LOUT5 0x13 /* LOUT5 Volume Control */ +#define ROUT5 0x14 /* ROUT5 Volume Control */ +#define LOUT6 0x15 /* LOUT6 Volume Control */ +#define ROUT6 0x16 /* ROUT6 Volume Control */ + +/* PW_MGMT1 */ +#define RSTN BIT(0) +#define PMDAC BIT(1) +#define PMADC BIT(2) +#define PMVR BIT(3) + +/* PW_MGMT2 */ +#define PMAD_ALL 0x7 + +/* PW_MGMT3 */ +#define PMDA_ALL 0x3f + +/* CTRL1 */ +#define DIF0 BIT(3) +#define DIF1 BIT(4) +#define DIF2 BIT(5) +#define TDM0 BIT(6) +#define TDM1 BIT(7) +#define NO_FMT (0xff) +#define FMT_MASK (0xf8) + +/* CTRL2 */ +#define DFS_NORMAL_SPEED (0 << 2) +#define DFS_DOUBLE_SPEED (1 << 2) +#define DFS_QUAD_SPEED (2 << 2) + +struct ak4613_priv { + struct mutex lock; + + unsigned int fmt; + u8 fmt_ctrl; + int cnt; +}; + +struct ak4613_formats { + unsigned int width; + unsigned int fmt; +}; + +struct ak4613_interface { + struct ak4613_formats capture; + struct ak4613_formats playback; +}; + +static const struct reg_default ak4613_reg[] = { + { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, + { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, + { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 }, + { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 }, + { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, +}; + +#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3) +#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } +static const struct ak4613_interface ak4613_iface[] = { + /* capture */ /* playback */ + [0] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(16, RIGHT_J) }, + [1] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(20, RIGHT_J) }, + [2] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, RIGHT_J) }, + [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) }, + [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) }, +}; + +static const struct regmap_config ak4613_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x16, + .reg_defaults = ak4613_reg, + .num_reg_defaults = ARRAY_SIZE(ak4613_reg), +}; + +static const struct of_device_id ak4613_of_match[] = { + { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4613_of_match); + +static const struct i2c_device_id ak4613_i2c_id[] = { + { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id); + +static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = { + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("LOUT4"), + SND_SOC_DAPM_OUTPUT("LOUT5"), + SND_SOC_DAPM_OUTPUT("LOUT6"), + + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + SND_SOC_DAPM_OUTPUT("ROUT4"), + SND_SOC_DAPM_OUTPUT("ROUT5"), + SND_SOC_DAPM_OUTPUT("ROUT6"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0), + SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0), + SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0), + SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0), + SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0), +}; + +static const struct snd_soc_dapm_route ak4613_intercon[] = { + {"LOUT1", NULL, "DAC1"}, + {"LOUT2", NULL, "DAC2"}, + {"LOUT3", NULL, "DAC3"}, + {"LOUT4", NULL, "DAC4"}, + {"LOUT5", NULL, "DAC5"}, + {"LOUT6", NULL, "DAC6"}, + + {"ROUT1", NULL, "DAC1"}, + {"ROUT2", NULL, "DAC2"}, + {"ROUT3", NULL, "DAC3"}, + {"ROUT4", NULL, "DAC4"}, + {"ROUT5", NULL, "DAC5"}, + {"ROUT6", NULL, "DAC6"}, + + {"DAC1", NULL, "Playback"}, + {"DAC2", NULL, "Playback"}, + {"DAC3", NULL, "Playback"}, + {"DAC4", NULL, "Playback"}, + {"DAC5", NULL, "Playback"}, + {"DAC6", NULL, "Playback"}, + + {"Capture", NULL, "ADC1"}, + {"Capture", NULL, "ADC2"}, + + {"ADC1", NULL, "LIN1"}, + {"ADC2", NULL, "LIN2"}, + + {"ADC1", NULL, "RIN1"}, + {"ADC2", NULL, "RIN2"}, +}; + +static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + struct device *dev = codec->dev; + + mutex_lock(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); + priv->cnt = 0; + } + if (!priv->cnt) + priv->fmt_ctrl = NO_FMT; + mutex_unlock(&priv->lock); +} + +static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + fmt &= SND_SOC_DAIFMT_FORMAT_MASK; + + switch (fmt) { + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + priv->fmt = fmt; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ak4613_dai_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 ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + const struct ak4613_formats *fmts; + struct device *dev = codec->dev; + unsigned int width = params_width(params); + unsigned int fmt = priv->fmt; + unsigned int rate; + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int i, ret; + u8 fmt_ctrl, ctrl2; + + rate = params_rate(params); + switch (rate) { + case 32000: + case 44100: + case 48000: + ctrl2 = DFS_NORMAL_SPEED; + break; + case 88200: + case 96000: + ctrl2 = DFS_DOUBLE_SPEED; + break; + case 176400: + case 192000: + ctrl2 = DFS_QUAD_SPEED; + break; + default: + return -EINVAL; + } + + /* + * FIXME + * + * It doesn't support TDM at this point + */ + fmt_ctrl = NO_FMT; + for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) { + fmts = (is_play) ? &ak4613_iface[i].playback : + &ak4613_iface[i].capture; + + if (fmts->fmt != fmt) + continue; + + if (fmt == SND_SOC_DAIFMT_RIGHT_J) { + if (fmts->width != width) + continue; + } else { + if (fmts->width < width) + continue; + } + + fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i); + break; + } + + ret = -EINVAL; + if (fmt_ctrl == NO_FMT) + goto hw_params_end; + + mutex_lock(&priv->lock); + if ((priv->fmt_ctrl == NO_FMT) || + (priv->fmt_ctrl == fmt_ctrl)) { + priv->fmt_ctrl = fmt_ctrl; + priv->cnt++; + ret = 0; + } + mutex_unlock(&priv->lock); + + if (ret < 0) + goto hw_params_end; + + snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); + snd_soc_write(codec, CTRL2, ctrl2); + +hw_params_end: + if (ret < 0) + dev_warn(dev, "unsupported data width/format combination\n"); + + return ret; +} + +static int ak4613_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u8 mgmt1 = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + mgmt1 |= RSTN; + /* fall through */ + case SND_SOC_BIAS_PREPARE: + mgmt1 |= PMADC | PMDAC; + /* fall through */ + case SND_SOC_BIAS_STANDBY: + mgmt1 |= PMVR; + /* fall through */ + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_write(codec, PW_MGMT1, mgmt1); + + return 0; +} + +static const struct snd_soc_dai_ops ak4613_dai_ops = { + .shutdown = ak4613_dai_shutdown, + .set_fmt = ak4613_dai_set_fmt, + .hw_params = ak4613_dai_hw_params, +}; + +#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_64000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4613_dai = { + .name = "ak4613-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .ops = &ak4613_dai_ops, + .symmetric_rates = 1, +}; + +static int ak4613_resume(struct snd_soc_codec *codec) +{ + struct regmap *regmap = dev_get_regmap(codec->dev, NULL); + + regcache_mark_dirty(regmap); + return regcache_sync(regmap); +} + +static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { + .resume = ak4613_resume, + .set_bias_level = ak4613_set_bias_level, + .dapm_widgets = ak4613_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), + .dapm_routes = ak4613_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), +}; + +static int ak4613_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; + const struct regmap_config *regmap_cfg; + struct regmap *regmap; + struct ak4613_priv *priv; + + regmap_cfg = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4613_of_match, dev); + if (of_id) + regmap_cfg = of_id->data; + } else { + regmap_cfg = (const struct regmap_config *)id->driver_data; + } + + if (!regmap_cfg) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->fmt_ctrl = NO_FMT; + priv->cnt = 0; + + mutex_init(&priv->lock); + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return snd_soc_register_codec(dev, &soc_codec_dev_ak4613, + &ak4613_dai, 1); +} + +static int ak4613_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver ak4613_i2c_driver = { + .driver = { + .name = "ak4613-codec", + .owner = THIS_MODULE, + .of_match_table = ak4613_of_match, + }, + .probe = ak4613_i2c_probe, + .remove = ak4613_i2c_remove, + .id_table = ak4613_i2c_id, +}; + +module_i2c_driver(ak4613_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4613 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_LICENSE("GPL v2");