From patchwork Mon Jul 13 08:41:00 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zidan Wang X-Patchwork-Id: 6776001 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 EEAA4C05AC for ; Mon, 13 Jul 2015 08:39:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9E748203E6 for ; Mon, 13 Jul 2015 08:39:53 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id E0D862063C for ; Mon, 13 Jul 2015 08:39:51 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 18E32261606; Mon, 13 Jul 2015 10:39:51 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Status: No, score=-1.9 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 5C135261519; Mon, 13 Jul 2015 10:39:43 +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 D9BD626151C; Mon, 13 Jul 2015 10:39:41 +0200 (CEST) Received: from na01-bl2-obe.outbound.protection.outlook.com (mail-bl2on0130.outbound.protection.outlook.com [65.55.169.130]) by alsa0.perex.cz (Postfix) with ESMTP id 578DD261519 for ; Mon, 13 Jul 2015 10:39:34 +0200 (CEST) Received: from BY2PR03CA043.namprd03.prod.outlook.com (10.141.249.16) by BN3PR0301MB1252.namprd03.prod.outlook.com (10.161.207.28) with Microsoft SMTP Server (TLS) id 15.1.207.19; Mon, 13 Jul 2015 08:39:32 +0000 Received: from BL2FFO11FD006.protection.gbl (2a01:111:f400:7c09::150) by BY2PR03CA043.outlook.office365.com (2a01:111:e400:2c5d::16) with Microsoft SMTP Server (TLS) id 15.1.213.14 via Frontend Transport; Mon, 13 Jul 2015 08:39:31 +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 BL2FFO11FD006.mail.protection.outlook.com (10.173.161.2) with Microsoft SMTP Server (TLS) id 15.1.213.8 via Frontend Transport; Mon, 13 Jul 2015 08:39:30 +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 t6D8dQ0q029013; Mon, 13 Jul 2015 01:39:27 -0700 From: Zidan Wang To: Date: Mon, 13 Jul 2015 16:41:00 +0800 Message-ID: X-Mailer: git-send-email 1.9.1 X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1; BL2FFO11FD006; 1:90uznQPHGFuwTWrpaowCBaxDLUzn+MX+IcE7X2h3ai+g+dA5bvOBdtUyN0o/7HfbbOpMOG18uLTB7hGcnzHmeQ4Laa6btbeKrpN7YTkwn0m5nVFlcrHhMarWIGco1mIMgN+EbKldVf8MPKaZu3mxXo5mzRuVX/+F6bJVbHEPhJXRslz+VPoQJkwX4HZoAYlJMgWGJsccoGNR6ypZad2AFZv5WO5R48wIC0CokFhH8fLkdYfzPFduKudETc6qOmJtIE7R+gAxgzWnRWC4MJj0XMlth4Fi+bG6Zt409xodCIx/QeYRGOCLt/vFZ56Gii/va6fMoP0oX0HhPNh9XFI0dwrcKuSCkxl0UW1j9ZKTpHnfTVsiph+26H8PVsDwEwx4wncdXQU5HFXXg51NOrTz633T8h0SV9LGPYgyjRhbxZZvjjapph4qXVHNlpnbp4tFx3vaS2KN8T1gLkrxXYdt9g== X-Forefront-Antispam-Report: CIP:192.88.158.2; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(2980300002)(1060300003)(339900001)(189002)(199003)(77096005)(50986999)(87936001)(86362001)(19580405001)(19580395003)(105606002)(85426001)(48376002)(50466002)(6806004)(77156002)(62966003)(50226001)(46102003)(33646002)(110136002)(92566002)(5001960100002)(107886002)(229853001)(104016003)(110436001)(36756003)(4290100001)(2351001)(118296001)(5003940100001)(47776003)(4001430100001); DIR:OUT; SFP:1102; SCL:1; SRVR:BN3PR0301MB1252; H:az84smr01.freescale.net; FPR:; SPF:Fail; MLV:sfv; MX:1; A:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Exchange-Diagnostics: 1; BN3PR0301MB1252; 2:zZ6yV+eBaxANGU+Xb0uQAcPF9hZ6T0P6xE+D73ZIrsonArVP7qK9fZuboAq7CUdd; 3:Pq43ZVBQ1kD057UgKTmw4wCQjDN8tI5JrZqDW1rY7bB3FkFuuyztX2USePSZzzQYXNKd78a/nJYF9J9YIdDHs8t38dW3tB2dNGiAiArUXJ2s7yMgQt7wLeX+DooL7DZ8iHNWEBgJOfyGGP2tpWf+ikjW/fHBcRG23NyRPQvb6ZZ5APHo9ijOCcafyeffKM0VLXEDOiHiVqdCjJA2hB1k4FDYzbryZyoNuYjj1IV/axU=; 25:bfaoTNqPyAC29QARu5pmLYSX2pj34afxe0bFe9fNjb6vsVm0Jrnbhm83YMndEnSq5zrFehGOllsrD15ErkZLpJx/El/6CCnzyeh53gfJIcOgy/4VYzTBhwQrz20stHnPWnLIwmTC/YzJWWA/a3cx4KSZW7dowO+6jJNZ8C8YH+5KgYoNDpmyWA2c+HNzQ+NKd0VKZmV0YnqkyHgIPiHTdTd7WRDRVF7OFw4BB8VyGfXAcv1BHhJ8AfVrU4NB6c7o; 20:qqTgLmSoTb3RHXKbVzuzYKhYqsQZYlRRBr5RwbW1dgT1ZaiGLHJ9aKYac+U1JZnGBlexUlGpVtbmI2F9/hCJJ4CNlfGYa9C5pRLIg5x+xY7hBAmTtnyRPHaVDEp3fGcUo/oiyZapIaLJiGWxrykO8cADdQkc1FilHJzaKSSHUfekA0I6yDOm/UXhZxGL1uLx/KPXFNdUhgPcJIN8Os2Z2QnoDmIjsJhH22dab6oHvBKRJ8adwJSgLGtOrX9OfAIzZV7n4tLKU3PjEHuudMz90ptmKM2JNwzQHc3pIj+KfKL5+4xys2wMJ+94sGcvUz+f9YQS/7K6UOSS+eA6v4rdf0OG5UuwzVfCloGMT3wQ18c= X-Microsoft-Antispam: UriScan:;BCL:1;PCL:0;RULEID:;SRVR:BN3PR0301MB1252; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:1; PCL:0; RULEID:(601004)(1201001)(5005006)(3002001); SRVR:BN3PR0301MB1252; BCL:1; PCL:0; RULEID:; SRVR:BN3PR0301MB1252; X-Microsoft-Exchange-Diagnostics: 1; BN3PR0301MB1252; 4:wedsgOJC5qhzFYx7IPDYwAC0wRNw2Pji4rCYhs+XLLPfR2BetHvQY5SqanKTFniww9gv89IkasMcVkFVMIvkq0cbx7TuL3SI6JNnqi8pdsuY+gq0Muy6Xlccjtc6LhKTKYIjnsLvUlylD4LU32APUdpWUPOC+8En19WgTCcXp3c5tHc17VrPGOu6xRPeWCFqAsLBld9jgg0MVVWbQqvW+G4jKamjCbJaFDNFnwGUG3BbkRUvMcS9BZapde13bEs6V5LhNHo3gmIF7BZIAX3VoPGKzJpD+ehHjnpj1uXN4JjTKAM0RqfYwWq0noS+vU0h X-Forefront-PRVS: 0636271852 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BN3PR0301MB1252; 23:qTId8NP7msGX/DszwXgCsUYtYAmg1cgqYV5/Ow0?= =?us-ascii?Q?hP49cIrcnHQJY87k5DoDfKZO0MRGGiv8oQER5+V1sXsoVV7TsWwzYCNEEzY5?= =?us-ascii?Q?MQDEP2JIbdQPknh+kx2XI1JY+1MS6lSWtSZVO4OiR3wyq/HLTpdRZlE45fuA?= =?us-ascii?Q?X7m/kRroL4Uqctwd3ikH3t6EQJj33r3caGzouyLSLrfLgPqQmw3MzSZmjzKK?= =?us-ascii?Q?aN0nSZzLHAasE2eg8xEInpFp7mfHgI4gEY9KnLBsNZzRKnuFz6PFr9qBd836?= =?us-ascii?Q?bMsaGtEI+1SFOkxnlYyV2yS9CE/tcO+TkdwcCvvUrW7N0hHrG8xQiJy4sVof?= =?us-ascii?Q?r3nPJfkEOZw3Lgt3M8M7yreuGpIDKcv65bEm+WFU0EPTku2WnUu4XFM9S2iF?= =?us-ascii?Q?j6Dz3cNJMWGbxWZWvcBiNuZ4R/wSOE+VLY1Xi3EbKuw7W+PyUUv6Qs3g6lA6?= =?us-ascii?Q?2tIOqXo2B2l35VPY8aP6vV9XLNBhLUgBJjuUSRE8Mn1obGccKL2WkH9mSs9M?= =?us-ascii?Q?pFrevmxpjb8OfZn/KCTilnasveP9KhXGkBPU6jD9/IqKnBHnmPcquBERMAwT?= =?us-ascii?Q?u8Lzf4UfW0SNJ/CGEY6LqqN7xVVYVGrbso4RBSc3sDKMY7luBMmzk8c8E2J/?= =?us-ascii?Q?eFAjAlOowJaLcxq1UFpwCzGkqvXTov4LJ1THt0g6nExou08Ckx5aC5hH0U89?= =?us-ascii?Q?+6zN83/VRwouGMgYopRSgu7n6wO1St5KRaZ03ipeOoel3o3n4zrym5eBENet?= =?us-ascii?Q?tarRRVesmQVh68olGnWpCfjZQMZOc0EWIcgwT/7ga1x9B8FTJFCvG/4DBwi9?= =?us-ascii?Q?RIZLV1qWZtBe3+japTaBymFxK9So6vwFr4t4/Xz8CHDpSGLu3LadPJWeESGw?= =?us-ascii?Q?iZM/4EKBlLvG8pXCDR7cBH1Azq8SnvfXbUhL2NKspl8pgTN/KFkwNzPxroSV?= =?us-ascii?Q?7AVzH+g+hGkvfpeCZvgpxNlDGaYuIAGuqjPCxE2wTaQ=3D=3D?= X-Microsoft-Exchange-Diagnostics: 1; BN3PR0301MB1252; 5:IOLFWOrt/oUuymhbphhLgvnNHhycwX6ZLWLoVHDwLjDLpHSQR+m72z5OAR0Ooli9RXQUpxt0LjJ9tFu2I8p7mzyJAYaeughGT1F1+Y1+j+ShUhDP9s3MiDNY9grlRq/yi9iHQg8onA4a+hvvUCicMQ==; 24:IDmgNID4ng4ROkaERE8tbi5IqAuO/5DEWJlmC6bs/eq8irsC/5MbLIEGNYG7JhDNoYUB9YdEDZ1T4DwR3vCteq8FFpaVqWBdb8vQHAyG6Eo=; 20:L+jxQgX0c620r+KluKWDIvDQFNOWv/HlOgOr9G3RJACV7nJcGgedGecewnrH46yAMijyj+HoI2ijjDE1ACoFUA== X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Jul 2015 08:39:30.1077 (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: BN3PR0301MB1252 Cc: alsa-devel@alsa-project.org, Zidan Wang , tiwai@suse.de, patches@opensource.wolfsonmicro.com, broonie@kernel.org Subject: [alsa-devel] [PATCH v3] 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 --- sound/soc/codecs/wm8960.c | 217 +++++++++++++++++++++++++++++++++++++--------- sound/soc/codecs/wm8960.h | 1 + 2 files changed, 177 insertions(+), 41 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 94c5c46..e9f8e41 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,109 @@ 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 +724,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 +739,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 (codec->dapm.bias_level == 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 +775,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) { @@ -711,6 +785,9 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { @@ -726,6 +803,13 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, 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 +864,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) { @@ -822,6 +907,10 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, msleep(100); + ret = wm8960_configure_clocking(codec); + if (ret) + return ret; + if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { @@ -834,6 +923,13 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, 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 +988,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 +1061,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 +1103,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 +1174,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 +1194,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)