From patchwork Tue Aug 11 11:25:15 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zidan Wang X-Patchwork-Id: 6991621 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 451199F344 for ; Tue, 11 Aug 2015 11:24:34 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E4DF3205D6 for ; Tue, 11 Aug 2015 11:24:32 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 1681520569 for ; Tue, 11 Aug 2015 11:24:31 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 3C903265E00; Tue, 11 Aug 2015 13:24:30 +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 80410265E2B; Tue, 11 Aug 2015 13:24:14 +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 0728D265E2B; Tue, 11 Aug 2015 13:24:12 +0200 (CEST) Received: from na01-by2-obe.outbound.protection.outlook.com (mail-by2on0103.outbound.protection.outlook.com [207.46.100.103]) by alsa0.perex.cz (Postfix) with ESMTP id AF90F2604FA for ; Tue, 11 Aug 2015 13:23:46 +0200 (CEST) Received: from BY1PR0301MB1255.namprd03.prod.outlook.com (10.161.203.27) by BY1PR0301MB1207.namprd03.prod.outlook.com (10.161.203.156) with Microsoft SMTP Server (TLS) id 15.1.225.19; Tue, 11 Aug 2015 11:23:43 +0000 Received: from BLUPR03CA005.namprd03.prod.outlook.com (10.255.124.22) by BY1PR0301MB1255.namprd03.prod.outlook.com (10.161.203.27) with Microsoft SMTP Server (TLS) id 15.1.225.19; Tue, 11 Aug 2015 11:23:41 +0000 Received: from BY2FFO11FD040.protection.gbl (10.255.124.4) by BLUPR03CA005.outlook.office365.com (10.255.124.22) with Microsoft SMTP Server (TLS) id 15.1.225.19 via Frontend Transport; Tue, 11 Aug 2015 11:23:40 +0000 Authentication-Results: spf=fail (sender IP is 192.88.158.2) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.158.2 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.158.2; helo=az84smr01.freescale.net; Received: from az84smr01.freescale.net (192.88.158.2) by BY2FFO11FD040.mail.protection.outlook.com (10.1.14.225) with Microsoft SMTP Server (TLS) id 15.1.243.9 via Frontend Transport; Tue, 11 Aug 2015 11:23:40 +0000 Received: from b50113.ap.freescale.net (b50113.ap.freescale.net [10.192.241.89]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id t7BBNNbH000400; Tue, 11 Aug 2015 04:23:26 -0700 From: Zidan Wang To: Date: Tue, 11 Aug 2015 19:25:15 +0800 Message-ID: <11490c0414fec756d2fb0ebcbcd94a6c7fafadd7.1439292054.git.zidan.wang@freescale.com> X-Mailer: git-send-email 1.9.1 X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1; BY2FFO11FD040; 1:W5qTpzBRgeKw5HM+yH5fXTJ5HTHxfUo4SdrBZsCW7Y6PC0OxxRuRTNsnza0a5VEW/p2GwaBaBzipq0WvneQd4p3kkdC4eLKdJ0ui8iMIkZCenGQPUOwfCUhkgtIM69BC8ijdSFPnyU3vqH+bmusiCl26LZegWoWt16JRyxuD9YeuvlamWC7kFb5KfcEbhIVJZm0eb5alHwXfsSTKiuOgzBQBQjtXojxxCJOtqicKIxX7meBqgqk3qfirs0Mc6NUot9OQGlgxHlmDdr+vi5sEJAJMwWCtulcJFUq4F5/F82BFGcqVgA02v1dMCpponIy6LeV3Fp1QCCa4WAsvmNl6HUQEkfPYjO4GM28BQncG9wLqQOZhxniQUyNMw7XAx1fbaG/lJeO4i+jJRdHEzaRHfA== X-Forefront-Antispam-Report: CIP:192.88.158.2; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(2980300002)(1060300003)(3050300001)(339900001)(199003)(189002)(106466001)(50226001)(81156007)(97736004)(5001860100001)(69596002)(50986999)(5001830100001)(6806004)(4290100001)(118296001)(50466002)(5001960100002)(48376002)(19580405001)(104016003)(107886002)(575784001)(64706001)(4001540100001)(47776003)(77156002)(19580395003)(85426001)(86362001)(62966003)(229853001)(46102003)(110136002)(36756003)(87936001)(92566002)(33646002)(105606002)(68736005)(2351001)(77096005)(5003940100001)(110436001)(4001430100001); DIR:OUT; SFP:1102; SCL:1; SRVR:BY1PR0301MB1255; H:az84smr01.freescale.net; FPR:; SPF:Fail; PTR:InfoDomainNonexistent; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Exchange-Diagnostics: 1; BY1PR0301MB1255; 2:AmvPtJPYViXoWakJP5FvlkqVaxNyX+/cmTM1YzJn2hnPEWDzl+dXBkgQjqNvDhN0GclaJrSOMXTf9KmjOkmwnR8Gd50xlvvedDtj7K2RVLIaa294/aVY8tlm/pA41O7WvJCxJHv4mhgAXBT74AwqZUqZ+dg8I96T2fcb2znhYj0=; 3:pscGhUjrMrPOFSj4r4HPKKsKjxQZTtka6lCEzFRBFUW9ZAdmGwbZ/IM11EkuKHFGkWAPsvvuHbjM/cUXdv54A+tt1XOKcLSSGviks4Kuy+Df75L8SRVvhh7oNhC52iFH/tY542Ary4H1rkYFT1IlhlVGs+zT9dzul6u82Oz35kgNXWkJiAPMCfF+Ciab2z8Sh2kaXJ7TWJyf41C6wfxDRSJwte2ubDcGM34WpenLKyE=; 25:EP3O3LbWMRLEsrdvoMNzrYOXLJfe3cPLlRxv7qGwZ98UaLPV9zafZCUQc6C+dsBVKD9gUEnalXpxEvVtI1gvs0wcE+WZ76qx1bOb/qgrDJFgrC116unHnsksGwvohSnPtYAk2lnInlgujKdy4+2CGWGa2xZ3SedRxsX0FNWKoaqLQazEx46THPIGT0k1vXRmKVu9D9Mb/jkjT+WNOSrM9+XvrJ4nTrAIq/MTN/JeYvwPlXevUqw5MXBSXRZ06NPB X-Microsoft-Antispam: UriScan:; BCL:1; PCL:0; RULEID:; SRVR:BY1PR0301MB1255; UriScan:; BCL:1; PCL:0; RULEID:; SRVR:BY1PR0301MB1207; X-Microsoft-Exchange-Diagnostics: 1; BY1PR0301MB1255; 20:0ev5wgRXLoM1ir41SaVR8iyRghoHO9G/7lMVu6cpBfJrm+fNOX9LVHEubRRncwnrsGFSdSNhLGsxrdg56MLf3zcJQsnzWb5PIhwRDVnt3aw3hwPaPSrXtHnOW2dqJ2yswTgGm/EhlE2riXTnLuE3HHFoMp/ljfMKBJnw3avzbIPt1JOlHWuivAZLSWpBJNjlKF5Fwz1jOwUROLSpEtJWEx7ZbLbZEI02Zlqr8cdFPD7H+4u/dGTrSZvqIsjgSmsq4gMWYGixeRPIMm5+zvCK6VPZ9jwtKfJ3W2jFJesOlYOrnWEa0P+Lmp9Hu/l4QdN14uXYVrLYVFNAgCV+9zRXgxWp38ZsDBu3IqaZsxEBjNc=; 4:CqUQQTaCnwG0tu+KscgKrtF/Ios5OxOPfCNFfIsujxwUY8MrTMi7DpeNQ2nex9ARDDuMkkyvXxRp4efMsXfsfjTagO5ZoV4mXKxN7kquMqIfKxdHSsavcbq1gMmHfDoZXV/N6YodwW1mmlBVJxkd1m/Amwi0kPovvaRN8/C4TZ0/w/BP8eROYyxZfq9e3gwPX5+18uVSdsWECmfJhcTjvheqs1sJtAn8S7WWCfqBCjvM4HuponhGeeu1wHdl5HFvGQYpttWz8AXj41qu17aTF/bPheSUwFayTG3je9cpzw5neNDLItX46adZWtzG0TTx X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:1; PCL:0; RULEID:(601004)(5005006)(1201001)(3002001); SRVR:BY1PR0301MB1255; BCL:1; PCL:0; RULEID:; SRVR:BY1PR0301MB1255; X-Forefront-PRVS: 066517B35B X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BY1PR0301MB1255; 23:85SEPoXaT++T8ubqjPxPaJDW+INfqTUdC0AQLP9?= =?us-ascii?Q?m8imA3l3sBLOAPktGkaNDtJU5VERGWVTw35H/jVbV0XwWVyxbzantP24tKaB?= =?us-ascii?Q?nv/JpEXHgTTg2L8lN8Cx0I0wnegtKCKdGTBKHhXCOMlEAODDGXiPsvtbQ/QX?= =?us-ascii?Q?3CoX2Onxp9FIpUi8eypTXmRyNgjl7FdMqmzG8+YaXHWoAqHsVAkcKYA5wt9S?= =?us-ascii?Q?MDSUzLMSqvyTfEOE8D239XRCNvkQDfX4ppROWWGmnliRU1f6i8A8CR6awm2n?= =?us-ascii?Q?TxXhsz5VmWAKj3zs4S0ANkq+lCd2c+8i7YkzR9G3XbcSB43tESaa0cJav44o?= =?us-ascii?Q?OQqBpcftqqPwlhc69+hVGybyVtzYl/VWq9eISMbDCm7LMCo4xpvJW89nWM0d?= =?us-ascii?Q?RDzUF39GR7FmmG8ndltu02PGNsIZgHJ4cwQBsTuQCt1MaEswtPWCbywgTSd7?= =?us-ascii?Q?4zZHdNUBE8Ru0o+nQXg08EajM3flTq8ZbF8I8xsbQSPb18WuxjrpytMttK4x?= =?us-ascii?Q?JeqqDL459qkhdNWXJ6UVKOBtVHagNqys8naENLurvZjgWhJyNg6tdbt+TUse?= =?us-ascii?Q?jOovSW3/YCRz9b2Nb2bH1eNpvTFhcnChBhI4M9yypQW6TrGB/QrqFc7TKvBw?= =?us-ascii?Q?OlEdBevMJfx/jP71g7iNuNHxBRGo39FdJNapMMksf+Sf9s/ydH8YC7CGCTNE?= =?us-ascii?Q?XF13qfX1iJdGyxiIOuTrqqKE2mLIBY7BFqWh+1GxVkOj03Tgmuq8qeTkq0Uo?= =?us-ascii?Q?inx/ohTBl5glr/9EfAI8EhDa78/7UEKpJz3TWrinvn36skkkhL/w/6Bj9nJv?= =?us-ascii?Q?ZPq3SXnbH0h+TUXP2mHh1r+yc0aXO21pizx3+uuJZUvIh951Vx7LKv02meSX?= =?us-ascii?Q?DvsHKS16nmcyD9ICA/4XFrOvaY4rT8qyNaVAXjQAgIOTgJaMpGb/MWDBK7Rn?= =?us-ascii?Q?rGcLySo4eRxm8VhSxij9V9Df3nPEXifjtF7TaNxP4X+j9inJhC0nQcltNuRp?= =?us-ascii?Q?S0WSFLggJ3cLOqn6pEY8htOH5QIlr2r9kQZDE1Tdpfw0yww+f4mt3Cp2zKhP?= =?us-ascii?Q?TLEYfcaVeUarKr+R7qME5Zl9dyZQ1VA2GUZvoDf4Yh4OAhvu1aa7W51I3s/b?= =?us-ascii?Q?Hfth/lbxWjoXE0e2vIJoBaZIQDgru1JcdTE5rwyzQ3uJnDlsee35is4fLyiz?= =?us-ascii?Q?lRvr7NXeHJ5qo8FCwDr/LE731uyOsi09wpxQ549btZHsQJ4lWAq0zaHwYyFH?= =?us-ascii?Q?1t7FLYHs0ODCg6bCvTKg=3D?= X-Microsoft-Exchange-Diagnostics: 1; BY1PR0301MB1255; 5:dQo3ny5PAU0GHllBsz0AqHoucDqO09/9QwPjdmFxjyRXS4FZdrgJ/gR+krj/DKbsm3P2UU0+MW7W7tfnhGlLoGAZrcF5/QH6KX/8cytfPeuyiofRtt1IpAKTK9Afd0F20uMzg/IbfVmTVUHrFHNE0A==; 24:Ct9dfqj71gzZ0bBfC5raH/uYqE/YnvR/E/Vhe+Hi2DFxnNVegIYpWwpNLEU+4kBfEfE9FkIguYH+y5g2XtqJUl2cf1pLHEwok8eaAPYXcUg=; 20:KNH9wcxcvmXXeiT8cQTveSLU0g6Gs7/vMk6erNVtblm1j3SIS5H+hLcuBY4t+XTn9SZt/xTODrnDMrO7zbEWTA== X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Aug 2015 11:23:40.4333 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.158.2]; Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY1PR0301MB1255 X-Microsoft-Exchange-Diagnostics: 1; BY1PR0301MB1207; 2:iIitK1EQ520rYQIuVDe6GwDZ7EfMklIITm+MVjI+B0dutqsr0cNJaOSQzK/Z2+aO9GWnVrWkuIfpvJg7yvX7keheRqHsm6Ngc1kwb4tBDCR1M/G1ZeZ7KPBYAhRdloLxi00yNRskOhCUpltHZmGp+/BYSgO2ptJtHhbz6zd0tCM=; 3:YXCWdFUTvrzgZbkxzQlZ2iohGyUY2hbxlcwqQdz79XCzVkzaBUHDCj+f/uos+7mpe4/ws96LQ9LqXvTHiHQC9ySpYNJjwguyD7VmVTxO0LlmQxjouZDIuxFXnnCKlJPZruJyKpmJSTVSzePUko2B8L/R7/80VeK0NYTbXb2F+CtbT5FNdLfbHaVefV13gtyYsKUpeVlF5y2DgqV87pvZn2O8m3ENYqHERszgI4/Z2xQ=; 25:vsYyK4s2yo/mME8PuqXq0Kcl9zwMPgsPCuzpmda105hsGx9H+SmFjjU8j4bk1HIGlymiQ6L2HnhISOAxZ9kPejG5UoaLLg6UDDrr3XguDd+Y5YitqmYOcko5yG40eXJTbueQE0xKsn379gZ/IiaDH2b+BFP16araXoWTUmY/bse/RpsvTx+rW6FvR9TfbpUJdVqw/wWqWwvh+JlWaJmGL5d5vrKMcPRDK0zI0hUC0PS3TE8ZXK+mNxf/HaR0MwJW; 23:GAMAXVlTu4vu4LpRf8FabSWbYGHDKJvV6Oid3I3c+nnzL3Ixor7MKGPRmv7wHGQKjILCY8rYnpffbQwhjJjEE5mMYPEQW4SlhDP38f6A/L7ZwU9W3BWaWXn57+rqn4+92jSCmccDZavbJLypUaEPyGPT3uP4E1mFatF1aXk30qTgyymqVQ2qKh0yBJR1CshmGZQYc3i1hZVdsmyESAsoGgXGsS0MOSM/PMLxr1DFXVam56SFTxnGRoQ2TFmaGN+Q X-OriginatorOrg: freescale.com Cc: alsa-devel@alsa-project.org, Zidan Wang , patches@opensource.wolfsonmicro.com, tiwai@suse.com, broonie@kernel.org Subject: [alsa-devel] [PATCH v5] ASoC: wm8960: update pll and clock setting function 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 Add sysclk auto mode. When it's sysclk auto mode, if the MCLK is available for clock configure, using MCLK to provide sysclk directly, otherwise, search a available pll out frequcncy and set pll. Configure clock in hw_params may cause problems when using bypass style paths without hw_params in machine driver getting called. So add configure clock to set_bias_level. Signed-off-by: Zidan Wang Acked-by: Charles Keepax --- sound/soc/codecs/wm8960.c | 220 +++++++++++++++++++++++++++++++++++++--------- sound/soc/codecs/wm8960.h | 1 + 2 files changed, 180 insertions(+), 41 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 1ed0720..e3b7d0c 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -48,6 +48,9 @@ #define WM8960_DISOP 0x40 #define WM8960_DRES_MASK 0x30 +static bool is_pll_freq_available(unsigned int source, unsigned int target); +static int wm8960_set_pll(struct snd_soc_codec *codec, + unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache * We can't read the WM8960 register space when we are @@ -126,9 +129,12 @@ struct wm8960_priv { struct snd_soc_dapm_widget *rout1; struct snd_soc_dapm_widget *out3; bool deemph; - int playback_fs; + int lrclk; int bclk; int sysclk; + int clk_id; + int freq_in; + bool is_stream_in_use[2]; struct wm8960_data pdata; }; @@ -164,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) if (wm8960->deemph) { best = 1; for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { - if (abs(deemph_settings[i] - wm8960->playback_fs) < - abs(deemph_settings[best] - wm8960->playback_fs)) + if (abs(deemph_settings[i] - wm8960->lrclk) < + abs(deemph_settings[best] - wm8960->lrclk)) best = i; } @@ -565,6 +571,9 @@ static struct { { 8000, 5 }, }; +/* -1 for reserved value */ +static const int sysclk_divs[] = { 1, -1, 2, -1 }; + /* Multiply 256 for internal 256 div */ static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; @@ -574,61 +583,110 @@ static const int bclk_divs[] = { 120, 160, 220, 240, 320, 320, 320 }; -static void wm8960_configure_clocking(struct snd_soc_codec *codec, - bool tx, int lrclk) +static int wm8960_configure_clocking(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + int sysclk, bclk, lrclk, freq_out, freq_in; u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); - u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); - u32 sysclk; - int i, j; + int i, j, k; if (!(iface1 & (1<<6))) { dev_dbg(codec->dev, "Codec is slave mode, no need to configure clock\n"); - return; + return 0; + } + + if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { + dev_err(codec->dev, "No MCLK configured\n"); + return -EINVAL; } - if (!wm8960->sysclk) { - dev_dbg(codec->dev, "No SYSCLK configured\n"); - return; + freq_in = wm8960->freq_in; + bclk = wm8960->bclk; + lrclk = wm8960->lrclk; + /* + * If it's sysclk auto mode, check if the MCLK can provide sysclk or + * not. If MCLK can provide sysclk, using MCLK to provide sysclk + * directly. Otherwise, auto select a available pll out frequency + * and set PLL. + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { + /* disable the PLL and using MCLK to provide sysclk */ + wm8960_set_pll(codec, 0, 0); + freq_out = freq_in; + } else if (wm8960->sysclk) { + freq_out = wm8960->sysclk; + } else { + dev_err(codec->dev, "No SYSCLK configured\n"); + return -EINVAL; } - if (!wm8960->bclk || !lrclk) { - dev_dbg(codec->dev, "No audio clocks configured\n"); - return; + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk = freq_out / sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + if (sysclk == dac_divs[j] * lrclk) { + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) + if (sysclk == bclk * bclk_divs[k] / 10) + break; + if (k != ARRAY_SIZE(bclk_divs)) + break; + } + } + if (j != ARRAY_SIZE(dac_divs)) + break; } - for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { - if (wm8960->sysclk == lrclk * dac_divs[i]) { - for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { - sysclk = wm8960->bclk * bclk_divs[j] / 10; - if (wm8960->sysclk == sysclk) + if (i != ARRAY_SIZE(sysclk_divs)) { + goto configure_clock; + } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; + } + /* get a available pll out frequency and set pll */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + sysclk = lrclk * dac_divs[j]; + freq_out = sysclk * sysclk_divs[i]; + + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { + if (sysclk == bclk * bclk_divs[k] / 10 && + is_pll_freq_available(freq_in, freq_out)) { + wm8960_set_pll(codec, + freq_in, freq_out); break; + } else { + continue; + } } - if(j != ARRAY_SIZE(bclk_divs)) + if (k != ARRAY_SIZE(bclk_divs)) break; } + if (j != ARRAY_SIZE(dac_divs)) + break; } - if (i == ARRAY_SIZE(dac_divs)) { - dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); - return; + if (i == ARRAY_SIZE(sysclk_divs)) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; } - /* - * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC - * pin is used as a frame clock for ADCs and DACs. - */ - if (iface2 & (1<<6)) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); - else if (tx) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); - else if (!tx) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); +configure_clock: + /* configure sysclk clock */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); + + /* configure frame clock */ + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ - snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); + snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); + + return 0; } static int wm8960_hw_params(struct snd_pcm_substream *substream, @@ -667,9 +725,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - wm8960->playback_fs = params_rate(params); + if (tx) { wm8960_set_deemph(codec); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) @@ -682,7 +740,23 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); - wm8960_configure_clocking(codec, tx, params_rate(params)); + wm8960->is_stream_in_use[tx] = true; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON && + !wm8960->is_stream_in_use[!tx]) + return wm8960_configure_clocking(codec); + + return 0; +} + +static int wm8960_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + wm8960->is_stream_in_use[tx] = false; return 0; } @@ -702,6 +776,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int ret; switch (level) { @@ -721,11 +796,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, } } + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + /* Set VMID to 2x50k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(codec, 0, 0); + if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); break; @@ -780,6 +866,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int reg, ret; switch (level) { @@ -831,9 +918,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, return ret; } } + + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + break; case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(codec, 0, 0); + if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); @@ -892,6 +991,28 @@ struct _pll_div { u32 k:24; }; +static bool is_pll_freq_available(unsigned int source, unsigned int target) +{ + unsigned int Ndiv; + + if (source == 0 || target == 0) + return false; + + /* Scale up target to PLL operating frequency */ + target *= 4; + Ndiv = target / source; + + if (Ndiv < 6) { + source >>= 1; + Ndiv = target / source; + } + + if ((Ndiv < 6) || (Ndiv > 12)) + return false; + + return true; +} + /* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10) @@ -943,10 +1064,9 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } -static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - int source, unsigned int freq_in, unsigned int freq_out) +static int wm8960_set_pll(struct snd_soc_codec *codec, + unsigned int freq_in, unsigned int freq_out) { - struct snd_soc_codec *codec = codec_dai->codec; u16 reg; static struct _pll_div pll_div; int ret; @@ -986,6 +1106,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } +static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + wm8960->freq_in = freq_in; + + if (pll_id == WM8960_SYSCLK_AUTO) + return 0; + + return wm8960_set_pll(codec, freq_in, freq_out); +} + static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { @@ -1043,11 +1177,14 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; + case WM8960_SYSCLK_AUTO: + break; default: return -EINVAL; } wm8960->sysclk = freq; + wm8960->clk_id = clk_id; return 0; } @@ -1060,6 +1197,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, + .hw_free = wm8960_hw_free, .digital_mute = wm8960_mute, .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h index 2d8163d..ab3220d 100644 --- a/sound/soc/codecs/wm8960.h +++ b/sound/soc/codecs/wm8960.h @@ -82,6 +82,7 @@ #define WM8960_SYSCLK_MCLK (0 << 0) #define WM8960_SYSCLK_PLL (1 << 0) +#define WM8960_SYSCLK_AUTO (2 << 0) #define WM8960_DAC_DIV_1 (0 << 3) #define WM8960_DAC_DIV_1_5 (1 << 3)