diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c index 749f87b973..e9ccee430a 100644 --- a/asoc/codecs/bolero/bolero-cdc.c +++ b/asoc/codecs/bolero/bolero-cdc.c @@ -26,6 +26,7 @@ #define BOLERO_VERSION_1_1 0x0002 #define BOLERO_VERSION_1_2 0x0003 #define BOLERO_VERSION_ENTRY_SIZE 32 +#define BOLERO_CDC_STRING_LEN 80 static struct snd_soc_codec_driver bolero; @@ -118,6 +119,65 @@ err: return ret; } +static int bolero_cdc_update_wcd_event(void *handle, u16 event, u32 data) +{ + struct bolero_priv *priv = (struct bolero_priv *)handle; + + if (!priv) { + pr_err("%s:Invalid bolero priv handle\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_BOLERO_EVT_RX_MUTE: + if (priv->macro_params[RX_MACRO].event_handler) + priv->macro_params[RX_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_RX_MUTE, data); + break; + case WCD_BOLERO_EVT_IMPED_TRUE: + if (priv->macro_params[RX_MACRO].event_handler) + priv->macro_params[RX_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_IMPED_TRUE, data); + break; + case WCD_BOLERO_EVT_IMPED_FALSE: + if (priv->macro_params[RX_MACRO].event_handler) + priv->macro_params[RX_MACRO].event_handler(priv->codec, + BOLERO_MACRO_EVT_IMPED_FALSE, data); + break; + default: + dev_err(priv->dev, "%s: Invalid event %d trigger from wcd\n", + __func__, event); + return -EINVAL; + } + return 0; +} + +static int bolero_cdc_register_notifier(void *handle, + struct notifier_block *nblock, + bool enable) +{ + struct bolero_priv *priv = (struct bolero_priv *)handle; + + if (!priv) { + pr_err("%s: bolero priv is null\n", __func__); + return -EINVAL; + } + if (enable) + return blocking_notifier_chain_register(&priv->notifier, + nblock); + + return blocking_notifier_chain_unregister(&priv->notifier, + nblock); +} + +static void bolero_cdc_notifier_call(struct bolero_priv *priv, + u32 data) +{ + dev_dbg(priv->dev, "%s: notifier call, data:%d\n", __func__, data); + blocking_notifier_call_chain(&priv->notifier, + data, (void *)priv->wcd_dev); +} + static bool bolero_is_valid_macro_dev(struct device *dev) { if (of_device_is_compatible(dev->parent->of_node, "qcom,bolero-codec")) @@ -134,6 +194,46 @@ static bool bolero_is_valid_codec_dev(struct device *dev) return false; } +/** + * bolero_clear_amic_tx_hold - clears AMIC register on analog codec + * + * @dev: bolero device ptr. + * + */ +void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n) +{ + struct bolero_priv *priv; + u16 event; + u16 amic = 0; + + if (!dev) { + pr_err("%s: dev is null\n", __func__); + return; + } + + if (!bolero_is_valid_codec_dev(dev)) { + pr_err("%s: invalid codec\n", __func__); + return; + } + priv = dev_get_drvdata(dev); + if (!priv) { + dev_err(dev, "%s: priv is null\n", __func__); + return; + } + event = BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR; + if (adc_n == BOLERO_ADC0) + amic = 0x1; + else if (adc_n == BOLERO_ADC2) + amic = 0x2; + else if (adc_n == BOLERO_ADC3) + amic = 0x3; + else + return; + + bolero_cdc_notifier_call(priv, (amic << 0x10 | event)); +} +EXPORT_SYMBOL(bolero_clear_amic_tx_hold); + /** * bolero_get_device_ptr - Get child or macro device ptr * @@ -230,6 +330,7 @@ int bolero_register_macro(struct device *dev, u16 macro_id, 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].dev = dev; priv->current_mclk_mux_macro[macro_id] = bolero_mclk_mux_tbl[macro_id][MCLK_MUX0]; @@ -237,7 +338,7 @@ int bolero_register_macro(struct device *dev, u16 macro_id, priv->num_macros_registered++; priv->macros_supported[macro_id] = true; - if (priv->num_macros_registered == priv->child_num) { + if (priv->num_macros_registered == priv->num_macros) { ret = bolero_copy_dais_from_macro(priv); if (ret < 0) { dev_err(dev, "%s: copy_dais failed\n", __func__); @@ -290,12 +391,13 @@ void bolero_unregister_macro(struct device *dev, u16 macro_id) 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; priv->num_dais -= priv->macro_params[macro_id].num_dais; priv->num_macros_registered--; /* UNREGISTER CODEC HERE */ - if (priv->child_num - 1 == priv->num_macros_registered) + if (priv->num_macros - 1 == priv->num_macros_registered) snd_soc_unregister_codec(dev->parent); } EXPORT_SYMBOL(bolero_unregister_macro); @@ -555,7 +657,12 @@ static struct snd_soc_codec_driver bolero = { static void bolero_add_child_devices(struct work_struct *work) { struct bolero_priv *priv; - int rc; + bool wcd937x_node = false; + struct platform_device *pdev; + struct device_node *node; + int ret = 0, count = 0; + struct wcd_ctrl_platform_data *platdata = NULL; + char plat_dev_name[BOLERO_CDC_STRING_LEN] = ""; priv = container_of(work, struct bolero_priv, bolero_add_child_devices_work); @@ -569,12 +676,53 @@ static void bolero_add_child_devices(struct work_struct *work) __func__); return; } - rc = of_platform_populate(priv->dev->of_node, NULL, NULL, priv->dev); - if (rc) - dev_err(priv->dev, "%s: failed to add child nodes, rc=%d\n", - __func__, rc); - else - dev_dbg(priv->dev, "%s: added child node\n", __func__); + + platdata = &priv->plat_data; + priv->child_count = 0; + + for_each_available_child_of_node(priv->dev->of_node, node) { + wcd937x_node = false; + if (strnstr(node->name, "wcd937x", strlen("wcd937x")) != NULL) + wcd937x_node = true; + + strlcpy(plat_dev_name, node->name, + (BOLERO_CDC_STRING_LEN - 1)); + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = priv->dev; + pdev->dev.of_node = node; + + if (wcd937x_node) { + priv->dev->platform_data = platdata; + priv->wcd_dev = &pdev->dev; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + platform_device_put(pdev); + goto fail_pdev_add; + } + + if (priv->child_count < BOLERO_CDC_CHILD_DEVICES_MAX) + priv->pdev_child_devices[priv->child_count++] = pdev; + else + goto err; + } + return; +fail_pdev_add: + for (count = 0; count < priv->child_count; count++) + platform_device_put(priv->pdev_child_devices[count]); +err: + return; } static int bolero_probe(struct platform_device *pdev) @@ -595,11 +743,11 @@ static int bolero_probe(struct platform_device *pdev) __func__); return ret; } - priv->child_num = num_macros; - if (priv->child_num > MAX_MACRO) { + priv->num_macros = num_macros; + if (priv->num_macros > MAX_MACRO) { dev_err(&pdev->dev, - "%s:child_num(%d) > MAX_MACRO(%d) than supported\n", - __func__, priv->child_num, MAX_MACRO); + "%s:num_macros(%d) > MAX_MACRO(%d) than supported\n", + __func__, priv->num_macros, MAX_MACRO); return -EINVAL; } priv->va_without_decimation = of_property_read_bool(pdev->dev.of_node, @@ -617,6 +765,10 @@ static int bolero_probe(struct platform_device *pdev) priv->read_dev = __bolero_reg_read; priv->write_dev = __bolero_reg_write; + priv->plat_data.handle = (void *) priv; + priv->plat_data.update_wcd_event = bolero_cdc_update_wcd_event; + priv->plat_data.register_notifier = bolero_cdc_register_notifier; + dev_set_drvdata(&pdev->dev, priv); mutex_init(&priv->io_lock); mutex_init(&priv->clk_lock); diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h index f7e95eeae2..3e8197d58e 100644 --- a/asoc/codecs/bolero/bolero-cdc.h +++ b/asoc/codecs/bolero/bolero-cdc.h @@ -31,6 +31,20 @@ enum mclk_mux { MCLK_MUX_MAX }; +enum { + BOLERO_ADC0 = 1, + BOLERO_ADC1, + BOLERO_ADC2, + BOLERO_ADC3, + BOLERO_ADC_MAX +}; + +enum { + BOLERO_MACRO_EVT_RX_MUTE = 1, /* for RX mute/unmute */ + BOLERO_MACRO_EVT_IMPED_TRUE, /* for imped true */ + BOLERO_MACRO_EVT_IMPED_FALSE, /* for imped false */ +}; + struct macro_ops { int (*init)(struct snd_soc_codec *codec); int (*exit)(struct snd_soc_codec *codec); @@ -38,6 +52,8 @@ struct macro_ops { struct device *dev; struct snd_soc_dai_driver *dai_ptr; int (*mclk_fn)(struct device *dev, bool enable); + int (*event_handler)(struct snd_soc_codec *codec, u16 event, + u32 data); char __iomem *io_base; }; @@ -52,6 +68,7 @@ int bolero_request_clock(struct device *dev, u16 macro_id, int bolero_info_create_codec_entry( struct snd_info_entry *codec_root, struct snd_soc_codec *codec); +void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n); #else static inline int bolero_register_macro(struct device *dev, u16 macro_id, @@ -83,5 +100,9 @@ static int bolero_info_create_codec_entry( { return 0; } + +static inline void bolero_clear_amic_tx_hold(struct device *dev, u16 adc_n) +{ +} #endif /* CONFIG_SND_SOC_BOLERO */ #endif /* BOLERO_CDC_H */ diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h index 2979ee0669..9e8a74c5e3 100644 --- a/asoc/codecs/bolero/internal.h +++ b/asoc/codecs/bolero/internal.h @@ -15,6 +15,13 @@ #include "bolero-cdc-registers.h" +#define BOLERO_CDC_CHILD_DEVICES_MAX 5 + +/* from bolero to WCD events */ +enum { + BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR = 1, +}; + enum { REG_NO_ACCESS, RD_REG, @@ -22,6 +29,21 @@ enum { RD_WR_REG }; +/* from WCD to bolero events */ +enum { + WCD_BOLERO_EVT_RX_MUTE = 1, /* for RX mute/unmute */ + WCD_BOLERO_EVT_IMPED_TRUE, /* for imped true */ + WCD_BOLERO_EVT_IMPED_FALSE, /* for imped false */ +}; + +struct wcd_ctrl_platform_data { + void *handle; + int (*update_wcd_event)(void *handle, u16 event, u32 data); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + struct bolero_priv { struct device *dev; struct snd_soc_codec *codec; @@ -34,7 +56,7 @@ struct bolero_priv { struct snd_soc_dai_driver *bolero_dais; u16 num_dais; u16 num_macros_registered; - u16 child_num; + u16 num_macros; u16 current_mclk_mux_macro[MAX_MACRO]; struct work_struct bolero_add_child_devices_work; u32 version; @@ -47,6 +69,12 @@ struct bolero_priv { u16 macro_id, u16 reg, u8 *val); int (*write_dev)(struct bolero_priv *priv, u16 macro_id, u16 reg, u8 val); + struct platform_device *pdev_child_devices + [BOLERO_CDC_CHILD_DEVICES_MAX]; + u16 child_count; + struct wcd_ctrl_platform_data plat_data; + struct device *wcd_dev; + struct blocking_notifier_head notifier; }; struct regmap *bolero_regmap_init(struct device *dev, diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c index 62c4a371cf..a0e189b5d5 100644 --- a/asoc/codecs/bolero/rx-macro.c +++ b/asoc/codecs/bolero/rx-macro.c @@ -75,6 +75,107 @@ static const struct snd_kcontrol_new name##_mux = \ #define RX_MACRO_RX_PATH_OFFSET 0x80 #define RX_MACRO_COMP_OFFSET 0x40 +#define MAX_IMPED_PARAMS 6 + +struct wcd_imped_val { + u32 imped_val; + u8 index; +}; + +static const struct wcd_imped_val imped_index[] = { + {4, 0}, + {5, 1}, + {6, 2}, + {7, 3}, + {8, 4}, + {9, 5}, + {10, 6}, + {11, 7}, + {12, 8}, + {13, 9}, +}; + +struct rx_macro_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +static const struct rx_macro_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = { + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x01}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x00}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {BOLERO_CDC_RX_RX0_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX0_RX_PATH_SEC1, 0x01, 0x01}, + {BOLERO_CDC_RX_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {BOLERO_CDC_RX_RX1_RX_PATH_SEC1, 0x01, 0x01}, + }, +}; + enum { INTERP_HPHL, INTERP_HPHR, @@ -494,6 +595,80 @@ static struct snd_soc_dai_driver rx_macro_dai[] = { }, }; +static int get_impedance_index(int imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than %d Ohm\n", + __func__, imped_index[i].imped_val); + i = 0; + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than %d Ohm\n", + __func__, + imped_index[ARRAY_SIZE(imped_index) - 1].imped_val); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +/* + * rx_macro_wcd_clsh_imped_config - + * This function updates HPHL and HPHR gain settings + * according to the impedance value. + * + * @codec: codec pointer handle + * @imped: impedance value of HPHL/R + * @reset: bool variable to reset registers when teardown + */ +static void rx_macro_wcd_clsh_imped_config(struct snd_soc_codec *codec, + int imped, bool reset) +{ + int i; + int index = 0; + int table_size; + + static const struct rx_macro_reg_mask_val + (*imped_table_ptr)[MAX_IMPED_PARAMS]; + + table_size = ARRAY_SIZE(imped_table); + imped_table_ptr = imped_table; + /* reset = 1, which means request is to reset the register values */ + if (reset) { + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, 0); + return; + } + index = get_impedance_index(imped); + if (index >= (ARRAY_SIZE(imped_index) - 1)) { + pr_debug("%s, impedance not in range = %d\n", __func__, imped); + return; + } + if (index >= table_size) { + pr_debug("%s, impedance index not in range = %d\n", __func__, + index); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, + imped_table_ptr[index][i].val); +} + static bool rx_macro_get_data(struct snd_soc_codec *codec, struct device **rx_dev, struct rx_macro_priv **rx_priv, @@ -928,6 +1103,37 @@ static int rx_macro_mclk_ctrl(struct device *dev, bool enable) return 0; } +static int rx_macro_event_handler(struct snd_soc_codec *codec, u16 event, + u32 data) +{ + u16 reg = 0, reg_mix = 0, rx_idx = 0, mute = 0x0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + switch (event) { + case BOLERO_MACRO_EVT_RX_MUTE: + rx_idx = data >> 0x10; + mute = data & 0xffff; + reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + (rx_idx * + RX_MACRO_RX_PATH_OFFSET); + reg_mix = BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL + (rx_idx * + RX_MACRO_RX_PATH_OFFSET); + snd_soc_update_bits(codec, reg, 0x10, mute << 0x10); + snd_soc_update_bits(codec, reg_mix, 0x10, mute << 0x10); + break; + case BOLERO_MACRO_EVT_IMPED_TRUE: + rx_macro_wcd_clsh_imped_config(codec, data, true); + break; + case BOLERO_MACRO_EVT_IMPED_FALSE: + rx_macro_wcd_clsh_imped_config(codec, data, false); + break; + } + return 0; +} + static int rx_macro_find_playback_dai_id_for_port(int port_id, struct rx_macro_priv *rx_priv) { @@ -1078,7 +1284,6 @@ static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: snd_soc_write(codec, gain_reg, snd_soc_read(codec, gain_reg)); - snd_soc_update_bits(codec, mix_reg, 0x10, 0x00); break; case SND_SOC_DAPM_POST_PMD: /* Clk Disable */ @@ -1128,7 +1333,6 @@ static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: snd_soc_write(codec, gain_reg, snd_soc_read(codec, gain_reg)); - snd_soc_update_bits(codec, reg, 0x10, 0x00); break; case SND_SOC_DAPM_POST_PMD: rx_macro_enable_interp_clk(codec, event, w->shift); @@ -2955,6 +3159,7 @@ static void rx_macro_init_ops(struct macro_ops *ops, char __iomem *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; } static int rx_macro_probe(struct platform_device *pdev) diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c index 76d424be6b..9cfd94d6b6 100644 --- a/asoc/codecs/bolero/tx-macro.c +++ b/asoc/codecs/bolero/tx-macro.c @@ -42,6 +42,8 @@ #define TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED 0 #define TX_MACRO_MCLK_FREQ 9600000 #define TX_MACRO_TX_PATH_OFFSET 0x80 +#define TX_MACRO_SWR_MIC_MUX_SEL_MASK 0xF +#define TX_MACRO_ADC_MUX_CFG_OFFSET 0x2 #define TX_MACRO_TX_UNMUTE_DELAY_MS 40 @@ -107,6 +109,12 @@ enum { TX_MACRO_CLK_DIV_16, }; +enum { + MSM_DMIC, + SWR_MIC, + ANC_FB_TUNE1 +}; + struct tx_mute_work { struct tx_macro_priv *tx_priv; u32 decimator; @@ -303,6 +311,7 @@ static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) struct snd_soc_codec *codec = NULL; u16 dec_cfg_reg = 0, hpf_gate_reg = 0; u8 hpf_cut_off_freq = 0; + u16 adc_mux_reg = 0, adc_n = 0, adc_reg = 0; hpf_delayed_work = to_delayed_work(work); hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); @@ -318,6 +327,19 @@ static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", __func__, hpf_work->decimator, hpf_cut_off_freq); + adc_mux_reg = BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + TX_MACRO_ADC_MUX_CFG_OFFSET * hpf_work->decimator; + if (snd_soc_read(codec, adc_mux_reg) & SWR_MIC) { + adc_reg = BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + TX_MACRO_ADC_MUX_CFG_OFFSET * hpf_work->decimator; + adc_n = snd_soc_read(codec, adc_reg) & + TX_MACRO_SWR_MIC_MUX_SEL_MASK; + if (adc_n >= BOLERO_ADC_MAX) + goto tx_hpf_set; + /* analog mic clear TX hold */ + bolero_clear_amic_tx_hold(codec->dev, adc_n); + } +tx_hpf_set: snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, hpf_cut_off_freq << 5); snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); @@ -399,7 +421,7 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, __func__, e->reg); return -EINVAL; } - if (strnstr(widget->name, "smic", strlen(widget->name))) { + if (strnstr(widget->name, "SMIC", strlen(widget->name))) { if (val != 0) { if (val < 5) snd_soc_update_bits(codec, mic_sel_reg, diff --git a/asoc/codecs/wcd937x/internal.h b/asoc/codecs/wcd937x/internal.h index e2f1e03b34..0818c05eb6 100644 --- a/asoc/codecs/wcd937x/internal.h +++ b/asoc/codecs/wcd937x/internal.h @@ -74,6 +74,14 @@ struct wcd937x_priv { rx_port_mapping[MAX_PORT][MAX_CH_PER_PORT]; struct regulator_bulk_data *supplies; + struct notifier_block nblock; + /* wcd callback to bolero */ + void *handle; + int (*update_wcd_event)(void *handle, u16 event, u32 data); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); + u32 version; /* Entry for version info */ struct snd_info_entry *entry; @@ -99,6 +107,30 @@ struct wcd937x_pdata { int num_supplies; }; +struct wcd_ctrl_platform_data { + void *handle; + int (*update_wcd_event)(void *handle, u16 event, u32 data); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +enum { + WCD_RX1, + WCD_RX2, + WCD_RX3 +}; + +enum { + BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR = 1, +}; + +enum { + WCD_BOLERO_EVT_RX_MUTE = 1, /* for RX mute/unmute */ + WCD_BOLERO_EVT_IMPED_TRUE, /* for imped true */ + WCD_BOLERO_EVT_IMPED_FALSE, /* for imped false */ +}; + enum { /* INTR_CTRL_INT_MASK_0 */ WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET = 0, diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 2d7f40a3ff..b599b1f327 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/asoc/codecs/wcd937x/wcd937x.c @@ -568,8 +568,16 @@ static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, true); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX2 << 0x10)); break; case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX2 << 0x10 | 0x1)); blocking_notifier_call_chain(&wcd937x->mbhc->notifier, WCD_EVENT_PRE_HPHR_PA_OFF, &wcd937x->mbhc->wcd_mbhc); @@ -607,8 +615,16 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, true); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); break; case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); blocking_notifier_call_chain(&wcd937x->mbhc->notifier, WCD_EVENT_PRE_HPHL_PA_OFF, &wcd937x->mbhc->wcd_mbhc); @@ -650,6 +666,16 @@ static int wcd937x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, true); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX3 << 0x10)); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX3 << 0x10 | 0x1)); break; case SND_SOC_DAPM_POST_PMD: usleep_range(1000, 1010); @@ -685,6 +711,16 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, wcd937x->rx_swr_dev->dev_num, true); + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd937x->update_wcd_event) + wcd937x->update_wcd_event(wcd937x->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); break; case SND_SOC_DAPM_POST_PMD: usleep_range(7000, 7010); @@ -1180,6 +1216,35 @@ int wcd937x_micbias_control(struct snd_soc_codec *codec, } EXPORT_SYMBOL(wcd937x_micbias_control); +static int wcd937x_event_notify(struct notifier_block *block, + unsigned long val, + void *data) +{ + u16 event = (val & 0xffff); + u16 amic = (val >> 0x10); + u16 mask = 0x40, reg = 0x0; + struct wcd937x_priv *wcd937x = dev_get_drvdata((struct device *)data); + struct snd_soc_codec *codec = wcd937x->codec; + + switch (event) { + case BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR: + if (amic == 0x1 || amic == 0x2) + reg = WCD937X_ANA_TX_CH2; + else if (amic == 0x3) + reg = WCD937X_ANA_TX_CH3_HPF; + else + return 0; + if (amic == 0x2) + mask = 0x20; + snd_soc_update_bits(codec, reg, mask, 0x00); + break; + default: + dev_err(codec->dev, "%s: invalid event %d\n", __func__, event); + break; + } + return 0; +} + static int __wcd937x_codec_enable_micbias(struct snd_soc_dapm_widget *w, int event) { @@ -1443,11 +1508,11 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { SND_SOC_DAPM_PGA_E("EAR PGA", WCD937X_ANA_EAR, 7, 0, NULL, 0, wcd937x_codec_enable_ear_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("AUX PGA", WCD937X_AUX_AUXPA, 7, 0, NULL, 0, wcd937x_codec_enable_aux_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_E("HPHL PGA", WCD937X_ANA_HPH, 7, 0, NULL, 0, wcd937x_codec_enable_hphl_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | @@ -1754,7 +1819,6 @@ static int wcd937x_soc_codec_probe(struct snd_soc_codec *codec) return -EINVAL; wcd937x->codec = codec; - variant = (snd_soc_read(codec, WCD937X_DIGITAL_EFUSE_REG_0) & 0x0E) >> 1; wcd937x->variant = variant; @@ -1802,6 +1866,19 @@ static int wcd937x_soc_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_sync(dapm); } wcd937x->version = WCD937X_VERSION_1_0; + /* Register event notifier */ + wcd937x->nblock.notifier_call = wcd937x_event_notify; + if (wcd937x->register_notifier) { + ret = wcd937x->register_notifier(wcd937x->handle, + &wcd937x->nblock, + true); + if (ret) { + dev_err(codec->dev, + "%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } return ret; err_hwdep: @@ -1818,6 +1895,10 @@ static int wcd937x_soc_codec_remove(struct snd_soc_codec *codec) if (!wcd937x) return -EINVAL; + if (wcd937x->register_notifier) + return wcd937x->register_notifier(wcd937x->handle, + &wcd937x->nblock, + false); return 0; } @@ -1985,6 +2066,7 @@ static int wcd937x_bind(struct device *dev) int ret = 0, i = 0; struct wcd937x_priv *wcd937x = NULL; struct wcd937x_pdata *pdata = NULL; + struct wcd_ctrl_platform_data *plat_data = NULL; wcd937x = devm_kzalloc(dev, sizeof(struct wcd937x_priv), GFP_KERNEL); if (!wcd937x) @@ -2008,6 +2090,30 @@ static int wcd937x_bind(struct device *dev) return ret; } + plat_data = dev_get_platdata(dev->parent); + if (!plat_data) { + dev_err(dev, "%s: platform data from parent is NULL\n", + __func__); + return -EINVAL; + } + wcd937x->handle = (void *)plat_data->handle; + if (!wcd937x->handle) { + dev_err(dev, "%s: handle is NULL\n", __func__); + return -EINVAL; + } + wcd937x->update_wcd_event = plat_data->update_wcd_event; + if (!wcd937x->update_wcd_event) { + dev_err(dev, "%s: update_wcd_event api is null!\n", + __func__); + return -EINVAL; + } + wcd937x->register_notifier = plat_data->register_notifier; + if (!wcd937x->register_notifier) { + dev_err(dev, "%s: register_notifier api is null!\n", + __func__); + return -EINVAL; + } + ret = msm_cdc_enable_static_supplies(dev, wcd937x->supplies, pdata->regulator, pdata->num_supplies);