From patchwork Fri May 27 07:01:09 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuninori Morimoto X-Patchwork-Id: 9137759 X-Patchwork-Delegate: geert@linux-m68k.org 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 3922C6075A for ; Fri, 27 May 2016 07:01:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2AAE227D10 for ; Fri, 27 May 2016 07:01:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1F5EE28093; Fri, 27 May 2016 07:01:19 +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=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 48C4A27D10 for ; Fri, 27 May 2016 07:01:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754626AbcE0HBS (ORCPT ); Fri, 27 May 2016 03:01:18 -0400 Received: from relmlor2.renesas.com ([210.160.252.172]:55821 "EHLO relmlie1.idc.renesas.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1754145AbcE0HBR (ORCPT ); Fri, 27 May 2016 03:01:17 -0400 Received: from unknown (HELO relmlir2.idc.renesas.com) ([10.200.68.152]) by relmlie1.idc.renesas.com with ESMTP; 27 May 2016 16:01:14 +0900 Received: from relmlac1.idc.renesas.com (relmlac1.idc.renesas.com [10.200.69.21]) by relmlir2.idc.renesas.com (Postfix) with ESMTP id BFF1B609AE; Fri, 27 May 2016 16:01:13 +0900 (JST) Received: by relmlac1.idc.renesas.com (Postfix, from userid 0) id 9ED3F8002E; Fri, 27 May 2016 16:01:13 +0900 (JST) Received: from relmlac1.idc.renesas.com (localhost [127.0.0.1]) by relmlac1.idc.renesas.com (Postfix) with ESMTP id 980D38002D; Fri, 27 May 2016 16:01:13 +0900 (JST) Received: from relmlii2.idc.renesas.com [10.200.68.66] by relmlac1.idc.renesas.com with ESMTP id SAB06379; Fri, 27 May 2016 16:01:13 +0900 X-IronPort-AV: E=Sophos;i="5.22,559,1449500400"; d="scan'";a="212206129" Received: from mail-hk2apc01lp0209.outbound.protection.outlook.com (HELO APC01-HK2-obe.outbound.protection.outlook.com) ([65.55.88.209]) by relmlii2.idc.renesas.com with ESMTP/TLS/AES256-SHA; 27 May 2016 16:01:12 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=renesasgroup.onmicrosoft.com; s=selector1-renesas-com; h=From:To:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=f+dGUxR/NNgTJAkrwWMynuZpj7YTPWHNh2/2al2jeZU=; b=GQR+MZJ1baE5fTQVoxq2BkFukkW4iZdyHoBL8Vq7R4Y6YUUQ77r+SMti8dfBxs04rmZqmp0svOs1k7IZapC+j69ofzsZJobw7oPvC4pvxB/Is/8VXEjKo2+0HXfPbFtA1V8zGB6FekTA1GWrSZRicMtWGFbGECTzWOhrCtKDkqE= Authentication-Results: kernel.org; dkim=none (message not signed) header.d=none; kernel.org; dmarc=none action=none header.from=renesas.com; Received: from morimoto-PC.renesas.com (211.11.155.144) by PS1PR06MB1705.apcprd06.prod.outlook.com (10.167.53.15) with Microsoft SMTP Server (TLS) id 15.1.501.7; Fri, 27 May 2016 07:01:09 +0000 Message-ID: <871t4okn2h.wl%kuninori.morimoto.gx@renesas.com> From: Kuninori Morimoto Subject: [PATCH 1/2][RFC] ASoC: add simple-graph-card support User-Agent: Wanderlust/2.15.9 Emacs/24.3 Mule/6.0 To: Mark Brown CC: Simon , Linux-Renesas , Linux-ALSA , Liam Girdwood , Laurent , Lars-Peter Clausen In-Reply-To: <874m9kkn5f.wl%kuninori.morimoto.gx@renesas.com> References: <874m9kkn5f.wl%kuninori.morimoto.gx@renesas.com> MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Date: Fri, 27 May 2016 07:01:09 +0000 X-Originating-IP: [211.11.155.144] X-ClientProxiedBy: KAWPR01CA0017.jpnprd01.prod.outlook.com (10.161.24.27) To PS1PR06MB1705.apcprd06.prod.outlook.com (10.167.53.15) X-MS-Office365-Filtering-Correlation-Id: c509efb4-ca21-442e-395d-08d385fcaee6 X-Microsoft-Exchange-Diagnostics: 1; PS1PR06MB1705; 2:0B+hn+akNrN3pFHT2U+FacEF8B/Py6LUCmrs+/Xh9pt96Y6R/2F0FFDqC/bbDoICVdc1IChZh2zbR20stTGW+gFa6eWSBo0IxnHyyK4UMlMf8JpWFcbtujyw5p0YJXQvltQ6DKTaqmsK9RwG3N8qH4iDLhWtxDNMBT2j+LPzlOMg7QncTpT8iMdPLpdiWm3s; 3:CEWgg7HB74wY7RX0ncAbanptGiOpkkDEILvxJvj8umUcQEU8/qcahJEUruSiH56GuOcjWVcMVcWOkyoTc8xrpT2jbDHMRIS9m0LPhJxYyiA7xScEpEPG7NtjC3bCP4Gy; 25:0K66LqtnFIiMz3eb0+KimGPTgBBaBodgpz2RJInStSh021EZfNoRuZbPvu3deb2yayLhSSQjwgs62kViFaioWxiD6mx3qk0IyMoHJXZnh3PwFauFjPTyxkjkUZvzO6g/n/a1voE35RxuTBxwIPv47VmBWTErfiTlWwfYgPrRfv2FuKyIh75JOvlKLFN5zS264Glga3XDQfB54FHE1hMVMjI3mWiSwvPN3rh/HADPU/BW1stmllrlup3T/ZAYBo6YQsQrct8RkMHVM3TfR9z0WaI5HP+TJ8CW7WVZm7Z1Nw7VQI4RvjHnFkeacy9nVdEXAYgqdPkJGcymaGZ7EHeM9zK9Z+/nw/2C2PP1wGBNnqcJjX3cDlQE2uzWZTRT9dZIoOoL8g5V2xYD/8d/uINtGc9GY6wvAeM7OKS2v/v5raw= X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:PS1PR06MB1705; X-Microsoft-Exchange-Diagnostics: 1; PS1PR06MB1705; 20:+C0KQdqcEhkEKJkhv9+oBOkwynKKZxY3Ey45Ly4i6FodSqYs8Z1fr8dQUzEQONMefWntblwcFKuN5JPQlZn/+EhsdgHlgeackC9R0YnaI7xGTz29N8ZCz5DV0N+fqJNQ1xmkMl9O0z4mYARndK3zGMTCEPKSHso/v0iHh9V45xui2sljfhPt18SjzOxAgqlBULfZNK7iAGtXQpC8F+mfdrLCZEmWf42SigGVkEP/rIsSszf+EkhaGcAt3qV5c1XzSkLwhOeouttLngGccMJ5bmlRwPB1cnAOcxrSs9FlO5J5U6yFRpNw3O4Lgd8H3yftx6GQt72qWPQyNcfRMLukGP2P0jlosL5XIeIPw+DqZmelJ/EFB+0erzWXecxNKnrwMws4HGZhofUhiE64kpjzghutF9w6sNVGaBkzvpKgDwbIOEOTuPh1x2sSSHwUJW1ZX5w4bPPrt/vieqeuBd+3wyHR6DLElWRJ9G819FB/TepLqNbBIXVXz5gXvUrb1d7D; 4:lvlY8TAesrq5THiVeBD7FcTLliW+PfHKQrQnkuew9FnXtWvgPaofDbyy1YF2tyWgKlXDFqbPU+WdhfYmey4XkqRcNdAq5R/OJlLZhjGUxAPk3RD4+cix6/INqqmf6D8b3tAj2wn1wZQ+sbP0YAkHbbcNMZ/O//3GXGxuTlHvtudPRcorh56DD2UlIL8Pxeb4q2f3Uez+5pbHGXg1xEQaPjBeT6orbzH2baIUAitpTjY5Lp9q1VmRYhBoUpuIP9ebJ9EOOIVlpFceDOmV68PzJxxxWc2XGf1Kv/xbEqdK42xmjaYlT2WOLmbeGDB4jnNGkx3nxLHLK4qAmTC3Ti37L1FAgv12BwfPCLdDLTQ26i9nmIytuDGzoy7eZRJis11CHZ1OVLCrsjZO93bkLs22Gg== X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(8121501046)(5005006)(10201501046)(3002001)(6055026); SRVR:PS1PR06MB1705; BCL:0; PCL:0; RULEID:; SRVR:PS1PR06MB1705; X-Forefront-PRVS: 09555FB1AD X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(6009001)(46406003)(50466002)(4326007)(5008740100001)(36756003)(5004730100002)(81166006)(92566002)(230783001)(8676002)(19580395003)(19580405001)(83506001)(53416004)(54356999)(76176999)(50986999)(586003)(86362001)(2906002)(229853001)(575784001)(33646002)(42186005)(66066001)(189998001)(47776003)(23726003)(110136002)(77096005)(3846002)(2950100001)(4001350100001)(6116002)(16060500001); DIR:OUT; SFP:1102; SCL:1; SRVR:PS1PR06MB1705; H:morimoto-PC.renesas.com; FPR:; SPF:None; MLV:sfv; LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; PS1PR06MB1705; 23:6fzrTa7Tb0+UFpEgdi5lkVyA95El7csTSFHh8uB3p?= =?us-ascii?Q?w5Hyir1vU3cCwUwROsEZOVgw5tMr9KkIOeryTwE6gTrWXkQHt8kbKYBMLDSh?= =?us-ascii?Q?A4tmxGQFhBnAS2gTr68rK/lWJyQVbe2x0J6RjT0Ry7euEydUYRVN5tZdhwcR?= =?us-ascii?Q?tusY2rhgu1r8eq4GT4rna6mmGNqarPrhij58k6OciqT3bSDkNxzPKJwKMT6f?= =?us-ascii?Q?6Ac5QNBvRRNG0v9Qd29nfBv+hitVZpyB2DUgGcJl2vbF/V8H8yyHxv0KNesG?= =?us-ascii?Q?uimk3nMbARN5mBPJLnuENCMJnmUkeTX8IzgKMRpWXPQTkTFLbhrn0p+WbtKh?= =?us-ascii?Q?4/Inkt465NJoMVg50T88m2RWKj8yjhEWPsLo6jZ1/LkyNC35KpA9q0u199S+?= =?us-ascii?Q?k+WtU33FxbPnWvoQQ6CM6xYKhuo2Vbo4D4SLaKggmtXRyTGqHPciopDauw/l?= =?us-ascii?Q?w4TsYaV6TmbBihyAXs3OhB+iF1XimbJfducwuv09ouf5pPNcTWTW/oiXVNpC?= =?us-ascii?Q?hBebMDRj9S8Cl0fn7wOxswEeELqIxz2/mAZpbRM+AXck2/L3BtspuCgogdb7?= =?us-ascii?Q?EGRH+AYaO7O5WtK0QVDZkz//HWTTzS6oeFtwsF3wDAmbgBoAdjA4G1Bbphy5?= =?us-ascii?Q?oEqncnHdYd2FGeCCp5cLGHwgtNYvkUlQ470moqdQbHy+KwsQcILDp251aCN8?= =?us-ascii?Q?o2LGH+uRsOwb4BTCiq7WOmu3G2EtHMXRJliTH4uyUm6bRZgOAcinfpM/97qZ?= =?us-ascii?Q?mlnUyYFS8S8bZozrKAj5AJZRABXWapb1iXz45ZVxC2aUlREjR5Vxsu4COani?= =?us-ascii?Q?6UeNULC5tR9Y1yrwxlC+PWrmFf0pqTWMfuCU3O5vRG+6Battj8rh0yZWZ4PV?= =?us-ascii?Q?zA/ZiX9KelZ8TEfT1rqRx/4m60dL7lpZhKaH51zgotpOV/apULIjlqFbtjdi?= =?us-ascii?Q?b9CYAhiBYMZFBs8zDiaIQs01N9S6+ssk2zVdld4zkrNbCVoMaH6gvno2N8lD?= =?us-ascii?Q?hYUiq1xo4AsHIfw8Xy2+iDl?= X-Microsoft-Exchange-Diagnostics: 1; PS1PR06MB1705; 5:Xw4NTKR8YHrNA+XoXnMdYfgwtiPzY22SEZfZBgdEs3WN591ia5+2N6kAik4RJ6rPy8o9P9JxYuXTUM/pfzdlwV0A68rL4/wggO2fNUbTXctb84Wt4SaCpSY2nYltJzIICQ65aYi/OvB4MfDg7RNb4g==; 24:/EZ8TXncdAuLQV+7v5CVsLkbehubzrEJ7diFZgdLDTBu04i97yp44+UT0O00XgOF9werOSyyamP7a2GuErR+TZQ+E8GnFbjhNB00WyG2+lY=; 7:h/zjUI+QOhS3URQ7RBLLYduq831Wbg7ltsQd2KrduJk0xL84FfGN6XaP0cgIfvrEy7t07jWz0BI/+fJWjr4MtyFHtvpigEmHto5d34vvCl7zjAoS51PXoIYwHG0XJ0mdhl4dB0Iq3JXT7FKOiY/lo0JhEPf+9uHQIxLOi3YGUh9EpT/462lCMZRG3VyHM6zJ; 20:ePJCN9s1OiOiYIfGBPa9KRyQvDYZKkTb7Y6mytrfoiImSLRFwDDwQNipDxv6oKrRLu4zJewag4ZvmlZaqp4bea/FAKfopTkHgOvVdNfF37OwjFg6LJLxROYkmqFdeIsUEcIltRipYGa5w5WPfXMb/KzoGIJ3pZaLzEgDoYMw1as= SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: renesas.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 May 2016 07:01:09.6948 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: PS1PR06MB1705 Sender: linux-renesas-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP graph base DT binding are used on V4L2, and ALSA SoC is using different style of DT. In case of simple case, ALSA SoC supports simple-card driver. Int the future, V4L2 / ALSA will support HDMI, and then, DT bindings between V4L2 / ALSA should be merged somehow. This patch adds graph base DT binding with simple-card style Signed-off-by: Kuninori Morimoto --- sound/soc/generic/Kconfig | 6 + sound/soc/generic/Makefile | 2 + sound/soc/generic/simple-graph-card.c | 351 ++++++++++++++++++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 sound/soc/generic/simple-graph-card.c diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index fbba1e6..49554fd 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -12,3 +12,9 @@ config SND_SIMPLE_DPCM_CARD select SND_SIMPLE_CARD_CORE help This option enables generic simple DPCM sound card support + +config SND_SIMPLE_GRAPH_CARD + tristate "ASoC Simple Graph sound card support" + select SND_SIMPLE_CARD_CORE + help + This option enables generic simple Graph sound card support diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index a5f0e4d..2f7d392 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_SND_SIMPLE_CARD_CORE) := simple-card-core.o snd-soc-simple-card-objs := simple-card.o snd-soc-simple-dpcm-card-objs := simple-dpcm-card.o +snd-soc-simple-graph-card-objs := simple-graph-card.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_SIMPLE_DPCM_CARD) += snd-soc-simple-dpcm-card.o +obj-$(CONFIG_SND_SIMPLE_GRAPH_CARD) += snd-soc-simple-graph-card.o diff --git a/sound/soc/generic/simple-graph-card.c b/sound/soc/generic/simple-graph-card.c new file mode 100644 index 0000000..ef1e90f --- /dev/null +++ b/sound/soc/generic/simple-graph-card.c @@ -0,0 +1,351 @@ +/* + * ASoC simple graph sound card support + * + * Copyright (C) 2015 Renesas Solutions Corp. + * Kuninori Morimoto + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * 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 +#include + +struct simple_card_data { + struct snd_soc_card snd_card; + struct simple_dai_props { + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; + unsigned int mclk_fs; + } *dai_props; + struct asoc_simple_jack hp_jack; + struct asoc_simple_jack mic_jack; + struct snd_soc_dai_link *dai_link; + unsigned int mclk_fs; +}; + +#define simple_priv_to_dev(priv) ((priv)->snd_card.dev) +#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) +#define simple_priv_to_props(priv, i) ((priv)->dai_props + i) + +#define DAI "remote-endpoint" +#define PREFIX "simple-audio-card," + +static int asoc_simple_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = + &priv->dai_props[rtd->num]; + int ret; + + ret = clk_prepare_enable(dai_props->cpu_dai.clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + return ret; +} + +static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = + &priv->dai_props[rtd->num]; + + clk_disable_unprepare(dai_props->cpu_dai.clk); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + +static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = &priv->dai_props[rtd->num]; + unsigned int mclk, mclk_fs = 0; + int ret = 0; + + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + goto err; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + goto err; + } + return 0; +err: + return ret; +} + +static struct snd_soc_ops asoc_simple_card_ops = { + .startup = asoc_simple_card_startup, + .shutdown = asoc_simple_card_shutdown, + .hw_params = asoc_simple_card_hw_params, +}; + +static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct device *dev = simple_priv_to_dev(priv); + struct device *cpu_dev = dev->parent; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct simple_dai_props *dai_props = &priv->dai_props[rtd->num]; + struct device_node *cpu_port = of_graph_get_top_port(cpu_dev); + int ret; + + ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_hp(card, cpu_port, &priv->hp_jack, PREFIX); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_mic(card, cpu_port, &priv->hp_jack, PREFIX); + if (ret < 0) + return ret; + + return 0; +} + +static int asoc_simple_card_dai_link_of(struct device_node *cpu_ep, + struct simple_card_data *priv, + int idx) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); + struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct device_node *cpu_port; + struct device_node *codec_ep; + int ret, single_cpu; + u32 val; + + cpu_ep = of_node_get(cpu_ep); + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + + if (cpu_ep != of_graph_get_remote_endpoint(codec_ep)) { + dev_err(dev, "endpoint parse error\n"); + goto dai_link_of_err; + } + + cpu_port = cpu_ep->parent; + + ret = asoc_simple_card_parse_daifmt(dev, cpu_port, codec_ep, + PREFIX, &dai_link->dai_fmt); + if (ret < 0) + goto dai_link_of_err; + + if (!of_property_read_u32(cpu_port, "mclk-fs", &val)) + dai_props->mclk_fs = val; + + ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link, + &single_cpu); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_tdm(cpu_ep, cpu_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_tdm(codec_ep, codec_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_cpu(cpu_ep, dai_link, cpu_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_codec(codec_ep, dai_link, codec_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_dailink_name(dev, dai_link); + if (ret < 0) + goto dai_link_of_err; + + dai_link->ops = &asoc_simple_card_ops; + dai_link->init = asoc_simple_card_dai_init; + + dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %d\n", + dai_link->cpu_dai_name, + dai_props->cpu_dai.sysclk); + dev_dbg(dev, "\tcodec : %s / %d\n", + dai_link->codec_dai_name, + dai_props->codec_dai.sysclk); + + ret = asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); + if (ret < 0) + goto dai_link_of_err; + +dai_link_of_err: + of_node_put(codec_ep); + + return ret; +} + +static int asoc_simple_card_parse_of(struct device_node *node, + struct simple_card_data *priv) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device *cpu_dev = dev->parent; + struct device_node *cpu_port = of_graph_get_top_port(cpu_dev); + struct snd_soc_card *card = &priv->snd_card; + struct device_node *ep; + int i = 0; + + u32 val; + int ret; + + if (!node) + return -EINVAL; + + /* Factor to mclk, used in hw_params() */ + ret = of_property_read_u32(node, PREFIX "mclk-fs", &val); + if (ret == 0) + priv->mclk_fs = val; + + for_each_endpoint_of_node(node, ep) { + ret = asoc_simple_card_dai_link_of(ep, priv, i); + if (ret < 0) { + of_node_put(ep); + return ret; + } + i++; + } + + ret = asoc_simple_card_parse_card_widgets(card, cpu_port, PREFIX); + if (ret) + return ret; + + ret = asoc_simple_card_parse_card_route(card, cpu_port, PREFIX); + if (ret) + return ret; + + ret = asoc_simple_card_parse_card_name(card, cpu_port, PREFIX); + if (ret) + return ret; + + return 0; +} + +static int asoc_simple_card_probe(struct platform_device *pdev) +{ + struct simple_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct simple_dai_props *dai_props; + struct device *dev = &pdev->dev; + struct device *cpu_dev = pdev->dev.parent; + struct device_node *cpu_node = cpu_dev->of_node; + int num, ret; + + num = of_graph_get_endpoint_count(cpu_node); + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + if (!dai_props) + return -ENOMEM; + + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_link) + return -ENOMEM; + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + + ret = asoc_simple_card_parse_of(cpu_node, priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + return ret; + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(dev, &priv->snd_card); + if (ret >= 0) + return ret; + + return 0; +} + +static int asoc_simple_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct simple_card_data *priv = snd_soc_card_get_drvdata(card); + + asoc_simple_card_remove_jack(&priv->hp_jack); + asoc_simple_card_remove_jack(&priv->mic_jack); + + return 0; +} + +static struct platform_driver asoc_simple_card = { + .driver = { + .name = "asoc-simple-graph-card", + }, + .probe = asoc_simple_card_probe, + .remove = asoc_simple_card_remove, +}; +module_platform_driver(asoc_simple_card); + +MODULE_ALIAS("platform:asoc-simple-graph-card"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ASoC Simple Graph Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto ");