Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp6821375imu; Mon, 3 Dec 2018 03:27:37 -0800 (PST) X-Google-Smtp-Source: AFSGD/UsuWnWH0cq1vXF8HoAImaN74yyUGEJEiqZdfdIcZf9nZfy/ldw/16rIsltVLcwJ3fT8RsM X-Received: by 2002:a63:b94c:: with SMTP id v12mr12879273pgo.221.1543836457668; Mon, 03 Dec 2018 03:27:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543836457; cv=none; d=google.com; s=arc-20160816; b=WcJQ/Y0NEaznsrd/P0OHycgQgCvlxU/aWVuDXiyjRY+zU5zJsg1Ns/7xYzF72ZwlEw Y3V2GEV6NWEpS2oEFKxQy5BjVpJZdZuvNoV1CbgfUtmIo9xfu9VTGhbJgJ2OH7cFx3rZ PRy8mS7+fGqrbKqA32MejOZmYPrfz6QNCjSE/CQK8hvJzdO6EWOqG6Nsu9hurjKmZdI6 EX86JQdBlSl8ZU2qrJxsiwnrG2j9JG2Dp34WsX4pZxiZzxeOhApWK3zGNpUHIDJ1Q2kk KpLVwhBjbke8yHygEG87Z0XMsA3lKJJmbt9vVhq1mRzbGuuWLedhhZSnGnuU8Nqap00E DGAA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=4Hv/u7o6KMleFJ2pHm2GZvj4dOPH/1RYDLqroKwfTEE=; b=0fHLTqF0PcB2xpMXFcw/V6vuPGQIKdsUWho4wLnMg4S7+EUih7GU7TnS2HwywfMvgT Ssi5yPAQfD/A+zbdWht1AEuhaV10yTdpzp/yLZRITdESOmyzpWxSEuTeXHCjPhn92QsM anciVBEt1mcHdqNFuwlcKYjrUVLOzXq6XcreyKvhBYjoypQiPkLQ0/zLKT2u8i5EWvhL XrVENJArv4PZB6IS+S0CwGLya5hYLMKyd/Ct/u2no5kDC3LFo8zKKNgEVd82FZHrjdV0 9btlwqjwtUZLfgDF21Sx4Ik6DpfkUpSjaNy4euxFcEVFcb4Sljs9NeumVRUAE8++ADsA 2lTw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f1si14084796pld.92.2018.12.03.03.27.22; Mon, 03 Dec 2018 03:27:37 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726328AbeLCLZo (ORCPT + 99 others); Mon, 3 Dec 2018 06:25:44 -0500 Received: from relay1.mentorg.com ([192.94.38.131]:40887 "EHLO relay1.mentorg.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725883AbeLCLZo (ORCPT ); Mon, 3 Dec 2018 06:25:44 -0500 Received: from svr-orw-mbx-03.mgc.mentorg.com ([147.34.90.203]) by relay1.mentorg.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-SHA384:256) id 1gTmKo-0004kd-DO from Jiada_Wang@mentor.com ; Mon, 03 Dec 2018 03:24:18 -0800 Received: from jiwang-OptiPlex-980.tokyo.mentorg.com (147.34.91.1) by svr-orw-mbx-03.mgc.mentorg.com (147.34.90.203) with Microsoft SMTP Server (TLS) id 15.0.1320.4; Mon, 3 Dec 2018 03:24:15 -0800 From: To: , , , , CC: , , Subject: [PATCH linux-next v2 6/6] ASoC: rsnd: add avb clocks Date: Mon, 3 Dec 2018 20:24:27 +0900 Message-ID: <20181203112427.18324-1-jiada_wang@mentor.com> X-Mailer: git-send-email 2.17.0 MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit X-ClientProxiedBy: SVR-ORW-MBX-09.mgc.mentorg.com (147.34.90.209) To svr-orw-mbx-03.mgc.mentorg.com (147.34.90.203) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Jiada Wang There are AVB Counter Clocks in ADG, each clock has 12bits integral and 8 bits fractional dividers which operates with S0D1ϕ clock. This patch registers 8 AVB Counter Clocks when clock-cells of rcar_sound node is 2, Signed-off-by: Jiada Wang --- sound/soc/sh/rcar/adg.c | 306 +++++++++++++++++++++++++++++++++++++-- sound/soc/sh/rcar/gen.c | 9 ++ sound/soc/sh/rcar/rsnd.h | 9 ++ 3 files changed, 315 insertions(+), 9 deletions(-) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 6768a66588eb..2c03d420ae76 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -5,6 +5,8 @@ // Copyright (C) 2013 Kuninori Morimoto #include +#include +#include #include "rsnd.h" #define CLKA 0 @@ -21,13 +23,33 @@ #define BRRx_MASK(x) (0x3FF & x) +#define ADG_CLK_NAME "adg" +#define AVB_CLK_NAME "avb" +#define AVB_CLK_NUM 8 +#define AVB_CLK_NAME_SIZE 10 +#define AVB_MAX_RATE 25000000 +#define AVB_DIV_EN_COM BIT(31) +#define AVB_DIV_MASK 0x3ffff +#define AVB_MAX_DIV 0x3ffc0 + static struct rsnd_mod_ops adg_ops = { .name = "adg", }; +struct clk_avb { + struct clk_hw hw; + unsigned int idx; + struct rsnd_mod *mod; + /* lock reg access */ + spinlock_t *lock; +}; + +#define to_clk_avb(_hw) container_of(_hw, struct clk_avb, hw) + struct rsnd_adg { struct clk *clk[CLKMAX]; struct clk *clkout[CLKOUTMAX]; + struct clk *clkavb[AVB_CLK_NUM]; struct clk_onecell_data onecell; struct rsnd_mod mod; u32 flags; @@ -37,6 +59,7 @@ struct rsnd_adg { int rbga_rate_for_441khz; /* RBGA */ int rbgb_rate_for_48khz; /* RBGB */ + spinlock_t avb_lock; }; #define LRCLK_ASYNC (1 << 0) @@ -408,6 +431,239 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) } } +static struct clk *rsnd_adg_clk_src_twocell_get(struct of_phandle_args *clkspec, + void *data) +{ + unsigned int clkidx = clkspec->args[1]; + struct rsnd_adg *adg = data; + const char *type; + struct clk *clk; + + switch (clkspec->args[0]) { + case ADG_FIX: + type = "fixed"; + if (clkidx >= CLKOUTMAX) { + pr_err("Invalid %s clock index %u\n", type, + clkidx); + return ERR_PTR(-EINVAL); + } + clk = adg->clkout[clkidx]; + break; + case ADG_AVB: + type = "avb"; + if (clkidx >= AVB_CLK_NUM) { + pr_err("Invalid %s clock index %u\n", type, + clkidx); + return ERR_PTR(-EINVAL); + } + clk = adg->clkavb[clkidx]; + break; + default: + pr_err("Invalid ADG clock type %u\n", clkspec->args[0]); + return ERR_PTR(-EINVAL); + } + + return clk; +} + +static void clk_avb_div_write(struct rsnd_mod *mod, u32 data, int idx) +{ + switch (idx) { + case 0: + rsnd_mod_write(mod, AVB_CLK_DIV0, data); + break; + case 1: + rsnd_mod_write(mod, AVB_CLK_DIV1, data); + break; + case 2: + rsnd_mod_write(mod, AVB_CLK_DIV2, data); + break; + case 3: + rsnd_mod_write(mod, AVB_CLK_DIV3, data); + break; + case 4: + rsnd_mod_write(mod, AVB_CLK_DIV4, data); + break; + case 5: + rsnd_mod_write(mod, AVB_CLK_DIV5, data); + break; + case 6: + rsnd_mod_write(mod, AVB_CLK_DIV6, data); + break; + case 7: + rsnd_mod_write(mod, AVB_CLK_DIV7, data); + break; + } +} + +static u32 clk_avb_div_read(struct rsnd_mod *mod, int idx) +{ + u32 val = 0; + + switch (idx) { + case 0: + val = rsnd_mod_read(mod, AVB_CLK_DIV0); + break; + case 1: + val = rsnd_mod_read(mod, AVB_CLK_DIV1); + break; + case 2: + val = rsnd_mod_read(mod, AVB_CLK_DIV2); + break; + case 3: + val = rsnd_mod_read(mod, AVB_CLK_DIV3); + break; + case 4: + val = rsnd_mod_read(mod, AVB_CLK_DIV4); + break; + case 5: + val = rsnd_mod_read(mod, AVB_CLK_DIV5); + break; + case 6: + val = rsnd_mod_read(mod, AVB_CLK_DIV6); + break; + case 7: + val = rsnd_mod_read(mod, AVB_CLK_DIV7); + break; + } + + return val; +} + +static int clk_avb_is_enabled(struct clk_hw *hw) +{ + struct clk_avb *avb = to_clk_avb(hw); + + return rsnd_mod_read(avb->mod, AVB_CLK_CONFIG) & BIT(avb->idx); +} + +static int clk_avb_enabledisable(struct clk_hw *hw, int enable) +{ + struct clk_avb *avb = to_clk_avb(hw); + u32 val; + + spin_lock(avb->lock); + + val = rsnd_mod_read(avb->mod, AVB_CLK_CONFIG); + if (enable) + val |= BIT(avb->idx); + else + val &= ~BIT(avb->idx); + rsnd_mod_write(avb->mod, AVB_CLK_CONFIG, val); + + spin_unlock(avb->lock); + + return 0; +} + +static int clk_avb_enable(struct clk_hw *hw) +{ + return clk_avb_enabledisable(hw, 1); +} + +static void clk_avb_disable(struct clk_hw *hw) +{ + clk_avb_enabledisable(hw, 0); +} + +static unsigned long clk_avb_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_avb *avb = to_clk_avb(hw); + u32 div; + + div = clk_avb_div_read(avb->mod, avb->idx) & AVB_DIV_MASK; + if (!div) + return parent_rate; + + return parent_rate * 32 / div; +} + +static unsigned int clk_avb_calc_div(unsigned long rate, + unsigned long parent_rate) +{ + unsigned int div; + + if (!rate) + rate = 1; + + if (rate > AVB_MAX_RATE) + rate = AVB_MAX_RATE; + + div = DIV_ROUND_CLOSEST(parent_rate * 32, rate); + + if (div > AVB_MAX_DIV) + div = AVB_MAX_DIV; + + return div; +} + +static long clk_avb_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div = clk_avb_calc_div(rate, *parent_rate); + + return (*parent_rate * 32) / div; +} + +static int clk_avb_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_avb *avb = to_clk_avb(hw); + unsigned int div = clk_avb_calc_div(rate, parent_rate); + u32 val; + + val = clk_avb_div_read(avb->mod, avb->idx) & ~AVB_DIV_MASK; + clk_avb_div_write(avb->mod, val | div, avb->idx); + + return 0; +} + +static const struct clk_ops clk_avb_ops = { + .enable = clk_avb_enable, + .disable = clk_avb_disable, + .is_enabled = clk_avb_is_enabled, + .recalc_rate = clk_avb_recalc_rate, + .round_rate = clk_avb_round_rate, + .set_rate = clk_avb_set_rate, +}; + +static struct clk *clk_register_avb(struct device *dev, struct rsnd_mod *mod, + unsigned int id, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_avb *avb; + struct clk *clk; + char name[AVB_CLK_NAME_SIZE]; + const char *parent_name = ADG_CLK_NAME; + + avb = devm_kzalloc(dev, sizeof(*avb), GFP_KERNEL); + if (!avb) + return ERR_PTR(-ENOMEM); + + snprintf(name, AVB_CLK_NAME_SIZE, "%s%u", AVB_CLK_NAME, id); + + avb->idx = id; + avb->lock = lock; + avb->mod = mod; + + /* Register the clock. */ + init.name = name; + init.ops = &clk_avb_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + avb->hw.init = &init; + + /* init DIV to a valid state */ + clk_avb_div_write(avb->mod, avb->idx, AVB_MAX_DIV); + + clk = devm_clk_register(dev, &avb->hw); + + return clk; +} + static void rsnd_adg_get_clkin(struct rsnd_priv *priv, struct rsnd_adg *adg) { @@ -436,6 +692,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, unsigned long req_48kHz_rate, req_441kHz_rate; int i, req_size; const char *parent_clk_name = NULL; + struct rsnd_mod *mod = rsnd_mod_get(adg); static const char * const clkout_name[] = { [CLKOUT] = "audio_clkout", [CLKOUT1] = "audio_clkout1", @@ -540,21 +797,23 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, */ of_property_read_u32(np, "#clock-cells", &count); - /* - * for clkout - */ - if (!count) { + + switch (count) { + case 0: + /* + * for clkout + */ clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT], parent_clk_name, 0, req_rate[0]); if (!IS_ERR(clk)) { adg->clkout[CLKOUT] = clk; of_clk_add_provider(np, of_clk_src_simple_get, clk); } - } - /* - * for clkout0/1/2/3 - */ - else { + break; + case 1: + /* + * for clkout0/1/2/3 + */ for (i = 0; i < CLKOUTMAX; i++) { clk = clk_register_fixed_rate(dev, clkout_name[i], parent_clk_name, 0, @@ -566,6 +825,33 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, adg->onecell.clk_num = CLKOUTMAX; of_clk_add_provider(np, of_clk_src_onecell_get, &adg->onecell); + break; + case 2: + /* + * for clkout0/1/2/3 and avb clocks + */ + for (i = 0; i < CLKOUTMAX; i++) { + clk = clk_register_fixed_rate(dev, clkout_name[i], + parent_clk_name, 0, + req_rate[0]); + if (!IS_ERR(clk)) + adg->clkout[i] = clk; + } + + for (i = 0; i < AVB_CLK_NUM; i++) { + clk = clk_register_avb(dev, mod, i, &adg->avb_lock); + if (!IS_ERR(clk)) + adg->clkavb[i] = clk; + } + + of_clk_add_provider(np, rsnd_adg_clk_src_twocell_get, adg); + + rsnd_mod_write(mod, AVB_CLK_CONFIG, AVB_DIV_EN_COM); + + break; + default: + dev_err(dev, "Invalid clock-cell %d\n", count); + break; } rsnd_adg_get_clkout_end: @@ -612,6 +898,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv) if (!adg) return -ENOMEM; + spin_lock_init(&adg->avb_lock); + ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, NULL, 0, 0); if (ret) diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ca639404f2cd..1b000d03b76e 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -355,6 +355,15 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54), RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58), RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c), + RSND_GEN_S_REG(AVB_CLK_DIV0, 0x11c), + RSND_GEN_S_REG(AVB_CLK_DIV1, 0x120), + RSND_GEN_S_REG(AVB_CLK_DIV2, 0x124), + RSND_GEN_S_REG(AVB_CLK_DIV3, 0x128), + RSND_GEN_S_REG(AVB_CLK_DIV4, 0x12c), + RSND_GEN_S_REG(AVB_CLK_DIV5, 0x130), + RSND_GEN_S_REG(AVB_CLK_DIV6, 0x134), + RSND_GEN_S_REG(AVB_CLK_DIV7, 0x138), + RSND_GEN_S_REG(AVB_CLK_CONFIG, 0x13c), }; static const struct rsnd_regmap_field_conf conf_ssi[] = { RSND_GEN_M_REG(SSICR, 0x00, 0x40), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 3c57129af6d1..d31b8a65985f 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -148,6 +148,15 @@ enum rsnd_reg { RSND_REG_AUDIO_CLK_SEL0, RSND_REG_AUDIO_CLK_SEL1, RSND_REG_AUDIO_CLK_SEL2, + RSND_REG_AVB_CLK_DIV0, + RSND_REG_AVB_CLK_DIV1, + RSND_REG_AVB_CLK_DIV2, + RSND_REG_AVB_CLK_DIV3, + RSND_REG_AVB_CLK_DIV4, + RSND_REG_AVB_CLK_DIV5, + RSND_REG_AVB_CLK_DIV6, + RSND_REG_AVB_CLK_DIV7, + RSND_REG_AVB_CLK_CONFIG, /* SSIU */ RSND_REG_SSI_MODE, -- 2.17.0