Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752148AbbFZLHz (ORCPT ); Fri, 26 Jun 2015 07:07:55 -0400 Received: from mail-bl2on0112.outbound.protection.outlook.com ([65.55.169.112]:55552 "EHLO na01-bl2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751682AbbFZLHs (ORCPT ); Fri, 26 Jun 2015 07:07:48 -0400 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; From: Zidan Wang To: CC: , , , , , , , Zidan Wang Subject: [alsa-devel][PATCH] ASoC: wm8960: update pll and clock setting function Date: Fri, 26 Jun 2015 19:09:22 +0800 Message-ID: <5630bd343217e8fa895c5d133497f50739417453.1435316484.git.zidan.wang@freescale.com> X-Mailer: git-send-email 1.9.1 X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11FD030;1:mX11biqgt4O1iYN63Kvy9SvauNV1l2/2L+aiETAkgtFny5nfRnrx0osu7vn89CP2qckJYk3VVEQmFlLx+fE2Zq/4oAveqnVJaAoKm+hHdBxPKg4ZAxRadDPM7JKffDJ6NixC7pIMLgJmJ7lzwIBw5CwWkNjKNwI1ENPq0u5/fiva36s53T9IOJyynA2HNFUAds4aPfqsA4amHax/3iAUoqkg0UugtpMfAFDr9pxJ0kSCMOupXqL8OME0zxnGV7JSq4IY9H3BE9dT6HP3gK5+vgm7OpTIl6eB2Z5nEe2wJZov2Leb84i+nfsv0DhD3/pQuqWD2ixba7HuSBTuXZpgMLmbyDSNomcU7L6PwN17uYdAltSWytnPS9UNveXFWc8S X-Forefront-Antispam-Report: CIP:192.88.168.50;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(2980300002)(1060300003)(339900001)(199003)(189002)(118296001)(46102003)(50226001)(87936001)(5001960100002)(107886002)(110136002)(86362001)(19580405001)(19580395003)(77156002)(47776003)(106466001)(62966003)(36756003)(77096005)(50986999)(229853001)(110436001)(50466002)(2351001)(4290100001)(85426001)(6806004)(48376002)(104016003)(92566002)(33646002)(4001430100001);DIR:OUT;SFP:1102;SCL:1;SRVR:BN3PR0301MB1249;H:tx30smr01.am.freescale.net;FPR:;SPF:Fail;MLV:sfv;A:1;MX:1;LANG:en; MIME-Version: 1.0 Content-Type: text/plain X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1249;2:iDdfNZQE85IIelTc32lKMlQCrg7cG2zGACQZ4Yu+eAuw3myimIP9i/pt415NX+w3;3:uv9dy9VsQHbaFreLdpF/C8S9FsR2ITxWb4g352eCuwcahSC+nDiYt+JVcjGyBq82u8bkG4TpM6ymMpqK3myWROCauBzWCTlioICO9XCEcVVWGGvCtf4TGERHSbTiCBsAmVy4EaKOBq+YTLHV85xhvyV8991QMX84/IJGPW82k/o2KyAa4R1u+5+PGdef3mfEbEuRF/YNTXgDoXpKOdG41pZX6Id6ADdFiC4pDaAP/tI=;20:jcg+LidzJibUdXmSWn/NOPdfc81u0Tc5jqGv9+lDo1tIYoBviZ+e8FMXh/b6fhZHNmV26Q/RHvbbNrj/FDm6nCzckj39isbEC8fZru2SiL9YGp4uyGBFqzCFU3PuIRjWV3P+dJ2NrSmohkE+rIMeS30kThfnsHyZLmWtbI3vyXNspFE3EzDJcOR/Ul2Aw19AAIhykrDDdeamknH2ST4qi4KC0dZNZwQTRmiEjmJcmMtLugiodCK6fwrVLkDuqn34VG+5eL774P8fMO5HoZEUddxEBoBEF7a2M18aoN0SFuJgx63BXMmmi5SxTd+gOhE1v7RVc1UCHUjM+QdYpVUuJaq8t9tFIQZ6XQShS6NzU84= X-Microsoft-Antispam: UriScan:;BCL:1;PCL:0;RULEID:;SRVR:BN3PR0301MB1249; 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:BN3PR0301MB1249;BCL:1;PCL:0;RULEID:;SRVR:BN3PR0301MB1249; X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1249;4:7BBPQSzUkhr3ytaO048d+4BS/ayFxend9hvAW7NgYl4UvCYEgeMLmuBS+19dCkB65mAvsi0Ji7qdKI1QFrNyhUNKI7xzzMRQ8Cpu9AlidLGJ51/T6u/cWo6qgd5L72XlkHgcGHFeGyiXly7yLeKQPKNffpgy6Du8iAYfCeGZ99CUQlSHvkqFyZt6mmsZ1PAN9iRvxMUgY3lvdUy3IO0IlKyQOnmVVH1RrBgKXc8Y0PefgICGuFoaTvWgCNtGuUqOFlyIrc3acgaklRr4ztARNYwikV6nSQWwOoMiziuk/tNQ3qMTzxFSZrWHhdpgKwge X-Forefront-PRVS: 0619D53754 X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1249;23:JwznIiPF/S18HrvNxeSgL4pep1E3KtzorEVAVmPNz8qF/FSPX8fVvlg7jsRpcMU4YHsyMx/gI0lwZ7kaL0mspZb1vGFule+zlbN26RPLvZO/LIiKW0CeyveDr9kTxBgMGOBaGYXuVSAt0fSCOTK1Z6Pm8zI2Z+3ZH51ajMuOY+BgfXfOHcdZviqt5tsqi1xKfYetmniN7xSh6o14mxkOOvCs6CjbAfnzX/6VmHJUNVx/UGQMWGp+WPSOzk5zYvcUqhauDG63lj4di8dF5QGMg2qU7N4TJMAGKGBbZHtc1uPj21D4Drwq0R0c9vGMqwL4eNM2Hs8R+sw5a6RnHTE1zv7r9hbFCNCnWXBN2m65P1DW+MjW/F6sZM5GAa/zFU8Yxsv2l9tYLEm3VVHuHql2+K9V9orJhpGX/gIoDcdJdK2vbUSQp/q0SYAij2Lm4iLDqWHQjJNwEUcdu15mGzjduFPsRdur7k9/T85u7n6TWynci1PxqzCCpD6P0Yc6YGlOkBC4R9HHpJXGlxMgJga/LVNAPwid5MDGFWDU4o9wvzqFnSq5kSQfW2dbR+XmT1QGR7v3yoGEsQbj+AW7Hvs6lz1xFhZWhvlNTxPluytLV1hdAGtOohVaOTxLV9Qaz+EqZKKxx9echMzG8w0QP6YGeNyMyfXZFbrD2k1zvYKJO0hVJkmZLwns33nOM0AjoAmQxEMM51w9ffyk9C+mY8lBTAr2q70w+E9IMuHBy2Kx17XH1J6Gn4MVnj+AdK71BzEmkZIa+yxQKmmp0YncTPOtMtg+Z19rHW0KqPwH2p0poaeEK+BAcmr/ZOniSwIGQdCUoCWoDc3XtwxyQYxR8GeOh0JMOsvU57u5y1OuGH4dqCrZQURrj26MO8lxoVdJGcGrD+ioG1tTDW6irGpResEl0XFRGBKY/lNf/BqoYzH3/bs= X-Microsoft-Exchange-Diagnostics: 1;BN3PR0301MB1249;5:K2VMJTOcryIH515zpeqOkRsFPqIoE+7nS6nWZcLd2IKHxXs6NnvLU4n/9kUAp/jmPQOoqeqeVXE+M8qhFbGXqhQiJcHcarr3JUtgCOahxocisca8XS2na4MT7kbq7fLyuDZpRWM7CaDwRuTTblmFjQ==;24:ofx9a8zIH9q2vdsHFHXAhE6TQJBjEI9vewCH3wMzZO4H8KiDNn+lyMbWGiWd9acgpI1YADV4lKZqSl6r9Bj46twZtXR3A3aCCfh1cuvTHhU=;20:KWaK63KvVn4me5GTcGTNGyZyHAQqYbYsqbY0Q4iyyu53G5IuQTwgcaCvJK3hBnLTJIZpq5m2ToAxjfSv6tqzwg== X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Jun 2015 11:07:45.0692 (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.168.50];Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN3PR0301MB1249 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8877 Lines: 297 When using snd_soc_dai_set_pll to set pll in machine driver, we should set pll in and pll out freq and ensure 5 < PLLN < 13, otherwise set pll will be failed. In order to support more formats and sample rates for a certain MCLK, if snd_soc_dai_set_pll failed, it will calculate a available pll out freq and set the pll again. Signed-off-by: Zidan Wang --- sound/soc/codecs/wm8960.c | 160 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 34 deletions(-) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 94c5c46..9b17ca7 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_dai *codec_dai, + unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache * We can't read the WM8960 register space when we are @@ -127,8 +130,9 @@ struct wm8960_priv { struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; - int bclk; + int freq_in; int sysclk; + int clk_id; struct wm8960_data pdata; }; @@ -565,6 +569,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 +581,119 @@ 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_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { + struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + unsigned int sample_rate = params_rate(params); + unsigned int channels = params_channels(params); + unsigned int sysclk, bclk, pll_out, freq_in; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 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->sysclk) { dev_dbg(codec->dev, "No SYSCLK configured\n"); - return; + return -EINVAL; } - if (!wm8960->bclk || !lrclk) { - dev_dbg(codec->dev, "No audio clocks configured\n"); - return; + bclk = snd_soc_params_to_bclk(params); + if (channels == 1) + bclk *= 2; + + sysclk = wm8960->sysclk; + + if (wm8960->clk_id == WM8960_SYSCLK_PLL) { + if (!wm8960->freq_in) { + dev_dbg(codec->dev, "No PLL input clock configured\n"); + return -EINVAL; + } + + pll_out = sysclk; + /* + * If the PLL input and output frequency are not available for + * wm8960 PLL, try to calculte a available pll out frequency and + * set pll again. + */ + if (!is_pll_freq_available(wm8960->freq_in, pll_out)) + goto get_pll_freq; } - 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) + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk /= sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) + if (sysclk == dac_divs[j] * sample_rate) + break; + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) + if (sysclk == bclk * bclk_divs[k] / 10) + break; + if (j != ARRAY_SIZE(dac_divs) && k != ARRAY_SIZE(bclk_divs)) + break; + } + if (i != ARRAY_SIZE(sysclk_divs)) + goto configure_clock; + +get_pll_freq: + freq_in = wm8960->freq_in; + /* + * If the pll out frequcncy set from machine driver is not available, + * try to find a pll out frequcncy 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 = sample_rate * dac_divs[j]; + pll_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, pll_out)) { + wm8960_set_pll(dai, freq_in, pll_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_clock: + snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); /* * 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); + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); else if (tx) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); else if (!tx) - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); + 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, @@ -638,13 +703,8 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; - wm8960->bclk = snd_soc_params_to_bclk(params); - if (params_channels(params) == 1) - wm8960->bclk *= 2; - /* bit size */ switch (params_width(params)) { case 16: @@ -682,9 +742,7 @@ 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)); - - return 0; + return wm8960_configure_clocking(substream, params, dai); } static int wm8960_mute(struct snd_soc_dai *dai, int mute) @@ -892,6 +950,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) @@ -916,7 +996,7 @@ static int pll_factors(unsigned int source, unsigned int target, pll_div->pre_div = 0; if ((Ndiv < 6) || (Ndiv > 12)) { - pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); + pr_debug("WM8960 PLL: Unsupported N=%d\n", Ndiv); return -EINVAL; } @@ -943,8 +1023,8 @@ 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_dai *codec_dai, + unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; @@ -986,6 +1066,17 @@ 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; + + return wm8960_set_pll(codec_dai, freq_in, freq_out); +} + static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { @@ -1048,6 +1139,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, } wm8960->sysclk = freq; + wm8960->clk_id = clk_id; return 0; } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/