Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752807AbbELHKp (ORCPT ); Tue, 12 May 2015 03:10:45 -0400 Received: from mail-by2on0127.outbound.protection.outlook.com ([207.46.100.127]:25139 "EHLO na01-by2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752515AbbELHKn (ORCPT ); Tue, 12 May 2015 03:10:43 -0400 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; From: Zidan Wang To: CC: , , , , , , , Zidan Wang Subject: [alsa-devel][PATCH 1/4] ASoC: wm8960: Let wm8960 driver configure its bit clock and frame clock Date: Tue, 12 May 2015 14:58:08 +0800 Message-ID: X-Mailer: git-send-email 1.9.1 X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BN1BFFO11FD005;1:QUizeYCuAJVtGdHk5kM3YVD+j7sGqmyFClNB6URmpojGsp/5zIBJYFUNXI1R9yOsxMis0/X7/REeh3uuUwFhUIzHxcLlchKKw4oWOVvjr9MTKoMudmGODtx6tYo1JEjWKE4xY0i5KuEtshHt0otdfHELXmpq8xRmriV5EjZFqsgPfZf2CDtNZ5FuqLS1cul/Kghaim86bSrecJkVJby7kRA9aLPgewmOFSJ17Sop/KrhSNheL/3sVih9PSiYcS1bKTdcet8u3wPv3FWnsCa82IrwtN4Blt8BTsPxFup8ohMNWskyVuExFH1EaEiA6DTRs2Bx9qi0oLx93tspKV7Egw== X-Forefront-Antispam-Report: CIP:192.88.158.2;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(339900001)(189002)(199003)(50226001)(36756003)(62966003)(6806004)(77096005)(105606002)(2351001)(87936001)(5001960100002)(107886002)(50466002)(19580395003)(110136002)(46102003)(48376002)(85426001)(50986999)(189998001)(106466001)(229853001)(118296001)(77156002)(33646002)(86362001)(47776003)(104016003)(92566002)(19580405001)(4001430100001);DIR:OUT;SFP:1102;SCL:1;SRVR:DM2PR0301MB1262;H:az84smr01.freescale.net;FPR:;SPF:Fail;MLV:sfv;MX:1;A:1;LANG:en; MIME-Version: 1.0 Content-Type: text/plain X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DM2PR0301MB1262; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(3002001);SRVR:DM2PR0301MB1262;BCL:0;PCL:0;RULEID:;SRVR:DM2PR0301MB1262; X-Forefront-PRVS: 0574D4712B X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 May 2015 06:56:28.9237 (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: DM2PR0301MB1262 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4766 Lines: 167 wm8960 codec driver missing configure its bit clock and frame clock for codec master mode, so add support for it. It will calculate a appropriate frequency dividing ratio according to the system clock, bit clock and frame clock, then set the corresponding registers. Signed-off-by: Zidan Wang --- sound/soc/codecs/wm8960.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 6fa832b..b072501 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -127,6 +127,8 @@ struct wm8960_priv { struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; + int bclk; + int sysclk; struct wm8960_data pdata; }; @@ -563,6 +565,72 @@ static struct { { 8000, 5 }, }; +/* Multiply 256 for internal 256 div */ +static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; + +/* Multiply 10 to eliminate decimials */ +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, + 120, 160, 220, 240, 320, 320, 320 +}; + +static void wm8960_configure_clocking(struct snd_soc_codec *codec, + bool tx, int lrclk) +{ + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); + u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); + u32 sysclk; + int i, j; + + if (!(iface1 & (1<<6))) { + dev_dbg(codec->dev, + "Codec is slave mode, no need to configure clock\n"); + return; + } + + if (!wm8960->sysclk) { + dev_dbg(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!wm8960->bclk || !lrclk) { + dev_dbg(codec->dev, "No audio clocks configured\n"); + return; + } + + 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) + break; + } + if(j != ARRAY_SIZE(bclk_divs)) + break; + } + } + + if (i == ARRAY_SIZE(dac_divs)) { + dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); + return; + } + + /* + * 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 bit clock */ + snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); +} + static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -570,8 +638,13 @@ 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: @@ -602,6 +675,9 @@ 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; } @@ -946,6 +1022,30 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec, return wm8960->set_bias_level(codec, level); } +static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case WM8960_SYSCLK_MCLK: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_MCLK); + break; + case WM8960_SYSCLK_PLL: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_PLL); + break; + default: + return -EINVAL; + } + + wm8960->sysclk = freq; + + return 0; +} + #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 #define WM8960_FORMATS \ @@ -958,6 +1058,7 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = { .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, .set_pll = wm8960_set_dai_pll, + .set_sysclk = wm8960_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8960_dai = { -- 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/