Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932822AbcJQIja (ORCPT ); Mon, 17 Oct 2016 04:39:30 -0400 Received: from relmlor3.renesas.com ([210.160.252.173]:18461 "EHLO relmlie2.idc.renesas.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1758435AbcJQIjI (ORCPT ); Mon, 17 Oct 2016 04:39:08 -0400 X-IronPort-AV: E=Sophos;i="5.22,559,1449500400"; d="scan'";a="222518734" Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=kuninori.morimoto.gx@renesas.com; Message-ID: <87y41n2xb4.wl%kuninori.morimoto.gx@renesas.com> From: Kuninori Morimoto Subject: [PATCH 21/23] ASoC: add simple-graph-card support User-Agent: Wanderlust/2.15.9 Emacs/24.3 Mule/6.0 To: Rob Herring , Mark Brown CC: Linux-ALSA , Liam Girdwood , Simon , Laurent , Guennadi , Grant Likely , Frank Rowand , Linux-DT , Linux-Kernel In-Reply-To: <87shrv4c8x.wl%kuninori.morimoto.gx@renesas.com> References: <87shrv4c8x.wl%kuninori.morimoto.gx@renesas.com> MIME-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Content-Type: text/plain; charset="US-ASCII" Date: Mon, 17 Oct 2016 08:38:48 +0000 X-Originating-IP: [211.11.155.144] X-ClientProxiedBy: TY1PR06CA0001.apcprd06.prod.outlook.com (10.164.91.11) To SG2PR0601MB1456.apcprd06.prod.outlook.com (10.169.106.22) X-MS-Office365-Filtering-Correlation-Id: 21a7120e-088e-46ce-fae6-08d3f6690427 X-Microsoft-Exchange-Diagnostics: 1;SG2PR0601MB1456;2:sBRNpPfrN1mUI8uNFj4go59P2LnEvBOzTJ9PUtuh17kT6K66r1AcA0o3eOJAJTwXY4f/C73mjSPH5FUtY0b0KVIGAm6U7CSTz62HLeL22k+pxR1Ooe3Xb0IAfEZ3BOhb5xQxd3YocPlyBE57kv40T8gIAEFWAOCf6ZvSemTTW7Sn3uwRFtU2efpSbn/oVcDCnHiG94T24s1ng7IMPY7/QQ==;3:y8un10i+LJp1J99649dcp6e9BT1JnitUwHpKO/1sRuf28crjrR56d+md2zHcIY3RcwlCRY4YHIjn4nAZ+PQH4ZKNh2HvftPgj2XS3TD3BYmO5SNplIpCoKmqC+mGP7ms+TY2oFTvq+5CXfHzwHSeSw==;25:6x0Vn2g0uTLhd8nK6ZJnEv1712Y/c3HFRtuNG7WQpNvpWWK4eIW7kwjsWz84ZnUF6dnWyd2FyHKVSOF/uw/amNJGDJQPcjxEJQvJofaaqR4vljpVyiXiLJh5O7NNGFqQ4eY0ygn9m535aM36aKVHr540HKhtML1jcl4hQ01aqwFTUb8BRWpViY+IgjU/Z0mjcVsFixlGcpoFZvHxE5muyzgxj6+tEH+NvW4dil5z6KYXcJ7yMNFXNZE8eh89OVfXWkNBA5E5WXDClC8IRiwXPRSdGlzYFA5YpsQ+461abUPjPS9H1+jwc6tvZNtpHfC91DSRLaVXndvAhzULk4s8ryKh6S13oU/HprWewgt89sEbEjfdn3OfUgnrdfGWqsbUbHIsHZtBsfk/QbFGD7hRGsx6295tpaO+ZdWMvXeZdgep5RypUJ+cIlVfaQjqMMy1 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:SG2PR0601MB1456; X-Microsoft-Exchange-Diagnostics: 1;SG2PR0601MB1456;31:cSjJM4eiUAdzzaQmaQDgbdeZdDYrURQ4qdS8VT3ah2w7/fY2hHNL0oZAuTF8ver3grm6abC/aY+G1GSsRBYAsd8PSD7vmbH4t9hMZykSyENzDIhpOXj7mfXn6JMUOGmVymjhZ98N0TnlXJ8t3f+TOndK8uH+0+Ud+jsUW+2e0S6LshOAKZOMcQ91MD2bNlGSmIUytPKzP9rqXwHqmZxeERPho38XyMakQ8cxFWRXgD5vGctGVra2VKY0szsbTDnm;20:ZkTFjm5ZRepFgZSKQzCv1f55Y3RiUNeMNKdf1EjHlzVppngaVdosXj54p0zH6tHP6fh22JoKMKY+47CtqnHFuot4II5ioTUQ96+Qf/Iw8sJ8/2nx3ulrYbTZa7WE/J2sSE7YlvfjDqUU6+TfuHsQwDCBP5WSJ3bLPCohAMoUJoUgAxum+IoT+DCfxL2PgvdpxK/gGaTL7EXt7aWnjLwKB6U0DkOIuckUnW8zWPD7dP8VhiEekAXI5eBc0j+F48S4tMVgVoyUwK3VPgYpZKYI8MG3KY1p91cZdSyXjsLAoyhyxUdwkcJnyCLTMqr3Ro7xW1iA2OBqqLem6fzCjJx0DQH6vgAd1f7A67GrPk/t2l1xWrOopEJv4dYfs7eLTX3PcmbGacHIj73SXTXmZ82ys+Weo7B7ngBxWSNyd1CI/Ow0O9w5/smRhlvCGg2YXGgbQ3uMCqWsgfjCoskXmtnxtFEp1AIjsCQbofJpBE5smaoByMtz/XRP8rh+bpCodlot X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040176)(601004)(2401047)(8121501046)(5005006)(10201501046)(3002001)(6055026);SRVR:SG2PR0601MB1456;BCL:0;PCL:0;RULEID:;SRVR:SG2PR0601MB1456; X-Microsoft-Exchange-Diagnostics: 1;SG2PR0601MB1456;4:4D2AoO5VVNkJgNdsuHZwiT7u/8a06UxeXMcsGCGbKHylf+Rgta70qlWEMs639x/a93EXyfrbycLbtTjdoAeHl8dFYhGOa/+Hy/kHt4wb31GxbMfQ/PpiBggvQ3sSJYVVs0aPjSlEd9u+J/09+XVWNJTRsboBhBv7qTGj5eFcdHWc66xWA3jpISzD4pvB6yMdkvlPy6yheHRKXKx+2Ds1rJq0A8bbD9CqGD2f2/2q4+YRNTcRrgAlvil6S65cvseH5jUKBcAZtM7+c22dZTYlVWs5BxosYjh3ytNP6SFUnbN1I7U6m/hkup7/PEXqTck2RB0s8FayPXoaZzZxog2GYn3WvBhBlMuUOMM9YJB9n0cS4q/ULa5vQ+2t/nvl/eOT/Rk9OQq7c2FphJTf9Jf3vsMrkzjBus29qIX2bZqwHlI= X-Forefront-PRVS: 0098BA6C6C X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(6009001)(7916002)(189002)(199003)(23726003)(97736004)(189998001)(4326007)(7736002)(7846002)(2906002)(19580395003)(54356999)(101416001)(76176999)(50986999)(8676002)(81156014)(69596002)(86362001)(81166006)(229853001)(19580405001)(53416004)(8666005)(230783001)(36756003)(92566002)(50466002)(2950100002)(33646002)(305945005)(77096005)(5660300001)(7416002)(586003)(6116002)(3846002)(106356001)(46406003)(105586002)(42186005)(5001770100001)(66066001)(68736007)(83506001)(4001350100001)(47776003)(7059030)(2004002)(21314002)(16060500001);DIR:OUT;SFP:1102;SCL:1;SRVR:SG2PR0601MB1456;H:morimoto-PC.renesas.com;FPR:;SPF:None;PTR:InfoNoRecords;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;SG2PR0601MB1456;23:05hJElmWh4yE5xuTlFVCxhOpQ9I04hB0b31MXmA?= =?us-ascii?Q?Bd+y+0c3P3LaXnCNmnvvWvASS76vhtTgPDbUi6ewnRaW6FmzH1dOoMYxL6es?= =?us-ascii?Q?pvX82syfDQxui8AYTJifIWjxn0IR19EFPLXQ6hBPESI4wDnksij3oSDfLIGw?= =?us-ascii?Q?ZFgg1B98mMrf4nlbE3mibzJCStNl1Eec276v9H9KsfyawryEdimMpH95P/uR?= =?us-ascii?Q?pmp5p5yx5wt5KmwzR/XXzIOntYSN/K8MqvPdQGM20GgsLXCKk6lx9BCxMyHt?= =?us-ascii?Q?vjfpjptTqnoM1vh40Hyn/l3xBwBh/2IXHeW8ltzHHRE5zgZs8aofuattx7PR?= =?us-ascii?Q?fiGunS90v2g2NhX+GXUgZblPiLOgEfyO0EvYPK7QYa4scf07xVrZXKPnbKin?= =?us-ascii?Q?020yrI4l+wvk+Fi1pxBePuFfdNbqyZwz3y4Bts6HwjPefCu6G1YZFvO4wJft?= =?us-ascii?Q?u82yzLSuv0/jWwX5ReiG+NH4j2K3uSBbpsVp+dnKBy7DI+TuEl2T5VCgk/1x?= =?us-ascii?Q?6gGJbEiNRZHUp/B4urV0gsEGZRCC8RYx6rpekWhm5kR8j7UNdGC1NkNT1qZE?= =?us-ascii?Q?jSxsfARPKwe0Lu7sjQiSO37keHMRHwJcf70dkhyjfu0R/wCvaekzRBXpnt2u?= =?us-ascii?Q?UPvp1djc1WB11ROH91K0LEeDXrOdlMlPx5+7AJLoInMIAOGu90E2y0SmJ8eN?= =?us-ascii?Q?K/hyKdwhSUwLDFCiaBFtBCTaWTUNoLVwbsGD+k+lAmHPtFfElh0yd4QiRxQI?= =?us-ascii?Q?P2pztFz6Gbu09SyAusFrXaNKKzhwlf7moaYXwlINVGI0N9Id7ZkrycVqryrj?= =?us-ascii?Q?JTcuctICFd5mYBvzE9I3lKvdZQU2mE6t22UbGqnXFxU5foDS6FZZrIo1g11y?= =?us-ascii?Q?5OtVeWf37AADdsYXfd6F5zAbAmundScfTva76hOEQ1oBT29mJpoZMY0w1sMM?= =?us-ascii?Q?6aIkjO4D+ZnVMr5LaVWU0LW+dapdLojpMjSgrqgdLZ9iyyMoAEqLTnymA44i?= =?us-ascii?Q?b6m13r0J/ICVb0oJ9S+e5ILs7awxSlV9znmqEGmgHwgh3anewNkTHn1bEttR?= =?us-ascii?Q?G9kY1CELHxI22DpC+J4YlVKdCGBpRlKfffJAFXFD3giXTZH5Pddwu/EdsfXh?= =?us-ascii?Q?PMhEWPLf1aio3gpdGFOlMXC2c3BsI+VGTmymeXTcj40corFMAfq273a2zImo?= =?us-ascii?Q?ArztRG3kHfIMahDsyOglUbQFWI09EIWfwqusOoeuLrizT2DvPcuaEF5lTgMy?= =?us-ascii?Q?c7hNFgruReLjQ9QUQ9LsM2D1PRmxHNC/gJ8goESc7FbMaMC9GS9Bs35W6N0Z?= =?us-ascii?Q?FyyJUdxtdQm9ZUqdhANlWowc=3D?= X-Microsoft-Exchange-Diagnostics: 1;SG2PR0601MB1456;6:YUgxmnndUPLJT7jpYJ3pH2V1ogjyESMbGDjAnbGTZodZjZobaPDFay0hLbYb8fMyP7i05u+l3Ilx0+7Xbtn5dpdG9LjOFqh+qFcIoXuhzkD0NhDfDK5KSK3QhG8hkUkNZTJpoQtvJ43pVaN+wKnnwsedEMmrm3Igi20nW8hwEVGPgC7tslVJvMzYBXh2DetCupSh7UcgpFhapgG8T1+QcHL2w9KEcc2D4OHjgdPKbMTfWmcXdkTFQ8nazybVr5y2jB32ACtS+yNiWbm9o/h7mEb5/SybueYqv28fhUUHshugACmnkZH9y+fO4nilyv15U7BxryXAq0yE5CLZbgSOU1G4/nJ3aAM3tzzOaKS4w4c=;5:pfkpsIxYJLmR4aGHxSU9JWpKwJjQP5R0KSdKFEBCJiUc7HGbYbwZxz01M9c0InG2kD0hOTSFNLeoULjWXMQYgnOFI7v2p2+gE/RgbsCh1SVIwI4XrjIX5109Cj8umSkG/9owKX4OEPR0Em1XMfQpOklfjwjlS3AUOtH3x3zSHDo=;24:gICBtWDf0U45a5sCE7beffgJbvsqLeHH7yLEqgogiYNdWbg8j8onu8mjYXG3/4dEBDhhY58cNG7b6/lRUCp0GidVcepWE51YfDlxld6GxTE= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;SG2PR0601MB1456;7:AoG1iEKTOMMPIK4xR1PiC64vU/gRQGma87lG2tBky7mxJhlFdiZp3DpwC0hZtbT7ChTgjEpIHXzzgYP9uH/17yLvbTY9ZHA/NQKhaxQ6twdetEanGC3zMmQeatA/8Kte1N1sMafx+XYPZad86YFM5VB79jmGJX315fZxcJgR4+4jiIU7kTtwDwjpz5nH6D1DYOs4HfeNUQVYRAchj86LeW5vFbJXAjZkk5btTq5JvOWR+8vJK3e9W7j8v7VAYgn9P/+mLkXX6CX2cCP4VTVWYKSt8bGnNT1wTCUGTuVOJgcf0MWVASgo3w8w/vH6paZQbymJ/05D2jFT4J0lhkGUymb5htbxl7Y6pe88gcsJZng=;20:2hC8voGU6U0fy1SKuWxQ5R6V2bp3kLf8fDG3bOH20rsbxNZap1c6Higj1BqaUKkouiX5Oce3km+mzO2VhCOyPya71h/2nqc65MbiuH/z0SAgPk6Wb4tUU0C2qogwXz3eUhFybRcwoZTga+QzqU0tDgKj7MpL5dRFsxNaKSLCjeQ= X-OriginatorOrg: renesas.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Oct 2016 08:38:48.0671 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: SG2PR0601MB1456 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15339 Lines: 516 From: Kuninori Morimoto 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. In 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 | 7 + sound/soc/generic/Makefile | 2 + sound/soc/generic/simple-graph-card.c | 461 ++++++++++++++++++++++++++++++++++ 3 files changed, 470 insertions(+) create mode 100644 sound/soc/generic/simple-graph-card.c diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index b95ae49..764bc83 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -14,3 +14,10 @@ config SND_SIMPLE_SCU_CARD help This option enables generic simple SCU sound card support. It supports DPCM of multi CPU single Codec ststem. + +config SND_SIMPLE_GRAPH_CARD + tristate "ASoC Simple Graph sound card support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple Graph sound card support diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index ee750f3..94eb6f1 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,7 +1,9 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-simple-scu-card-objs := simple-scu-card.o +snd-soc-simple-graph-card-objs := simple-graph-card.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-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..3962b86 --- /dev/null +++ b/sound/soc/generic/simple-graph-card.c @@ -0,0 +1,461 @@ +/* + * ASoC simple graph sound card support + * + * Copyright (C) 2016 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 +#include + +struct asoc_simple_jack { + struct snd_soc_jack jack; + struct snd_soc_jack_pin pin; + struct snd_soc_jack_gpio gpio; +}; + +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 PREFIX "simple-audio-card," + +#define asoc_simple_card_init_hp(card, node, sjack, prefix) \ + asoc_simple_card_init_jack(card, node, sjack, 1, prefix) +#define asoc_simple_card_init_mic(card, node, sjack, prefix) \ + asoc_simple_card_init_jack(card, node, sjack, 0, prefix) +static int asoc_simple_card_init_jack(struct snd_soc_card *card, + struct device_node *node, + struct asoc_simple_jack *sjack, + int is_hp, char *prefix) +{ + enum of_gpio_flags flags; + char prop[128]; + char *pin_name; + char *gpio_name; + int mask; + int det; + + sjack->gpio.gpio = -ENOENT; + + if (is_hp) { + snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix); + pin_name = "Headphones"; + gpio_name = "Headphone detection"; + mask = SND_JACK_HEADPHONE; + } else { + snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix); + pin_name = "Mic Jack"; + gpio_name = "Mic detection"; + mask = SND_JACK_MICROPHONE; + } + + det = of_get_named_gpio_flags(node, prop, 0, &flags); + if (det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (gpio_is_valid(det)) { + sjack->pin.pin = pin_name; + sjack->pin.mask = mask; + + sjack->gpio.name = gpio_name; + sjack->gpio.report = mask; + sjack->gpio.gpio = det; + sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); + sjack->gpio.debounce_time = 150; + + snd_soc_card_jack_new(card, pin_name, mask, + &sjack->jack, + &sjack->pin, 1); + + snd_soc_jack_add_gpios(&sjack->jack, 1, + &sjack->gpio); + } + + return 0; +} + +static void asoc_simple_card_remove_jack(struct asoc_simple_jack *sjack) +{ + if (gpio_is_valid(sjack->gpio.gpio)) + snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio); +} + +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 = + simple_priv_to_props(priv, 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 = + simple_priv_to_props(priv, 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 = + simple_priv_to_props(priv, 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 = + simple_priv_to_props(priv, 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 *cpu_remote_ep; + struct device_node *codec_ep; + int ret, single_cpu; + + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + cpu_remote_ep = of_graph_get_remote_endpoint(codec_ep); + + if (cpu_ep != cpu_remote_ep) { + dev_err(dev, "endpoint parse error\n"); + ret = -EINVAL; + 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; + + of_property_read_u32(cpu_port, "mclk-fs", &dai_props->mclk_fs); + + 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 = snd_soc_of_parse_tdm_slot(cpu_ep, + &cpu_dai->tx_slot_mask, + &cpu_dai->rx_slot_mask, + &cpu_dai->slots, + &cpu_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(codec_ep, + &codec_dai->tx_slot_mask, + &codec_dai->rx_slot_mask, + &codec_dai->slots, + &codec_dai->slot_width); + 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_set_dailink_name(dev, dai_link, + "%s-%s", + dai_link->cpu_dai_name, + dai_link->codec_dai_name); + 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); + + asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); + +dai_link_of_err: + of_node_put(codec_ep); + of_node_put(cpu_remote_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 *top_port = of_graph_get_top_port(cpu_dev); + struct snd_soc_card *card = &priv->snd_card; + struct device_node *port, *ep; + int i = 0, ret; + + if (!node) + return -EINVAL; + + for_each_of_port(node, port) { + if (!of_graph_port_type_is_sound(port)) + continue; + + /* The off-codec widgets */ + if (of_property_read_bool(port, PREFIX "widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets_from_node( + &priv->snd_card, + port, PREFIX "widgets"); + if (ret) + return ret; + } + + /* DAPM routes */ + if (of_property_read_bool(port, PREFIX "routing")) { + ret = snd_soc_of_parse_audio_routing_from_node( + &priv->snd_card, + port, PREFIX "routing"); + if (ret) + return ret; + } + + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(port, PREFIX "mclk-fs", &priv->mclk_fs); + + for_each_of_endpoint_in_port(port, 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_name(card, top_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; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + num = of_graph_get_sound_endpoint_count(cpu_node); + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !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); + goto err; + } + + /* + * FIXME + * + * This driver is assuming that it will be called from + * asoc_simple_card_try_to_probe_graph_card() which + * is using platform_device_register_full(). + * This means it is not came from DT. But this driver itself + * will be used as part of ALSA SoC (= sound card). + * Because of these background, it might fail in + * snd_pcm_lib_malloc_pages() on .hw_params. + * Because, noone cares its dma_ops, and result of get_dma_ops() + * is based on its architecture. + * So, it should call arch_setup_dma_ops() from somewhere, + * otherwise, for example, ARM is no problem, but ARM64 will be fail. + * But, of_platform_device_xxx() are not good solution today. + * This driver calls it by itself here. Please fixme + * see also + * linux/sound/soc/generic/simple-card-utils.c :: + * asoc_simple_card_try_to_probe_graph_card() + */ + of_dma_configure(dev, dev->of_node); + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + asoc_simple_card_clean_reference(&priv->snd_card); + + return ret; +} + +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 asoc_simple_card_clean_reference(&priv->snd_card); +} + +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 v2"); +MODULE_DESCRIPTION("ASoC Simple Graph Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto "); -- 1.9.1