From 9e996a3bc91beba1bc826a5f574fca91edb23de6 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Wed, 19 May 2021 10:34:00 +0800 Subject: [PATCH] ASoC: SOF: add DAI clock driver A virtual clock driver is added to SOF platform driver. Machine driver could use the clock interface to send IPCs to SOF FW to enable/disable the clocks. Signed-off-by: Brent Lu --- include/sound/sof.h | 5 ++ include/sound/sof/dai-intel.h | 10 +++ include/sound/sof/dai.h | 10 +++ include/sound/sof/header.h | 1 + sound/soc/sof/Makefile | 2 +- sound/soc/sof/ipc.c | 2 + sound/soc/sof/pcm.c | 11 +++ sound/soc/sof/sof-dai-clk.c | 153 ++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-dai-clk.h | 19 +++++ sound/soc/sof/sof-pci-dev.c | 38 +++++++++ sound/soc/sof/sof-priv.h | 9 ++ 11 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/sof-dai-clk.c create mode 100644 sound/soc/sof/sof-dai-clk.h diff --git a/include/sound/sof.h b/include/sound/sof.h index 502ed9b8d6a1fe..e51673bea038eb 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -15,6 +15,7 @@ #include #include +struct sof_dai_clk; struct snd_sof_dsp_ops; /* @@ -52,6 +53,10 @@ struct snd_sof_pdata { struct platform_device *pdev_mach; const struct snd_soc_acpi_mach *machine; + /* dai clocks */ + struct sof_dai_clk *dai_clks; + int num_clks; + void *hw_pdata; }; diff --git a/include/sound/sof/dai-intel.h b/include/sound/sof/dai-intel.h index 136adf6686e267..8b032147c1828f 100644 --- a/include/sound/sof/dai-intel.h +++ b/include/sound/sof/dai-intel.h @@ -52,6 +52,16 @@ /* DMIC max. four controllers for eight microphone channels */ #define SOF_DAI_INTEL_DMIC_NUM_CTRL 4 +/* SSP clocks to enable/disable + * + * Macros for clk_id field in sof_ipc_dai_ssp_clkctrl struct. + */ + +/* mclk */ +#define SOF_DAI_INTEL_CLKCTRL_MCLK 1 +/* bclk */ +#define SOF_DAI_INTEL_CLKCTRL_BCLK 2 + /* SSP Configuration Request - SOF_IPC_DAI_SSP_CONFIG */ struct sof_ipc_dai_ssp_params { struct sof_ipc_hdr hdr; diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 6bb403e8c5ee79..f49f514d4d55be 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -85,4 +85,14 @@ struct sof_ipc_dai_config { }; } __packed; +/* general purpose DAI clock control */ +struct sof_ipc_dai_clkctrl { + struct sof_ipc_cmd_hdr hdr; + uint32_t type; /**< DAI type - enum sof_ipc_dai_type */ + uint32_t dai_index; /**< index of this type dai */ + + uint32_t clk_en; /**< zero to disable the clock */ + uint32_t clk_id; /**< clock id to operate */ +} __packed; + #endif diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 4c747c52e01b1e..a901c09308f688 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -89,6 +89,7 @@ /* DAI messages */ #define SOF_IPC_DAI_CONFIG SOF_CMD_TYPE(0x001) #define SOF_IPC_DAI_LOOPBACK SOF_CMD_TYPE(0x002) +#define SOF_IPC_DAI_CLKCTRL SOF_CMD_TYPE(0x003) /* stream */ #define SOF_IPC_STREAM_PCM_PARAMS SOF_CMD_TYPE(0x001) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 606d8137cd9887..cc6ddd1744d638 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o utils.o sof-audio.o + control.o trace.o utils.o sof-audio.o sof-dai-clk.o snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += probe.o compress.o snd-sof-pci-objs := sof-pci-dev.o diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 444e3ba55a8eb9..91ee7a1fee775b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -168,6 +168,8 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) str2 = "CONFIG"; break; case SOF_IPC_DAI_LOOPBACK: str2 = "LOOPBACK"; break; + case SOF_IPC_DAI_CLKCTRL: + str2 = "CLKCTRL"; break; default: str2 = "unknown type"; break; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index c9ea05c643c3f6..70481de837d200 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -862,11 +862,22 @@ static int sof_pcm_probe(struct snd_soc_component *component) return ret; } + /* Register DAI clock control */ + ret = snd_sof_dai_clks_probe(component); + if (ret) { + dev_err(component->dev, "error: failed to register clock control %d\n", + ret); + return ret; + } + return ret; } static void sof_pcm_remove(struct snd_soc_component *component) { + /* remove DAI clock control */ + snd_sof_dai_clks_remove(component); + /* remove topology */ snd_soc_tplg_component_remove(component); } diff --git a/sound/soc/sof/sof-dai-clk.c b/sound/soc/sof/sof-dai-clk.c new file mode 100644 index 00000000000000..9e8ccd8e1d7ff9 --- /dev/null +++ b/sound/soc/sof/sof-dai-clk.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Author: Brent Lu +// +// DAI clock layer to support explicit clock control in machine driver. +// + +#include +#include +#include +#include "sof-priv.h" +#include "sof-dai-clk.h" + +struct sof_clk { + struct clk_hw hw; + struct snd_sof_dev *sdev; + struct sof_dai_clk *dai_clk; +}; + +static int sof_clk_ctrl(struct snd_sof_dev *sdev, struct sof_dai_clk *dai_clk, int enable) +{ + struct sof_ipc_dai_clkctrl clkctrl; + struct sof_ipc_reply reply; + int ret; + + memset(&clkctrl, 0, sizeof(clkctrl)); + + /* set IPC DAI clock control */ + clkctrl.hdr.size = sizeof(clkctrl); + clkctrl.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CLKCTRL; + + clkctrl.type = dai_clk->type; + clkctrl.dai_index = dai_clk->dai_index; + + clkctrl.clk_en = enable ? 1 : 0; + clkctrl.clk_id = dai_clk->clk_id; + + dev_dbg(sdev->dev, "clkctrl: SSP%d clk_en %d clk_id %d\n", + clkctrl.dai_index, clkctrl.clk_en, clkctrl.clk_id); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, clkctrl.hdr.cmd, &clkctrl, + sizeof(clkctrl), &reply, sizeof(reply)); + + if (ret < 0) { + dev_err(sdev->dev, + "failed to control DAI clock for type %d index %d\n", + dai_clk->type, dai_clk->dai_index); + return ret; + } + + return 0; +} + +static int sof_clk_prepare(struct clk_hw *hw) +{ + struct sof_clk *clkdev = container_of(hw, struct sof_clk, hw); + struct snd_sof_dev *sdev = clkdev->sdev; + + return sof_clk_ctrl(sdev, clkdev->dai_clk, 1); +} + +static void sof_clk_unprepare(struct clk_hw *hw) +{ + struct sof_clk *clkdev = container_of(hw, struct sof_clk, hw); + struct snd_sof_dev *sdev = clkdev->sdev; + + sof_clk_ctrl(sdev, clkdev->dai_clk, 0); +} + +static const struct clk_ops sof_clk_ops = { + .prepare = sof_clk_prepare, + .unprepare = sof_clk_unprepare, +}; + +int snd_sof_dai_clks_probe(struct snd_soc_component *component) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_pdata *pdata = sdev->pdata; + struct device *dev = component->dev; + int i, ret; + + sdev->num_clks = pdata->num_clks; + + if (sdev->num_clks > SOF_DAI_CLKS) + sdev->num_clks = SOF_DAI_CLKS; + + for (i = 0; i < sdev->num_clks; ++i) { + struct clk_init_data init = {}; + struct clk_lookup *dai_clk_lookup; + struct sof_clk *clkdev; + + clkdev = devm_kzalloc(dev, sizeof(*clkdev), GFP_KERNEL); + if (!clkdev) { + ret = -ENOMEM; + goto err; + } + + init.name = pdata->dai_clks[i].name; + init.ops = &sof_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + init.parent_names = NULL; + init.num_parents = 0; + + clkdev->hw.init = &init; + clkdev->sdev = sdev; + clkdev->dai_clk = &pdata->dai_clks[i]; + + ret = devm_clk_hw_register(dev, &clkdev->hw); + if (ret) { + dev_warn(dev, "failed to register %s: %d\n", + init.name, ret); + goto err; + } + + dai_clk_lookup = clkdev_hw_create(&clkdev->hw, init.name, + NULL); + if (!dai_clk_lookup) { + dev_warn(dev, "failed to create %s: %d\n", + init.name, ret); + ret = -ENOMEM; + goto err; + } + + sdev->dai_clks_lookup[i] = dai_clk_lookup; + } + + return 0; + +err: + do { + if (sdev->dai_clks_lookup[i]) + clkdev_drop(sdev->dai_clks_lookup[i]); + } while (i-- > 0); + + return ret; +} + +void snd_sof_dai_clks_remove(struct snd_soc_component *component) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + int i; + + for (i = sdev->num_clks - 1; i >= 0; --i) { + if (sdev->dai_clks_lookup[i]) + clkdev_drop(sdev->dai_clks_lookup[i]); + } +} diff --git a/sound/soc/sof/sof-dai-clk.h b/sound/soc/sof/sof-dai-clk.h new file mode 100644 index 00000000000000..b86a9ccbf17304 --- /dev/null +++ b/sound/soc/sof/sof-dai-clk.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Intel Corporation. All rights reserved. + */ + +#ifndef __SOUND_SOC_SOF_DAI_CLK_H +#define __SOUND_SOC_SOF_DAI_CLK_H + +struct sof_dai_clk { + const char *name; + int type; + int dai_index; + int clk_id; +}; + +#endif diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 03119462f9e29d..5db802a1a2ec59 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -16,9 +16,14 @@ #include #include #include +#include +#include "sof-dai-clk.h" #include "ops.h" #include "sof-pci-dev.h" +/* platform specific devices */ +#include "intel/shim.h" + static char *fw_path; module_param(fw_path, charp, 0444); MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware."); @@ -110,11 +115,40 @@ static void sof_pci_probe_complete(struct device *dev) pm_runtime_put_noidle(dev); } +static struct sof_dai_clk sof_dai_clks[] = { + {.name = "ssp0_mclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 0, + .clk_id = SOF_DAI_INTEL_CLKCTRL_MCLK}, + {.name = "ssp0_sclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 0, + .clk_id = SOF_DAI_INTEL_CLKCTRL_BCLK}, + {.name = "ssp1_mclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 1, + .clk_id = SOF_DAI_INTEL_CLKCTRL_MCLK}, + {.name = "ssp1_sclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 1, + .clk_id = SOF_DAI_INTEL_CLKCTRL_BCLK}, + {.name = "ssp2_mclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 2, + .clk_id = SOF_DAI_INTEL_CLKCTRL_MCLK}, + {.name = "ssp2_sclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 2, + .clk_id = SOF_DAI_INTEL_CLKCTRL_BCLK}, + {.name = "ssp3_mclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 3, + .clk_id = SOF_DAI_INTEL_CLKCTRL_MCLK}, + {.name = "ssp3_sclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 3, + .clk_id = SOF_DAI_INTEL_CLKCTRL_BCLK}, + {.name = "ssp4_mclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 4, + .clk_id = SOF_DAI_INTEL_CLKCTRL_MCLK}, + {.name = "ssp4_sclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 4, + .clk_id = SOF_DAI_INTEL_CLKCTRL_BCLK}, + {.name = "ssp5_mclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 5, + .clk_id = SOF_DAI_INTEL_CLKCTRL_MCLK}, + {.name = "ssp5_sclk", .type = SOF_DAI_INTEL_SSP, .dai_index = 5, + .clk_id = SOF_DAI_INTEL_CLKCTRL_BCLK}, +}; + int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { struct device *dev = &pci->dev; const struct sof_dev_desc *desc = (const struct sof_dev_desc *)pci_id->driver_data; + const struct sof_intel_dsp_desc *chip = + (const struct sof_intel_dsp_desc *)desc->chip_info; struct snd_sof_pdata *sof_pdata; int ret; @@ -177,6 +211,10 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path; + /* 2 clocks per SSP port: mclk and bclk */ + sof_pdata->dai_clks = sof_dai_clks; + sof_pdata->num_clks = 2 * chip->ssp_count; + dmi_check_system(sof_tplg_table); if (sof_override_tplg_name) sof_pdata->tplg_filename = sof_override_tplg_name; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3ab743792fcdea..c5d9a318dde318 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -47,6 +47,8 @@ extern int sof_core_debug; #define SOF_IPC_DSP_REPLY 0 #define SOF_IPC_HOST_REPLY 1 +#define SOF_DAI_CLKS 12 + /* convenience constructor for DAI driver streams */ #define SOF_DAI_STREAM(sname, scmin, scmax, srates, sfmt) \ {.stream_name = sname, .channels_min = scmin, .channels_max = scmax, \ @@ -457,6 +459,10 @@ struct snd_sof_dev { bool dtrace_error; bool dtrace_draining; + /* DAI clock control */ + struct clk_lookup *dai_clks_lookup[SOF_DAI_CLKS]; + int num_clks; + bool msi_enabled; void *private; /* core does not touch this */ @@ -486,6 +492,9 @@ int snd_sof_create_page_table(struct device *dev, struct snd_dma_buffer *dmab, unsigned char *page_table, size_t size); +int snd_sof_dai_clks_probe(struct snd_soc_component *component); +void snd_sof_dai_clks_remove(struct snd_soc_component *component); + /* * Firmware loading. */