diff options
Diffstat (limited to 'sound/soc/codecs/wcd9xxx-resmgr-v2.c')
| -rw-r--r-- | sound/soc/codecs/wcd9xxx-resmgr-v2.c | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c new file mode 100644 index 000000000000..f16fc05a5eaa --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/mfd/wcd9xxx/core.h> +#include <linux/mfd/wcd9335/registers.h> +#include <linux/mfd/wcd934x/registers.h> +#include <sound/soc.h> +#include "wcd9xxx-resmgr-v2.h" + +#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000 +#define WCD93XX_ANA_BIAS 0x0601 +#define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 + + +static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type) +{ + if (clk_type == WCD_CLK_OFF) + return "WCD_CLK_OFF"; + else if (clk_type == WCD_CLK_RCO) + return "WCD_CLK_RCO"; + else if (clk_type == WCD_CLK_MCLK) + return "WCD_CLK_MCLK"; + else + return "WCD_CLK_UNDEFINED"; +} + +static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr, + u16 reg, u8 mask, u8 val) +{ + bool change; + int ret; + + if (resmgr->codec_type == WCD934X) { + /* Tavil does not support ANA_CLK_TOP register */ + if (reg == WCD9335_ANA_CLK_TOP) + return 0; + } else { + /* Tasha does not support CLK_SYS_MCLK_PRG register */ + if (reg == WCD934X_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + ret = snd_soc_update_bits(resmgr->codec, reg, mask, val); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_update_bits_check( + resmgr->core_res->wcd_core_regmap, + reg, mask, val, &change); + if (!ret) + ret = change; + } else { + pr_err("%s: codec/regmap not defined\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr, + unsigned int reg) +{ + int val, ret; + + if (resmgr->codec_type == WCD934X) { + if (reg == WCD9335_ANA_CLK_TOP) + return 0; + } else { + if (reg == WCD934X_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + val = snd_soc_read(resmgr->codec, reg); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_read(resmgr->core_res->wcd_core_regmap, + reg, &val); + if (ret) + val = ret; + } else { + pr_err("%s: wcd regmap is null\n", __func__); + return -EINVAL; + } + + return val; +} + +/* + * wcd_resmgr_get_clk_type() + * Returns clk type that is currently enabled + */ +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (!resmgr) { + pr_err("%s: resmgr not initialized\n", __func__); + return -EINVAL; + } + return resmgr->clk_type; +} + +static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr, + int clk_users) +{ + /* Caller of this function should have acquired BG_CLK lock */ + if (clk_users) { + if (resmgr->resmgr_cb && + resmgr->resmgr_cb->cdc_rco_ctrl) { + while (clk_users--) + resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec, + true); + } + } +} + +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr) +{ + int old_bg_audio_users; + int old_clk_rco_users, old_clk_mclk_users; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + + old_bg_audio_users = resmgr->master_bias_users; + old_clk_mclk_users = resmgr->clk_mclk_users; + old_clk_rco_users = resmgr->clk_rco_users; + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->clk_type = WCD_CLK_OFF; + + pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n", + __func__, old_bg_audio_users, + old_clk_mclk_users, old_clk_rco_users); + + if (old_bg_audio_users) { + while (old_bg_audio_users--) + wcd_resmgr_enable_master_bias(resmgr); + } + + if (old_clk_mclk_users) { + while (old_clk_mclk_users--) + wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK); + } + + if (old_clk_rco_users) + wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users); + + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} + + +/* + * wcd_resmgr_enable_master_bias: enable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + + resmgr->master_bias_users++; + if (resmgr->master_bias_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x40); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + + pr_debug("%s: current master bias users: %d\n", __func__, + resmgr->master_bias_users); + + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} + +/* + * wcd_resmgr_disable_master_bias: disable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + if (resmgr->master_bias_users <= 0) { + mutex_unlock(&resmgr->master_bias_lock); + return -EINVAL; + } + + resmgr->master_bias_users--; + if (resmgr->master_bias_users == 0) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} + +static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + /* Enable mclk requires master bias to be enabled first */ + if (resmgr->master_bias_users <= 0) { + pr_err("%s: Cannot turn on MCLK, BG is not enabled\n", + __func__); + return -EINVAL; + } + + if (((resmgr->clk_mclk_users == 0) && + (resmgr->clk_type == WCD_CLK_MCLK)) || + ((resmgr->clk_mclk_users > 0) && + (resmgr->clk_type != WCD_CLK_MCLK))) { + pr_err("%s: Error enabling MCLK, clk_type: %s\n", + __func__, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + return -EINVAL; + } + + if (++resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x04, 0x04); + if (resmgr->codec_type == WCD934X) { + /* + * In tavil clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x30, 0x10); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x00); + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + } + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + resmgr->clk_type = WCD_CLK_MCLK; + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (resmgr->clk_mclk_users <= 0) { + pr_err("%s: No mclk users, cannot disable mclk\n", __func__); + return -EINVAL; + } + + if (--resmgr->clk_mclk_users == 0) { + if (resmgr->clk_rco_users > 0) { + /* MCLK to RCO switch */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x02); + /* Disable clock buffer */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x80, 0x00); + resmgr->clk_type = WCD_CLK_RCO; + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x80, 0x00); + } + + if ((resmgr->codec_type == WCD934X) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr) +{ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x02, 0x02); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x01, 0x01); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x04); + /* 100us sleep needed after HIGH_ACCURACY_EN */ + usleep_range(100, 110); +} + +static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + bool rco_cal_done = true; + + resmgr->clk_rco_users++; + if ((resmgr->clk_rco_users == 1) && + ((resmgr->clk_type == WCD_CLK_OFF) || + (resmgr->clk_mclk_users == 0))) { + pr_warn("%s: RCO enable requires MCLK to be ON first\n", + __func__); + resmgr->clk_rco_users--; + return -EINVAL; + } else if ((resmgr->clk_rco_users == 1) && + (resmgr->clk_mclk_users)) { + /* RCO Enable */ + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x80); + if (resmgr->codec_type == WCD934X) + wcd_resmgr_set_buck_accuracy(resmgr); + } + + /* + * 20us required after RCO BG is enabled as per HW + * requirements + */ + usleep_range(20, 25); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x40); + /* + * 20us required after RCO is enabled as per HW + * requirements + */ + usleep_range(20, 25); + /* RCO Calibration */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x04, 0x04); + if (resmgr->codec_type == WCD934X) + /* + * For wcd934x codec, 20us sleep is needed + * after enabling RCO calibration + */ + usleep_range(20, 25); + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x04, 0x00); + if (resmgr->codec_type == WCD934X) + /* + * For wcd934x codec, 20us sleep is needed + * after disabling RCO calibration + */ + usleep_range(20, 25); + + /* RCO calibration takes app. 5ms to complete */ + usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US, + WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100); + if (wcd_resmgr_codec_reg_read(resmgr, WCD9335_ANA_RCO) & 0x02) + rco_cal_done = false; + + WARN((!rco_cal_done), "RCO Calibration failed\n"); + + /* Switch MUX to RCO */ + if (resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x02, 0x02); + resmgr->clk_type = WCD_CLK_RCO; + } + } + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if ((resmgr->clk_rco_users <= 0) || + (resmgr->clk_type == WCD_CLK_OFF)) { + pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n", + __func__, resmgr->clk_rco_users, resmgr->clk_type); + return -EINVAL; + } + + resmgr->clk_rco_users--; + + if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_type == WCD_CLK_RCO)) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x01, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } else if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_mclk_users)) { + /* Disable RCO while MCLK is ON */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x00); + } + + if ((resmgr->codec_type == WCD934X) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +/* + * wcd_resmgr_enable_clk_block: enable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to enable + */ +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_enable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_enable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Enable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} + +void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + if (sido_src == resmgr->sido_input_src) + return; + + if (sido_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x03, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO, + 0x80, 0x00); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + pr_debug("%s: sido input src to internal\n", __func__); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO, + 0x80, 0x80); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x02, 0x02); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x01, 0x01); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x04); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; + pr_debug("%s: sido input src to external\n", __func__); + } +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src); + +/* + * wcd_resmgr_set_sido_input_src_locked: + * Set SIDO input in BG_CLK locked context + * + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @sido_src: Select the SIDO input source + */ +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + wcd_resmgr_set_sido_input_src(resmgr, sido_src); + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked); + +/* + * wcd_resmgr_disable_clk_block: disable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to disable + */ +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_disable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_disable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Disable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} + +/* + * wcd_resmgr_init: initialize wcd resource manager + * @core_res: handle to struct wcd9xxx_core_resource + * + * Early init call without a handle to snd_soc_codec * + */ +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx *wcd9xxx; + + resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL); + if (!resmgr) { + pr_err("%s: Cannot allocate memory for wcd resmgr\n", __func__); + return ERR_PTR(-ENOMEM); + } + + wcd9xxx = container_of(core_res, struct wcd9xxx, core_res); + if (!wcd9xxx) { + kfree(resmgr); + pr_err("%s: Cannot get wcd9xx pointer\n", __func__); + return ERR_PTR(-EINVAL); + } + + mutex_init(&resmgr->codec_bg_clk_lock); + mutex_init(&resmgr->master_bias_lock); + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->master_bias_users = 0; + resmgr->codec = codec; + resmgr->core_res = core_res; + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + resmgr->codec_type = wcd9xxx->type; + + return resmgr; +} + +/* + * wcd_resmgr_remove: Clean-up wcd resource manager + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_destroy(&resmgr->master_bias_lock); + kfree(resmgr); +} + +/* + * wcd_resmgr_post_init: post init call to assign codec handle + * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init + * @resmgr_cb: codec callback function for resmgr + * @codec: handle to struct snd_soc_codec + */ +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec) +{ + if (!resmgr) { + pr_err("%s: resmgr not allocated\n", __func__); + return -EINVAL; + } + + if (!codec) { + pr_err("%s: Codec memory is NULL, nothing to post init\n", + __func__); + return -EINVAL; + } + + resmgr->codec = codec; + resmgr->resmgr_cb = resmgr_cb; + + return 0; +} +MODULE_DESCRIPTION("wcd9xxx resmgr v2 module"); +MODULE_LICENSE("GPL v2"); |
