asoc: codecs: bolero: add clk resource manager driver
Add Bolero clock resource manager driver to handle/manage bolero clocks for all the concurrency usecases like record + voice activation. Change-Id: I970a05d96fc9060b44bfe670d465f0b9d72cc53b Signed-off-by: Vidyakumar Athota <vathota@codeaurora.org>
This commit is contained in:
parent
b4c9798b90
commit
5d45f4c865
@ -70,6 +70,7 @@ ifdef CONFIG_SND_SOC_BOLERO
|
||||
BOLERO_OBJS += bolero-cdc-utils.o
|
||||
BOLERO_OBJS += bolero-cdc-regmap.o
|
||||
BOLERO_OBJS += bolero-cdc-tables.o
|
||||
BOLERO_OBJS += bolero-clk-rsc.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WSA_MACRO
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <soc/swr-common.h>
|
||||
#include "bolero-cdc.h"
|
||||
#include "internal.h"
|
||||
#include "bolero-clk-rsc.h"
|
||||
|
||||
#define DRV_NAME "bolero_codec"
|
||||
|
||||
@ -93,43 +94,44 @@ static void bolero_ahb_read_device(char __iomem *io_base,
|
||||
static int __bolero_reg_read(struct bolero_priv *priv,
|
||||
u16 macro_id, u16 reg, u8 *val)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u16 current_mclk_mux_macro;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&priv->clk_lock);
|
||||
if (!priv->dev_up) {
|
||||
dev_dbg_ratelimited(priv->dev,
|
||||
"%s: SSR in progress, exit\n", __func__);
|
||||
goto err;
|
||||
ret = -EINVAL;
|
||||
goto ssr_err;
|
||||
}
|
||||
|
||||
if (priv->macro_params[VA_MACRO].dev)
|
||||
pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
|
||||
current_mclk_mux_macro =
|
||||
priv->current_mclk_mux_macro[macro_id];
|
||||
if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
|
||||
dev_dbg_ratelimited(priv->dev,
|
||||
"%s: mclk_fn not init for macro-id:%d, current_mclk_mux_macro:%d\n",
|
||||
__func__, macro_id, current_mclk_mux_macro);
|
||||
goto err;
|
||||
}
|
||||
ret = priv->macro_params[current_mclk_mux_macro].mclk_fn(
|
||||
priv->macro_params[current_mclk_mux_macro].dev, true);
|
||||
if (ret) {
|
||||
dev_dbg_ratelimited(priv->dev,
|
||||
"%s: clock enable failed for macro-id:%d, current_mclk_mux_macro:%d\n",
|
||||
__func__, macro_id, current_mclk_mux_macro);
|
||||
|
||||
/* Request Clk before register access */
|
||||
ret = bolero_clk_rsc_request_clock(priv->macro_params[macro_id].dev,
|
||||
priv->macro_params[macro_id].default_clk_id,
|
||||
priv->macro_params[macro_id].clk_id_req,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(priv->dev,
|
||||
"%s: Failed to enable clock, ret:%d\n", __func__, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
bolero_ahb_read_device(
|
||||
priv->macro_params[macro_id].io_base, reg, val);
|
||||
priv->macro_params[current_mclk_mux_macro].mclk_fn(
|
||||
priv->macro_params[current_mclk_mux_macro].dev, false);
|
||||
|
||||
bolero_clk_rsc_request_clock(priv->macro_params[macro_id].dev,
|
||||
priv->macro_params[macro_id].default_clk_id,
|
||||
priv->macro_params[macro_id].clk_id_req,
|
||||
false);
|
||||
|
||||
err:
|
||||
if (priv->macro_params[VA_MACRO].dev) {
|
||||
pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
|
||||
pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
|
||||
}
|
||||
ssr_err:
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
return ret;
|
||||
}
|
||||
@ -137,42 +139,43 @@ err:
|
||||
static int __bolero_reg_write(struct bolero_priv *priv,
|
||||
u16 macro_id, u16 reg, u8 val)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u16 current_mclk_mux_macro;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&priv->clk_lock);
|
||||
if (!priv->dev_up) {
|
||||
dev_dbg_ratelimited(priv->dev,
|
||||
"%s: SSR in progress, exit\n", __func__);
|
||||
goto err;
|
||||
ret = -EINVAL;
|
||||
goto ssr_err;
|
||||
}
|
||||
if (priv->macro_params[VA_MACRO].dev)
|
||||
ret = pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
|
||||
current_mclk_mux_macro =
|
||||
priv->current_mclk_mux_macro[macro_id];
|
||||
if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
|
||||
dev_dbg_ratelimited(priv->dev,
|
||||
"%s: mclk_fn not init for macro-id:%d, current_mclk_mux_macro:%d\n",
|
||||
__func__, macro_id, current_mclk_mux_macro);
|
||||
goto err;
|
||||
}
|
||||
ret = priv->macro_params[current_mclk_mux_macro].mclk_fn(
|
||||
priv->macro_params[current_mclk_mux_macro].dev, true);
|
||||
if (ret) {
|
||||
dev_dbg_ratelimited(priv->dev,
|
||||
"%s: clock enable failed for macro-id:%d, current_mclk_mux_macro:%d\n",
|
||||
__func__, macro_id, current_mclk_mux_macro);
|
||||
pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
|
||||
|
||||
/* Request Clk before register access */
|
||||
ret = bolero_clk_rsc_request_clock(priv->macro_params[macro_id].dev,
|
||||
priv->macro_params[macro_id].default_clk_id,
|
||||
priv->macro_params[macro_id].clk_id_req,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(priv->dev,
|
||||
"%s: Failed to enable clock, ret:%d\n", __func__, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
bolero_ahb_write_device(
|
||||
priv->macro_params[macro_id].io_base, reg, val);
|
||||
priv->macro_params[current_mclk_mux_macro].mclk_fn(
|
||||
priv->macro_params[current_mclk_mux_macro].dev, false);
|
||||
priv->macro_params[macro_id].io_base, reg, val);
|
||||
|
||||
bolero_clk_rsc_request_clock(priv->macro_params[macro_id].dev,
|
||||
priv->macro_params[macro_id].default_clk_id,
|
||||
priv->macro_params[macro_id].clk_id_req,
|
||||
false);
|
||||
|
||||
err:
|
||||
if (priv->macro_params[VA_MACRO].dev) {
|
||||
pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
|
||||
pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
|
||||
}
|
||||
ssr_err:
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
return ret;
|
||||
}
|
||||
@ -239,7 +242,7 @@ static void bolero_cdc_notifier_call(struct bolero_priv *priv,
|
||||
data, (void *)priv->wcd_dev);
|
||||
}
|
||||
|
||||
static bool bolero_is_valid_macro_dev(struct device *dev)
|
||||
static bool bolero_is_valid_child_dev(struct device *dev)
|
||||
{
|
||||
if (of_device_is_compatible(dev->parent->of_node, "qcom,bolero-codec"))
|
||||
return true;
|
||||
@ -326,6 +329,36 @@ struct device *bolero_get_device_ptr(struct device *dev, u16 macro_id)
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_get_device_ptr);
|
||||
|
||||
/**
|
||||
* bolero_get_rsc_clk_device_ptr - Get rsc clk device ptr
|
||||
*
|
||||
* @dev: bolero device ptr.
|
||||
*
|
||||
* Returns dev ptr on success or NULL on error.
|
||||
*/
|
||||
struct device *bolero_get_rsc_clk_device_ptr(struct device *dev)
|
||||
{
|
||||
struct bolero_priv *priv;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: dev is null\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!bolero_is_valid_codec_dev(dev)) {
|
||||
pr_err("%s: invalid codec\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
priv = dev_get_drvdata(dev);
|
||||
if (!priv) {
|
||||
dev_err(dev, "%s: priv is null\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return priv->clk_dev;
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_get_rsc_clk_device_ptr);
|
||||
|
||||
static int bolero_copy_dais_from_macro(struct bolero_priv *priv)
|
||||
{
|
||||
struct snd_soc_dai_driver *dai_ptr;
|
||||
@ -355,6 +388,69 @@ static int bolero_copy_dais_from_macro(struct bolero_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bolero_register_res_clk - Registers rsc clk driver to bolero
|
||||
*
|
||||
* @dev: rsc clk device ptr.
|
||||
* @rsc_clk_cb: event handler callback for notifications like SSR
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on error.
|
||||
*/
|
||||
int bolero_register_res_clk(struct device *dev, rsc_clk_cb_t rsc_clk_cb)
|
||||
{
|
||||
struct bolero_priv *priv;
|
||||
|
||||
if (!dev || !rsc_clk_cb) {
|
||||
pr_err("%s: dev or rsc_clk_cb is null\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bolero_is_valid_child_dev(dev)) {
|
||||
dev_err(dev, "%s: child device :%pK not added yet\n",
|
||||
__func__, dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv = dev_get_drvdata(dev->parent);
|
||||
if (!priv) {
|
||||
dev_err(dev, "%s: priv is null\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->clk_dev = dev;
|
||||
priv->rsc_clk_cb = rsc_clk_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_register_res_clk);
|
||||
|
||||
/**
|
||||
* bolero_unregister_res_clk - Unregisters rsc clk driver from bolero
|
||||
*
|
||||
* @dev: resource clk device ptr.
|
||||
*/
|
||||
void bolero_unregister_res_clk(struct device *dev)
|
||||
{
|
||||
struct bolero_priv *priv;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: dev is NULL\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (!bolero_is_valid_child_dev(dev)) {
|
||||
dev_err(dev, "%s: child device :%pK not added\n",
|
||||
__func__, dev);
|
||||
return;
|
||||
}
|
||||
priv = dev_get_drvdata(dev->parent);
|
||||
if (!priv) {
|
||||
dev_err(dev, "%s: priv is null\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
priv->clk_dev = NULL;
|
||||
priv->rsc_clk_cb = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_unregister_res_clk);
|
||||
|
||||
/**
|
||||
* bolero_register_macro - Registers macro to bolero
|
||||
*
|
||||
@ -374,7 +470,7 @@ int bolero_register_macro(struct device *dev, u16 macro_id,
|
||||
pr_err("%s: dev or ops is null\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bolero_is_valid_macro_dev(dev)) {
|
||||
if (!bolero_is_valid_child_dev(dev)) {
|
||||
dev_err(dev, "%s: child device for macro:%d not added yet\n",
|
||||
__func__, macro_id);
|
||||
return -EINVAL;
|
||||
@ -385,12 +481,13 @@ int bolero_register_macro(struct device *dev, u16 macro_id,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->macro_params[macro_id].clk_id_req = ops->clk_id_req;
|
||||
priv->macro_params[macro_id].default_clk_id = ops->default_clk_id;
|
||||
priv->macro_params[macro_id].init = ops->init;
|
||||
priv->macro_params[macro_id].exit = ops->exit;
|
||||
priv->macro_params[macro_id].io_base = ops->io_base;
|
||||
priv->macro_params[macro_id].num_dais = ops->num_dais;
|
||||
priv->macro_params[macro_id].dai_ptr = ops->dai_ptr;
|
||||
priv->macro_params[macro_id].mclk_fn = ops->mclk_fn;
|
||||
priv->macro_params[macro_id].event_handler = ops->event_handler;
|
||||
priv->macro_params[macro_id].set_port_map = ops->set_port_map;
|
||||
priv->macro_params[macro_id].dev = dev;
|
||||
@ -441,7 +538,7 @@ void bolero_unregister_macro(struct device *dev, u16 macro_id)
|
||||
pr_err("%s: dev is null\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (!bolero_is_valid_macro_dev(dev)) {
|
||||
if (!bolero_is_valid_child_dev(dev)) {
|
||||
dev_err(dev, "%s: macro:%d not in valid registered macro-list\n",
|
||||
__func__, macro_id);
|
||||
return;
|
||||
@ -455,7 +552,6 @@ void bolero_unregister_macro(struct device *dev, u16 macro_id)
|
||||
priv->macro_params[macro_id].init = NULL;
|
||||
priv->macro_params[macro_id].num_dais = 0;
|
||||
priv->macro_params[macro_id].dai_ptr = NULL;
|
||||
priv->macro_params[macro_id].mclk_fn = NULL;
|
||||
priv->macro_params[macro_id].event_handler = NULL;
|
||||
priv->macro_params[macro_id].dev = NULL;
|
||||
if (macro_id == TX_MACRO)
|
||||
@ -470,150 +566,6 @@ void bolero_unregister_macro(struct device *dev, u16 macro_id)
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_unregister_macro);
|
||||
|
||||
static void bolero_fs_gen_enable(struct bolero_priv *priv, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
if (++priv->clk_users == 1) {
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
regmap_update_bits(priv->regmap,
|
||||
BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
|
||||
0x01, 0x01);
|
||||
regmap_update_bits(priv->regmap,
|
||||
BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
|
||||
0x01, 0x01);
|
||||
regmap_update_bits(priv->regmap,
|
||||
BOLERO_CDC_VA_TOP_CSR_TOP_CFG0,
|
||||
0x02, 0x02);
|
||||
mutex_lock(&priv->clk_lock);
|
||||
}
|
||||
} else {
|
||||
if (priv->clk_users <= 0) {
|
||||
dev_err(priv->dev,
|
||||
"%s:clock already disabled\n",
|
||||
__func__);
|
||||
priv->clk_users = 0;
|
||||
return;
|
||||
}
|
||||
if (--priv->clk_users == 0) {
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
regmap_update_bits(priv->regmap,
|
||||
BOLERO_CDC_VA_TOP_CSR_TOP_CFG0,
|
||||
0x02, 0x00);
|
||||
regmap_update_bits(priv->regmap,
|
||||
BOLERO_CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL,
|
||||
0x01, 0x00);
|
||||
regmap_update_bits(priv->regmap,
|
||||
BOLERO_CDC_VA_CLK_RST_CTRL_MCLK_CONTROL,
|
||||
0x01, 0x00);
|
||||
mutex_lock(&priv->clk_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bolero_request_clock - request for clock enable/disable
|
||||
*
|
||||
* @dev: macro device ptr.
|
||||
* @macro_id: ID of macro calling this API.
|
||||
* @mclk_mux_id: MCLK_MUX ID.
|
||||
* @enable: enable or disable clock flag
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on error.
|
||||
*/
|
||||
int bolero_request_clock(struct device *dev, u16 macro_id,
|
||||
enum mclk_mux mclk_mux_id,
|
||||
bool enable)
|
||||
{
|
||||
struct bolero_priv *priv;
|
||||
u16 mclk_mux0_macro, mclk_mux1_macro;
|
||||
int ret = 0, ret1 = 0;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: dev is null\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!bolero_is_valid_macro_dev(dev)) {
|
||||
dev_err(dev, "%s: macro:%d not in valid registered macro-list\n",
|
||||
__func__, macro_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv = dev_get_drvdata(dev->parent);
|
||||
if (!priv || (macro_id >= MAX_MACRO)) {
|
||||
dev_err(dev, "%s: priv is null or invalid macro\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
mclk_mux0_macro = bolero_mclk_mux_tbl[macro_id][MCLK_MUX0];
|
||||
mutex_lock(&priv->clk_lock);
|
||||
switch (mclk_mux_id) {
|
||||
case MCLK_MUX0:
|
||||
ret = priv->macro_params[mclk_mux0_macro].mclk_fn(
|
||||
priv->macro_params[mclk_mux0_macro].dev, enable);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"%s: MCLK_MUX0 %s failed for macro:%d, mclk_mux0_macro:%d\n",
|
||||
__func__,
|
||||
enable ? "enable" : "disable",
|
||||
macro_id, mclk_mux0_macro);
|
||||
goto err;
|
||||
}
|
||||
bolero_fs_gen_enable(priv, enable);
|
||||
break;
|
||||
case MCLK_MUX1:
|
||||
mclk_mux1_macro = bolero_mclk_mux_tbl[macro_id][MCLK_MUX1];
|
||||
ret = priv->macro_params[mclk_mux0_macro].mclk_fn(
|
||||
priv->macro_params[mclk_mux0_macro].dev,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"%s: MCLK_MUX0 en failed for macro:%d mclk_mux0_macro:%d\n",
|
||||
__func__, macro_id, mclk_mux0_macro);
|
||||
/*
|
||||
* for disable case, need to proceed still for mclk_mux1
|
||||
* counter to decrement
|
||||
*/
|
||||
if (enable)
|
||||
goto err;
|
||||
}
|
||||
bolero_fs_gen_enable(priv, enable);
|
||||
/*
|
||||
* need different return value as ret variable
|
||||
* is used to track mclk_mux0 enable success or fail
|
||||
*/
|
||||
ret1 = priv->macro_params[mclk_mux1_macro].mclk_fn(
|
||||
priv->macro_params[mclk_mux1_macro].dev, enable);
|
||||
if (ret1 < 0)
|
||||
dev_err(dev,
|
||||
"%s: MCLK_MUX1 %s failed for macro:%d, mclk_mux1_macro:%d\n",
|
||||
__func__,
|
||||
enable ? "enable" : "disable",
|
||||
macro_id, mclk_mux1_macro);
|
||||
/* disable mclk_mux0 only if ret is success(0) */
|
||||
if (!ret)
|
||||
priv->macro_params[mclk_mux0_macro].mclk_fn(
|
||||
priv->macro_params[mclk_mux0_macro].dev,
|
||||
false);
|
||||
if (enable && ret1)
|
||||
goto err;
|
||||
break;
|
||||
case MCLK_MUX_MAX:
|
||||
default:
|
||||
dev_err(dev, "%s: invalid mclk_mux_id: %d\n",
|
||||
__func__, mclk_mux_id);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if (enable)
|
||||
priv->current_mclk_mux_macro[macro_id] =
|
||||
bolero_mclk_mux_tbl[macro_id][mclk_mux_id];
|
||||
else
|
||||
priv->current_mclk_mux_macro[macro_id] =
|
||||
bolero_mclk_mux_tbl[macro_id][MCLK_MUX0];
|
||||
err:
|
||||
mutex_unlock(&priv->clk_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_request_clock);
|
||||
|
||||
static ssize_t bolero_version_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file,
|
||||
@ -657,6 +609,9 @@ static int bolero_ssr_enable(struct device *dev, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (priv->rsc_clk_cb)
|
||||
priv->rsc_clk_cb(priv->clk_dev, BOLERO_MACRO_EVT_SSR_UP);
|
||||
|
||||
if (priv->macro_params[VA_MACRO].event_handler)
|
||||
priv->macro_params[VA_MACRO].event_handler(
|
||||
priv->component,
|
||||
@ -683,6 +638,9 @@ static void bolero_ssr_disable(struct device *dev, void *data)
|
||||
struct bolero_priv *priv = data;
|
||||
int macro_idx;
|
||||
|
||||
if (priv->rsc_clk_cb)
|
||||
priv->rsc_clk_cb(priv->clk_dev, BOLERO_MACRO_EVT_SSR_DOWN);
|
||||
|
||||
bolero_cdc_notifier_call(priv, BOLERO_WCD_EVT_PA_OFF_PRE_SSR);
|
||||
regcache_cache_only(priv->regmap, true);
|
||||
|
||||
@ -1074,7 +1032,30 @@ static struct platform_driver bolero_drv = {
|
||||
.remove = bolero_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(bolero_drv);
|
||||
static int bolero_drv_init(void)
|
||||
{
|
||||
return platform_driver_register(&bolero_drv);
|
||||
}
|
||||
|
||||
static void bolero_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bolero_drv);
|
||||
}
|
||||
|
||||
static int __init bolero_init(void)
|
||||
{
|
||||
bolero_drv_init();
|
||||
bolero_clk_rsc_mgr_init();
|
||||
return 0;
|
||||
}
|
||||
module_init(bolero_init);
|
||||
|
||||
static void __exit bolero_exit(void)
|
||||
{
|
||||
bolero_clk_rsc_mgr_exit();
|
||||
bolero_drv_exit();
|
||||
}
|
||||
module_exit(bolero_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Bolero driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef BOLERO_CDC_H
|
||||
@ -54,16 +54,20 @@ struct macro_ops {
|
||||
int (*set_port_map)(struct snd_soc_component *component, u32 uc,
|
||||
u32 size, void *data);
|
||||
char __iomem *io_base;
|
||||
u16 clk_id_req;
|
||||
u16 default_clk_id;
|
||||
};
|
||||
|
||||
typedef int (*rsc_clk_cb_t)(struct device *dev, u16 event);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_BOLERO)
|
||||
int bolero_register_res_clk(struct device *dev, rsc_clk_cb_t cb);
|
||||
void bolero_unregister_res_clk(struct device *dev);
|
||||
int bolero_register_macro(struct device *dev, u16 macro_id,
|
||||
struct macro_ops *ops);
|
||||
void bolero_unregister_macro(struct device *dev, u16 macro_id);
|
||||
struct device *bolero_get_device_ptr(struct device *dev, u16 macro_id);
|
||||
int bolero_request_clock(struct device *dev, u16 macro_id,
|
||||
enum mclk_mux mclk_mux_id,
|
||||
bool enable);
|
||||
struct device *bolero_get_rsc_clk_device_ptr(struct device *dev);
|
||||
int bolero_info_create_codec_entry(
|
||||
struct snd_info_entry *codec_root,
|
||||
struct snd_soc_component *component);
|
||||
@ -73,6 +77,14 @@ int bolero_runtime_resume(struct device *dev);
|
||||
int bolero_runtime_suspend(struct device *dev);
|
||||
int bolero_set_port_map(struct snd_soc_component *component, u32 size, void *data);
|
||||
#else
|
||||
static inline int bolero_register_res_clk(struct device *dev, rsc_clk_cb_t cb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void bolero_unregister_res_clk(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int bolero_register_macro(struct device *dev,
|
||||
u16 macro_id,
|
||||
struct macro_ops *ops)
|
||||
@ -90,13 +102,6 @@ static inline struct device *bolero_get_device_ptr(struct device *dev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int bolero_request_clock(struct device *dev, u16 macro_id,
|
||||
enum mclk_mux mclk_mux_id,
|
||||
bool enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bolero_info_create_codec_entry(
|
||||
struct snd_info_entry *codec_root,
|
||||
struct snd_soc_component *component)
|
||||
|
613
asoc/codecs/bolero/bolero-clk-rsc.c
Normal file
613
asoc/codecs/bolero/bolero-clk-rsc.c
Normal file
@ -0,0 +1,613 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include "bolero-cdc.h"
|
||||
#include "bolero-clk-rsc.h"
|
||||
|
||||
#define DRV_NAME "bolero-clk-rsc"
|
||||
#define BOLERO_CLK_NAME_LENGTH 30
|
||||
#define NPL_CLK_OFFSET (TX_NPL_CLK - TX_CORE_CLK)
|
||||
|
||||
static char clk_src_name[MAX_CLK][BOLERO_CLK_NAME_LENGTH] = {
|
||||
"tx_core_clk",
|
||||
"rx_core_clk",
|
||||
"wsa_core_clk",
|
||||
"va_core_clk",
|
||||
"tx_npl_clk",
|
||||
"rx_npl_clk",
|
||||
"wsa_npl_clk",
|
||||
"va_npl_clk",
|
||||
};
|
||||
|
||||
struct bolero_clk_rsc {
|
||||
struct device *dev;
|
||||
struct mutex rsc_clk_lock;
|
||||
struct clk *clk[MAX_CLK];
|
||||
int clk_cnt[MAX_CLK];
|
||||
int reg_seq_en_cnt;
|
||||
int va_tx_clk_cnt;
|
||||
bool dev_up;
|
||||
u32 num_fs_reg;
|
||||
u32 *fs_gen_seq;
|
||||
int default_clk_id[MAX_CLK];
|
||||
struct regmap *regmap;
|
||||
char __iomem *rx_clk_muxsel;
|
||||
char __iomem *wsa_clk_muxsel;
|
||||
char __iomem *va_clk_muxsel;
|
||||
};
|
||||
|
||||
static int bolero_clk_rsc_cb(struct device *dev, u16 event)
|
||||
{
|
||||
struct bolero_clk_rsc *priv;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: Invalid device pointer\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv = dev_get_drvdata(dev);
|
||||
if (!priv) {
|
||||
pr_err("%s: Invalid clk rsc priviate data\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->rsc_clk_lock);
|
||||
if (event == BOLERO_MACRO_EVT_SSR_UP)
|
||||
priv->dev_up = true;
|
||||
else if (event == BOLERO_MACRO_EVT_SSR_DOWN)
|
||||
priv->dev_up = false;
|
||||
mutex_unlock(&priv->rsc_clk_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __iomem *bolero_clk_rsc_get_clk_muxsel(struct bolero_clk_rsc *priv,
|
||||
int clk_id)
|
||||
{
|
||||
switch (clk_id) {
|
||||
case RX_CORE_CLK:
|
||||
return priv->rx_clk_muxsel;
|
||||
case WSA_CORE_CLK:
|
||||
return priv->wsa_clk_muxsel;
|
||||
case VA_CORE_CLK:
|
||||
return priv->va_clk_muxsel;
|
||||
case TX_CORE_CLK:
|
||||
default:
|
||||
dev_err_ratelimited(priv->dev, "%s: Invalid case\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int bolero_clk_rsc_mux0_clk_request(struct bolero_clk_rsc *priv,
|
||||
int clk_id,
|
||||
bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (enable) {
|
||||
/* Enable Requested Core clk */
|
||||
if (priv->clk_cnt[clk_id] == 0) {
|
||||
ret = clk_prepare_enable(priv->clk[clk_id]);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s:clk_id %d enable failed\n",
|
||||
__func__, clk_id);
|
||||
goto done;
|
||||
}
|
||||
if (priv->clk[clk_id + NPL_CLK_OFFSET]) {
|
||||
ret = clk_prepare_enable(
|
||||
priv->clk[clk_id + NPL_CLK_OFFSET]);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s:clk_id %d enable failed\n",
|
||||
__func__,
|
||||
clk_id + NPL_CLK_OFFSET);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
priv->clk_cnt[clk_id]++;
|
||||
} else {
|
||||
if (priv->clk_cnt[clk_id] <= 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s: clk_id: %d is already disabled\n",
|
||||
__func__, clk_id);
|
||||
priv->clk_cnt[clk_id] = 0;
|
||||
goto done;
|
||||
}
|
||||
priv->clk_cnt[clk_id]--;
|
||||
if (priv->clk_cnt[clk_id] == 0) {
|
||||
if (priv->clk[clk_id + NPL_CLK_OFFSET])
|
||||
clk_disable_unprepare(
|
||||
priv->clk[clk_id + NPL_CLK_OFFSET]);
|
||||
clk_disable_unprepare(priv->clk[clk_id]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
err:
|
||||
clk_disable_unprepare(priv->clk[clk_id]);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bolero_clk_rsc_mux1_clk_request(struct bolero_clk_rsc *priv,
|
||||
int clk_id,
|
||||
bool enable)
|
||||
{
|
||||
char __iomem *clk_muxsel = NULL;
|
||||
int ret = 0;
|
||||
int default_clk_id = priv->default_clk_id[clk_id];
|
||||
|
||||
clk_muxsel = bolero_clk_rsc_get_clk_muxsel(priv, clk_id);
|
||||
if (!clk_muxsel) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
if (priv->clk_cnt[clk_id] == 0) {
|
||||
ret = bolero_clk_rsc_mux0_clk_request(priv, default_clk_id,
|
||||
true);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk[clk_id]);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s:clk_id %d enable failed\n",
|
||||
__func__, clk_id);
|
||||
goto err_clk;
|
||||
}
|
||||
if (priv->clk[clk_id + NPL_CLK_OFFSET]) {
|
||||
ret = clk_prepare_enable(
|
||||
priv->clk[clk_id + NPL_CLK_OFFSET]);
|
||||
if (ret < 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s:clk_id %d enable failed\n",
|
||||
__func__,
|
||||
clk_id + NPL_CLK_OFFSET);
|
||||
goto err_npl_clk;
|
||||
}
|
||||
}
|
||||
iowrite32(0x1, clk_muxsel);
|
||||
bolero_clk_rsc_mux0_clk_request(priv, default_clk_id,
|
||||
false);
|
||||
}
|
||||
priv->clk_cnt[clk_id]++;
|
||||
} else {
|
||||
if (priv->clk_cnt[clk_id] <= 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s: clk_id: %d is already disabled\n",
|
||||
__func__, clk_id);
|
||||
priv->clk_cnt[clk_id] = 0;
|
||||
goto done;
|
||||
}
|
||||
priv->clk_cnt[clk_id]--;
|
||||
if (priv->clk_cnt[clk_id] == 0) {
|
||||
bolero_clk_rsc_mux0_clk_request(priv, default_clk_id,
|
||||
true);
|
||||
|
||||
iowrite32(0x0, clk_muxsel);
|
||||
if (priv->clk[clk_id + NPL_CLK_OFFSET])
|
||||
clk_disable_unprepare(
|
||||
priv->clk[clk_id + NPL_CLK_OFFSET]);
|
||||
clk_disable_unprepare(priv->clk[clk_id]);
|
||||
|
||||
bolero_clk_rsc_mux0_clk_request(priv, default_clk_id,
|
||||
false);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
err_npl_clk:
|
||||
clk_disable_unprepare(priv->clk[clk_id]);
|
||||
|
||||
err_clk:
|
||||
bolero_clk_rsc_mux0_clk_request(priv, default_clk_id, false);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bolero_clk_rsc_check_and_update_va_clk(struct bolero_clk_rsc *priv,
|
||||
bool mux_switch,
|
||||
int clk_id,
|
||||
bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (enable) {
|
||||
if (clk_id == VA_CORE_CLK && mux_switch) {
|
||||
/*
|
||||
* Handle the following usecase scenarios during enable
|
||||
* 1. VA only, Active clk is VA_CORE_CLK
|
||||
* 2. record -> record + VA, Active clk is TX_CORE_CLK
|
||||
*/
|
||||
if (priv->clk_cnt[TX_CORE_CLK] == 0) {
|
||||
ret = bolero_clk_rsc_mux1_clk_request(priv,
|
||||
VA_CORE_CLK, enable);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
} else {
|
||||
ret = bolero_clk_rsc_mux0_clk_request(priv,
|
||||
TX_CORE_CLK, enable);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
priv->va_tx_clk_cnt++;
|
||||
}
|
||||
} else if ((priv->clk_cnt[TX_CORE_CLK] > 0) &&
|
||||
(priv->clk_cnt[VA_CORE_CLK] > 0)) {
|
||||
/*
|
||||
* Handle following concurrency scenario during enable
|
||||
* 1. VA-> Record+VA, Increment TX CLK and Disable VA
|
||||
* 2. VA-> Playback+VA, Increment TX CLK and Disable VA
|
||||
*/
|
||||
while (priv->clk_cnt[VA_CORE_CLK] > 0) {
|
||||
ret = bolero_clk_rsc_mux0_clk_request(priv,
|
||||
TX_CORE_CLK, true);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
bolero_clk_rsc_mux1_clk_request(priv,
|
||||
VA_CORE_CLK, false);
|
||||
priv->va_tx_clk_cnt++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (clk_id == VA_CORE_CLK && mux_switch) {
|
||||
/*
|
||||
* Handle the following usecase scenarios during disable
|
||||
* 1. VA only, disable VA_CORE_CLK
|
||||
* 2. Record + VA -> Record, decrement TX CLK count
|
||||
*/
|
||||
if (priv->clk_cnt[VA_CORE_CLK]) {
|
||||
bolero_clk_rsc_mux1_clk_request(priv,
|
||||
VA_CORE_CLK, enable);
|
||||
} else if (priv->va_tx_clk_cnt) {
|
||||
bolero_clk_rsc_mux0_clk_request(priv,
|
||||
TX_CORE_CLK, enable);
|
||||
priv->va_tx_clk_cnt--;
|
||||
}
|
||||
} else if (priv->va_tx_clk_cnt == priv->clk_cnt[TX_CORE_CLK]) {
|
||||
/*
|
||||
* Handle the following usecase scenarios during disable
|
||||
* Record+VA-> VA: enable VA CLK, decrement TX CLK count
|
||||
*/
|
||||
while (priv->va_tx_clk_cnt) {
|
||||
ret = bolero_clk_rsc_mux1_clk_request(priv,
|
||||
VA_CORE_CLK, true);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
bolero_clk_rsc_mux0_clk_request(priv,
|
||||
TX_CORE_CLK, false);
|
||||
priv->va_tx_clk_cnt--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bolero_clk_rsc_fs_gen_request - request to enable/disable fs generation
|
||||
* sequence
|
||||
*
|
||||
* @dev: Macro device pointer
|
||||
* @enable: enable or disable flag
|
||||
*/
|
||||
void bolero_clk_rsc_fs_gen_request(struct device *dev, bool enable)
|
||||
{
|
||||
int i;
|
||||
struct regmap *regmap;
|
||||
struct device *clk_dev = NULL;
|
||||
struct bolero_clk_rsc *priv = NULL;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: dev is null %d\n", __func__);
|
||||
return;
|
||||
}
|
||||
clk_dev = bolero_get_rsc_clk_device_ptr(dev->parent);
|
||||
if (!clk_dev) {
|
||||
pr_err("%s: Invalid rsc clk device\n", __func__);
|
||||
return;
|
||||
}
|
||||
priv = dev_get_drvdata(clk_dev);
|
||||
if (!priv) {
|
||||
pr_err("%s: Invalid rsc clk priviate data\n", __func__);
|
||||
return;
|
||||
}
|
||||
regmap = dev_get_regmap(priv->dev->parent, NULL);
|
||||
if (enable) {
|
||||
if (priv->reg_seq_en_cnt++ == 0) {
|
||||
for (i = 0; i < (priv->num_fs_reg * 2); i += 2) {
|
||||
dev_dbg(priv->dev, "%s: Register: %d, value: %d\n",
|
||||
__func__, priv->fs_gen_seq[i],
|
||||
priv->fs_gen_seq[i + 1]);
|
||||
regmap_update_bits(regmap,
|
||||
priv->fs_gen_seq[i],
|
||||
priv->fs_gen_seq[i + 1],
|
||||
priv->fs_gen_seq[i + 1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (priv->reg_seq_en_cnt <= 0) {
|
||||
dev_err_ratelimited(priv->dev, "%s: req_seq_cnt: %d is already disabled\n",
|
||||
__func__, priv->reg_seq_en_cnt);
|
||||
priv->reg_seq_en_cnt = 0;
|
||||
return;
|
||||
}
|
||||
if (--priv->reg_seq_en_cnt == 0) {
|
||||
for (i = ((priv->num_fs_reg - 1) * 2); i >= 0; i -= 2) {
|
||||
dev_dbg(priv->dev, "%s: Register: %d, value: %d\n",
|
||||
__func__, priv->fs_gen_seq[i],
|
||||
priv->fs_gen_seq[i + 1]);
|
||||
regmap_update_bits(regmap, priv->fs_gen_seq[i],
|
||||
priv->fs_gen_seq[i + 1], 0x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_clk_rsc_fs_gen_request);
|
||||
|
||||
/**
|
||||
* bolero_clk_rsc_request_clock - request for clock to
|
||||
* enable/disable
|
||||
*
|
||||
* @dev: Macro device pointer.
|
||||
* @default_clk_id: mux0 Core clock ID input.
|
||||
* @clk_id_req: Core clock ID requested to enable/disable
|
||||
* @enable: enable or disable clock flag
|
||||
*
|
||||
* Returns 0 on success or -EINVAL on error.
|
||||
*/
|
||||
int bolero_clk_rsc_request_clock(struct device *dev,
|
||||
int default_clk_id,
|
||||
int clk_id_req,
|
||||
bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *clk_dev = NULL;
|
||||
struct bolero_clk_rsc *priv = NULL;
|
||||
bool mux_switch = false;
|
||||
|
||||
if (!dev) {
|
||||
pr_err("%s: dev is null %d\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((clk_id_req < 0 || clk_id_req >= MAX_CLK) &&
|
||||
(default_clk_id < 0 || default_clk_id >= MAX_CLK)) {
|
||||
pr_err("%s: Invalid clk_id_req: %d or default_clk_id: %d\n",
|
||||
__func__, clk_id_req, default_clk_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
clk_dev = bolero_get_rsc_clk_device_ptr(dev->parent);
|
||||
if (!clk_dev) {
|
||||
pr_err("%s: Invalid rsc clk device\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv = dev_get_drvdata(clk_dev);
|
||||
if (!priv) {
|
||||
pr_err("%s: Invalid rsc clk priviate data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&priv->rsc_clk_lock);
|
||||
if (!priv->dev_up) {
|
||||
dev_err_ratelimited(priv->dev, "%s: SSR is in progress..\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
priv->default_clk_id[clk_id_req] = default_clk_id;
|
||||
if (default_clk_id != clk_id_req)
|
||||
mux_switch = true;
|
||||
|
||||
if (mux_switch) {
|
||||
if (clk_id_req != VA_CORE_CLK) {
|
||||
ret = bolero_clk_rsc_mux1_clk_request(priv, clk_id_req,
|
||||
enable);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
ret = bolero_clk_rsc_mux0_clk_request(priv, clk_id_req, enable);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = bolero_clk_rsc_check_and_update_va_clk(priv, mux_switch,
|
||||
clk_id_req,
|
||||
enable);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
dev_dbg(priv->dev, "%s: clk_cnt: %d for requested clk: %d, enable: %d\n",
|
||||
__func__, priv->clk_cnt[clk_id_req], clk_id_req,
|
||||
enable);
|
||||
|
||||
mutex_unlock(&priv->rsc_clk_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mutex_unlock(&priv->rsc_clk_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(bolero_clk_rsc_request_clock);
|
||||
|
||||
|
||||
static int bolero_clk_rsc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0, fs_gen_size, i, j;
|
||||
const char **clk_name_array;
|
||||
int clk_cnt;
|
||||
struct clk *clk;
|
||||
struct bolero_clk_rsc *priv = NULL;
|
||||
u32 muxsel = 0;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct bolero_clk_rsc),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get clk fs gen sequence from device tree */
|
||||
if (!of_find_property(pdev->dev.of_node, "qcom,fs-gen-sequence",
|
||||
&fs_gen_size)) {
|
||||
dev_err(&pdev->dev, "%s: unable to find qcom,fs-gen-sequence property\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
priv->num_fs_reg = fs_gen_size/(2 * sizeof(u32));
|
||||
priv->fs_gen_seq = devm_kzalloc(&pdev->dev, fs_gen_size, GFP_KERNEL);
|
||||
if (!priv->fs_gen_seq) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
dev_dbg(&pdev->dev, "%s: num_fs_reg %d\n", __func__, priv->num_fs_reg);
|
||||
/* Parse fs-gen-sequence */
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
"qcom,fs-gen-sequence",
|
||||
priv->fs_gen_seq,
|
||||
priv->num_fs_reg * 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "%s: unable to parse fs-gen-sequence, ret = %d\n",
|
||||
__func__, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Get clk details from device tree */
|
||||
clk_cnt = of_property_count_strings(pdev->dev.of_node, "clock-names");
|
||||
if (clk_cnt <= 0 || clk_cnt > MAX_CLK) {
|
||||
dev_err(&pdev->dev, "%s: Invalid number of clocks %d",
|
||||
__func__, clk_cnt);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
clk_name_array = devm_kzalloc(&pdev->dev, clk_cnt * sizeof(char *),
|
||||
GFP_KERNEL);
|
||||
|
||||
ret = of_property_read_string_array(pdev->dev.of_node, "clock-names",
|
||||
clk_name_array, clk_cnt);
|
||||
|
||||
for (i = 0; i < MAX_CLK; i++) {
|
||||
priv->clk[i] = NULL;
|
||||
for (j = 0; j < clk_cnt; j++) {
|
||||
if (!strcmp(clk_src_name[i], clk_name_array[j])) {
|
||||
clk = devm_clk_get(&pdev->dev, clk_src_name[i]);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
dev_err(&pdev->dev, "%s: clk get failed for %s with ret %d\n",
|
||||
__func__, clk_src_name[i], ret);
|
||||
goto err;
|
||||
}
|
||||
priv->clk[i] = clk;
|
||||
dev_dbg(&pdev->dev, "%s: clk get success for clk name %s\n",
|
||||
__func__, clk_src_name[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,rx_mclk_mode_muxsel", &muxsel);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "%s: could not find qcom,rx_mclk_mode_muxsel entry in dt\n",
|
||||
__func__);
|
||||
} else {
|
||||
priv->rx_clk_muxsel = devm_ioremap(&pdev->dev, muxsel, 0x4);
|
||||
if (!priv->rx_clk_muxsel) {
|
||||
dev_err(&pdev->dev, "%s: ioremap failed for rx muxsel\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,wsa_mclk_mode_muxsel", &muxsel);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "%s: could not find qcom,wsa_mclk_mode_muxsel entry in dt\n",
|
||||
__func__);
|
||||
} else {
|
||||
priv->wsa_clk_muxsel = devm_ioremap(&pdev->dev, muxsel, 0x4);
|
||||
if (!priv->wsa_clk_muxsel) {
|
||||
dev_err(&pdev->dev, "%s: ioremap failed for wsa muxsel\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,va_mclk_mode_muxsel", &muxsel);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "%s: could not find qcom,va_mclk_mode_muxsel entry in dt\n",
|
||||
__func__);
|
||||
} else {
|
||||
priv->va_clk_muxsel = devm_ioremap(&pdev->dev, muxsel, 0x4);
|
||||
if (!priv->va_clk_muxsel) {
|
||||
dev_err(&pdev->dev, "%s: ioremap failed for va muxsel\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bolero_register_res_clk(&pdev->dev, bolero_clk_rsc_cb);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "%s: Failed to register cb %d",
|
||||
__func__, ret);
|
||||
goto err;
|
||||
}
|
||||
priv->dev = &pdev->dev;
|
||||
priv->dev_up = true;
|
||||
mutex_init(&priv->rsc_clk_lock);
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bolero_clk_rsc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bolero_clk_rsc *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
bolero_unregister_res_clk(&pdev->dev);
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
mutex_destroy(&priv->rsc_clk_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bolero_clk_rsc_dt_match[] = {
|
||||
{.compatible = "qcom,bolero-clk-rsc-mngr"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bolero_clk_rsc_dt_match);
|
||||
|
||||
static struct platform_driver bolero_clk_rsc_mgr = {
|
||||
.driver = {
|
||||
.name = "bolero-clk-rsc-mngr",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = bolero_clk_rsc_dt_match,
|
||||
},
|
||||
.probe = bolero_clk_rsc_probe,
|
||||
.remove = bolero_clk_rsc_remove,
|
||||
};
|
||||
|
||||
int bolero_clk_rsc_mgr_init(void)
|
||||
{
|
||||
return platform_driver_register(&bolero_clk_rsc_mgr);
|
||||
}
|
||||
|
||||
void bolero_clk_rsc_mgr_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bolero_clk_rsc_mgr);
|
||||
}
|
||||
MODULE_DESCRIPTION("Bolero clock resource manager driver");
|
||||
MODULE_LICENSE("GPL v2");
|
42
asoc/codecs/bolero/bolero-clk-rsc.h
Normal file
42
asoc/codecs/bolero/bolero-clk-rsc.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef BOLERO_CLK_RSC_H
|
||||
#define BOLERO_CLK_RSC_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <dt-bindings/sound/qcom,bolero-clk-rsc.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_BOLERO)
|
||||
int bolero_clk_rsc_mgr_init(void);
|
||||
void bolero_clk_rsc_mgr_exit(void);
|
||||
void bolero_clk_rsc_fs_gen_request(struct device *dev,
|
||||
bool enable);
|
||||
int bolero_clk_rsc_request_clock(struct device *dev,
|
||||
int default_clk_id,
|
||||
int clk_id_req,
|
||||
bool enable);
|
||||
#else
|
||||
static inline void bolero_clk_rsc_fs_gen_request(struct device *dev,
|
||||
bool enable)
|
||||
{
|
||||
}
|
||||
static inline int bolero_clk_rsc_mgr_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void bolero_clk_rsc_mgr_exit(void)
|
||||
{
|
||||
}
|
||||
static inline int bolero_clk_rsc_request_clock(struct device *dev,
|
||||
int default_clk_id,
|
||||
int clk_id_req,
|
||||
bool enable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SND_SOC_BOLERO */
|
||||
#endif /* BOLERO_CLK_RSC_H */
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "bolero-cdc-registers.h"
|
||||
|
||||
#define BOLERO_CDC_CHILD_DEVICES_MAX 5
|
||||
#define BOLERO_CDC_CHILD_DEVICES_MAX 6
|
||||
|
||||
/* from bolero to WCD events */
|
||||
enum {
|
||||
@ -73,7 +73,8 @@ struct bolero_priv {
|
||||
struct wcd_ctrl_platform_data plat_data;
|
||||
struct device *wcd_dev;
|
||||
struct blocking_notifier_head notifier;
|
||||
int clk_users;
|
||||
struct device *clk_dev;
|
||||
rsc_clk_cb_t rsc_clk_cb;
|
||||
};
|
||||
|
||||
struct regmap *bolero_regmap_init(struct device *dev,
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <asoc/msm-cdc-pinctrl.h>
|
||||
#include "bolero-cdc.h"
|
||||
#include "bolero-cdc-registers.h"
|
||||
#include "bolero-clk-rsc.h"
|
||||
|
||||
#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
|
||||
@ -331,8 +332,6 @@ enum {
|
||||
* @swr_plat_data: Soundwire platform data
|
||||
* @rx_macro_add_child_devices_work: work for adding child devices
|
||||
* @rx_swr_gpio_p: used by pinctrl API
|
||||
* @rx_core_clk: MCLK for rx macro
|
||||
* @rx_npl_clk: NPL clock for RX soundwire
|
||||
* @component: codec handle
|
||||
*/
|
||||
struct rx_macro_priv {
|
||||
@ -353,15 +352,12 @@ struct rx_macro_priv {
|
||||
bool dev_up;
|
||||
bool hph_pwr_mode;
|
||||
bool hph_hd2_mode;
|
||||
u16 mclk_mux;
|
||||
struct mutex mclk_lock;
|
||||
struct mutex swr_clk_lock;
|
||||
struct rx_swr_ctrl_data *swr_ctrl_data;
|
||||
struct rx_swr_ctrl_platform_data swr_plat_data;
|
||||
struct work_struct rx_macro_add_child_devices_work;
|
||||
struct device_node *rx_swr_gpio_p;
|
||||
struct clk *rx_core_clk;
|
||||
struct clk *rx_npl_clk;
|
||||
struct snd_soc_component *component;
|
||||
unsigned long active_ch_mask[RX_MACRO_MAX_DAIS];
|
||||
unsigned long active_ch_cnt[RX_MACRO_MAX_DAIS];
|
||||
@ -378,6 +374,8 @@ struct rx_macro_priv {
|
||||
int is_softclip_on;
|
||||
int softclip_clk_users;
|
||||
struct rx_macro_bcl_pmic_params bcl_pmic_params;
|
||||
u16 clk_id;
|
||||
u16 default_clk_id;
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rx_macro_dai[];
|
||||
@ -1059,7 +1057,7 @@ static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv,
|
||||
bool mclk_enable, bool dapm)
|
||||
{
|
||||
struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL);
|
||||
int ret = 0, mclk_mux = MCLK_MUX0;
|
||||
int ret = 0;
|
||||
|
||||
if (regmap == NULL) {
|
||||
dev_err(rx_priv->dev, "%s: regmap is NULL\n", __func__);
|
||||
@ -1073,16 +1071,19 @@ static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv,
|
||||
if (mclk_enable) {
|
||||
if (rx_priv->rx_mclk_users == 0) {
|
||||
if (rx_priv->is_native_on)
|
||||
mclk_mux = MCLK_MUX1;
|
||||
ret = bolero_request_clock(rx_priv->dev,
|
||||
RX_MACRO, mclk_mux, true);
|
||||
rx_priv->clk_id = RX_CORE_CLK;
|
||||
ret = bolero_clk_rsc_request_clock(rx_priv->dev,
|
||||
rx_priv->default_clk_id,
|
||||
rx_priv->clk_id,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err(rx_priv->dev,
|
||||
"%s: rx request clock enable failed\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
rx_priv->mclk_mux = mclk_mux;
|
||||
bolero_clk_rsc_fs_gen_request(rx_priv->dev,
|
||||
true);
|
||||
regcache_mark_dirty(regmap);
|
||||
regcache_sync_region(regmap,
|
||||
RX_START_OFFSET,
|
||||
@ -1113,10 +1114,13 @@ static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv,
|
||||
regmap_update_bits(regmap,
|
||||
BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL,
|
||||
0x01, 0x00);
|
||||
mclk_mux = rx_priv->mclk_mux;
|
||||
bolero_request_clock(rx_priv->dev,
|
||||
RX_MACRO, mclk_mux, false);
|
||||
rx_priv->mclk_mux = MCLK_MUX0;
|
||||
bolero_clk_rsc_fs_gen_request(rx_priv->dev,
|
||||
false);
|
||||
bolero_clk_rsc_request_clock(rx_priv->dev,
|
||||
rx_priv->default_clk_id,
|
||||
rx_priv->clk_id,
|
||||
false);
|
||||
rx_priv->clk_id = rx_priv->default_clk_id;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
@ -1142,9 +1146,9 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w,
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
/* if swr_clk_users > 0, call device down */
|
||||
if (rx_priv->swr_clk_users > 0) {
|
||||
if ((rx_priv->mclk_mux == MCLK_MUX0 &&
|
||||
if ((rx_priv->clk_id == rx_priv->default_clk_id &&
|
||||
rx_priv->is_native_on) ||
|
||||
(rx_priv->mclk_mux == MCLK_MUX1 &&
|
||||
(rx_priv->clk_id == RX_CORE_CLK &&
|
||||
!rx_priv->is_native_on)) {
|
||||
swrm_wcd_notify(
|
||||
rx_priv->swr_ctrl_data[0].rx_swr_pdev,
|
||||
@ -1174,51 +1178,13 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rx_macro_mclk_ctrl(struct device *dev, bool enable)
|
||||
{
|
||||
struct rx_macro_priv *rx_priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(rx_priv->rx_core_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:rx mclk enable failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_prepare_enable(rx_priv->rx_npl_clk);
|
||||
if (ret < 0) {
|
||||
clk_disable_unprepare(rx_priv->rx_core_clk);
|
||||
dev_err(dev, "%s:rx npl_clk enable failed\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
if (rx_priv->rx_mclk_cnt++ == 0) {
|
||||
if (rx_priv->dev_up)
|
||||
iowrite32(0x1, rx_priv->rx_mclk_mode_muxsel);
|
||||
}
|
||||
} else {
|
||||
if (rx_priv->rx_mclk_cnt <= 0) {
|
||||
dev_dbg(dev, "%s:rx mclk already disabled\n", __func__);
|
||||
rx_priv->rx_mclk_cnt = 0;
|
||||
return 0;
|
||||
}
|
||||
if (--rx_priv->rx_mclk_cnt == 0) {
|
||||
if (rx_priv->dev_up)
|
||||
iowrite32(0x0, rx_priv->rx_mclk_mode_muxsel);
|
||||
}
|
||||
clk_disable_unprepare(rx_priv->rx_npl_clk);
|
||||
clk_disable_unprepare(rx_priv->rx_core_clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx_macro_event_handler(struct snd_soc_component *component,
|
||||
u16 event, u32 data)
|
||||
{
|
||||
u16 reg = 0, reg_mix = 0, rx_idx = 0, mute = 0x0, val = 0;
|
||||
struct device *rx_dev = NULL;
|
||||
struct rx_macro_priv *rx_priv = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (!rx_macro_get_data(component, &rx_dev, &rx_priv, __func__))
|
||||
return -EINVAL;
|
||||
@ -1256,17 +1222,25 @@ static int rx_macro_event_handler(struct snd_soc_component *component,
|
||||
rx_priv->dev_up = true;
|
||||
/* reset swr after ssr/pdr */
|
||||
rx_priv->reset_swr = true;
|
||||
/* enable&disable MCLK_MUX1 to reset GFMUX reg */
|
||||
bolero_request_clock(rx_priv->dev,
|
||||
RX_MACRO, MCLK_MUX1, true);
|
||||
bolero_request_clock(rx_priv->dev,
|
||||
RX_MACRO, MCLK_MUX1, false);
|
||||
/* enable&disable RX_CORE_CLK to reset GFMUX reg */
|
||||
ret = bolero_clk_rsc_request_clock(rx_priv->dev,
|
||||
rx_priv->default_clk_id,
|
||||
RX_CORE_CLK, true);
|
||||
if (ret < 0)
|
||||
dev_err_ratelimited(rx_priv->dev,
|
||||
"%s, failed to enable clk, ret:%d\n",
|
||||
__func__, ret);
|
||||
else
|
||||
bolero_clk_rsc_request_clock(rx_priv->dev,
|
||||
rx_priv->default_clk_id,
|
||||
RX_CORE_CLK, false);
|
||||
|
||||
swrm_wcd_notify(
|
||||
rx_priv->swr_ctrl_data[0].rx_swr_pdev,
|
||||
SWR_DEVICE_SSR_UP, NULL);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rx_macro_find_playback_dai_id_for_port(int port_id,
|
||||
@ -3573,7 +3547,6 @@ static void rx_macro_init_ops(struct macro_ops *ops, char __iomem *rx_io_base)
|
||||
ops->io_base = rx_io_base;
|
||||
ops->dai_ptr = rx_macro_dai;
|
||||
ops->num_dais = ARRAY_SIZE(rx_macro_dai);
|
||||
ops->mclk_fn = rx_macro_mclk_ctrl;
|
||||
ops->event_handler = rx_macro_event_handler;
|
||||
ops->set_port_map = rx_macro_set_port_map;
|
||||
}
|
||||
@ -3585,8 +3558,8 @@ static int rx_macro_probe(struct platform_device *pdev)
|
||||
u32 rx_base_addr = 0, muxsel = 0;
|
||||
char __iomem *rx_io_base = NULL, *muxsel_io = NULL;
|
||||
int ret = 0;
|
||||
struct clk *rx_core_clk = NULL, *rx_npl_clk = NULL;
|
||||
u8 bcl_pmic_params[3];
|
||||
u32 default_clk_id = 0;
|
||||
|
||||
rx_priv = devm_kzalloc(&pdev->dev, sizeof(struct rx_macro_priv),
|
||||
GFP_KERNEL);
|
||||
@ -3608,6 +3581,13 @@ static int rx_macro_probe(struct platform_device *pdev)
|
||||
__func__, "reg");
|
||||
return ret;
|
||||
}
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,default-clk-id",
|
||||
&default_clk_id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: could not find %s entry in dt\n",
|
||||
__func__, "qcom,default-clk-id");
|
||||
default_clk_id = RX_CORE_CLK;
|
||||
}
|
||||
rx_priv->rx_swr_gpio_p = of_parse_phandle(pdev->dev.of_node,
|
||||
"qcom,rx-swr-gpios", 0);
|
||||
if (!rx_priv->rx_swr_gpio_p) {
|
||||
@ -3639,25 +3619,6 @@ static int rx_macro_probe(struct platform_device *pdev)
|
||||
rx_priv->swr_plat_data.clk = rx_swrm_clock;
|
||||
rx_priv->swr_plat_data.handle_irq = NULL;
|
||||
|
||||
/* Register MCLK for rx macro */
|
||||
rx_core_clk = devm_clk_get(&pdev->dev, "rx_core_clk");
|
||||
if (IS_ERR(rx_core_clk)) {
|
||||
ret = PTR_ERR(rx_core_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed %d\n",
|
||||
__func__, "rx_core_clk", ret);
|
||||
return ret;
|
||||
}
|
||||
rx_priv->rx_core_clk = rx_core_clk;
|
||||
/* Register npl clk for soundwire */
|
||||
rx_npl_clk = devm_clk_get(&pdev->dev, "rx_npl_clk");
|
||||
if (IS_ERR(rx_npl_clk)) {
|
||||
ret = PTR_ERR(rx_npl_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed %d\n",
|
||||
__func__, "rx_npl_clk", ret);
|
||||
return ret;
|
||||
}
|
||||
rx_priv->rx_npl_clk = rx_npl_clk;
|
||||
|
||||
ret = of_property_read_u8_array(pdev->dev.of_node,
|
||||
"qcom,rx-bcl-pmic-params", bcl_pmic_params,
|
||||
sizeof(bcl_pmic_params));
|
||||
@ -3669,6 +3630,10 @@ static int rx_macro_probe(struct platform_device *pdev)
|
||||
rx_priv->bcl_pmic_params.sid = bcl_pmic_params[1];
|
||||
rx_priv->bcl_pmic_params.ppid = bcl_pmic_params[2];
|
||||
}
|
||||
rx_priv->clk_id = default_clk_id;
|
||||
rx_priv->default_clk_id = default_clk_id;
|
||||
ops.clk_id_req = rx_priv->clk_id;
|
||||
ops.default_clk_id = default_clk_id;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, rx_priv);
|
||||
mutex_init(&rx_priv->mclk_lock);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <asoc/msm-cdc-pinctrl.h>
|
||||
#include "bolero-cdc.h"
|
||||
#include "bolero-cdc-registers.h"
|
||||
#include "bolero-clk-rsc.h"
|
||||
|
||||
#define TX_MACRO_MAX_OFFSET 0x1000
|
||||
|
||||
@ -129,8 +130,6 @@ struct tx_macro_priv {
|
||||
int swr_clk_users;
|
||||
bool dapm_mclk_enable;
|
||||
bool reset_swr;
|
||||
struct clk *tx_core_clk;
|
||||
struct clk *tx_npl_clk;
|
||||
struct mutex mclk_lock;
|
||||
struct mutex swr_clk_lock;
|
||||
struct snd_soc_component *component;
|
||||
@ -198,14 +197,18 @@ static int tx_macro_mclk_enable(struct tx_macro_priv *tx_priv,
|
||||
mutex_lock(&tx_priv->mclk_lock);
|
||||
if (mclk_enable) {
|
||||
if (tx_priv->tx_mclk_users == 0) {
|
||||
ret = bolero_request_clock(tx_priv->dev,
|
||||
TX_MACRO, MCLK_MUX0, true);
|
||||
ret = bolero_clk_rsc_request_clock(tx_priv->dev,
|
||||
TX_CORE_CLK,
|
||||
TX_CORE_CLK,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err(tx_priv->dev,
|
||||
"%s: request clock enable failed\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
bolero_clk_rsc_fs_gen_request(tx_priv->dev,
|
||||
true);
|
||||
regcache_mark_dirty(regmap);
|
||||
regcache_sync_region(regmap,
|
||||
TX_START_OFFSET,
|
||||
@ -236,8 +239,13 @@ static int tx_macro_mclk_enable(struct tx_macro_priv *tx_priv,
|
||||
regmap_update_bits(regmap,
|
||||
BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
|
||||
0x01, 0x00);
|
||||
bolero_request_clock(tx_priv->dev,
|
||||
TX_MACRO, MCLK_MUX0, false);
|
||||
bolero_clk_rsc_fs_gen_request(tx_priv->dev,
|
||||
false);
|
||||
|
||||
bolero_clk_rsc_request_clock(tx_priv->dev,
|
||||
TX_CORE_CLK,
|
||||
TX_CORE_CLK,
|
||||
false);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
@ -278,33 +286,6 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tx_macro_mclk_ctrl(struct device *dev, bool enable)
|
||||
{
|
||||
struct tx_macro_priv *tx_priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(tx_priv->tx_core_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:tx mclk enable failed\n", __func__);
|
||||
goto exit;
|
||||
}
|
||||
ret = clk_prepare_enable(tx_priv->tx_npl_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:tx npl_clk enable failed\n",
|
||||
__func__);
|
||||
clk_disable_unprepare(tx_priv->tx_core_clk);
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
clk_disable_unprepare(tx_priv->tx_npl_clk);
|
||||
clk_disable_unprepare(tx_priv->tx_core_clk);
|
||||
}
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tx_macro_event_handler(struct snd_soc_component *component,
|
||||
u16 event, u32 data)
|
||||
{
|
||||
@ -1769,7 +1750,6 @@ static void tx_macro_init_ops(struct macro_ops *ops,
|
||||
ops->io_base = tx_io_base;
|
||||
ops->dai_ptr = tx_macro_dai;
|
||||
ops->num_dais = ARRAY_SIZE(tx_macro_dai);
|
||||
ops->mclk_fn = tx_macro_mclk_ctrl;
|
||||
ops->event_handler = tx_macro_event_handler;
|
||||
ops->reg_wake_irq = tx_macro_reg_wake_irq;
|
||||
ops->set_port_map = tx_macro_set_port_map;
|
||||
@ -1781,7 +1761,6 @@ static int tx_macro_probe(struct platform_device *pdev)
|
||||
struct tx_macro_priv *tx_priv = NULL;
|
||||
u32 tx_base_addr = 0, sample_rate = 0;
|
||||
char __iomem *tx_io_base = NULL;
|
||||
struct clk *tx_core_clk = NULL, *tx_npl_clk = NULL;
|
||||
int ret = 0;
|
||||
const char *dmic_sample_rate = "qcom,tx-dmic-sample-rate";
|
||||
|
||||
@ -1835,34 +1814,19 @@ static int tx_macro_probe(struct platform_device *pdev)
|
||||
tx_priv->swr_plat_data.bulk_write = NULL;
|
||||
tx_priv->swr_plat_data.clk = tx_macro_swrm_clock;
|
||||
tx_priv->swr_plat_data.handle_irq = NULL;
|
||||
/* Register MCLK for tx macro */
|
||||
tx_core_clk = devm_clk_get(&pdev->dev, "tx_core_clk");
|
||||
if (IS_ERR(tx_core_clk)) {
|
||||
ret = PTR_ERR(tx_core_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed %d\n",
|
||||
__func__, "tx_core_clk", ret);
|
||||
return ret;
|
||||
}
|
||||
tx_priv->tx_core_clk = tx_core_clk;
|
||||
/* Register npl clk for soundwire */
|
||||
tx_npl_clk = devm_clk_get(&pdev->dev, "tx_npl_clk");
|
||||
if (IS_ERR(tx_npl_clk)) {
|
||||
ret = PTR_ERR(tx_npl_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed %d\n",
|
||||
__func__, "tx_npl_clk", ret);
|
||||
return ret;
|
||||
}
|
||||
tx_priv->tx_npl_clk = tx_npl_clk;
|
||||
|
||||
mutex_init(&tx_priv->mclk_lock);
|
||||
mutex_init(&tx_priv->swr_clk_lock);
|
||||
tx_macro_init_ops(&ops, tx_io_base);
|
||||
ops.clk_id_req = TX_CORE_CLK;
|
||||
ops.default_clk_id = TX_CORE_CLK;
|
||||
ret = bolero_register_macro(&pdev->dev, TX_MACRO, &ops);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: register macro failed\n", __func__);
|
||||
goto err_reg_macro;
|
||||
}
|
||||
|
||||
schedule_work(&tx_priv->tx_macro_add_child_devices_work);
|
||||
return 0;
|
||||
err_reg_macro:
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "bolero-cdc.h"
|
||||
#include "bolero-cdc-registers.h"
|
||||
#include "bolero-clk-rsc.h"
|
||||
|
||||
/* pm runtime auto suspend timer in msecs */
|
||||
#define VA_AUTO_SUSPEND_DELAY 100 /* delay in msec */
|
||||
@ -94,7 +95,6 @@ struct va_macro_priv {
|
||||
struct device *dev;
|
||||
bool dec_active[VA_MACRO_NUM_DECIMATORS];
|
||||
bool va_without_decimation;
|
||||
struct clk *va_core_clk;
|
||||
struct mutex mclk_lock;
|
||||
struct snd_soc_component *component;
|
||||
struct hpf_work va_hpf_work[VA_MACRO_NUM_DECIMATORS];
|
||||
@ -114,6 +114,8 @@ struct va_macro_priv {
|
||||
u32 micb_voltage;
|
||||
u32 micb_current;
|
||||
int micb_users;
|
||||
u16 default_clk_id;
|
||||
u16 clk_id;
|
||||
};
|
||||
|
||||
static bool va_macro_get_data(struct snd_soc_component *component,
|
||||
@ -153,15 +155,18 @@ static int va_macro_mclk_enable(struct va_macro_priv *va_priv,
|
||||
mutex_lock(&va_priv->mclk_lock);
|
||||
if (mclk_enable) {
|
||||
if (va_priv->va_mclk_users == 0) {
|
||||
ret = bolero_request_clock(va_priv->dev,
|
||||
VA_MACRO,
|
||||
va_priv->mclk_mux_sel, true);
|
||||
ret = bolero_clk_rsc_request_clock(va_priv->dev,
|
||||
va_priv->default_clk_id,
|
||||
va_priv->clk_id,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err(va_priv->dev,
|
||||
"%s: va request clock en failed\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
bolero_clk_rsc_fs_gen_request(va_priv->dev,
|
||||
true);
|
||||
regcache_mark_dirty(regmap);
|
||||
regcache_sync_region(regmap,
|
||||
VA_START_OFFSET,
|
||||
@ -177,9 +182,12 @@ static int va_macro_mclk_enable(struct va_macro_priv *va_priv,
|
||||
}
|
||||
va_priv->va_mclk_users--;
|
||||
if (va_priv->va_mclk_users == 0) {
|
||||
bolero_request_clock(va_priv->dev,
|
||||
VA_MACRO,
|
||||
va_priv->mclk_mux_sel, false);
|
||||
bolero_clk_rsc_fs_gen_request(va_priv->dev,
|
||||
false);
|
||||
bolero_clk_rsc_request_clock(va_priv->dev,
|
||||
va_priv->default_clk_id,
|
||||
va_priv->clk_id,
|
||||
false);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
@ -252,29 +260,6 @@ static int va_macro_mclk_event(struct snd_soc_dapm_widget *w,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int va_macro_mclk_ctrl(struct device *dev, bool enable)
|
||||
{
|
||||
struct va_macro_priv *va_priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(va_priv->va_core_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:va mclk enable failed\n", __func__);
|
||||
goto exit;
|
||||
}
|
||||
if (va_priv->mclk_mux_sel == MCLK_MUX1)
|
||||
iowrite32(0x1, va_priv->va_island_mode_muxsel);
|
||||
} else {
|
||||
if (va_priv->mclk_mux_sel == MCLK_MUX1)
|
||||
iowrite32(0x0, va_priv->va_island_mode_muxsel);
|
||||
clk_disable_unprepare(va_priv->va_core_clk);
|
||||
}
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void va_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *hpf_delayed_work;
|
||||
@ -1581,7 +1566,6 @@ static void va_macro_init_ops(struct macro_ops *ops,
|
||||
ops->init = va_macro_init;
|
||||
ops->exit = va_macro_deinit;
|
||||
ops->io_base = va_io_base;
|
||||
ops->mclk_fn = va_macro_mclk_ctrl;
|
||||
ops->event_handler = va_macro_event_handler;
|
||||
}
|
||||
|
||||
@ -1589,10 +1573,8 @@ static int va_macro_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct macro_ops ops;
|
||||
struct va_macro_priv *va_priv;
|
||||
u32 va_base_addr, sample_rate = 0, island_sel = 0;
|
||||
u32 va_base_addr, sample_rate = 0;
|
||||
char __iomem *va_io_base;
|
||||
char __iomem *va_muxsel_io = NULL;
|
||||
struct clk *va_core_clk;
|
||||
bool va_without_decimation = false;
|
||||
const char *micb_supply_str = "va-vdd-micb-supply";
|
||||
const char *micb_supply_str1 = "va-vdd-micb";
|
||||
@ -1600,7 +1582,7 @@ static int va_macro_probe(struct platform_device *pdev)
|
||||
const char *micb_current_str = "qcom,va-vdd-micb-current";
|
||||
int ret = 0;
|
||||
const char *dmic_sample_rate = "qcom,va-dmic-sample-rate";
|
||||
u16 mclk_mux_sel = MCLK_MUX0;
|
||||
u32 default_clk_id = 0;
|
||||
|
||||
va_priv = devm_kzalloc(&pdev->dev, sizeof(struct va_macro_priv),
|
||||
GFP_KERNEL);
|
||||
@ -1639,53 +1621,6 @@ static int va_macro_probe(struct platform_device *pdev)
|
||||
}
|
||||
va_priv->va_io_base = va_io_base;
|
||||
|
||||
ret = of_property_read_u16(va_priv->dev->of_node,
|
||||
"qcom,va-clk-mux-select", &mclk_mux_sel);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: could not find %s entry in dt, use default\n",
|
||||
__func__, "qcom,va-clk-mux-select");
|
||||
} else {
|
||||
if (mclk_mux_sel != MCLK_MUX0 && mclk_mux_sel != MCLK_MUX1) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: mclk_mux_sel: %d is invalid\n",
|
||||
__func__, mclk_mux_sel);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
va_priv->mclk_mux_sel = mclk_mux_sel;
|
||||
|
||||
if (va_priv->mclk_mux_sel == MCLK_MUX1) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,va-island-mode-muxsel",
|
||||
&island_sel);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: could not find %s entry in dt\n",
|
||||
__func__, "qcom,va-island-mode-muxsel");
|
||||
return ret;
|
||||
} else {
|
||||
va_muxsel_io = devm_ioremap(&pdev->dev,
|
||||
island_sel, 0x4);
|
||||
if (!va_muxsel_io) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: ioremap failed for island_sel\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
va_priv->va_island_mode_muxsel = va_muxsel_io;
|
||||
}
|
||||
/* Register MCLK for va macro */
|
||||
va_core_clk = devm_clk_get(&pdev->dev, "va_core_clk");
|
||||
if (IS_ERR(va_core_clk)) {
|
||||
ret = PTR_ERR(va_core_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed\n",
|
||||
__func__, "va_core_clk");
|
||||
return ret;
|
||||
}
|
||||
va_priv->va_core_clk = va_core_clk;
|
||||
|
||||
if (of_parse_phandle(pdev->dev.of_node, micb_supply_str, 0)) {
|
||||
va_priv->micb_supply = devm_regulator_get(&pdev->dev,
|
||||
micb_supply_str1);
|
||||
@ -1717,10 +1652,21 @@ static int va_macro_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,default-clk-id",
|
||||
&default_clk_id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: could not find %s entry in dt\n",
|
||||
__func__, "qcom,default-clk-id");
|
||||
default_clk_id = VA_CORE_CLK;
|
||||
}
|
||||
va_priv->clk_id = VA_CORE_CLK;
|
||||
va_priv->default_clk_id = default_clk_id;
|
||||
|
||||
mutex_init(&va_priv->mclk_lock);
|
||||
dev_set_drvdata(&pdev->dev, va_priv);
|
||||
va_macro_init_ops(&ops, va_io_base, va_without_decimation);
|
||||
ops.clk_id_req = va_priv->default_clk_id;
|
||||
ops.default_clk_id = va_priv->default_clk_id;
|
||||
ret = bolero_register_macro(&pdev->dev, VA_MACRO, &ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "%s: register macro failed\n", __func__);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "bolero-cdc.h"
|
||||
#include "bolero-cdc-registers.h"
|
||||
#include "wsa-macro.h"
|
||||
#include "bolero-clk-rsc.h"
|
||||
|
||||
#define WSA_MACRO_MAX_OFFSET 0x1000
|
||||
|
||||
@ -175,8 +176,6 @@ enum {
|
||||
* @swr_plat_data: Soundwire platform data
|
||||
* @wsa_macro_add_child_devices_work: work for adding child devices
|
||||
* @wsa_swr_gpio_p: used by pinctrl API
|
||||
* @wsa_core_clk: MCLK for wsa macro
|
||||
* @wsa_npl_clk: NPL clock for WSA soundwire
|
||||
* @component: codec handle
|
||||
* @rx_0_count: RX0 interpolation users
|
||||
* @rx_1_count: RX1 interpolation users
|
||||
@ -201,8 +200,6 @@ struct wsa_macro_priv {
|
||||
struct wsa_macro_swr_ctrl_platform_data swr_plat_data;
|
||||
struct work_struct wsa_macro_add_child_devices_work;
|
||||
struct device_node *wsa_swr_gpio_p;
|
||||
struct clk *wsa_core_clk;
|
||||
struct clk *wsa_npl_clk;
|
||||
struct snd_soc_component *component;
|
||||
int rx_0_count;
|
||||
int rx_1_count;
|
||||
@ -219,6 +216,8 @@ struct wsa_macro_priv {
|
||||
int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX];
|
||||
int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX];
|
||||
struct wsa_macro_bcl_pmic_params bcl_pmic_params;
|
||||
char __iomem *mclk_mode_muxsel;
|
||||
u16 default_clk_id;
|
||||
};
|
||||
|
||||
static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component,
|
||||
@ -808,14 +807,18 @@ static int wsa_macro_mclk_enable(struct wsa_macro_priv *wsa_priv,
|
||||
mutex_lock(&wsa_priv->mclk_lock);
|
||||
if (mclk_enable) {
|
||||
if (wsa_priv->wsa_mclk_users == 0) {
|
||||
ret = bolero_request_clock(wsa_priv->dev,
|
||||
WSA_MACRO, MCLK_MUX0, true);
|
||||
ret = bolero_clk_rsc_request_clock(wsa_priv->dev,
|
||||
wsa_priv->default_clk_id,
|
||||
wsa_priv->default_clk_id,
|
||||
true);
|
||||
if (ret < 0) {
|
||||
dev_err(wsa_priv->dev,
|
||||
"%s: wsa request clock enable failed\n",
|
||||
__func__);
|
||||
goto exit;
|
||||
}
|
||||
bolero_clk_rsc_fs_gen_request(wsa_priv->dev,
|
||||
true);
|
||||
regcache_mark_dirty(regmap);
|
||||
regcache_sync_region(regmap,
|
||||
WSA_START_OFFSET,
|
||||
@ -846,8 +849,13 @@ static int wsa_macro_mclk_enable(struct wsa_macro_priv *wsa_priv,
|
||||
regmap_update_bits(regmap,
|
||||
BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL,
|
||||
0x01, 0x00);
|
||||
bolero_request_clock(wsa_priv->dev,
|
||||
WSA_MACRO, MCLK_MUX0, false);
|
||||
bolero_clk_rsc_fs_gen_request(wsa_priv->dev,
|
||||
false);
|
||||
|
||||
bolero_clk_rsc_request_clock(wsa_priv->dev,
|
||||
wsa_priv->default_clk_id,
|
||||
wsa_priv->default_clk_id,
|
||||
false);
|
||||
}
|
||||
}
|
||||
exit:
|
||||
@ -888,35 +896,6 @@ static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wsa_macro_mclk_ctrl(struct device *dev, bool enable)
|
||||
{
|
||||
struct wsa_macro_priv *wsa_priv = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (!wsa_priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(wsa_priv->wsa_core_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:wsa mclk enable failed\n", __func__);
|
||||
goto exit;
|
||||
}
|
||||
ret = clk_prepare_enable(wsa_priv->wsa_npl_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s:wsa npl_clk enable failed\n",
|
||||
__func__);
|
||||
clk_disable_unprepare(wsa_priv->wsa_core_clk);
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
clk_disable_unprepare(wsa_priv->wsa_npl_clk);
|
||||
clk_disable_unprepare(wsa_priv->wsa_core_clk);
|
||||
}
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wsa_macro_event_handler(struct snd_soc_component *component,
|
||||
u16 event, u32 data)
|
||||
{
|
||||
@ -2809,7 +2788,6 @@ static void wsa_macro_init_ops(struct macro_ops *ops,
|
||||
ops->io_base = wsa_io_base;
|
||||
ops->dai_ptr = wsa_macro_dai;
|
||||
ops->num_dais = ARRAY_SIZE(wsa_macro_dai);
|
||||
ops->mclk_fn = wsa_macro_mclk_ctrl;
|
||||
ops->event_handler = wsa_macro_event_handler;
|
||||
ops->set_port_map = wsa_macro_set_port_map;
|
||||
}
|
||||
@ -2818,10 +2796,9 @@ static int wsa_macro_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct macro_ops ops;
|
||||
struct wsa_macro_priv *wsa_priv;
|
||||
u32 wsa_base_addr;
|
||||
u32 wsa_base_addr, default_clk_id;
|
||||
char __iomem *wsa_io_base;
|
||||
int ret = 0;
|
||||
struct clk *wsa_core_clk, *wsa_npl_clk;
|
||||
u8 bcl_pmic_params[3];
|
||||
|
||||
wsa_priv = devm_kzalloc(&pdev->dev, sizeof(struct wsa_macro_priv),
|
||||
@ -2861,24 +2838,13 @@ static int wsa_macro_probe(struct platform_device *pdev)
|
||||
wsa_priv->swr_plat_data.clk = wsa_swrm_clock;
|
||||
wsa_priv->swr_plat_data.handle_irq = NULL;
|
||||
|
||||
/* Register MCLK for wsa macro */
|
||||
wsa_core_clk = devm_clk_get(&pdev->dev, "wsa_core_clk");
|
||||
if (IS_ERR(wsa_core_clk)) {
|
||||
ret = PTR_ERR(wsa_core_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed\n",
|
||||
__func__, "wsa_core_clk");
|
||||
return ret;
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "qcom,default-clk-id",
|
||||
&default_clk_id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: could not find %s entry in dt\n",
|
||||
__func__, "qcom,mux0-clk-id");
|
||||
default_clk_id = WSA_CORE_CLK;
|
||||
}
|
||||
wsa_priv->wsa_core_clk = wsa_core_clk;
|
||||
/* Register npl clk for soundwire */
|
||||
wsa_npl_clk = devm_clk_get(&pdev->dev, "wsa_npl_clk");
|
||||
if (IS_ERR(wsa_npl_clk)) {
|
||||
ret = PTR_ERR(wsa_npl_clk);
|
||||
dev_err(&pdev->dev, "%s: clk get %s failed\n",
|
||||
__func__, "wsa_npl_clk");
|
||||
return ret;
|
||||
}
|
||||
wsa_priv->wsa_npl_clk = wsa_npl_clk;
|
||||
|
||||
ret = of_property_read_u8_array(pdev->dev.of_node,
|
||||
"qcom,wsa-bcl-pmic-params", bcl_pmic_params,
|
||||
@ -2891,11 +2857,14 @@ static int wsa_macro_probe(struct platform_device *pdev)
|
||||
wsa_priv->bcl_pmic_params.sid = bcl_pmic_params[1];
|
||||
wsa_priv->bcl_pmic_params.ppid = bcl_pmic_params[2];
|
||||
}
|
||||
wsa_priv->default_clk_id = default_clk_id;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, wsa_priv);
|
||||
mutex_init(&wsa_priv->mclk_lock);
|
||||
mutex_init(&wsa_priv->swr_clk_lock);
|
||||
wsa_macro_init_ops(&ops, wsa_io_base);
|
||||
ops.clk_id_req = wsa_priv->default_clk_id;
|
||||
ops.default_clk_id = wsa_priv->default_clk_id;
|
||||
ret = bolero_register_macro(&pdev->dev, WSA_MACRO, &ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "%s: register macro failed\n", __func__);
|
||||
|
Loading…
Reference in New Issue
Block a user