From patchwork Mon Sep 18 04:56:48 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Subhransu S. Prusty" X-Patchwork-Id: 9955497 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A6BE860208 for ; Mon, 18 Sep 2017 05:16:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9892928B4C for ; Mon, 18 Sep 2017 05:16:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8C7F028B50; Mon, 18 Sep 2017 05:16:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3202328B4C for ; Mon, 18 Sep 2017 05:16:49 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 378CD26703C; Mon, 18 Sep 2017 07:15:09 +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 0F49C267035; Mon, 18 Sep 2017 07:15:07 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by alsa0.perex.cz (Postfix) with ESMTP id 61B49266819 for ; Mon, 18 Sep 2017 07:15:02 +0200 (CEST) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Sep 2017 22:15:02 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.42,411,1500966000"; d="scan'208";a="136441186" Received: from subhransu-desktop.iind.intel.com ([10.223.96.135]) by orsmga002.jf.intel.com with ESMTP; 17 Sep 2017 22:14:59 -0700 From: "Subhransu S. Prusty" To: alsa-devel@alsa-project.org Date: Mon, 18 Sep 2017 10:26:48 +0530 Message-Id: <1505710610-17952-6-git-send-email-subhransu.s.prusty@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1505710610-17952-1-git-send-email-subhransu.s.prusty@intel.com> References: <1505710610-17952-1-git-send-email-subhransu.s.prusty@intel.com> Cc: harshapriya.n@intel.com, tiwai@suse.de, mturquette@baylibre.com, sboyd@codeaurora.org, lgirdwood@gmail.com, Jaikrishna Nemallapudi , patches.audio@intel.com, broonie@kernel.org, "Subhransu S. Prusty" , linux-clk@vger.kernel.org Subject: [alsa-devel] [PATCH v2 5/7] ASoC: Intel: Skylake: Add ssp clock driver 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: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP From: Jaikrishna Nemallapudi For certain platforms it is required to start the clocks (mclk/sclk/fs) before the stream start. Example: for few chrome systems, codec needs the mclk/sclk to be enabled early for a successful clock synchronization and for few IVI platforms, clock need to be enabled at boot and should be ON always. This patch creates virtual clock driver, which allows the machine driver to use the clock interface to send IPCs to DSP to enable/disable the clocks. Signed-off-by: Jaikrishna Nemallapudi Signed-off-by: Subhransu S. Prusty --- sound/soc/intel/Kconfig | 7 + sound/soc/intel/skylake/Makefile | 5 + sound/soc/intel/skylake/skl-ssp-clk.c | 288 ++++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 sound/soc/intel/skylake/skl-ssp-clk.c diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index b3c7f554ec30..b9d9d692b4e3 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -269,6 +269,13 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_SKYLAKE_SSP_CLK + tristate + help + Enabling this will make the clk driver to be compiled and allow to + control MCLK/SCLK/FS clocks independent of the stream. It should be + enabled by the respective machine driver. + config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 3380deb81015..9131c35ad4bb 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -13,3 +13,8 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-utils.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o + +#Skylake Clock device support +snd-soc-skl-ssp-clk-objs := skl-ssp-clk.o + +obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE_SSP_CLK) += snd-soc-skl-ssp-clk.o diff --git a/sound/soc/intel/skylake/skl-ssp-clk.c b/sound/soc/intel/skylake/skl-ssp-clk.c new file mode 100644 index 000000000000..769ece306f58 --- /dev/null +++ b/sound/soc/intel/skylake/skl-ssp-clk.c @@ -0,0 +1,288 @@ +/* + * skl-ssp-clk.c - ASoC skylake ssp clock driver + * + * Copyright (C) 2017 Intel Corp + * Author: Jaikrishna Nemallapudi + * Author: Subhransu S. Prusty + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "skl-ssp-clk.h" + +#define to_skl_clk(_hw) container_of(_hw, struct skl_clk, hw) + +struct skl_clk_parent { + struct clk_hw *hw; + struct clk_lookup *lookup; +}; + +struct skl_clk { + struct clk_hw hw; + struct clk_lookup *lookup; + struct skl_clk_ops *ops; + unsigned long rate; + void *pvt_data; + u32 id; +}; + +struct skl_clk_data { + struct skl_clk_parent parent[SKL_MAX_CLK_SRC]; + struct skl_clk *clk[SKL_MAX_CLK_CNT]; + u8 avail_clk_cnt; +}; + +static int skl_clk_prepare(struct clk_hw *hw) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + + if (!clkdev->ops || !clkdev->ops->prepare) + return -EIO; + + if (!clkdev->rate) + return -EINVAL; + + return clkdev->ops->prepare(clkdev->pvt_data, clkdev->id, clkdev->rate); +} + +static void skl_clk_unprepare(struct clk_hw *hw) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + + if (!clkdev->ops || !clkdev->ops->unprepare) + return; + + if (!clkdev->rate) + return; + + clkdev->ops->unprepare(clkdev->pvt_data, clkdev->id, clkdev->rate); +} + +static int skl_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + int ret; + + if (!clkdev->ops || !clkdev->ops->set_rate) + return -EIO; + + ret = clkdev->ops->set_rate(clkdev->id, rate); + if (!ret) + clkdev->rate = rate; + + return ret; +} + +unsigned long skl_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct skl_clk *clkdev = to_skl_clk(hw); + + if (clkdev->rate) + return clkdev->rate; + + if (!clkdev->ops || !clkdev->ops->recalc_rate) + return -EIO; + + clkdev->rate = clkdev->ops->recalc_rate(clkdev->id, parent_rate); + + return clkdev->rate; +} + +/* Not supported by clk driver. Implemented to satisfy clk fw */ +long skl_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static const struct clk_ops skl_clk_ops = { + .prepare = skl_clk_prepare, + .unprepare = skl_clk_unprepare, + .set_rate = skl_clk_set_rate, + .round_rate = skl_clk_round_rate, + .recalc_rate = skl_clk_recalc_rate, +}; + +static void unregister_parent_src_clk(struct skl_clk_parent *pclk, u8 id) +{ + while (id--) { + clkdev_drop(pclk[id].lookup); + clk_hw_unregister_fixed_rate(pclk[id].hw); + } +} + +static void unregister_src_clk(struct skl_clk_data *dclk) +{ + u8 cnt = dclk->avail_clk_cnt; + + while (cnt--) + clkdev_drop(dclk->clk[cnt]->lookup); +} + +static int skl_register_parent_clks(struct device *dev, + struct skl_clk_parent *parent, + struct skl_clk_parent_src *pclk) +{ + int i, ret; + + for (i = 0; i < SKL_MAX_CLK_SRC; i++) { + + /* Register Parent clock */ + parent[i].hw = clk_hw_register_fixed_rate(dev, pclk[i].name, + pclk[i].parent_name, 0, pclk[i].rate); + if (IS_ERR(parent[i].hw)) { + ret = PTR_ERR_OR_ZERO(parent[i].hw); + goto err; + } + + parent[i].lookup = clkdev_hw_create(parent[i].hw, pclk[i].name, + NULL); + if (!parent[i].lookup) { + clk_hw_unregister_fixed_rate(parent[i].hw); + ret = PTR_ERR_OR_ZERO(parent[i].lookup); + goto err; + } + } + + return 0; +err: + unregister_parent_src_clk(parent, i); + return ret; +} + +/* REMOVE: send only ssp_clks[i], ops */ +/* Assign fmt_config to clk_data */ +static struct skl_clk *register_skl_clk(struct device *dev, + struct skl_ssp_clk *clk, + struct skl_clk_pdata *clk_pdata, int id) +{ + struct skl_clk *clkdev; + struct clk_init_data init; + int ret; + + clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL); + if (!clkdev) + return ERR_PTR(-ENOMEM); + + init.name = clk->name; + init.ops = &skl_clk_ops; + init.flags = 0; + init.parent_names = &clk->parent_name; + init.num_parents = 1; + clkdev->hw.init = &init; + clkdev->ops = clk_pdata->ops; + clkdev->pvt_data = clk_pdata->pvt_data; + + clkdev->id = id; + ret = devm_clk_hw_register(dev, &clkdev->hw); + if (ret) { + clkdev = ERR_PTR(ret); + return clkdev; + } + + clkdev->lookup = clkdev_hw_create(&clkdev->hw, init.name, NULL); + if (!clkdev->lookup) + clkdev = ERR_PTR(-ENOMEM); + + return clkdev; +} + +static int skl_clk_dev_probe(struct platform_device *pdev) +{ + struct skl_clk_pdata *clk_pdata; + struct skl_clk_parent_src *parent_clks; + struct skl_ssp_clk *clks; + struct skl_clk_data *data; + struct device *dev = &pdev->dev; + struct device *parent_dev = dev->parent; + int ret, i; + + clk_pdata = dev_get_platdata(&pdev->dev); + parent_clks = clk_pdata->parent_clks; + clks = clk_pdata->ssp_clks; + if (!parent_clks || !clks) + return -EIO; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* Register Parent clock */ + ret = skl_register_parent_clks(parent_dev, data->parent, parent_clks); + if (ret) + return ret; + + for (i = 0; i < clk_pdata->num_clks; i++) { + /* + * Only register valid clocks + * i.e. for which nhlt entry is present. + */ + if (clks[i].rate_cfg[0].rate == 0) + continue; + + data->clk[i] = register_skl_clk(dev, &clks[i], clk_pdata, i); + if (IS_ERR(data->clk[i])) { + ret = PTR_ERR(data->clk[i]); + goto err_unreg_skl_clk; + } + + data->avail_clk_cnt++; + } + + platform_set_drvdata(pdev, data); + + return 0; + +err_unreg_skl_clk: + unregister_src_clk(data); + unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC); + + return ret; +} + +static int skl_clk_dev_remove(struct platform_device *pdev) +{ + struct skl_clk_data *data; + + data = platform_get_drvdata(pdev); + unregister_parent_src_clk(data->parent, SKL_MAX_CLK_SRC); + unregister_src_clk(data); + + return 0; +} + +static struct platform_driver skl_clk_driver = { + .driver = { + .name = "skl-ssp-clk", + }, + .probe = skl_clk_dev_probe, + .remove = skl_clk_dev_remove, +}; + +module_platform_driver(skl_clk_driver); + +MODULE_DESCRIPTION("Skylake clock driver"); +MODULE_AUTHOR("Jaikrishna Nemallapudi "); +MODULE_AUTHOR("Subhransu S. Prusty "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl-ssp-clk");