From 7716c71112cd5ac0f446b9bad3e2079844bb1136 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Wed, 19 Aug 2020 02:36:28 +0530 Subject: [PATCH 01/79] asoc: holi: add wcd937x mbhc detection at probe Add support for wcd937x mbhc detetion at probe. Change-Id: Ibcd04d2670ff1d9c8535d098e11f58448cf3c15e Signed-off-by: Vangala, Amarnath --- asoc/holi.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/asoc/holi.c b/asoc/holi.c index b5fa3d0b343a..b9cfbf7cf8cf 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -6368,6 +6368,7 @@ static int msm_aux_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_info_entry *entry; struct snd_card *card = NULL; struct msm_asoc_mach_data *pdata; + bool is_wcd938x = false; pdata = snd_soc_card_get_drvdata(rtd->card); if(!pdata) @@ -6387,9 +6388,11 @@ static int msm_aux_codec_init(struct snd_soc_pcm_runtime *rtd) } component = snd_soc_rtdcom_lookup(rtd, WCD938X_DRV_NAME); - if (!component) { + if (!component) component = snd_soc_rtdcom_lookup(rtd, WCD937X_DRV_NAME); - } + else + is_wcd938x = true; + if (!component) { pr_err("%s component is NULL\n", __func__); return -EINVAL; @@ -6460,7 +6463,12 @@ mbhc_cfg_cal: if (!mbhc_calibration) return -ENOMEM; wcd_mbhc_cfg.calibration = mbhc_calibration; - ret = wcd938x_mbhc_hs_detect(component, &wcd_mbhc_cfg); + + if (is_wcd938x) + ret = wcd938x_mbhc_hs_detect(component, &wcd_mbhc_cfg); + else + ret = wcd937x_mbhc_hs_detect(component, &wcd_mbhc_cfg); + if (ret) { dev_err(component->dev, "%s: mbhc hs detect failed, err:%d\n", __func__, ret); From 11190356117deaa63a64db5f1c953cf88d10df2a Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Thu, 20 Aug 2020 13:31:50 +0530 Subject: [PATCH 02/79] asoc: wcd937x: Update slvdevpath after connect_port in powerup As ADC and MIXER dapm widgets powerup sequence in soc-dapm got re-ordered, need update same in codec driver to handle calls to soundwire. Change-Id: Ibc99d21e29f9e9d7195b4ba8d4c995c46d6aef6f Signed-off-by: Laxminath Kasam --- asoc/codecs/wcd937x/wcd937x.c | 48 +++++++++++++++++++---------------- asoc/holi-port-config.h | 4 +-- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 00233511566d..4538f71bcb06 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/asoc/codecs/wcd937x/wcd937x.c @@ -719,9 +719,6 @@ static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, 0x10, 0x10); usleep_range(100, 110); set_bit(HPH_PA_DELAY, &wcd937x->status_mask); - ret = swr_slvdev_datapath_control(wcd937x->rx_swr_dev, - wcd937x->rx_swr_dev->dev_num, - true); snd_soc_component_update_bits(component, WCD937X_DIGITAL_PDM_WD_CTL1, 0x17, 0x13); break; @@ -1214,7 +1211,9 @@ static int wcd937x_codec_enable_dmic(struct snd_soc_dapm_widget *w, dmic_clk_reg, 0x08, 0x08); snd_soc_component_update_bits(component, dmic_clk_reg, 0x70, 0x20); - wcd937x_tx_connect_port(component, DMIC0 + (w->shift), true); + ret = swr_slvdev_datapath_control(wcd937x->tx_swr_dev, + wcd937x->tx_swr_dev->dev_num, + true); break; case SND_SOC_DAPM_POST_PMD: wcd937x_tx_connect_port(component, DMIC0 + (w->shift), false); @@ -1330,9 +1329,17 @@ static int wcd937x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - ret = swr_slvdev_datapath_control(wcd937x->tx_swr_dev, - wcd937x->tx_swr_dev->dev_num, - true); + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + /* Enable BCS for Headset mic */ + if (w->shift == 1 && !(snd_soc_component_read32(component, + WCD937X_TX_NEW_TX_CH2_SEL) & 0x80)) { + wcd937x_tx_connect_port(component, MBHC, true); + set_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask); + } + wcd937x_tx_connect_port(component, ADC1 + (w->shift), true); + } else { + wcd937x_tx_connect_port(component, DMIC0 + (w->shift), true); + } break; case SND_SOC_DAPM_POST_PMD: ret = swr_slvdev_datapath_control(wcd937x->tx_swr_dev, @@ -1352,6 +1359,7 @@ static int wcd937x_codec_enable_adc(struct snd_soc_dapm_widget *w, snd_soc_dapm_to_component(w->dapm); struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + int ret = 0; dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, w->name, event); @@ -1367,13 +1375,9 @@ static int wcd937x_codec_enable_adc(struct snd_soc_dapm_widget *w, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x08, 0x08); snd_soc_component_update_bits(component, WCD937X_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); - /* Enable BCS for Headset mic */ - if (w->shift == 1 && !(snd_soc_component_read32(component, - WCD937X_TX_NEW_TX_CH2_SEL) & 0x80)) { - wcd937x_tx_connect_port(component, MBHC, true); - set_bit(AMIC2_BCS_ENABLE, &wcd937x->status_mask); - } - wcd937x_tx_connect_port(component, ADC1 + (w->shift), true); + ret = swr_slvdev_datapath_control(wcd937x->tx_swr_dev, + wcd937x->tx_swr_dev->dev_num, + true); break; case SND_SOC_DAPM_POST_PMD: wcd937x_tx_connect_port(component, ADC1 + (w->shift), false); @@ -1387,7 +1391,7 @@ static int wcd937x_codec_enable_adc(struct snd_soc_dapm_widget *w, break; }; - return 0; + return ret; } static int wcd937x_enable_req(struct snd_soc_dapm_widget *w, @@ -2263,7 +2267,7 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { adc1_switch, ARRAY_SIZE(adc1_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 1, 0, adc2_switch, ARRAY_SIZE(adc2_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -2414,27 +2418,27 @@ static const struct snd_soc_dapm_widget wcd9375_dapm_widgets[] = { 0, dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 1, 0, dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 2, 0, dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 3, 0, dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 4, 0, dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 5, 0, dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, adc3_switch, + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 2, 0, adc3_switch, ARRAY_SIZE(adc3_switch), wcd937x_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), diff --git a/asoc/holi-port-config.h b/asoc/holi-port-config.h index c2b00743187b..9eb5adf3f121 100644 --- a/asoc/holi-port-config.h +++ b/asoc/holi-port-config.h @@ -47,13 +47,13 @@ static struct port_params tx_frame_params_wcd937x[SWR_MSTR_PORT_LEN] = { }; static struct swr_mstr_port_map sm_port_map[] = { - {TX_MACRO, SWR_UC0, tx_frame_params_default}, + {VA_MACRO, SWR_UC0, tx_frame_params_default}, {RX_MACRO, SWR_UC0, rx_frame_params_default}, {RX_MACRO, SWR_UC1, rx_frame_params_dsd}, }; static struct swr_mstr_port_map sm_port_map_wcd937x[] = { - {TX_MACRO, SWR_UC0, tx_frame_params_wcd937x}, + {VA_MACRO, SWR_UC0, tx_frame_params_wcd937x}, {RX_MACRO, SWR_UC0, rx_frame_params_default}, {RX_MACRO, SWR_UC1, rx_frame_params_dsd}, }; From 534959821d76fa103021015a3cd606d248f81fc2 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Fri, 28 Aug 2020 01:50:04 +0530 Subject: [PATCH 03/79] dsp: print debug messages for adsp variant print debug messages while loading adsp variant. Change-Id: I0ac31af512e2743b85e53ff54682e7e7af548fb5 --- dsp/adsp-loader.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dsp/adsp-loader.c b/dsp/adsp-loader.c index a8579e40c081..cc1b747491a4 100644 --- a/dsp/adsp-loader.c +++ b/dsp/adsp-loader.c @@ -179,11 +179,11 @@ load_adsp: goto fail; } if (!priv->adsp_fw_name) { - dev_dbg(&pdev->dev, "%s: Load default ADSP\n", + dev_err(&pdev->dev, "%s: Load default ADSP\n", __func__); priv->pil_h = subsystem_get("adsp"); } else { - dev_dbg(&pdev->dev, "%s: Load ADSP with fw name %s\n", + dev_err(&pdev->dev, "%s: Load ADSP with fw name %s\n", __func__, priv->adsp_fw_name); priv->pil_h = subsystem_get_with_fwname("adsp", priv->adsp_fw_name); } @@ -478,6 +478,8 @@ static int adsp_loader_probe(struct platform_device *pdev) goto wqueue; } + dev_err(&pdev->dev, "%s: looking for adsp_var_idx 0x%x\n", + __func__, adsp_var_idx); for (i = 0; i < adsp_fw_cnt; i++) { if (adsp_fw_bit_values[i] == adsp_var_idx) { fw_name_size = strlen(adsp_fw_name_array[i]) + 1; From 56bec641db500850bb7b6dc11220faf3a39c0123 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Sat, 29 Aug 2020 07:28:55 +0530 Subject: [PATCH 04/79] ASOC: update soundwire master port config for holi Update soundwire master port config to support wcd937x slave for holi. Change-Id: I87afe6942a52d576591bf8f6f27cc47441a356c1 Signed-off-by: Vangala, Amarnath --- asoc/holi-port-config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/holi-port-config.h b/asoc/holi-port-config.h index 9eb5adf3f121..abc23596a6a3 100644 --- a/asoc/holi-port-config.h +++ b/asoc/holi-port-config.h @@ -41,8 +41,8 @@ static struct port_params tx_frame_params_default[SWR_MSTR_PORT_LEN] = { }; static struct port_params tx_frame_params_wcd937x[SWR_MSTR_PORT_LEN] = { - {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX1 */ - {3, 2, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0x00, 0x00}, /* TX2 */ + {3, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0x00, 0x00}, /* TX1 */ + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX2 */ {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX3 */ }; From 8c1964df0a3c24b7e2fbe8009f03f830f4420d27 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Sat, 29 Aug 2020 06:31:34 +0530 Subject: [PATCH 05/79] soc: soundwire: fix for headset mic recording issue with wcd937x. Add slave device address for wcd937x in soundwire master. Update slave port configuration for wcd937x for holi. Change-Id: I1f953e47b1520efd99b6f400593242ae5fc6158a Signed-off-by: Vangala, Amarnath --- soc/swr-mstr-ctrl.c | 9 +++++++++ soc/swr-slave-port-config.h | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index f7e66a82a5f4..b2868e9c2204 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -2583,6 +2583,7 @@ static int swrm_probe(struct platform_device *pdev) int ret = 0; struct clk *lpass_core_hw_vote = NULL; struct clk *lpass_core_audio = NULL; + u32 is_wcd937x = 0; /* Allocate soundwire master driver structure */ swrm = devm_kzalloc(&pdev->dev, sizeof(struct swr_mstr_ctrl), @@ -2615,6 +2616,14 @@ static int swrm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s: failed to get master id\n", __func__); goto err_pdata_fail; } + /* update the physical device address if wcd937x. */ + ret = of_property_read_u32(pdev->dev.of_node, "qcom,is_wcd937x", + &is_wcd937x); + if (ret) + dev_dbg(&pdev->dev, "%s: failed to get wcd info\n", __func__); + else if (is_wcd937x) + swrm_phy_dev[1] = 0xa01170223; + ret = of_property_read_u32(pdev->dev.of_node, "qcom,dynamic-port-map-supported", &swrm->dynamic_port_map_supported); if (ret) { diff --git a/soc/swr-slave-port-config.h b/soc/swr-slave-port-config.h index 77f280bcf86b..7eb7b5feafa0 100644 --- a/soc/swr-slave-port-config.h +++ b/soc/swr-slave-port-config.h @@ -23,12 +23,21 @@ static struct port_params tx_dummy[SWR_MSTR_PORT_LEN] = { }; /* AMIC 9.6 MHz clock */ +#ifdef CONFIG_SND_SOC_HOLI +static struct port_params tx_wcd_9p6MHz[SWR_MSTR_PORT_LEN] = { + {3, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0x00, 0x00}, /* TX1 */ + {3, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX2 */ + {7, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX3 */ + {7, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX4 */ +}; +#else static struct port_params tx_wcd_9p6MHz[SWR_MSTR_PORT_LEN] = { {3, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 1, 0x00, 0x00}, /* TX1 */ {7, 5, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX2 */ {7, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX3 */ {7, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0x00, 0x00}, /* TX4 */ }; +#endif /* AMIC 4.8 MHz clock */ static struct port_params tx_wcd_4p8MHz[SWR_MSTR_PORT_LEN] = { From 0eeab6481cce3d9afea34e0f224c7d86234047d5 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Wed, 26 Aug 2020 10:57:22 +0530 Subject: [PATCH 06/79] asoc: codec: add delay if regmap is not registered Add delay if the regmap of wsa slave is not registered to avoid Null pointer dereference. Change-Id: Ic3e5d1cc7c3fc33f4f4c480708cd562b73c1a04b Signed-off-by: Prasad Kumpatla --- asoc/codecs/wsa881x-analog.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wsa881x-analog.c b/asoc/codecs/wsa881x-analog.c index 690d5d1b671c..28c5976e6338 100644 --- a/asoc/codecs/wsa881x-analog.c +++ b/asoc/codecs/wsa881x-analog.c @@ -34,6 +34,7 @@ #define SPK_GAIN_12DB 4 #define WIDGET_NAME_MAX_SIZE 80 +#define REGMAP_REGISTER_CHECK_RETRY 30 #define MAX_NAME_LEN 30 #define WSA881X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ @@ -102,7 +103,7 @@ static int32_t wsa881x_resource_acquire(struct snd_soc_component *component, const char *wsa_tz_names[] = {"wsa881x.0e", "wsa881x.0f"}; -struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE]; +static struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE]; static bool pinctrl_init; @@ -1142,6 +1143,7 @@ static int wsa881x_probe(struct snd_soc_component *component) { struct i2c_client *client; int ret = 0; + int retry = REGMAP_REGISTER_CHECK_RETRY; int wsa881x_index = 0; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); @@ -1165,6 +1167,13 @@ static int wsa881x_probe(struct snd_soc_component *component) wsa_pdata[wsa881x_index].tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read; snd_soc_component_set_drvdata(component, &wsa_pdata[wsa881x_index]); + while (retry) { + if (wsa_pdata[wsa881x_index].regmap[WSA881X_ANALOG_SLAVE] + != NULL) + break; + msleep(100); + retry--; + } wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata); INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work, wsa881x_ocp_ctl_work); From 6b52dc17da514ad5b34a737fb3308363d56ab685 Mon Sep 17 00:00:00 2001 From: Niranjan Reddy Dumbala Date: Sun, 20 Sep 2020 11:59:23 +0530 Subject: [PATCH 07/79] asoc: codec: add delay if regmap is not registered Add delay if the regmap of wsa slave is not registered to avoid Null pointer dereference. Change-Id: I4b0aefb08066a2c2d4e622725dd8ca60ee9e3db4 Signed-off-by: Niranjan Reddy Dumbala --- asoc/codecs/wsa881x-analog.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/asoc/codecs/wsa881x-analog.c b/asoc/codecs/wsa881x-analog.c index 28c5976e6338..2a07f9928517 100644 --- a/asoc/codecs/wsa881x-analog.c +++ b/asoc/codecs/wsa881x-analog.c @@ -1174,6 +1174,9 @@ static int wsa881x_probe(struct snd_soc_component *component) msleep(100); retry--; } + if (!retry) + dev_err(&client->dev, "%s: max retry expired and regmap of\n" + "analog slave not initilized\n", __func__); wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata); INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work, wsa881x_ocp_ctl_work); @@ -1391,9 +1394,9 @@ static int wsa881x_i2c_probe(struct i2c_client *client, } if (pdata->status == WSA881X_STATUS_I2C) { - dev_dbg(&client->dev, "%s:probe for other slaves\n" - "devices of codec I2C slave Addr = %x\n", - __func__, client->addr); + dev_info(&client->dev, "%s:probe for other slaves\n" + "devices of codec I2C slave Addr = %x wsa_idx = %d\n", + __func__, client->addr, wsa881x_index); dev_dbg(&client->dev, "%s:wsa_idx = %d SLAVE = %d\n", __func__, wsa881x_index, WSA881X_ANALOG_SLAVE); pdata->regmap[WSA881X_ANALOG_SLAVE] = @@ -1411,6 +1414,7 @@ static int wsa881x_i2c_probe(struct i2c_client *client, client->dev.platform_data = pdata; i2c_set_clientdata(client, pdata); pdata->client[WSA881X_ANALOG_SLAVE] = client; + pdata->regmap_flag = true; if (pdata->version == WSA881X_2_0) wsa881x_update_regmap_2_0( pdata->regmap[WSA881X_ANALOG_SLAVE], @@ -1476,7 +1480,6 @@ static int wsa881x_i2c_probe(struct i2c_client *client, goto err; } pdata->client[WSA881X_DIGITAL_SLAVE] = client; - pdata->regmap_flag = true; ret = check_wsa881x_presence(client); if (ret < 0) { dev_err(&client->dev, @@ -1560,6 +1563,8 @@ static int wsa881x_i2c_probe(struct i2c_client *client, component->name_prefix = pdata->wsa881x_name_prefix; pdata->status = WSA881X_STATUS_I2C; + dev_info(&client->dev, "%s:pdata status changed to I2C\n", + __func__); goto err1; } err_mem: From 11922a3591298d6864e637e75289f99eb4319873 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Mon, 14 Sep 2020 12:44:05 +0530 Subject: [PATCH 08/79] ASOC: Disable Temperature sensor of WSA881x for holi Temperature sensor of WSA881x is not used in holi. Hence disable compilation. Change-Id: I9fe1d297413c0029970c7719aad6c04ee3f84ab4 Signed-off-by: Vangala, Amarnath --- config/holiauto.conf | 1 + config/holiautoconf.h | 1 + 2 files changed, 2 insertions(+) diff --git a/config/holiauto.conf b/config/holiauto.conf index e46212b20fee..d0d6dd0456c3 100644 --- a/config/holiauto.conf +++ b/config/holiauto.conf @@ -29,6 +29,7 @@ export CONFIG_SND_SOC_WCD938X_SLAVE=m export CONFIG_SND_SOC_WCD937X=m export CONFIG_SND_SOC_WCD937X_SLAVE=m export CONFIG_SND_SOC_WSA881X_ANALOG=m +export CONFIG_WSA881X_TEMP_SENSOR_DISABLE=m export CONFIG_SND_SOC_HOLI=m export CONFIG_SND_EVENT=m export CONFIG_TDM_DISABLE=m diff --git a/config/holiautoconf.h b/config/holiautoconf.h index b1758be036d6..2eb9a6d5a432 100644 --- a/config/holiautoconf.h +++ b/config/holiautoconf.h @@ -33,6 +33,7 @@ #define CONFIG_SND_SOC_WCD937X 1 #define CONFIG_SND_SOC_WCD937X_SLAVE 1 #define CONFIG_SND_SOC_WSA881X_ANALOG 1 +#define CONFIG_WSA881X_TEMP_SENSOR_DISABLE 1 #define CONFIG_SND_SOC_HOLI 1 #define CONFIG_SND_EVENT 1 #define CONFIG_TDM_DISABLE 1 From 41e86c25ad7175e619f5c415efa2a47e339fbc43 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Sun, 13 Sep 2020 01:02:56 +0530 Subject: [PATCH 09/79] ASOC: add support to disable WSA881x temperature sensor Add support to disable compilation of temperature sensor for wsa881x analog module, when not needed. Change-Id: I054e90d4b1abae3c0cdc6623b1f688120ba87dbd Signed-off-by: Vangala, Amarnath --- asoc/codecs/Kbuild | 2 ++ asoc/codecs/wsa881x-temp-sensor.h | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 3d302bd7c25a..e861791dd127 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -164,8 +164,10 @@ ifdef CONFIG_SND_SOC_WSA881X_ANALOG WSA881X_ANALOG_OBJS += wsa881x-analog.o WSA881X_ANALOG_OBJS += wsa881x-tables-analog.o WSA881X_ANALOG_OBJS += wsa881x-regmap-analog.o +ifndef CONFIG_WSA881X_TEMP_SENSOR_DISABLE WSA881X_ANALOG_OBJS += wsa881x-temp-sensor.o endif +endif ifdef CONFIG_SND_SOC_MSM_STUB STUB_OBJS += msm_stub.o endif diff --git a/asoc/codecs/wsa881x-temp-sensor.h b/asoc/codecs/wsa881x-temp-sensor.h index fbb5941abab6..483560db2b6a 100644 --- a/asoc/codecs/wsa881x-temp-sensor.h +++ b/asoc/codecs/wsa881x-temp-sensor.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/* Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2018, 2020 The Linux Foundation. All rights reserved. */ #ifndef WSA881X_TEMP_SENSOR_H #define WSA881X_TEMP_SENSOR_H @@ -29,7 +29,13 @@ struct wsa881x_tz_priv { int curr_temp; }; -int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp); +#ifndef CONFIG_WSA881X_TEMP_SENSOR_DISABLE int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata); void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev); +int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp); +#else +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata){ return 0; } +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev){} +int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp){ return 0; } +#endif #endif From 8492f866f09ab0a59b7d806d50508697ccbeb6a8 Mon Sep 17 00:00:00 2001 From: Niranjan Reddy Dumbala Date: Mon, 12 Oct 2020 18:28:38 +0530 Subject: [PATCH 10/79] Revert "dsp: print debug messages for adsp variant" This reverts commit 534959821d76fa103021015a3cd606d248f81fc2. --- dsp/adsp-loader.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dsp/adsp-loader.c b/dsp/adsp-loader.c index cc1b747491a4..a8579e40c081 100644 --- a/dsp/adsp-loader.c +++ b/dsp/adsp-loader.c @@ -179,11 +179,11 @@ load_adsp: goto fail; } if (!priv->adsp_fw_name) { - dev_err(&pdev->dev, "%s: Load default ADSP\n", + dev_dbg(&pdev->dev, "%s: Load default ADSP\n", __func__); priv->pil_h = subsystem_get("adsp"); } else { - dev_err(&pdev->dev, "%s: Load ADSP with fw name %s\n", + dev_dbg(&pdev->dev, "%s: Load ADSP with fw name %s\n", __func__, priv->adsp_fw_name); priv->pil_h = subsystem_get_with_fwname("adsp", priv->adsp_fw_name); } @@ -478,8 +478,6 @@ static int adsp_loader_probe(struct platform_device *pdev) goto wqueue; } - dev_err(&pdev->dev, "%s: looking for adsp_var_idx 0x%x\n", - __func__, adsp_var_idx); for (i = 0; i < adsp_fw_cnt; i++) { if (adsp_fw_bit_values[i] == adsp_var_idx) { fw_name_size = strlen(adsp_fw_name_array[i]) + 1; From 842d42ad343c78d5f62db2a94ddee08c219dd6c9 Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Wed, 7 Oct 2020 21:31:04 +0530 Subject: [PATCH 11/79] asoc: holi: use new pm_qos API This change ensure all concurrent low latency/ ultra low latency use cases run on core 1 and 2. Change-Id: I187787ea609768bbfb6098a844dab4ebef611a9c --- asoc/holi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asoc/holi.c b/asoc/holi.c index 8c3bdbf24db6..04d2c2ddf890 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -4343,7 +4343,8 @@ err: static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) { - (void)substream; + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); qos_client_active_cnt++; if (qos_client_active_cnt == 1) From 8cc0a7373c75221e9573b2b4a413571fe7fa670e Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Fri, 16 Oct 2020 17:53:22 +0530 Subject: [PATCH 12/79] asoc: msm-pcm-q6-v2: Update memset for period size tinycap test can attempt with different size to read from driver and need to avoid access more than period size. Change-Id: Ifa4ddfb086bd83aa981da62e88da3a9395f5aabc Signed-off-by: Laxminath Kasam --- asoc/msm-pcm-q6-v2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index d496d8921eb6..7f2368d00208 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1008,9 +1008,9 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, xfer = size; offset = prtd->in_frame_info[idx].offset; pr_debug("Offset value = %d\n", offset); - if (size == 0 || size < fbytes) { - memset(bufptr + offset + size, 0, fbytes - size); - size = xfer = fbytes; + if (size == 0 || size < prtd->pcm_count) { + memset(bufptr + offset + size, 0, prtd->pcm_count - size); + size = xfer = prtd->pcm_count; } if (copy_to_user(buf, bufptr+offset, xfer)) { From fb9f777220bb67766431d70f02b8c7e2aa7c4ede Mon Sep 17 00:00:00 2001 From: "Vangala, Amarnath" Date: Tue, 27 Oct 2020 23:53:04 +0530 Subject: [PATCH 13/79] asoc: codecs: fix race condition of core vote and reg access Auto suspend timer for core vote is triggering before read write complete. Move the auto suspend of core vote to post read write operation. Change-Id: Ib0f6b026fe0e7fd3fbe052691db492915e436a78 Signed-off-by: Vangala, Amarnath --- asoc/codecs/bolero/rx-macro.c | 17 ++++++++----- asoc/codecs/bolero/tx-macro.c | 13 ++++++---- asoc/codecs/bolero/va-macro.c | 13 ++++++---- asoc/codecs/bolero/wsa-macro.c | 13 ++++++---- soc/swr-mstr-ctrl.c | 45 ++++++++++++++++++++++++++++------ 5 files changed, 73 insertions(+), 28 deletions(-) diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c index a924c240263a..9deb652f0fe5 100644 --- a/asoc/codecs/bolero/rx-macro.c +++ b/asoc/codecs/bolero/rx-macro.c @@ -1280,6 +1280,7 @@ static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv, rx_priv->default_clk_id, rx_priv->clk_id, true); + rx_macro_core_vote(rx_priv, false); if (ret < 0) { dev_err(rx_priv->dev, "%s: rx request clock enable failed\n", @@ -1334,6 +1335,7 @@ static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv, rx_priv->default_clk_id, rx_priv->clk_id, false); + rx_macro_core_vote(rx_priv, false); rx_priv->clk_id = rx_priv->default_clk_id; } } @@ -1453,11 +1455,11 @@ static int rx_macro_event_handler(struct snd_soc_component *component, "%s, failed to enable clk, ret:%d\n", __func__, ret); } else { - rx_macro_core_vote(rx_priv, true); bolero_clk_rsc_request_clock(rx_priv->dev, rx_priv->default_clk_id, RX_CORE_CLK, false); } + rx_macro_core_vote(rx_priv, false); break; case BOLERO_MACRO_EVT_SSR_UP: trace_printk("%s, enter SSR up\n", __func__); @@ -3735,22 +3737,25 @@ static const struct snd_soc_dapm_route rx_audio_map[] = { static int rx_macro_core_vote(void *handle, bool enable) { + int rc = 0; struct rx_macro_priv *rx_priv = (struct rx_macro_priv *) handle; if (rx_priv == NULL) { pr_err("%s: rx priv data is NULL\n", __func__); return -EINVAL; } + if (enable) { pm_runtime_get_sync(rx_priv->dev); + if (bolero_check_core_votes(rx_priv->dev)) + rc = 0; + else + rc = -ENOTSYNC; + } else { pm_runtime_put_autosuspend(rx_priv->dev); pm_runtime_mark_last_busy(rx_priv->dev); } - - if (bolero_check_core_votes(rx_priv->dev)) - return 0; - else - return -EINVAL; + return rc; } static int rx_swrm_clock(void *handle, bool enable) diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c index e63a23c19380..e6936a441fe6 100644 --- a/asoc/codecs/bolero/tx-macro.c +++ b/asoc/codecs/bolero/tx-macro.c @@ -2918,22 +2918,25 @@ ret: static int tx_macro_core_vote(void *handle, bool enable) { + int rc = 0; struct tx_macro_priv *tx_priv = (struct tx_macro_priv *) handle; if (tx_priv == NULL) { pr_err("%s: tx priv data is NULL\n", __func__); return -EINVAL; } + if (enable) { pm_runtime_get_sync(tx_priv->dev); + if (bolero_check_core_votes(tx_priv->dev)) + rc = 0; + else + rc = -ENOTSYNC; + } else { pm_runtime_put_autosuspend(tx_priv->dev); pm_runtime_mark_last_busy(tx_priv->dev); } - - if (bolero_check_core_votes(tx_priv->dev)) - return 0; - else - return -EINVAL; + return rc; } static int tx_macro_swrm_clock(void *handle, bool enable) diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c index eed5d316197d..34564add16bb 100644 --- a/asoc/codecs/bolero/va-macro.c +++ b/asoc/codecs/bolero/va-macro.c @@ -725,22 +725,25 @@ done: static int va_macro_core_vote(void *handle, bool enable) { + int rc = 0; struct va_macro_priv *va_priv = (struct va_macro_priv *) handle; if (va_priv == NULL) { pr_err("%s: va priv data is NULL\n", __func__); return -EINVAL; } + if (enable) { pm_runtime_get_sync(va_priv->dev); + if (bolero_check_core_votes(va_priv->dev)) + rc = 0; + else + rc = -ENOTSYNC; + } else { pm_runtime_put_autosuspend(va_priv->dev); pm_runtime_mark_last_busy(va_priv->dev); } - - if (bolero_check_core_votes(va_priv->dev)) - return 0; - else - return -EINVAL; + return rc; } static int va_macro_swrm_clock(void *handle, bool enable) diff --git a/asoc/codecs/bolero/wsa-macro.c b/asoc/codecs/bolero/wsa-macro.c index 27854492f2b9..489f8ad11836 100644 --- a/asoc/codecs/bolero/wsa-macro.c +++ b/asoc/codecs/bolero/wsa-macro.c @@ -2833,22 +2833,25 @@ static void wsa_macro_init_reg(struct snd_soc_component *component) static int wsa_macro_core_vote(void *handle, bool enable) { + int rc = 0; struct wsa_macro_priv *wsa_priv = (struct wsa_macro_priv *) handle; if (wsa_priv == NULL) { pr_err("%s: wsa priv data is NULL\n", __func__); return -EINVAL; } + if (enable) { pm_runtime_get_sync(wsa_priv->dev); + if (bolero_check_core_votes(wsa_priv->dev)) + rc = 0; + else + rc = -ENOTSYNC; + } else { pm_runtime_put_autosuspend(wsa_priv->dev); pm_runtime_mark_last_busy(wsa_priv->dev); } - - if (bolero_check_core_votes(wsa_priv->dev)) - return 0; - else - return -EINVAL; + return rc; } static int wsa_swrm_clock(void *handle, bool enable) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index 3633f95c5dff..ab2c2f7abfa5 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -508,7 +508,7 @@ static int swrm_get_ssp_period(struct swr_mstr_ctrl *swrm, return ((swrm->bus_clk * 2) / ((row * col) * frame_sync)); } -static int swrm_core_vote_request(struct swr_mstr_ctrl *swrm) +static int swrm_core_vote_request(struct swr_mstr_ctrl *swrm, bool enable) { int ret = 0; @@ -521,7 +521,7 @@ static int swrm_core_vote_request(struct swr_mstr_ctrl *swrm) goto exit; } if (swrm->core_vote) { - ret = swrm->core_vote(swrm->handle, true); + ret = swrm->core_vote(swrm->handle, enable); if (ret) dev_err_ratelimited(swrm->dev, "%s: core vote request failed\n", __func__); @@ -552,8 +552,10 @@ static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) dev_err_ratelimited(swrm->dev, "%s: core vote request failed\n", __func__); + swrm->core_vote(swrm->handle, false); goto exit; } + ret = swrm->core_vote(swrm->handle, false); } } swrm->clk_ref_count++; @@ -589,6 +591,7 @@ static int swrm_ahb_write(struct swr_mstr_ctrl *swrm, { u32 temp = (u32)(*value); int ret = 0; + int vote_ret = 0; mutex_lock(&swrm->devlock); if (!swrm->dev_up) @@ -602,13 +605,20 @@ static int swrm_ahb_write(struct swr_mstr_ctrl *swrm, __func__); goto err; } - } else if (swrm_core_vote_request(swrm)) { - goto err; + } else { + vote_ret = swrm_core_vote_request(swrm, true); + if (vote_ret == -ENOTSYNC) + goto err_vote; + else if (vote_ret) + goto err; } iowrite32(temp, swrm->swrm_dig_base + reg); if (is_swr_clk_needed(swrm)) swrm_clk_request(swrm, FALSE); +err_vote: + if (!is_swr_clk_needed(swrm)) + swrm_core_vote_request(swrm, false); err: mutex_unlock(&swrm->devlock); return ret; @@ -619,6 +629,7 @@ static int swrm_ahb_read(struct swr_mstr_ctrl *swrm, { u32 temp = 0; int ret = 0; + int vote_ret = 0; mutex_lock(&swrm->devlock); if (!swrm->dev_up) @@ -631,14 +642,21 @@ static int swrm_ahb_read(struct swr_mstr_ctrl *swrm, __func__); goto err; } - } else if (swrm_core_vote_request(swrm)) { - goto err; + } else { + vote_ret = swrm_core_vote_request(swrm, true); + if (vote_ret == -ENOTSYNC) + goto err_vote; + else if (vote_ret) + goto err; } temp = ioread32(swrm->swrm_dig_base + reg); *value = temp; if (is_swr_clk_needed(swrm)) swrm_clk_request(swrm, FALSE); +err_vote: + if (!is_swr_clk_needed(swrm)) + swrm_core_vote_request(swrm, false); err: mutex_unlock(&swrm->devlock); return ret; @@ -2582,6 +2600,7 @@ static int swrm_probe(struct platform_device *pdev) struct clk *lpass_core_hw_vote = NULL; struct clk *lpass_core_audio = NULL; u32 is_wcd937x = 0; + u32 swrm_hw_ver = 0; /* Allocate soundwire master driver structure */ swrm = devm_kzalloc(&pdev->dev, sizeof(struct swr_mstr_ctrl), @@ -2608,6 +2627,14 @@ static int swrm_probe(struct platform_device *pdev) ret = -EINVAL; goto err_pdata_fail; } + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,swr-master-version", + &swrm->version); + if (ret) { + dev_dbg(&pdev->dev, "%s: swrm version not defined, use default\n", + __func__); + swrm->version = SWRM_VERSION_1_6; + } ret = of_property_read_u32(pdev->dev.of_node, "qcom,swr_master_id", &swrm->master_id); if (ret) { @@ -2883,11 +2910,15 @@ static int swrm_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s: Audio HW Vote is failed\n", __func__); mutex_lock(&swrm->mlock); swrm_clk_request(swrm, true); - swrm->version = swr_master_read(swrm, SWRM_COMP_HW_VERSION); swrm->rd_fifo_depth = ((swr_master_read(swrm, SWRM_COMP_PARAMS) & SWRM_COMP_PARAMS_RD_FIFO_DEPTH) >> 15); swrm->wr_fifo_depth = ((swr_master_read(swrm, SWRM_COMP_PARAMS) & SWRM_COMP_PARAMS_WR_FIFO_DEPTH) >> 10); + swrm_hw_ver = swr_master_read(swrm, SWRM_COMP_HW_VERSION); + if (swrm->version != swrm_hw_ver) + dev_info(&pdev->dev, + "%s: version specified in dtsi: 0x%x not match with HW read version 0x%x\n", + __func__, swrm->version, swrm_hw_ver); ret = swrm_master_init(swrm); if (ret < 0) { dev_err(&pdev->dev, From bb39f155cb21fe48569f812456bd371327146d25 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 10 Nov 2020 16:19:55 +0800 Subject: [PATCH 14/79] asoc: codec: add range check to avoid overflow Add range check for ucontrol value to avoid array overflow. Change-Id: I7b699519bec6f605e5ca5ece8f67badbefc7a094 Signed-off-by: Meng Wang --- asoc/codecs/swr-dmic.c | 8 +++++-- asoc/codecs/wcd937x/wcd937x.c | 45 +++++++++++++++++++++++++---------- asoc/codecs/wcd938x/wcd938x.c | 9 ++++--- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/asoc/codecs/swr-dmic.c b/asoc/codecs/swr-dmic.c index 436715c68808..bfcdf1323e96 100644 --- a/asoc/codecs/swr-dmic.c +++ b/asoc/codecs/swr-dmic.c @@ -174,7 +174,7 @@ static int swr_dmic_tx_master_port_put(struct snd_kcontrol *kcontrol, snd_soc_kcontrol_component(kcontrol); struct swr_dmic_priv *swr_dmic = NULL; int ret = 0; - unsigned int slave_port_idx = SWR_DMIC_MAX_PORTS; + unsigned int slave_port_idx = SWR_DMIC_MAX_PORTS, idx = 0; if (NULL == component) { pr_err("%s: swr dmic component is NULL\n", __func__); @@ -199,8 +199,12 @@ static int swr_dmic_tx_master_port_put(struct snd_kcontrol *kcontrol, return -EINVAL; } + idx = ucontrol->value.enumerated.item[0]; + if (idx < 0 || idx >= ARRAY_SIZE(swr_master_channel_map)) + return -EINVAL; + swr_dmic->tx_master_port_map[slave_port_idx] = - swr_master_channel_map[ucontrol->value.enumerated.item[0]]; + swr_master_channel_map[idx]; dev_dbg(component->dev, "%s: slv port id: %d, master_port_type: %d\n", __func__, slave_port_idx, swr_dmic->tx_master_port_map[slave_port_idx]); diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 7967dff74cf1..b8a2c6cadbc8 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/asoc/codecs/wcd937x/wcd937x.c @@ -2048,15 +2048,24 @@ static int wcd937x_tx_master_ch_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); - int slave_ch_idx; + struct wcd937x_priv *wcd937x = NULL; + int slave_ch_idx = -EINVAL; + + if (component == NULL) + return -EINVAL; + + wcd937x = snd_soc_component_get_drvdata(component); + if (wcd937x == NULL) + return -EINVAL; wcd937x_tx_get_slave_ch_type_idx(kcontrol->id.name, &slave_ch_idx); - if (slave_ch_idx != -EINVAL) - ucontrol->value.integer.value[0] = - wcd937x_slave_get_master_ch_val( - wcd937x->tx_master_ch_map[slave_ch_idx]); + if (slave_ch_idx < 0 || slave_ch_idx >= WCD937X_MAX_SLAVE_CH_TYPES) + return -EINVAL; + + ucontrol->value.integer.value[0] = + wcd937x_slave_get_master_ch_val( + wcd937x->tx_master_ch_map[slave_ch_idx]); dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); @@ -2068,19 +2077,31 @@ static int wcd937x_tx_master_ch_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); - int slave_ch_idx; + struct wcd937x_priv *wcd937x; + int slave_ch_idx = -EINVAL, idx = 0; + + if (component == NULL) + return -EINVAL; + + wcd937x = snd_soc_component_get_drvdata(component); + if (wcd937x == NULL) + return -EINVAL; wcd937x_tx_get_slave_ch_type_idx(kcontrol->id.name, &slave_ch_idx); + if (slave_ch_idx < 0 || slave_ch_idx >= WCD937X_MAX_SLAVE_CH_TYPES) + return -EINVAL; + dev_dbg(component->dev, "%s: slave_ch_idx: %d", __func__, slave_ch_idx); dev_dbg(component->dev, "%s: ucontrol->value.enumerated.item[0] = %ld\n", __func__, ucontrol->value.enumerated.item[0]); - if (slave_ch_idx != -EINVAL) - wcd937x->tx_master_ch_map[slave_ch_idx] = - wcd937x_slave_get_master_ch( - ucontrol->value.enumerated.item[0]); + idx = ucontrol->value.enumerated.item[0]; + if (idx < 0 || idx >= ARRAY_SIZE(wcd937x_swr_master_ch_map)) + return -EINVAL; + + wcd937x->tx_master_ch_map[slave_ch_idx] = + wcd937x_slave_get_master_ch(idx); return 0; } diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c index c4d2807a3cfc..b8e05ae02f33 100644 --- a/asoc/codecs/wcd938x/wcd938x.c +++ b/asoc/codecs/wcd938x/wcd938x.c @@ -2869,7 +2869,7 @@ static int wcd938x_tx_master_ch_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wcd938x_priv *wcd938x = NULL; - int slave_ch_idx = -EINVAL; + int slave_ch_idx = -EINVAL, idx = 0; if (component == NULL) return -EINVAL; @@ -2887,8 +2887,11 @@ static int wcd938x_tx_master_ch_put(struct snd_kcontrol *kcontrol, dev_dbg(component->dev, "%s: ucontrol->value.enumerated.item[0] = %ld\n", __func__, ucontrol->value.enumerated.item[0]); - wcd938x->tx_master_ch_map[slave_ch_idx] = wcd938x_slave_get_master_ch( - ucontrol->value.enumerated.item[0]); + idx = ucontrol->value.enumerated.item[0]; + if (idx < 0 || idx >= ARRAY_SIZE(swr_master_ch_map)) + return -EINVAL; + + wcd938x->tx_master_ch_map[slave_ch_idx] = wcd938x_slave_get_master_ch(idx); return 0; } From ac318792a3666b743633ab27b3d1eae39d4dea30 Mon Sep 17 00:00:00 2001 From: xsang Date: Tue, 24 Nov 2020 23:22:36 +0800 Subject: [PATCH 15/79] asoc: compress: support gapless playback in GKI mode Next track gapless mode requires stream state of running. Set stream state to running after partial buffer, EOS and zero buffer. Change-Id: I61e55989305cd495a9a03306ccecdd62b4f7504f Signed-off-by: xsang --- asoc/msm-compress-q6-v2.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index feb09b9c7426..96166c1f4ee9 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -711,6 +711,11 @@ static void compr_event_handler(uint32_t opcode, pr_debug("%s: issue CMD_RUN", __func__); q6asm_run_nowait(prtd->audio_client, 0, 0, 0); snd_compr_drain_notify(cstream); + /* + * Next track requires state of running. Otherwise, + * it fails. + */ + cstream->runtime->state = SNDRV_PCM_STATE_RUNNING; spin_unlock_irqrestore(&prtd->lock, flags); break; } @@ -2550,9 +2555,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct audio_client *ac = prtd->audio_client; unsigned long fe_id = rtd->dai_link->id; int rc = 0; -#if IS_ENABLED(CONFIG_AUDIO_QGKI) int bytes_to_write; -#endif unsigned long flags; int stream_id; uint32_t stream_index; @@ -2941,6 +2944,21 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) q6asm_run_nowait(prtd->audio_client, 0, 0, 0); } #else + if ((prtd->bytes_received > prtd->copied_total) && + (prtd->bytes_received < runtime->fragment_size)) { + pr_debug("%s: send the only partial buffer to dsp\n", + __func__); + bytes_to_write = prtd->bytes_received + - prtd->copied_total; + if (bytes_to_write > 0) { + pr_debug("%s: send %d partial bytes at the end", + __func__, bytes_to_write); + atomic_set(&prtd->xrun, 0); + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); + } + } + atomic_set(&prtd->drain, 1); spin_unlock_irqrestore(&prtd->lock, flags); #endif From 13e702abd00d324b8939b7b20ead3ed82b45559b Mon Sep 17 00:00:00 2001 From: Harshal Ahire Date: Wed, 20 Jan 2021 17:50:21 +0530 Subject: [PATCH 16/79] Revert "Merge "dsp: codecs: Synchronize the audio_aio_open and SSR event"" This reverts commit ed43bdaa545a2fc2ba6d5dc6422a2290413e3b81 reversing changes made to 53feb46e27beedc826fc27f3b14f84c6bcf48bd5. Change-Id: Ic9fdc04d08c9bfd4b0be2f29c315e1602375bddd Signed-off-by: Harshal Ahire --- dsp/codecs/audio_aac.c | 11 ++--------- dsp/codecs/audio_alac.c | 11 ++--------- dsp/codecs/audio_amrnb.c | 12 +++--------- dsp/codecs/audio_amrwb.c | 12 ++++-------- dsp/codecs/audio_amrwbplus.c | 11 ++--------- dsp/codecs/audio_ape.c | 11 ++--------- dsp/codecs/audio_evrc.c | 11 +++-------- dsp/codecs/audio_g711alaw.c | 10 +--------- dsp/codecs/audio_g711mlaw.c | 10 +--------- dsp/codecs/audio_mp3.c | 11 +++-------- dsp/codecs/audio_multi_aac.c | 11 ++--------- dsp/codecs/audio_qcelp.c | 11 +++-------- dsp/codecs/audio_wma.c | 12 +++--------- dsp/codecs/audio_wmapro.c | 12 +++--------- 14 files changed, 34 insertions(+), 122 deletions(-) diff --git a/dsp/codecs/audio_aac.c b/dsp/codecs/audio_aac.c index 6f08bbf1bedb..9bafe76ba25c 100644 --- a/dsp/codecs/audio_aac.c +++ b/dsp/codecs/audio_aac.c @@ -355,24 +355,20 @@ static int audio_open(struct inode *inode, struct file *file) struct q6audio_aio *audio = NULL; int rc = 0; struct msm_audio_aac_config *aac_config = NULL; - unsigned long flags = 0; #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_aac_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), GFP_KERNEL); if (audio->codec_cfg == NULL) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } aac_config = audio->codec_cfg; @@ -393,17 +389,14 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_alac.c b/dsp/codecs/audio_alac.c index bf198b5a01bc..ed8e25020d5d 100644 --- a/dsp/codecs/audio_alac.c +++ b/dsp/codecs/audio_alac.c @@ -259,22 +259,18 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_alac_" + 5]; - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (!audio) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (!audio) return -ENOMEM; - } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_alac_config), GFP_KERNEL); if (!audio->codec_cfg) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -290,17 +286,14 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_amrnb.c b/dsp/codecs/audio_amrnb.c index 07248907e366..c7c371e56b9a 100644 --- a/dsp/codecs/audio_amrnb.c +++ b/dsp/codecs/audio_amrnb.c @@ -3,7 +3,7 @@ * * Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2011-2017, 2019-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, 2019, 2021 The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -122,18 +122,15 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; + #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_amrnb_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; audio->miscdevice = &audio_amrnb_misc; @@ -146,17 +143,14 @@ static int audio_open(struct inode *inode, struct file *file) if (!audio->ac) { pr_err("Could not allocate memory for audio client\n"); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_amrwb.c b/dsp/codecs/audio_amrwb.c index c676e68a0cb9..d7ff5f174927 100644 --- a/dsp/codecs/audio_amrwb.c +++ b/dsp/codecs/audio_amrwb.c @@ -115,18 +115,16 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; + #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_amrwb_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; audio->miscdevice = &audio_amrwb_misc; audio->wakelock_voted = false; @@ -138,17 +136,15 @@ static int audio_open(struct inode *inode, struct file *file) if (!audio->ac) { pr_err("Could not allocate memory for audio client\n"); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); + /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_amrwbplus.c b/dsp/codecs/audio_amrwbplus.c index 5aae84b7bf19..d456f6ee8a91 100644 --- a/dsp/codecs/audio_amrwbplus.c +++ b/dsp/codecs/audio_amrwbplus.c @@ -295,21 +295,16 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } audio->codec_cfg = kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL); if (audio->codec_cfg == NULL) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; @@ -324,17 +319,15 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); + /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_ape.c b/dsp/codecs/audio_ape.c index 7c5c39a31a9d..62bc5d6176b3 100644 --- a/dsp/codecs/audio_ape.c +++ b/dsp/codecs/audio_ape.c @@ -240,22 +240,18 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_ape_" + 5]; - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (!audio) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (!audio) return -ENOMEM; - } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_ape_config), GFP_KERNEL); if (!audio->codec_cfg) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -271,17 +267,14 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_evrc.c b/dsp/codecs/audio_evrc.c index a99577cfbc5a..4b004d1896a1 100644 --- a/dsp/codecs/audio_evrc.c +++ b/dsp/codecs/audio_evrc.c @@ -76,19 +76,16 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_evrc_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + /* Settings will be re-config at AUDIO_SET_CONFIG, * but at least we need to have initial config */ @@ -103,17 +100,15 @@ static int audio_open(struct inode *inode, struct file *file) if (!audio->ac) { pr_err("Could not allocate memory for audio client\n"); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); + /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_g711alaw.c b/dsp/codecs/audio_g711alaw.c index 3540380772b9..8c51533ff034 100644 --- a/dsp/codecs/audio_g711alaw.c +++ b/dsp/codecs/audio_g711alaw.c @@ -214,22 +214,17 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_g711_" + 5]; - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (!audio) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (!audio) return -ENOMEM; - } audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config), GFP_KERNEL); if (!audio->codec_cfg) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -248,17 +243,14 @@ static int audio_open(struct inode *inode, struct file *file) __func__); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ /*foramt:G711_ALAW*/ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_g711mlaw.c b/dsp/codecs/audio_g711mlaw.c index af47fd9824c9..e4a7830d3f65 100644 --- a/dsp/codecs/audio_g711mlaw.c +++ b/dsp/codecs/audio_g711mlaw.c @@ -212,22 +212,17 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_g711_" + 5]; - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (!audio) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (!audio) return -ENOMEM; - } audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config), GFP_KERNEL); if (!audio->codec_cfg) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -246,17 +241,14 @@ static int audio_open(struct inode *inode, struct file *file) __func__); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ /*foramt:G711_ALAW*/ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_mp3.c b/dsp/codecs/audio_mp3.c index aa575c82e3d3..597b1a4c3bf1 100644 --- a/dsp/codecs/audio_mp3.c +++ b/dsp/codecs/audio_mp3.c @@ -81,19 +81,16 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_mp3_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN; audio->miscdevice = &audio_mp3_misc; audio->wakelock_voted = false; @@ -105,17 +102,15 @@ static int audio_open(struct inode *inode, struct file *file) if (!audio->ac) { pr_err("Could not allocate memory for audio client\n"); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); + /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_multi_aac.c b/dsp/codecs/audio_multi_aac.c index 3cffa092f25f..6e0915b0150e 100644 --- a/dsp/codecs/audio_multi_aac.c +++ b/dsp/codecs/audio_multi_aac.c @@ -411,24 +411,20 @@ static int audio_open(struct inode *inode, struct file *file) struct q6audio_aio *audio = NULL; int rc = 0; struct msm_audio_aac_config *aac_config = NULL; - unsigned long flags = 0; #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_multi_aac_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config), GFP_KERNEL); if (audio->codec_cfg == NULL) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -447,17 +443,14 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { diff --git a/dsp/codecs/audio_qcelp.c b/dsp/codecs/audio_qcelp.c index 33420be297b5..7fad58ec3a18 100644 --- a/dsp/codecs/audio_qcelp.c +++ b/dsp/codecs/audio_qcelp.c @@ -78,18 +78,15 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; + #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_qcelp_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } /* Settings will be re-config at AUDIO_SET_CONFIG, * but at least we need to have initial config @@ -110,17 +107,15 @@ static int audio_open(struct inode *inode, struct file *file) if (!audio->ac) { pr_err("Could not allocate memory for audio client\n"); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); + /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_wma.c b/dsp/codecs/audio_wma.c index c462427be326..9bb173683551 100644 --- a/dsp/codecs/audio_wma.c +++ b/dsp/codecs/audio_wma.c @@ -230,23 +230,20 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; + #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_wma_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2), GFP_KERNEL); if (audio->codec_cfg == NULL) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -262,17 +259,14 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } rc = audio_aio_open(audio, file); if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, diff --git a/dsp/codecs/audio_wmapro.c b/dsp/codecs/audio_wmapro.c index f2a66be5ef6c..5b79e7326cce 100644 --- a/dsp/codecs/audio_wmapro.c +++ b/dsp/codecs/audio_wmapro.c @@ -302,23 +302,20 @@ static int audio_open(struct inode *inode, struct file *file) { struct q6audio_aio *audio = NULL; int rc = 0; - unsigned long flags = 0; + #ifdef CONFIG_DEBUG_FS /* 4 bytes represents decoder number, 1 byte for terminate string */ char name[sizeof "msm_wmapro_" + 5]; #endif - spin_lock_irqsave(&enc_dec_lock, flags); audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL); - if (audio == NULL) { - spin_unlock_irqrestore(&enc_dec_lock, flags); + if (audio == NULL) return -ENOMEM; - } + audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config), GFP_KERNEL); if (audio->codec_cfg == NULL) { kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -335,7 +332,6 @@ static int audio_open(struct inode *inode, struct file *file) pr_err("Could not allocate memory for audio client\n"); kfree(audio->codec_cfg); kfree(audio); - spin_unlock_irqrestore(&enc_dec_lock, flags); return -ENOMEM; } @@ -343,10 +339,8 @@ static int audio_open(struct inode *inode, struct file *file) if (rc < 0) { pr_err_ratelimited("%s: audio_aio_open rc=%d\n", __func__, rc); - spin_unlock_irqrestore(&enc_dec_lock, flags); goto fail; } - spin_unlock_irqrestore(&enc_dec_lock, flags); /* open in T/NT mode */ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) { rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM, From 6791ee997375692ef19ce9ce223a6bec30b69880 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Tue, 30 Mar 2021 13:27:06 +0530 Subject: [PATCH 17/79] ASoC: Add check to prevent buffer overflow for port_idx Add check to prevent buffer overflow for port_idx in dai driver. Change-Id: Id3126520e52590204fdb8bf92d3c4c52b1867354 Signed-off-by: Vatsal Bucha --- asoc/msm-dai-q6-v2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index afec883e3265..e545f2be3c10 100755 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ #include @@ -9296,6 +9296,10 @@ static int msm_pcm_afe_port_logging_ctl_get(struct snd_kcontrol *kcontrol, int port_idx = afe_port_logging_port_id - AFE_PORT_ID_TDM_PORT_RANGE_START; ucontrol->value.integer.value[0] = afe_port_logging_port_id; + if (port_idx < 0 || port_idx >= IDX_TDM_MAX) { + pr_err_ratelimited("%s: port_idx = %d\n", __func__, port_idx); + return -EINVAL; + } ucontrol->value.integer.value[1] = afe_port_logging_item[port_idx]; return 0; @@ -9322,6 +9326,10 @@ static int msm_pcm_afe_port_logging_ctl_put(struct snd_kcontrol *kcontrol, afe_port_logging_port_id = port_id; port_idx = port_id - AFE_PORT_ID_TDM_PORT_RANGE_START; + if (port_idx < 0 || port_idx >= IDX_TDM_MAX) { + pr_err_ratelimited("%s: port_idx = %d\n", __func__, port_idx); + return -EINVAL; + } afe_port_logging_item[port_idx] = ucontrol->value.integer.value[1]; return ret; From 90bf7292a4f0e72a04214324ffa18a3b55208c86 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 6 Jan 2023 14:37:20 +0530 Subject: [PATCH 18/79] ASoC: msm-pcm-q6-v2: Add dsp buf check Current logic copies user buf size of data from the avail dsp buf at a given offset. If this offset returned from DSP in READ_DONE event goes out of bounds or is corrupted, then it can lead to out of bounds DSP buffer access, resulting in memory fault. Fix is to add check for this buf offset, if it is within the buf size range. Change-Id: I7753cc6db394704dbb959477150141d42b836bef Signed-off-by: Soumya Managoli --- asoc/msm-pcm-q6-v2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 1dfa857be5ea..b26ae17520c7 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ /* * Add support for 24 and 32bit format for ASM loopback and playback session. @@ -1328,6 +1328,14 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, xfer = size; offset = prtd->in_frame_info[idx].offset; pr_debug("Offset value = %d\n", offset); + + if (offset >= size) { + pr_err("%s: Invalid dsp buf offset\n", __func__); + ret = -EFAULT; + q6asm_cpu_buf_release(OUT, prtd->audio_client); + goto fail; + } + if (size == 0 || size < prtd->pcm_count) { memset(bufptr + offset + size, 0, prtd->pcm_count - size); if (fbytes > prtd->pcm_count) From d607d15392285e7ba6c70aeb7bde102cdc1468ca Mon Sep 17 00:00:00 2001 From: Vaibhav Raut Date: Mon, 11 Sep 2023 16:02:05 +0530 Subject: [PATCH 19/79] asoc: msm-compress : Fix for CTS-on-gsi with gki compress_pause was failing on gki mode due to change in stream state. The change is done to enable pausing even when the stream state is in the draining state. Change-Id: I3c29236298f4257f3606827cd82f1b962b6ad802 Signed-off-by: Vaibhav Raut --- asoc/msm-compress-q6-v2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 745ff4771efd..6097ca2a7ea3 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2017,6 +2017,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); #if !IS_ENABLED(CONFIG_AUDIO_QGKI) + snd_compr_use_pause_in_draining(cstream); atomic_set(&prtd->partial_drain, 0); #endif atomic_set(&prtd->xrun, 0); From 708820818216db245b213ea12ceb74edf0f5f367 Mon Sep 17 00:00:00 2001 From: liaohj Date: Mon, 12 Oct 2020 11:50:22 +0800 Subject: [PATCH 20/79] Denver:audio EVB bringup denver audio bringup: 1.qcom 4350 platform 2. android R 3. kernel 5.4 4.wcd9370+aw88258 add aw88258 driver. Change-Id: I4f8696b7ffa6618eb5c2cb0c690001dcec3d5900 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/1771700 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/Kbuild | 1 + asoc/codecs/aw882xx/Android.mk | 8 + asoc/codecs/aw882xx/Kbuild | 9 + asoc/codecs/aw882xx/aw882xx.c | 3398 +++++++++++++++++++++++++++++ asoc/codecs/aw882xx/aw882xx.h | 279 +++ asoc/codecs/aw882xx/aw882xx_reg.h | 1570 +++++++++++++ asoc/msm_holi_dailink.h | 26 +- config/holiauto.conf | 1 - config/holiautoconf.h | 1 - dsp/Kbuild | 2 + dsp/q6adm.c | 43 + dsp/q6afe.c | 247 ++- 12 files changed, 5575 insertions(+), 10 deletions(-) create mode 100644 asoc/codecs/aw882xx/Android.mk create mode 100644 asoc/codecs/aw882xx/Kbuild create mode 100644 asoc/codecs/aw882xx/aw882xx.c create mode 100644 asoc/codecs/aw882xx/aw882xx.h create mode 100644 asoc/codecs/aw882xx/aw882xx_reg.h diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index e8cb6f140854..c1776112b0f6 100755 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -274,6 +274,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += bolero/ obj-y += wsa883x/ obj-y += rouleur/ + obj-y += aw882xx/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/asoc/codecs/aw882xx/Android.mk b/asoc/codecs/aw882xx/Android.mk new file mode 100644 index 000000000000..c03dcb163274 --- /dev/null +++ b/asoc/codecs/aw882xx/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := $(TOP)/device/qcom/common/dlkm +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw882xx_k419.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/asoc/codecs/aw882xx/Kbuild b/asoc/codecs/aw882xx/Kbuild new file mode 100644 index 000000000000..dfa42fe55981 --- /dev/null +++ b/asoc/codecs/aw882xx/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ + +EXTRA_CFLAGS += -DCONFIG_AW882XX_CODEC -DCONFIG_AW882XX_DSP + +aw882xx_k419-objs = aw882xx.o +obj-m += aw882xx_k419.o diff --git a/asoc/codecs/aw882xx/aw882xx.c b/asoc/codecs/aw882xx/aw882xx.c new file mode 100644 index 000000000000..6e6231d2382c --- /dev/null +++ b/asoc/codecs/aw882xx/aw882xx.c @@ -0,0 +1,3398 @@ +/* + * aw882xx.c aw882xx codec module + * + * Version: v0.1.12 + * + * keep same with AW882XX_VERSION + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define DEBUG + +#ifdef CONFIG_AW882XX_CODEC + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw882xx_reg.h" + + + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW882XX_I2C_NAME "aw882xx_smartpa" + +#define AW882XX_VERSION "v0.1.12" + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + + +#define CALI_BUF_MAX 100 +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" + +#ifdef CONFIG_AW882XX_DSP +extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write); +extern int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size); +extern int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t rx_port_id,uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write) { + return 0; +} + +static int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + return 0; +} + +static int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + return 0; +} + +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + return 0; +} +#endif + +//static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re); +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re); +static int aw882xx_load_profile_params(struct aw882xx *aw882xx); +static int aw882xx_skt_set_dsp(int value); +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade); +static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in); + +/*monitor voltage and temperature table*/ +static struct aw882xx_low_vol vol_down_table[] = { + {3500, IPEAK_2P50_A, GAIN_NEG_1P5_DB}, + {3700, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + {3900, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + }; +static struct aw882xx_low_vol vol_up_table[] = { + {4000, IPEAK_3P50_A, GAIN_NEG_0P0_DB}, + {3800, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + {3600, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + }; +static struct aw882xx_low_temp temp_down_table[] = { + {-5, IPEAK_2P50_A, GAIN_NEG_6P0_DB, VMAX_063_PERCENTAGE}, + { 0, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + { 5, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + }; +static struct aw882xx_low_temp temp_up_table[] = { + { 7, IPEAK_3P50_A, GAIN_NEG_0P0_DB, VMAX_100_PERCENTAGE}, + { 2, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + {-2, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + }; + +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor); +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor); + +/****************************************************** + * + * Value + * + ******************************************************/ +static int aw882xx_spk_control; +static int aw882xx_rcv_control; + +#ifdef AW882XX_RUNIN_TEST +static atomic_t g_runin_test; +#endif +static atomic_t g_algo_rx_enable; +static atomic_t g_algo_tx_enable; +static atomic_t g_skt_disable; +static struct aw882xx *g_aw882xx = NULL; +static int8_t g_aw882xx_cali_flag = 0; +static int8_t g_aw882xx_profile_flag = 0; + +#define AW882XX_CFG_NAME_MAX 64 +static char aw882xx_cfg_name[][AW882XX_CFG_NAME_MAX] = { + {"aw882xx_spk_reg.bin"}, + {"aw882xx_rcv_reg.bin"}, +}; + +static unsigned int aw882xx_mode_cfg_shift[AW882XX_MODE_SHIFT_MAX] = { + AW882XX_MODE_SPK_SHIFT, + AW882XX_MODE_RCV_SHIFT, +}; + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + pr_err("%s: i2c master send error\n", __func__); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + pr_err("%s: i2c master send error, ret=%d\n", + __func__, ret); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + pr_err("%s: couldn't read registers, return %d bytes\n", + __func__, ret); + return -ENXIO; + } + + return 0; +} + +static int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) + pr_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + else + break; + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + break; + } + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + return 0; +} + +/****************************************************** + * + * aw882xx control + * + ******************************************************/ +static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) +{ + pr_err("%s: enter\n", __func__); + + if (mute) { + if (aw882xx->need_fade) + aw882xx_fade_in_out(aw882xx, false); + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_DISABLE_VALUE); + if (aw882xx->need_fade) + aw882xx_fade_in_out(aw882xx, true); + } +} + +#if 0 +static bool aw882xx_get_power_status(struct aw882xx *aw882xx) +{ + unsigned int reg_value = 0; + int ret = 0; + pr_err("%s: enter\n", __func__); + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL_REG, ®_value); + if (ret < 0) { + pr_err("%s: read reg %d failed \n", __func__, AW882XX_SYSCTRL_REG); + return false; + } + /*bit 0: 1 power off, 0 power on*/ + if (reg_value & 0x01) { + return false; + } else { + return true; + } +} +#endif + +static void aw882xx_run_pwd(struct aw882xx *aw882xx, bool pwd) +{ + pr_err("%s: enter\n", __func__); + + if (pwd) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_POWER_DOWN_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_NORMAL_WORKING_VALUE); + } +} + +static int aw882xx_sysst_check(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_SYSST_CHECK_MAX; i++) { + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (((reg_val & (~AW882XX_SYSST_CHECK_MASK)) & AW882XX_SYSST_CHECK) == + AW882XX_SYSST_CHECK) { + ret = 0; + break; + } else { + pr_err("%s: check fail, cnt=%d, reg_val=0x%04x\n", + __func__, i, reg_val); + msleep(2); + } + } + if (ret < 0) + pr_err("%s: check fail\n", __func__); + + return ret; +} + +/* +static int aw882xx_get_sysint(struct aw882xx *aw882xx, unsigned int *sysint) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + if (ret < 0) + pr_err("%s: read sysint fail, ret=%d\n", __func__, ret); + else + *sysint = reg_val; + + return ret; +} + +static int aw882xx_get_iis_status(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val = 0; + + pr_err("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (reg_val & AW882XX_PLLS_LOCKED_VALUE) + ret = 0; + + return ret; +} +*/ + +static int aw882xx_get_icalk(struct aw882xx *aw882xx, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRM1_REG, ®_val); + reg_icalk = (uint16_t)reg_val & AW882XX_EF_ISN_GESLP_MASK; + + if (reg_icalk & AW882XX_EF_ISN_GESLP_SIGN_MASK) + reg_icalk = reg_icalk | AW882XX_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw882xx_get_vcalk(struct aw882xx *aw882xx, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRH_REG, ®_val); + + reg_vcalk = (uint16_t)reg_val & AW882XX_EF_VSN_GESLP_MASK; + + if (reg_vcalk & AW882XX_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW882XX_EF_VSN_GESLP_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw882xx_set_vcalb(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + ret = aw882xx_get_icalk(aw882xx, &icalk_val); + ret = aw882xx_get_vcalk(aw882xx, &vcalk_val); + + icalk = AW882XX_CABL_BASE_VALUE + AW882XX_ICABLK_FACTOR * icalk_val; + vcalk = AW882XX_CABL_BASE_VALUE + AW882XX_VCABLK_FACTOR * vcalk_val; + + vcalb = AW882XX_VCAL_FACTOR * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + pr_err("%s: icalk=%d, vcalk=%d, vcalb=%d, reg_val=%d\n", + __func__, icalk, vcalk, vcalb, reg_val); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_VTMCTRL3_REG, reg_val); + + return ret; +} + +static void aw882xx_send_cali_re_to_dsp(struct aw882xx *aw882xx) +{ + int ret = 0; + if((aw882xx != NULL) && (aw882xx->default_re == 0)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->default_re, sizeof(int32_t), false); + pr_err("aw882xx: default_re:%d\n", aw882xx->default_re); + } + if ((aw882xx != NULL) && (aw882xx->cali_re != ERRO_CALI_VALUE)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->cali_re, sizeof(int32_t), true); + } + if (ret) + pr_err("%s : set cali re to dsp failed 0x%x\n", + __func__ , AFE_PARAM_ID_AWDSP_RX_RE_L); +} + +static void aw882xx_start(struct aw882xx *aw882xx) +{ + int ret = -1; + uint32_t cali_re; + pr_err("%s: enter\n", __func__); + + + + //ret = aw882xx_get_cali_re_from_nv(&cali_re); + //if (ret < 0) { + cali_re = ERRO_CALI_VALUE; + pr_err("%s: use default vaule %d", __func__ , ERRO_CALI_VALUE); + //} + //ret = aw882xx_set_cali_re(aw882xx, cali_re); + //if (ret < 0) + pr_err("%s: set cali re failed: %d\n", __func__, ret); + + mutex_lock(&aw882xx->lock); + pr_err("%s: monitor is_enable %d,spk_rcv_mode %d\n", + __func__, aw882xx->monitor.is_enable, aw882xx->spk_rcv_mode); + if (aw882xx->monitor.is_enable && + (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE)) { + aw882xx->monitor.first_entry = AW_FIRST_ENTRY; + aw882xx->monitor.pre_vol = 0; + aw882xx->monitor.vol_count = 0; + schedule_work(&aw882xx->monitor.work); + } + if (aw882xx->afe_profile) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, aw882xx->profile.cur_profile, false); + if (ret) { + pr_err("%s: set profile %d failed \n", __func__,aw882xx->profile.cur_profile); + aw882xx->profile.cur_profile = 0; + } + } + //aw882xx_send_cali_re_to_dsp(aw882xx); + + aw882xx_run_pwd(aw882xx, false); + ret = aw882xx_sysst_check(aw882xx); + if (ret < 0) { + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + aw882xx->init = AW882XX_INIT_NG; + aw882xx->power_flag = AW882XX_POWER_DOWN; + } else { + aw882xx_run_mute(aw882xx, false); + aw882xx->init = AW882XX_INIT_OK; + aw882xx->power_flag = AW882XX_POWER_UP; + } +#ifdef AW882XX_RUNIN_TEST + schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); +#endif + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_stop(struct aw882xx *aw882xx) +{ + pr_err("%s: enter\n", __func__); + + mutex_lock(&aw882xx->lock); + aw882xx->power_flag = AW882XX_POWER_DOWN; + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + if (aw882xx->afe_profile) { + aw882xx->profile.cur_profile = 0; + } + if (aw882xx->monitor.is_enable) + aw882xx_monitor_stop(&aw882xx->monitor); + mutex_unlock(&aw882xx->lock); +} + +/****************************************************** + * + * aw882xx config + * + ******************************************************/ +#if 0 +static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re) +{ + /*custom add, if success return value is 0 , else -1*/ + struct file *fp; + char buf[CALI_BUF_MAX]; + uint32_t read_re; + loff_t pos = 0; + mm_segment_t fs; + + memset(buf, 0, CALI_BUF_MAX); + /*open cali file*/ + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s: open %s failed!", __func__, AWINIC_CALI_FILE); + return -EINVAL; + } + /*set fs kernel*/ + fs = get_fs(); + set_fs(KERNEL_DS); + + /*read file*/ + vfs_read(fp, buf, CALI_BUF_MAX - 1, &pos); + + /*get cali re value*/ + if (sscanf(buf, "%d", &read_re) != 1) { + pr_err("%s: file read error", __func__); + set_fs(fs); + filp_close(fp, NULL); + return -EINVAL; + } + set_fs(fs); + + /*close file*/ + filp_close(fp, NULL); + + *cali_re = read_re; + pr_err("%s: %d", __func__, read_re); + return 0; +} +#endif + +#if 1 +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re) +{ + if (aw882xx == NULL) + return -EINVAL; + aw882xx->cali_re = cali_re; + return 0; +} +#endif +static int aw882xx_reg_container_update(struct aw882xx *aw882xx, + struct aw882xx_container *aw882xx_cont) +{ + int i = 0; + int reg_addr = 0; + int reg_val = 0; + int ret = -1; + + pr_err("%s: enter\n", __func__); + + for (i = 0; i < aw882xx_cont->len; i += 4) { + reg_addr = (aw882xx_cont->data[i+1]<<8) + + aw882xx_cont->data[i+0]; + reg_val = (aw882xx_cont->data[i+3]<<8) + + aw882xx_cont->data[i+2]; + pr_err("%s: reg=0x%04x, val = 0x%04x\n", + __func__, reg_addr, reg_val); + ret = aw882xx_i2c_write(aw882xx, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + pr_err("%s: exit\n", __func__); + + return ret; +} + +static void aw882xx_reg_loaded(const struct firmware *cont, void *context) +{ + struct aw882xx *aw882xx = context; + struct aw882xx_container *aw882xx_cfg; + int ret = -1; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num]); + release_firmware(cont); + return; + } + + pr_err("%s: loaded %s - size: %zu\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num], cont ? cont->size : 0); + + aw882xx_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw882xx_cfg) { + release_firmware(cont); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw882xx_cfg->len = cont->size; + memcpy(aw882xx_cfg->data, cont->data, cont->size); + release_firmware(cont); + + mutex_lock(&aw882xx->lock); + ret = aw882xx_reg_container_update(aw882xx, aw882xx_cfg); + if (ret < 0) { + pr_err("%s: reg update fail\n", __func__); + } else { + pr_err("%s: reg update sucess\n", __func__); + aw882xx_run_mute(aw882xx, true); + aw882xx_set_vcalb(aw882xx); + } + mutex_unlock(&aw882xx->lock); + kfree(aw882xx_cfg); + if (aw882xx->afe_profile) { + aw882xx_load_profile_params(aw882xx); + aw882xx->need_fade = 0; + } + aw882xx_start(aw882xx); + + if (aw882xx->afe_profile) { + aw882xx->need_fade = 1; + } +} + +static int aw882xx_load_reg(struct aw882xx *aw882xx) +{ + pr_err("%s: enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw882xx_cfg_name[aw882xx->cfg_num], + aw882xx->dev, GFP_KERNEL, + aw882xx, aw882xx_reg_loaded); +} + +static void aw882xx_get_cfg_shift(struct aw882xx *aw882xx) +{ + aw882xx->cfg_num = aw882xx_mode_cfg_shift[aw882xx->spk_rcv_mode]; + pr_err("%s: cfg_num=%d\n", __func__, aw882xx->cfg_num); +} + +static void aw882xx_cold_start(struct aw882xx *aw882xx) +{ + int ret = -1; + + pr_err("%s: enter\n", __func__); + + aw882xx_get_cfg_shift(aw882xx); + + ret = aw882xx_load_reg(aw882xx); + if (ret < 0) + pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); + +} + +static void aw882xx_smartpa_cfg(struct aw882xx *aw882xx, bool flag) +{ + pr_err("%s: flag = %d\n", __func__, flag); + + if (flag == true) { + if ((aw882xx->init == AW882XX_INIT_ST) || + (aw882xx->init == AW882XX_INIT_NG)) { + pr_err("%s: init = %d\n", __func__, aw882xx->init); + aw882xx_cold_start(aw882xx); + } else { + aw882xx_start(aw882xx); + } + } else { + aw882xx_stop(aw882xx); + } +} + +/****************************************************** + * + * kcontrol + * + ******************************************************/ +static const char *const switch_status[] = { "Off", "On" }; +static const char *const awinic_algo[] = { "Disable", "Enable" }; +static const char *const awinic_profile[AW_PROFILE_MAX] = { +"Music", "Ringtone", "Notification", "Voice"}; +static const char *const awinic_profile_param_file_name[] = { + "aw882xx_afe_params.bin", + }; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 50, 0); + +struct soc_mixer_control aw882xx_mixer = { + .reg = AW882XX_HAGCCFG4_REG, + .shift = AW882XX_VOL_START_BIT, + .max = AW882XX_VOLUME_MAX, + .min = AW882XX_VOLUME_MIN, +}; + +static int aw882xx_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* set kcontrol info */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max - mc->min; + return 0; +} + +static int aw882xx_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int reg_val = 0; + unsigned int value = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + ucontrol->value.integer.value[0] = (value >> mc->shift) & + (AW882XX_VOL_MASK); + return 0; +} + +static int aw882xx_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int value = 0; + unsigned int reg_value = 0; + + /* value is right */ + value = ucontrol->value.integer.value[0]; + if (value > (mc->max-mc->min) || value < 0) { + pr_err("%s: value over range\n", __func__); + return -ERANGE; + } + + /* smartpa have clk */ + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK ,cat not write reg\n", __func__); + return 0; + } + + /* cal real value */ + value = (value << mc->shift) & AW882XX_VOL_MASK; + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + value = value | (reg_value & 0x00ff); + + /* write value */ + aw882xx->cur_gain = value >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, value); + + return 0; +} + +static struct snd_kcontrol_new aw882xx_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "aw882xx_rx_volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = (digital_gain), + .info = aw882xx_volume_info, + .get = aw882xx_volume_get, + .put = aw882xx_volume_put, + .private_value = (unsigned long)&aw882xx_mixer, +}; + +static int aw882xx_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s: aw882xx_spk_control=%d\n", + __func__, aw882xx_spk_control); + ucontrol->value.integer.value[0] = aw882xx_spk_control; + return 0; +} + +static int aw882xx_spk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_spk_control) + return 1; + + aw882xx_spk_control = ucontrol->value.integer.value[0]; + + if (AW882XX_SPEAKER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_SPEAKER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_rcv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s: aw882xx_rcv_control=%d\n", __func__, aw882xx_rcv_control); + ucontrol->value.integer.value[0] = aw882xx_rcv_control; + return 0; +} + +static int aw882xx_rcv_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_rcv_control) + return 1; + + aw882xx_rcv_control = ucontrol->value.integer.value[0]; + + if (AW882XX_RECEIVER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_RECEIVER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_algo_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s: aw882xx_algo enable=%d\n", + __func__, atomic_read(&g_algo_rx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_rx_enable); + + return 0; +} + +static int aw882xx_algo_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set algo %d failed, ret=%d\n", + __func__, ctrl_value, ret); + atomic_set(&g_algo_rx_enable, ctrl_value); + return 0; +} + +static int aw882xx_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s: aw882xx_tx_control=%d\n", __func__, + atomic_read(&g_algo_tx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_tx_enable); + + return 0; +} + +static int aw882xx_tx_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_afe_tx_module_enable(g_aw882xx->afe_tx_portid, &ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set tx enable %d, ret=%d\n", __func__, ctrl_value, ret); + atomic_set(&g_algo_tx_enable, ctrl_value); + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_err("%s: profile : %s \n", __func__, + awinic_profile[aw882xx->profile.cur_profile]); + if (aw882xx->afe_profile) { + ucontrol->value.integer.value[0] = aw882xx->profile.cur_profile; + } + return 0; +} +static int aw882xx_profile_firmware_parse(struct aw882xx *aw882xx, const struct firmware *cont) +{ + aw_afe_params_hdr_t *hdr; + char *fw_data = NULL; + uint8_t sum = 0; + uint32_t param_size = 0; + char *param_data = NULL; + int i; + pr_err("%s: loaded %s - size: %zu\n", __func__, + awinic_profile_param_file_name[0], cont ? cont->size : 0); + + if (cont->size == 0) { + goto load_failed; + } + + fw_data = kzalloc(cont->size, GFP_KERNEL); + if (fw_data == NULL) { + pr_err("%s: alloc memory failed \n", __func__); + goto load_failed; + } + memcpy(fw_data, cont->data, cont->size); + + /*output bin info*/ + hdr = (aw_afe_params_hdr_t *)fw_data; + pr_err("%s: fw v_%d.%d.%d \n", __func__, + hdr->fw[0], hdr->fw[1], hdr->fw[2]); + pr_err("%s: param v_%d.%d.%d \n", __func__, + hdr->cfg[0], hdr->cfg[1], hdr->cfg[2]); + pr_err("%s: project name : %s \n", __func__, hdr->project); + pr_err("%s: param len: %d", __func__, hdr->params_len); + + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + param_size = cont->size - sizeof(aw_afe_params_hdr_t); + + for (i = 0; i < param_size; i++) { + sum += param_data[i]; + } + if (sum != hdr->check_sum) { + pr_err("%s:data check sum failed : %d -- %d \n", __func__, + sum, hdr->check_sum); + kfree(fw_data); + goto load_failed; + } + + if (hdr->profile_num < AW_PROFILE_MAX) { + pr_err("%s: profile num is not match %d -- %d ", __func__, + hdr->profile_num, AW_PROFILE_MAX); + kfree(fw_data); + goto load_failed; + } + + if (hdr->params_len != (param_size / hdr->profile_num)) { + pr_err("%s: profile params len is not match %d ", __func__, + hdr->params_len); + kfree(fw_data); + goto load_failed; + } + + mutex_lock(&aw882xx->profile.lock); + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = kzalloc(hdr->params_len, GFP_KERNEL); + if (aw882xx->profile.data[i] == NULL) { + pr_err("%s: alloc failed !", __func__); + kfree(fw_data); + mutex_unlock(&aw882xx->profile.lock); + goto load_failed; + } + memcpy(aw882xx->profile.data[i], param_data , hdr->params_len); + param_data += hdr->params_len; + } + aw882xx->profile.len = hdr->params_len; + aw882xx->profile.status = AW882XX_INIT_OK; + mutex_unlock(&aw882xx->profile.lock); + kfree(fw_data); + return 0; + +load_failed: + mutex_lock(&aw882xx->profile.lock); + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (aw882xx->profile.data[i]) { + kfree(aw882xx->profile.data[i]); + aw882xx->profile.data[i] = NULL; + } + } + aw882xx->profile.status = AW882XX_INIT_NG; + aw882xx->profile.len = 0; + mutex_unlock(&aw882xx->profile.lock); + pr_err("%s: load params bin failed !", __func__); + return 0; +} + +static int aw882xx_load_profile_params(struct aw882xx *aw882xx) +{ + const struct firmware *fw = NULL; + int ret; + pr_err("%s: enter\n", __func__); + + ret = request_firmware(&fw, + awinic_profile_param_file_name[0], + aw882xx->dev); + if (ret) { + pr_err("%s: load profile params failed !", __func__); + return -EINVAL; + } + + ret = aw882xx_profile_firmware_parse(aw882xx, fw); + return ret; +} + +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value) +{ + unsigned int reg_value = 0; + unsigned int real_value = ((value / VOLUME_STEP_DB) << 4) + (value % VOLUME_STEP_DB) * 2; + + if(real_value > aw882xx->cur_gain) { + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + real_value = (real_value << 8) | (reg_value & 0x00ff); + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); + } +} + +static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) +{ + int i = 0; + + //volume up + if (is_fade_in) { + i = VOLUME_MIN_NEG_90_DB; + do { + aw882xx_volume_set(aw882xx, i); + i -= FADE_STEP_DB; + usleep_range(1400,1600); + } while (i >= 0); + } else { // volume down + do { + aw882xx_volume_set(aw882xx, i); + i += FADE_STEP_DB; + usleep_range(1400,1600); + } while (i <= VOLUME_MIN_NEG_90_DB); + } +} + +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade) +{ + int ret ; + int32_t params_id = AFE_PARAM_ID_AWDSP_RX_PARAMS; + + mutex_lock(&aw882xx->profile.lock); + if (aw882xx->profile.data[profile_id] == NULL) { + pr_err("%s: profile %s not data is NULL \n", __func__, + awinic_profile[profile_id]); + mutex_unlock(&aw882xx->profile.lock); + return -EINVAL; + } + if (is_fade) { + g_aw882xx_profile_flag = true; + //fade out + //aw882xx_fade_in_out(aw882xx, false); + aw882xx_run_mute(aw882xx, true); + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + params_id, + aw882xx->profile.data[profile_id], + aw882xx->profile.len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, params_id); + aw882xx_run_mute(aw882xx, false); + //aw882xx_fade_in_out(aw882xx, true); + g_aw882xx_profile_flag = false; + mutex_unlock(&aw882xx->profile.lock); + return ret; + } + if (is_fade) { + //fade out + aw882xx_run_mute(aw882xx, false); + //aw882xx_fade_in_out(aw882xx, true); + g_aw882xx_profile_flag = false; + } + mutex_unlock(&aw882xx->profile.lock); + + pr_err("%s: set params done", __func__); + return 0; +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int ctl_value = 0; + int ret; + ctl_value = ucontrol->value.integer.value[0]; + + if (aw882xx->afe_profile) { + if (ctl_value >= AW_PROFILE_MAX) { + pr_err("%s: unsuport profile %d", __func__, ctl_value); + return -EINVAL; + } + pr_err("%s: profile switch to %s \n", + __func__, awinic_profile[ctl_value]); + + mutex_lock(&aw882xx->lock); + if (aw882xx->profile.cur_profile != ctl_value) { + /*PA have power on ,set params direct*/ + if (aw882xx->power_flag == AW882XX_POWER_UP) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, ctl_value, true); + if (ret < 0) { + mutex_unlock(&aw882xx->lock); + return -EINVAL; + } + aw882xx_send_cali_re_to_dsp(aw882xx); + aw882xx->profile.cur_profile = ctl_value; + } else { + /*PA power only set params status*/ + aw882xx->profile.cur_profile = ctl_value; + } + } + mutex_unlock(&aw882xx->lock); + } + return 0; +} + +static int aw882xx_skt_disable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s: aw882xx_skt_disable %d\n", + __func__, atomic_read(&g_skt_disable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_skt_disable); + + return 0; +} + +static int aw882xx_skt_set_dsp(int value) +{ + int ret; + int port_id = g_aw882xx->afe_rx_portid; + int module_id = AW_MODULE_ID_COPP; + int param_id = AW_MODULE_PARAMS_ID_COPP_ENABLE; + + ret = aw_adm_param_enable(port_id, module_id, param_id, value); + if (ret) { + pr_err("%s: set skt %d failed \n", __func__, value); + return -EINVAL; + } + + pr_err("%s: set skt %s", __func__, value == 1 ? "enable" : "disable"); + return 0; +} + +static int aw882xx_skt_disable_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_err("%s: aw882xx_skt_disable %d \n", __func__, value); + aw882xx_skt_set_dsp(!value); + atomic_set(&g_skt_disable, value); + + return 0; +} + +static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + + pr_err("%s: aw882xx_monitor_get %d\n", + __func__, aw882xx->monitor.is_enable); + + ucontrol->value.integer.value[0] = aw882xx->monitor.is_enable; + + return 0; +} + +static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + int enable = ucontrol->value.integer.value[0]; + + pr_err("%s: aw882xx_monitor_set %d \n", __func__, enable); + + if (enable == aw882xx->monitor.is_enable) + return 1; + + aw882xx->monitor.is_enable = enable; + if (enable) + schedule_work(&aw882xx->monitor.work); + + return 0; +} + +#ifdef AW882XX_RUNIN_TEST +static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s: aw882xx_runin test %d\n", + __func__, atomic_read(&g_runin_test)); + + ucontrol->value.integer.value[0] = atomic_read(&g_runin_test); + + return 0; +} + +static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_err("%s: aw882xx_runin_test %d \n", __func__, value); + + if (value > 1 ) { + value = 0; + } + + atomic_set(&g_runin_test, value); + + return 0; +} + +static void aw882xx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + int32_t set_value; + + /*no test reg set default value*/ + if (!atomic_read(&g_runin_test)) { + return; + } + set_value = 0; + /*set afe rx module*/ + ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &set_value, sizeof(uint32_t)); + if (ret) { + pr_err("%s: disable afe rx module %d falied , ret=%d\n", + __func__, set_value, ret); + } + + /*set skt module*/ + ret = aw882xx_skt_set_dsp(false); + if (ret) { + pr_err("%s: disable skt failed !\n", __func__); + } + + pr_err("%s: disable skt and afe module \n", __func__); +} +#endif + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_algo), awinic_algo), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_profile), awinic_profile), +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_speaker_switch", aw882xx_snd_enum[0], + aw882xx_spk_get, aw882xx_spk_set), + SOC_ENUM_EXT("aw882xx_receiver_switch", aw882xx_snd_enum[0], + aw882xx_rcv_get, aw882xx_rcv_set), + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[1], + aw882xx_algo_get, aw882xx_algo_set), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[1], + aw882xx_tx_get, aw882xx_tx_set), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[0], + aw882xx_skt_disable_get, aw882xx_skt_disable_set), + SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[1], + aw882xx_monitor_get, aw882xx_monitor_set), +#ifdef AW882XX_RUNIN_TEST + SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[0], + aw882xx_runin_test_get, aw882xx_runin_test_set), +#endif +}; + +static struct snd_kcontrol_new aw882xx_profile_controls[] = { + SOC_ENUM_EXT("aw882xx_profile_switch", aw882xx_snd_enum[2], + aw882xx_profile_get, aw882xx_profile_set), +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + pr_err("%s: enter\n", __func__); + + snd_soc_add_component_controls(aw882xx->component, aw882xx_controls, + ARRAY_SIZE(aw882xx_controls)); + if(aw882xx->afe_profile) { + snd_soc_add_component_controls(aw882xx->component, aw882xx_profile_controls, + ARRAY_SIZE(aw882xx_profile_controls)); + } + snd_soc_add_component_controls(aw882xx->component, &aw882xx_volume, 1); +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ + + +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_err("%s: playback enter\n", __func__); + /*aw882xx_run_pwd(aw882xx, false);*/ + aw882xx->power_flag = AW882XX_POWER_ING; + } else { + pr_err("%s: capture enter\n", __func__); + } + + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(dai->codec);*/ + struct snd_soc_component *component = dai->component; + + pr_err("%s: fmt=0x%x\n", __func__, fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { +// dev_err(component->dev, "%s: invalid codec master mode\n", +// __func__); + return -EINVAL; + } + break; + default: + dev_err(component->dev, "%s: unsupported DAI format %d\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec_dai->component); + + pr_err("%s: freq=%d\n", __func__, freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + unsigned int rate = 0; + int reg_value = 0; + uint32_t cco_mux_value; + int width = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_err("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + return 0; + } + /* get rate param */ + aw882xx->rate = rate = params_rate(params); + pr_err("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + + /* match rate */ + switch (rate) { + case 8000: + reg_value = AW882XX_I2SSR_8KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 16000: + reg_value = AW882XX_I2SSR_16KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 32000: + reg_value = AW882XX_I2SSR_32KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 44100: + reg_value = AW882XX_I2SSR_44P1KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 48000: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 96000: + reg_value = AW882XX_I2SSR_96KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 192000: + reg_value = AW882XX_I2SSR_192KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + default: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + pr_err("%s: rate can not support\n", __func__); + break; + } + aw882xx_i2c_write_bits(aw882xx, AW882XX_PLLCTRL1_REG, + AW882XX_I2S_CCO_MUX_MASK, cco_mux_value); + + /* set chip rate */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SSR_MASK, reg_value); + } + + /* get bit width */ + width = params_width(params); + pr_err("%s: width = %d\n", __func__, width); + switch (width) { + case 16: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + break; + case 20: + reg_value = AW882XX_I2SFS_20_BITS_VALUE; + break; + case 24: + reg_value = AW882XX_I2SFS_24_BITS_VALUE; + break; + case 32: + reg_value = AW882XX_I2SFS_32_BITS_VALUE; + break; + default: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + pr_err("%s: width can not support\n", __func__); + break; + } + + /* get width */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SFS_MASK, reg_value); + } + + return 0; +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + pr_err("%s: mute state=%d\n", __func__, mute); + + if (!(aw882xx->flags & AW882XX_FLAG_START_ON_MUTE)) + return 0; + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, false); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_skt_disable, 0); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, true); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw882xx_run_pwd(aw882xx, true); + } +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .ops = &aw882xx_dai_ops, + .symmetric_rates = 1, +#if 0 + .symmetric_channels = 1, + .symmetric_samplebits = 1, +#endif + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static int aw882xx_probe(struct snd_soc_component *component) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + int ret = -1; + + pr_err("%s: enter\n", __func__); + + aw882xx->component = component; + + aw882xx_add_codec_controls(aw882xx); + + //if (component->dev->of_node) + //dev_set_name(component->dev, "%s", "aw882xx_smartpa"); + + pr_err("%s: exit\n", __func__); + + ret = 0; + return ret; +} + +static void aw882xx_remove(struct snd_soc_component *component) +{ + /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec);*/ + pr_err("%s: enter\n", __func__); + return; +} + + +static unsigned int aw882xx_codec_read(struct snd_soc_component *component, + unsigned int reg) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + unsigned int value = 0; + int ret = -1; + + pr_err("%s: enter\n", __func__); + + if (aw882xx_reg_access[reg] & REG_RD_ACCESS) { + ret = aw882xx_i2c_read(aw882xx, reg, &value); + if (ret < 0) + pr_err("%s: read register failed\n", __func__); + } else { + pr_err("%s: register 0x%x no read access\n", __func__, reg); + } + return ret; +} + +static int aw882xx_codec_write(struct snd_soc_component *component, + unsigned int reg, unsigned int value) +{ + int ret = -1; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + pr_err("%s: enter ,reg is 0x%x value is 0x%x\n", + __func__, reg, value); + + if (aw882xx_reg_access[reg]®_WR_ACCESS) { + ret = aw882xx_i2c_write(aw882xx, reg, value); + } else { + pr_err("%s: register 0x%x no write access\n", + __func__, reg); + } + + return ret; +} + +static const struct snd_soc_dapm_widget aw882xx_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AW_DAI_RX", "Speaker_Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("AW_DAI_TX", "Speaker_Capture", 0, 0, 0, 0), + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + SND_SOC_DAPM_INPUT("CAPTURE_IN"), +}; + +static const struct snd_soc_dapm_route aw882xx_audio_map[] = { + {"AW_DAI_RX", NULL, "Speaker_Playback"}, + {"Speaker_Capture", NULL, "AW_DAI_TX"}, + {"AW_DAI_TX", NULL, "CAPTURE_IN"}, + {"SPK_OUT", NULL, "AW_DAI_RX"}, +}; + + +static struct snd_soc_component_driver soc_component_dev_aw882xx = { + .probe = aw882xx_probe, + + .dapm_widgets = aw882xx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aw882xx_dapm_widgets), + .dapm_routes = aw882xx_audio_map, + .num_dapm_routes = ARRAY_SIZE(aw882xx_audio_map), + + .remove = aw882xx_remove, + .read = aw882xx_codec_read, + .write = aw882xx_codec_write, +// .reg_cache_size = AW882XX_REG_MAX, +// .reg_word_size = 2, +}; + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw882xx_interrupt_setup(struct aw882xx *aw882xx) +{ + unsigned int reg_val; + + pr_err("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + reg_val &= (~AW882XX_UVLS_VDD_BELOW_2P8V_VALUE); + reg_val &= (~AW882XX_NOCLKS_TRIG_VALUE); + reg_val &= (~AW882XX_CLKS_TRIG_VALUE); + aw882xx_i2c_write(aw882xx, AW882XX_SYSINTM_REG, reg_val); +} + +static void aw882xx_interrupt_clear(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + + pr_err("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + pr_err("%s: reg SYSST=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + pr_err("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + pr_err("%s: reg SYSINTM=0x%x\n", __func__, reg_val); +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = data; + + pr_err("%s: enter\n", __func__); + + aw882xx_interrupt_clear(aw882xx); + + pr_err("%s: exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +int aw882xx_parse_low_vol_cfg(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np, char *dir, struct aw882xx_low_vol **table, int *num) +{ + int ret; + int i; + uint32_t *cfg_value; + char cfg_name[32] = {0}; + int cfg_len; + + snprintf(cfg_name, sizeof(cfg_name), "low-vol-table-%s", dir); + + cfg_len = of_property_count_u32_elems(np, cfg_name); + if (cfg_len <= 0) { + dev_info(dev, "%s: %s get cfg_len:%d error\n", + __func__, cfg_name, cfg_len); + goto use_default; + } + + cfg_value = devm_kzalloc(dev, sizeof(uint32_t) * cfg_len, GFP_KERNEL); + if (!cfg_value) { + dev_info(dev,"%s: %s aw_cfg kzalloc failed\n", + __func__, cfg_name); + goto use_default; + } + + ret = of_property_read_u32_array(np, cfg_name, cfg_value, cfg_len); + if (ret != 0) { + dev_info(dev, "%s: fail get %s dt\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + *num = cfg_len / 3; + *table = devm_kzalloc(dev, sizeof(struct aw882xx_low_vol) * (*num), GFP_KERNEL); + if (!(*table)) { + dev_info(dev, "%s: %s table kzalloc failed\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + + for(i = 0; i < (*num); i++) { + (*table)[i].vol = cfg_value[3 * i]; + (*table)[i].ipeak = cfg_value[3 * i + 1]; + (*table)[i].gain = cfg_value[3 * i + 2]; + } + + devm_kfree(dev, cfg_value); + + return 0; + +use_default: + dev_info(dev, "%s: %s low-vol table use default cfg\n", __func__, cfg_name); + if (!strcmp(dir,"up")) { + *table = vol_up_table; + *num = sizeof(vol_up_table) / sizeof(struct aw882xx_low_vol); + } else if (!strcmp(dir,"down")) { + *table = vol_down_table; + *num = sizeof(vol_down_table) / sizeof(struct aw882xx_low_vol); + } else { + dev_err(dev,"%s: unsupport dir %s\n", + __func__, dir); + return -EINVAL; + } + return 0; +} + + +int aw882xx_parse_low_temp_cfg(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np, char *dir, struct aw882xx_low_temp **table, int *num) +{ + int ret; + int i; + uint32_t *cfg_value; + char cfg_name[32] = {0}; + int cfg_len; + + snprintf(cfg_name, sizeof(cfg_name), "low-temp-table-%s", dir); + + cfg_len = of_property_count_u32_elems(np, cfg_name); + if (cfg_len <= 0) { + dev_info(dev, "%s: %s get cfg_len:%d error\n", + __func__, cfg_name, cfg_len); + goto use_default; + } + + cfg_value = devm_kzalloc(dev, sizeof(uint32_t) * cfg_len, GFP_KERNEL); + if (!cfg_value) { + dev_info(dev,"%s: %s aw_cfg kzalloc failed\n", + __func__, cfg_name); + goto use_default; + } + + ret = of_property_read_u32_array(np, cfg_name, cfg_value, cfg_len); + if (ret != 0) { + dev_info(dev, "%s: fail get %s dt\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + *num = cfg_len / 4; + *table = devm_kzalloc(dev, sizeof(struct aw882xx_low_temp) * (*num), GFP_KERNEL); + if (!(*table)) { + dev_info(dev, "%s: %s table kzalloc failed\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + + for(i = 0; i < (*num); i++) { + (*table)[i].temp = cfg_value[4 * i]; + (*table)[i].ipeak = cfg_value[4 * i + 1]; + (*table)[i].gain = cfg_value[4 * i + 2]; + (*table)[i].vmax = cfg_value[4 * i + 3]; + } + + devm_kfree(dev, cfg_value); + + return 0; + +use_default: + dev_info(dev, "%s: %s temp table use default cfg\n", __func__, cfg_name); + if (!strcmp(dir,"up")) { + *table = temp_up_table; + *num = sizeof(temp_up_table) / sizeof(struct aw882xx_low_temp); + } else if (!strcmp(dir,"down")) { + *table = temp_down_table; + *num = sizeof(temp_down_table) / sizeof(struct aw882xx_low_temp); + } else { + dev_err(dev,"%s: unsupport dir %s\n", + __func__, dir); + return -EINVAL; + } + return 0; +} + +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret = 0; + int i = 0; + struct aw882xx_monitor *monitor = &aw882xx->monitor; + /* gpio */ + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", + __func__); + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) + dev_info(dev, "%s: no irq gpio provided.\n", __func__); + else + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + + ret = of_property_read_u32(np, "monitor-flag", &monitor->is_enable); + if (ret) { + monitor->is_enable = AW882XX_MONITOR_DEFAULT_FLAG; + dev_err(dev, "%s: monitor-flag get failed ,use default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-flag = %d\n", + __func__, monitor->is_enable); + } + + ret = of_property_read_u32(np, "monitor-timer-val", &monitor->timer_val); + if (ret) { + monitor->timer_val = AW882XX_MONITOR_DEFAULT_TIMER_VAL; + dev_err(dev, "%s: monitor-timer-val get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-timer-val = %d\n", + __func__, monitor->timer_val); + } + + ret = of_property_read_u32(np, "afe-rx-portid", &aw882xx->afe_rx_portid); + if (ret) { + aw882xx->afe_rx_portid = AW882XX_DEFAULT_AFE_RX_PROT_ID; + dev_err(dev, "%s: afe_rx_portid get failed,use default port: 0x%x!\n", + __func__, aw882xx->afe_rx_portid); + } else { + dev_info(dev, "%s: afe_rx_portid = 0x%x\n", + __func__, aw882xx->afe_rx_portid); + } + + ret = of_property_read_u32(np, "afe-tx-portid", &aw882xx->afe_tx_portid); + if (ret) { + aw882xx->afe_tx_portid = AW882XX_DEFAULT_AFE_TX_PROT_ID; + dev_err(dev, "%s: afe_tx_portid get failed,use default port: 0x%x!\n", + __func__, aw882xx->afe_tx_portid); + } else { + dev_info(dev, "%s: afe_tx_portid = 0x%x\n", + __func__, aw882xx->afe_tx_portid); + } + + ret = of_property_read_u32(np, "afe-profile", &aw882xx->afe_profile); + if (ret) { + aw882xx->afe_profile = 0; + dev_err(dev, "%s: afe-profile get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: afe-profile = %d\n", + __func__, aw882xx->afe_profile); + } + + /*get low vol table cfg*/ + ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "up", + &monitor->vol_up_table, &monitor->vol_up_num); + if (ret <0) + return ret; + + ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "down", + &monitor->vol_down_table, &monitor->vol_down_num); + if (ret < 0) + return ret; + + for(i = 0; i < monitor->vol_up_num; i++) + dev_info(dev,"%s:vol_up_table vol:%d, %d, %d\n", + __func__, monitor->vol_up_table[i].vol, + monitor->vol_up_table[i].ipeak, + monitor->vol_up_table[i].gain); + + for(i = 0; i < monitor->vol_down_num; i++) + dev_info(dev, "%s:vol_down_table vol:%d, %d, %d\n", + __func__, monitor->vol_down_table[i].vol, + monitor->vol_down_table[i].ipeak, + monitor->vol_down_table[i].gain); + + for(i = 0; i < monitor->vol_down_num; i++) { + if(monitor->vol_down_table[i].vol >= + monitor->vol_up_table[monitor->vol_up_num - 1 -i].vol) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->vol_up_table = vol_up_table; + monitor->vol_up_num= sizeof(vol_up_table) / + sizeof(struct aw882xx_low_vol); + monitor->vol_down_table = vol_down_table; + monitor->vol_down_num = sizeof(vol_down_table) / + sizeof(struct aw882xx_low_vol); + break; + } + } + + for(i = 1; i < monitor->vol_down_num; i++) { + if(monitor->vol_up_table[monitor->vol_up_num - i].vol >= + monitor->vol_down_table[i].vol) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->vol_up_table = vol_up_table; + monitor->vol_up_num= sizeof(vol_up_table) / + sizeof(struct aw882xx_low_vol); + monitor->vol_down_table = vol_down_table; + monitor->vol_down_num = sizeof(vol_down_table) / + sizeof(struct aw882xx_low_vol); + break; + } + } + /*get low temp table cfg*/ + ret = aw882xx_parse_low_temp_cfg(dev, aw882xx, np, "up", + &monitor->temp_up_table, &monitor->temp_up_num); + if (ret <0) + return ret; + + ret = aw882xx_parse_low_temp_cfg(dev, aw882xx, np, "down", + &monitor->temp_down_table, &monitor->temp_down_num); + + for(i = 0; i < monitor->temp_up_num; i++) + dev_info(dev,"%s:temp_up_table temp:%d, 0x%x, 0x%x, 0x%x\n", + __func__, monitor->temp_up_table[i].temp, + monitor->temp_up_table[i].ipeak, + monitor->temp_up_table[i].gain, + monitor->temp_up_table[i].vmax); + + for(i = 0; i < monitor->temp_down_num; i++) + dev_info(dev, "%s:temp_down_table temp:%d, 0x%x, 0x%x, 0x%x\n", + __func__, monitor->temp_down_table[i].temp, + monitor->temp_down_table[i].ipeak, + monitor->temp_down_table[i].gain, + monitor->temp_down_table[i].vmax); + + for(i = 0; i < monitor->temp_down_num; i++) { + if(monitor->temp_down_table[i].temp >= + monitor->temp_up_table[monitor->temp_up_num - 1 -i].temp) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->temp_up_table = temp_up_table; + monitor->temp_up_num= sizeof(temp_up_table) / + sizeof(struct aw882xx_low_temp); + monitor->temp_down_table = temp_down_table; + monitor->temp_down_num = sizeof(temp_down_table) / + sizeof(struct aw882xx_low_temp); + break; + } + } + + for(i = 1; i < monitor->temp_down_num; i++) { + if(monitor->temp_up_table[monitor->temp_up_num - i].temp >= + monitor->temp_down_table[i].temp) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->temp_up_table = temp_up_table; + monitor->temp_up_num= sizeof(temp_up_table) / + sizeof(struct aw882xx_low_temp); + monitor->temp_down_table = temp_down_table; + monitor->temp_down_num = sizeof(temp_down_table) / + sizeof(struct aw882xx_low_temp); + break; + } + } + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + pr_err("%s: enter\n", __func__); + + if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + msleep(2); + } else { + dev_err(aw882xx->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_ID_REG, ®); + if (ret < 0) { + dev_err(aw882xx->dev, + "%s: failed to read REG_ID: %d\n", + __func__, ret); + return -EIO; + } + switch (reg) { + case AW882XX_ID: + pr_err("%s: aw882xx detected\n", __func__); + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + aw882xx->flags |= AW882XX_FLAG_START_ON_MUTE; + aw882xx->chipid = AW882XX_ID; + pr_err("%s: aw882xx->flags=0x%x\n", + __func__, aw882xx->flags); + return 0; + default: + pr_err("%s: unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys group attribute: monitor + * + ******************************************************/ +static ssize_t aw882xx_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + dev_info(dev, "%s:monitor enable set =%d\n", + __func__, enable); + aw882xx->monitor.is_enable = enable; + if (enable) + aw882xx_monitor_start(&aw882xx->monitor); + + return count; +} + +static ssize_t aw882xx_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_enable; + + local_enable = aw882xx->monitor.is_enable; + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx monitor enable: %d\n", local_enable); + return len; +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_REG_MAX; i++) { + if (aw882xx_reg_access[i]®_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx_reg_access[aw882xx->reg_addr] & REG_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, aw882xx->reg_addr, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", aw882xx->reg_addr, reg_val); + } + return len; +} + +static ssize_t aw882xx_spk_rcv_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + int ret = -1; + unsigned int databuf[2] = {0}; + + ret = kstrtouint(buf, 0, &databuf[0]); + if (ret < 0) + return ret; + + aw882xx->spk_rcv_mode = databuf[0]; + + return count; +} + +static ssize_t aw882xx_spk_rcv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, speaker mode\n", + aw882xx->spk_rcv_mode); + else if (aw882xx->spk_rcv_mode == AW882XX_RECEIVER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, receiver mode\n", + aw882xx->spk_rcv_mode); + else + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, unknown mode\n", + aw882xx->spk_rcv_mode); + + return len; +} + +static ssize_t aw882xx_mec_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &mec_ctr); + if (ret < 0) + return ret; + + + pr_err("%s: mec_ctr=%d\n", __func__, mec_ctr); + + ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, + param_id, &mec_ctr, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg error, ret=%d\n", __func__, ret); + + mdelay(2); + + + + return count; +} + +static ssize_t aw882xx_mec_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + ssize_t len = 0; + int ret = -1; + uint8_t *buffer = NULL; + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + + buffer = kmalloc(sizeof(uint32_t), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s can not allocate memory\n", __func__); + return -ENOMEM; + } + + ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, + param_id, buffer, sizeof(uint32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + kfree(buffer); + return -EFAULT; + } + + memcpy(&mec_ctr, buffer, sizeof(uint32_t)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx mec: %d\n", mec_ctr); + + kfree(buffer); + + return len; +} + +static ssize_t aw882xx_default_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int ret = -1; + + if((aw882xx != NULL) && (aw882xx->default_re == 0)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->default_re, sizeof(int32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + return -EFAULT; + } + } + if(aw882xx != NULL) { + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx default_re: %d\n", aw882xx->default_re); + } + return len; +} + +#ifdef AW_DEBUG +static ssize_t aw882xx_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + pr_err("%s: vol set =%d\n", __func__, vol); + aw882xx->monitor.test_vol = vol; + + return count; +} + +static ssize_t aw882xx_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_vol = aw882xx->monitor.test_vol; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_vol); + return len; +} +static ssize_t aw882xx_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + pr_err("%s: temp set =%d\n", __func__, temp); + aw882xx->monitor.test_temp = temp; + + return count; +} + +static ssize_t aw882xx_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int32_t local_temp = aw882xx->monitor.test_temp; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_temp); + return len; +} +#endif +static int aw882xx_cali_operation(struct aw882xx *aw882xx, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + int ret = 0; + char *data_ptr = NULL; + + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + return -EFAULT; + } + + pr_err("cmd : %d, data_len%d\n", cmd , data_len); + switch (cmd) { + case AW882XX_IOCTL_ENABLE_CALI: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + g_aw882xx_cali_flag = (int8_t)data_ptr[0]; + pr_err("%s:set cali %s", __func__, + (g_aw882xx_cali_flag == 0) ? ("disable") : ("enable")); + } break; + case AW882XX_IOCTL_SET_CALI_CFG: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_CFG: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_DATA: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_NOISE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_NOISE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_NOISE_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_F0: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_F0_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_F0_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_CALI_RE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + if(aw882xx->default_re == 0) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->default_re, sizeof(int32_t), false); + pr_err("aw882xx: default_re:%d\n", aw882xx->default_re); + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + aw882xx_set_cali_re(aw882xx, *((int32_t *)data_ptr)); + } break; + case AW882XX_IOCTL_GET_CALI_RE: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, false); + if(aw882xx->default_re == 0) { + aw882xx->default_re = *((uint32_t *)data_ptr); + pr_err("aw882xx: default_re:%d\n", aw882xx->default_re); + } + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_VMAX: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error:0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_VMAX: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_PARAM: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_PARAMS, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + ret = -EFAULT; + goto exit; + } + pr_err("%s: set params done", __func__); + } break; + default: { + pr_err("%s : cmd %d\n", __func__, cmd); + } break; + } +exit: + kfree(data_ptr); + return ret; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw882xx_monitor_show, aw882xx_monitor_store); +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(spk_rcv, S_IWUSR | S_IRUGO, + aw882xx_spk_rcv_show, aw882xx_spk_rcv_store); +static DEVICE_ATTR(mec, S_IWUSR | S_IRUGO, + aw882xx_mec_show, aw882xx_mec_store); +static DEVICE_ATTR(default_re, S_IWUSR | S_IRUGO, + aw882xx_default_re_show, NULL); +#ifdef AW_DEBUG +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw882xx_vol_show, aw882xx_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw882xx_temp_show, aw882xx_temp_store); +#endif + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_monitor.attr, + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_spk_rcv.attr, + &dev_attr_mec.attr, + &dev_attr_default_re.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes +}; + +#define AW882XX_SMARTPA_NAME "aw882xx_smartpa" +static int aw882xx_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + file->private_data = (void *)g_aw882xx; + + pr_err("open success"); + return 0; +} + +static int aw882xx_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_err("release successi\n"); + return 0; +} + +static long aw882xx_file_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw882xx *aw882xx = NULL; + + if (((_IOC_TYPE(cmd)) != (AW882XX_IOCTL_MAGIC))) { + pr_err("%s: cmd magic err\n", __func__); + return -EINVAL; + } + aw882xx = (struct aw882xx *)file->private_data; + ret = aw882xx_cali_operation(aw882xx, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +static struct file_operations aw882xx_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aw882xx_file_unlocked_ioctl, + .open = aw882xx_file_open, + .release = aw882xx_file_release, +}; + +static struct miscdevice aw882xx_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW882XX_SMARTPA_NAME, + .fops = &aw882xx_fops, +}; + +void init_aw882xx_misc_driver(struct aw882xx *aw882xx) +{ + int ret; + + ret = misc_register(&aw882xx_misc); + if (ret) { + pr_err("%s: misc register fail: %d\n", __func__, ret); + return; + } + pr_err("%s: misc register success", __func__); +} + +/*monitor*/ +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor) +{ + pr_err("%s: enter\n", __func__); + + if (!hrtimer_active(&monitor->timer)) { + pr_err("%s: start monitor\n", __func__); + hrtimer_start(&monitor->timer, + ktime_set(monitor->timer_val/1000, + (monitor->timer_val%1000)*1000000), HRTIMER_MODE_REL); + } + + return 0; +} + +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor) +{ + pr_err("%s: enter\n", __func__); + + if (hrtimer_active(&monitor->timer)) { + pr_err("%s: stop monitor\n", __func__); + hrtimer_cancel(&monitor->timer); + } + return 0; +} + +static enum hrtimer_restart + aw882xx_monitor_timer_func(struct hrtimer *timer) +{ + struct aw882xx_monitor *monitor = + container_of(timer, struct aw882xx_monitor, timer); + + pr_err("%s : enter\n", __func__); + + if (monitor->is_enable) + schedule_work(&monitor->work); + + return HRTIMER_NORESTART; +} +static int aw882xx_monitor_get_voltage(struct aw882xx *aw882xx, + unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_VBAT_REG, vol); + if (ret < 0) { + pr_err("%s: read voltage failed !\n", __func__); + return ret; + } + local_vol = (*vol) * AW882XX_MONITOR_VBAT_RANGE + / AW882XX_MONITOR_INT_10BIT; + + if(aw882xx->monitor.vol_count < AW_VOL_COUNT) { + aw882xx->monitor.vol_count++; + if(aw882xx->monitor.pre_vol == 0) { + *vol = local_vol; + } else { + *vol = (aw882xx->monitor.pre_vol * (AW_VOL_COUNT -1 ) + + local_vol) / AW_VOL_COUNT; + } + } else { + *vol = (aw882xx->monitor.pre_vol * (AW_VOL_COUNT -1 ) + + local_vol) / AW_VOL_COUNT; + } + aw882xx->monitor.pre_vol = *vol; + pr_err("%s: cur voltage is %d, vol is %d\n", __func__, local_vol, *vol); + return 0; +} +static int aw882xx_monitor_voltage(struct aw882xx *aw882xx, + struct aw882xx_low_vol *vol_cfg) +{ + int ret = -1; + int i; + unsigned int voltage = 0; + struct aw882xx_monitor *monitor = NULL; + + if (aw882xx == NULL || vol_cfg == NULL) { + pr_err("%s: pointer is NUL\n", __func__); + return ret; + } + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } +#else + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; +#endif + + if (monitor->first_entry == AW_FIRST_ENTRY) { + for (i = 0; i < monitor->vol_down_num; i++) { + if (voltage < monitor->vol_down_table[i].vol) { + *vol_cfg = monitor->vol_down_table[i]; + return 0; + } + } + + *vol_cfg = monitor->vol_up_table[0]; + return 0; + } + + if (voltage < monitor->vol_down_table[0].vol) { + *vol_cfg = monitor->vol_down_table[0]; + return 0; + } + + for (i = 1; i < monitor->vol_down_num; i++) { + if ((monitor->vol_up_table[monitor->vol_down_num - i].vol < voltage) && + (voltage < monitor->vol_down_table[i].vol)) { + *vol_cfg = monitor->vol_down_table[i]; + return 0; + } + } + + if (voltage > monitor->vol_up_table[0].vol) { + *vol_cfg = monitor->vol_up_table[0]; + return 0; + } + + return 0; +} +static int aw882xx_monitor_get_temperature(struct aw882xx *aw882xx, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_TEMP_REG, ®_val); + if (ret < 0) { + pr_err("%s: get temperature failed !\n", __func__); + return ret; + } + + local_temp = reg_val; + if (local_temp & AW882XX_MONITOR_TEMP_SIGN_MASK) + local_temp = local_temp | AW882XX_MONITOR_TEMP_NEG_MASK; + + *temp = (int)local_temp; + pr_err("%s: chip temperature = %d\n", __func__, local_temp); + return 0; +} + +static int aw882xx_monitor_temperature(struct aw882xx *aw882xx, + struct aw882xx_low_temp *temp_cfg) +{ + int ret; + int i; + struct aw882xx_monitor *monitor = NULL; + int current_temp = 0; + + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_temp == 0) { + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret < 0) + return ret; +#endif + + if (monitor->first_entry == AW_FIRST_ENTRY) { + monitor->first_entry = AW_NOT_FIRST_ENTRY; + for (i = 0; i < monitor->temp_down_num; i++) { + if (current_temp < monitor->temp_down_table[i].temp) { + temp_cfg->ipeak = monitor->temp_down_table[i].ipeak; + temp_cfg->gain = monitor->temp_down_table[i].gain; + temp_cfg->vmax = monitor->temp_down_table[i].vmax; + return 0; + } + } + + temp_cfg->ipeak = monitor->temp_up_table[0].ipeak; + temp_cfg->gain = monitor->temp_up_table[0].gain; + temp_cfg->vmax = monitor->temp_up_table[0].vmax; + return 0; + } + + if (current_temp < monitor->temp_down_table[0].temp) { + temp_cfg->ipeak = monitor->temp_down_table[0].ipeak; + temp_cfg->gain = monitor->temp_down_table[0].gain; + temp_cfg->vmax = monitor->temp_down_table[0].vmax; + return 0; + } + + for (i = 1; i < monitor->temp_down_num; i++) { + if ((monitor->temp_up_table[monitor->temp_down_num - i].temp < current_temp) + && (current_temp < monitor->temp_down_table[i].temp)) { + temp_cfg->ipeak = monitor->temp_down_table[i].ipeak; + temp_cfg->gain = monitor->temp_down_table[i].gain; + temp_cfg->vmax = monitor->temp_down_table[i].vmax; + return 0; + } + } + + if (current_temp > monitor->temp_up_table[0].temp) { + temp_cfg->ipeak = monitor->temp_up_table[0].ipeak; + temp_cfg->gain = monitor->temp_up_table[0].gain; + temp_cfg->vmax = monitor->temp_up_table[0].vmax; + return 0; + } + + return 0; +} + +static void aw882xx_monitor_get_cfg(struct aw882xx_low_temp *temp, + struct aw882xx_low_vol *vol) +{ + if (vol->ipeak == IPEAK_NONE) + return; + + if (temp->ipeak == IPEAK_NONE) { + temp->ipeak = vol->ipeak; + temp->gain = vol->gain; + return; + } + + /*get min ipeak*/ + if (temp->ipeak > vol->ipeak) + temp->ipeak = vol->ipeak; + + /*get min gain*/ + if (temp->gain < vol->gain) + temp->gain = vol->gain; + +} +static void aw882xx_monitor_set_ipeak(struct aw882xx *aw882xx, uint8_t ipeak) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (ipeak == IPEAK_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if (ret < 0) { + pr_err("%s: read ipeak failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val &= AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK; + + if (read_reg_val == ipeak) { + pr_err("%s: ipeak = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= (~AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK); + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw882xx_i2c_write(aw882xx, AW882XX_SYSCTRL2_REG, reg_val); + if (ret < 0) { + pr_err("%s: write ipeak failed\n", __func__); + return; + } + pr_err("%s: set reg val = 0x%x, ipeak = 0x%x\n", + __func__, reg_val, ipeak); +} +static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (gain == GAIN_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + if (ret < 0) { + pr_err("%s: read gain failed\n", __func__); + return; + } + + read_reg_val = reg_val >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + aw882xx->cur_gain = gain; + if (read_reg_val == gain) { + pr_err("%s: gain = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= AW882XX_BIT_HAGCCFG4_GAIN_MASK; + read_reg_val = gain; + reg_val |= (read_reg_val << AW882XX_BIT_HAGCCFG4_GAIN_SHIFT); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, reg_val); + if (ret < 0) { + pr_err("%s: write gain failed\n", __func__); + return; + } + pr_err("%s: set reg val = 0x%x, gain = 0x%x\n", + __func__, reg_val, gain); +} +static void aw882xx_monitor_set_vmax(struct aw882xx *aw882xx, uint32_t vmax) +{ + uint32_t local_vmax = vmax; + int ret; + + if (vmax == VMAX_NONE) + return; + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + &local_vmax, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + + pr_err("%s: set vmax = 0x%x\n", __func__, vmax); +} +static void aw882xx_monitor_work(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol *vol_cfg = &aw882xx->monitor.vol_cfg; + struct aw882xx_low_temp *temp_cfg = &aw882xx->monitor.temp_cfg; + struct aw882xx_low_temp set_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + if (g_aw882xx_cali_flag != 0 || g_aw882xx_profile_flag != 0) { + pr_err("%s: done nothing while start cali", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + pr_err("%s: vol: ipeak = 0x%x, gain = 0x%x\n", + __func__, vol_cfg->ipeak, vol_cfg->gain); + pr_err("%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, temp_cfg->ipeak, temp_cfg->gain, temp_cfg->vmax); + + memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); + + aw882xx_monitor_get_cfg(&set_cfg, vol_cfg); + + aw882xx_monitor_set_ipeak(aw882xx, set_cfg.ipeak); + + aw882xx_monitor_set_gain(aw882xx, set_cfg.gain); + + aw882xx_monitor_set_vmax(aw882xx, set_cfg.vmax); +} + +static int aw882xx_get_hmute(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + int ret; + + pr_err("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if ((~AW882XX_HMUTE_MASK) & reg_val) + ret = 1; + else + ret = 0; + + return ret; +} +static void aw882xx_monitor_work_func(struct work_struct *work) +{ + struct aw882xx_monitor *monitor = container_of(work, + struct aw882xx_monitor, work); + struct aw882xx *aw882xx = container_of(monitor, + struct aw882xx, monitor); + + pr_err("%s: enter\n", __func__); + mutex_lock(&aw882xx->lock); + if (!aw882xx_get_hmute(aw882xx)) { + aw882xx_monitor_work(aw882xx); + aw882xx_monitor_start(monitor); + } + mutex_unlock(&aw882xx->lock); +} + +void init_aw882xx_monitor(struct aw882xx_monitor *monitor) +{ + pr_err("%s: enter\n", __func__); + hrtimer_init(&monitor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + monitor->timer.function = aw882xx_monitor_timer_func; + INIT_WORK(&monitor->work, aw882xx_monitor_work_func); + monitor->pre_vol = 0; + monitor->vol_count = 0; + monitor->vol_cfg.ipeak = monitor->vol_up_table[0].ipeak; + monitor->vol_cfg.gain = monitor->vol_up_table[0].gain; + monitor->temp_cfg.ipeak = monitor->temp_up_table[0].ipeak; + monitor->temp_cfg.gain = monitor->temp_up_table[0].gain; + monitor->temp_cfg.vmax = monitor->temp_up_table[0].vmax; + +#ifdef AW_DEBUG + monitor->test_vol = 0; + monitor->test_temp = 0; +#endif +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + int i; + + pr_err("%s: enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) + return -ENOMEM; + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + + i2c_set_clientdata(i2c, aw882xx); + mutex_init(&aw882xx->lock); + + /* aw882xx rst & int */ + if (np) { + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } else { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + } + + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + dev_err(&i2c->dev, "%s: rst request failed\n", + __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + dev_err(&i2c->dev, "%s: int request failed\n", + __func__); + goto err_irq_gpio_request; + } + } + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw882xx_read_chipid failed ret=%d\n", + __func__, ret); + goto err_id; + } + + /* aw882xx device name */ + //if (i2c->dev.of_node) + // dev_set_name(&i2c->dev, "%s", "aw882xx_smartpa"); + //else + // dev_err(&i2c->dev, "%s failed to set device name: %d\n", + // __func__, ret); + + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (!dai) + goto err_dai_kzalloc; + + memcpy(dai, aw882xx_dai, sizeof(aw882xx_dai)); + pr_err("%s: dai->name(%s)\n", __func__, dai->name); + + ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_aw882xx, + dai, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed to register aw882xx: %d\n", + __func__, ret); + goto err_register_codec; + } + + /* aw882xx irq */ + if (gpio_is_valid(aw882xx->irq_gpio) && + !(aw882xx->flags & AW882XX_FLAG_SKIP_INTERRUPTS)) { + aw882xx_interrupt_setup(aw882xx); + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + gpio_to_irq(aw882xx->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw882xx); + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, + "%s error creating sysfs attr files\n", + __func__); + goto err_sysfs; + } + + init_aw882xx_monitor(&aw882xx->monitor); + + init_aw882xx_misc_driver(aw882xx); + g_aw882xx = aw882xx; + + aw882xx->default_re = 0; + aw882xx->need_fade = 0; + /*init profile*/ + mutex_init(&aw882xx->profile.lock); + aw882xx->profile.cur_profile = 0; + aw882xx->profile.status = AW882XX_INIT_ST; + aw882xx->profile.len = 0; + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = NULL; + } + + /*global init*/ + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_skt_disable, 0); +#ifdef AW882XX_RUNIN_TEST + atomic_set(&g_runin_test, 0); + INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); +#endif + pr_err("%s: probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + devm_free_irq(&i2c->dev, gpio_to_irq(aw882xx->irq_gpio), aw882xx); +err_irq: + snd_soc_unregister_component(&i2c->dev); +err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; +err_dai_kzalloc: +err_id: + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); +err_irq_gpio_request: + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); +err_reset_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + pr_err("%s: enter\n", __func__); + misc_deregister(&aw882xx_misc); + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + snd_soc_unregister_component(&i2c->dev); + + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return 0; +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xx_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .id_table = aw882xx_i2c_id, +}; + + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + pr_err("%s: aw882xx driver version %s\n", __func__, AW882XX_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + pr_err("%s: fail to add aw882xx device into i2c\n", __func__); + + return ret; +} +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); + +#endif /* CONFIG_AW882XX_CODEC */ diff --git a/asoc/codecs/aw882xx/aw882xx.h b/asoc/codecs/aw882xx/aw882xx.h new file mode 100644 index 000000000000..b5941cf8d7dd --- /dev/null +++ b/asoc/codecs/aw882xx/aw882xx.h @@ -0,0 +1,279 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ + +//#define AW_DEBUG +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 + +#define AW_VOL_COUNT (3) +#define AW882XX_FLAG_START_ON_MUTE (1 << 0) +#define AW882XX_FLAG_SKIP_INTERRUPTS (1 << 1) +#define AW882XX_FLAG_SAAM_AVAILABLE (1 << 2) +#define AW882XX_FLAG_STEREO_DEVICE (1 << 3) +#define AW882XX_FLAG_MULTI_MIC_INPUTS (1 << 4) + +#define AW882XX_NUM_RATES 9 +#define AW882XX_SYSST_CHECK_MAX 10 + +#define AW882XX_MODE_SHIFT_MAX 2 + +#define AW882XX_DEFAULT_AFE_RX_PROT_ID 0x1000 /*AFE_PORT_ID_PRIMARY_MI2S_RX*/ +#define AW882XX_DEFAULT_AFE_TX_PROT_ID 0x1001 /*AFE_PORT_ID_PRIMARY_MI2S_TX*/ +#define AW_MODULE_ID_COPP (0X10013D02) /*SKT module id*/ +#define AW_MODULE_PARAMS_ID_COPP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#define DEFAULT_CALI_VALUE (7) +#define ERRO_CALI_VALUE (0) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_L (0X10013D17) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_R (0X10013D18) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L (0X10013D19) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_R (0x10013d1A) +#define AFE_PARAM_ID_AWDSP_RX_RE_L (0x10013d1B) +#define AFE_PARAM_ID_AWDSP_RX_RE_R (0X10013D1C) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_L (0X10013D1D) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_R (0X10013D1E) +#define AFE_PARAM_ID_AWDSP_RX_F0_L (0X10013D1F) +#define AFE_PARAM_ID_AWDSP_RX_F0_R (0X10013D20) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L (0X10013D21) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_R (0X10013D22) + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +enum AWINIC_CALI_CMD{ + AW_CALI_CMD_NONE = 0, + AW_CALI_CMD_START, + AW_CALI_CMD_END, + AW_CALI_CMD_SET_CALI_CFG, + AW_CALI_CMD_GET_CALI_CFG, + AW_CALI_CMD_GET_CALI_DATA, + AW_CALI_CMD_SET_NOISE, + AW_CALI_CMD_GET_F0, + AW_CALI_CMD_SET_CALI_RE, + AW_CALI_CMD_GET_CALI_RE, + AW_CALI_CMD_SET_VMAX, + AW_CALI_CMD_GET_VMAX, + AW_CALI_CMD_MAX, +}; + +#define AW882XX_CALI_CFG_NUM 3 +#define AW882XX_CALI_DATA_NUM 6 +#define AW882XX_PARAMS_NUM 400 +struct cali_cfg{ + int32_t data[AW882XX_CALI_CFG_NUM]; +}; +struct cali_data{ + int32_t data[AW882XX_CALI_DATA_NUM]; +}; +struct params_data{ + int32_t data[AW882XX_PARAMS_NUM]; +}; +struct ptr_params_data { + int len; + int32_t *data; +}; + + +#define AW882XX_IOCTL_MAGIC 'a' +#define AW882XX_IOCTL_SET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_DATA _IOWR(AW882XX_IOCTL_MAGIC, 3, struct cali_data) +#define AW882XX_IOCTL_SET_NOISE _IOWR(AW882XX_IOCTL_MAGIC, 4, int32_t) +#define AW882XX_IOCTL_GET_F0 _IOWR(AW882XX_IOCTL_MAGIC, 5, int32_t) +#define AW882XX_IOCTL_SET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 6, int32_t) +#define AW882XX_IOCTL_GET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 7, int32_t) +#define AW882XX_IOCTL_SET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 8, int32_t) +#define AW882XX_IOCTL_GET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 9, int32_t) +#define AW882XX_IOCTL_SET_PARAM _IOWR(AW882XX_IOCTL_MAGIC, 10,struct params_data) +#define AW882XX_IOCTL_ENABLE_CALI _IOWR(AW882XX_IOCTL_MAGIC, 11,int8_t) +#define AW882XX_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW882XX_IOCTL_MAGIC, 12, struct ptr_params_data) + + +enum aw882xx_init { + AW882XX_INIT_ST = 0, + AW882XX_INIT_OK = 1, + AW882XX_INIT_NG = 2, +}; + +enum aw882xx_power_status { + AW882XX_POWER_DOWN = 0, + AW882XX_POWER_ING = 1, + AW882XX_POWER_UP = 2, +}; + +enum aw882xx_chipid { + AW882XX_ID = 0x1852, +}; + +enum aw882xx_modeshift { + AW882XX_MODE_SPK_SHIFT = 0, + AW882XX_MODE_RCV_SHIFT = 1, +}; + +enum aw882xx_mode_spk_rcv { + AW882XX_SPEAKER_MODE = 0, + AW882XX_RECEIVER_MODE = 1, +}; +//smartpa monitor +enum aw882xx_ipeak { + IPEAK_3P50_A = 0x08, + IPEAK_3P00_A = 0x06, + IPEAK_2P75_A = 0x05, + IPEAK_2P50_A = 0x04, + IPEAK_NONE = 0xFF, +}; + +enum aw882xx_gain { + GAIN_NEG_0P0_DB = 0x00, + GAIN_NEG_0P5_DB = 0x01, + GAIN_NEG_1P0_DB = 0x02, + GAIN_NEG_1P5_DB = 0x03, + GAIN_NEG_3P0_DB = 0x06, + GAIN_NEG_4P5_DB = 0x09, + GAIN_NEG_6P0_DB = 0x10, + GAIN_NONE = 0xFF, +}; + +enum aw882xx_vmax_percentage { + VMAX_100_PERCENTAGE = 0x00000000, + VMAX_086_PERCENTAGE = 0xFFED714D, + VMAX_075_PERCENTAGE = 0xFFD80505, + VMAX_063_PERCENTAGE = 0xFFBEAE7E, + VMAX_NONE = 0xFFFFFFFF, +}; + +#define AW882XX_MONITOR_DEFAULT_FLAG 0 +#define AW882XX_MONITOR_DEFAULT_TIMER_VAL 30000 +#define AW882XX_MONITOR_VBAT_RANGE 6025 +#define AW882XX_MONITOR_INT_10BIT 1023 +#define AW882XX_MONITOR_TEMP_SIGN_MASK (1<<9) +#define AW882XX_MONITOR_TEMP_NEG_MASK (0XFC00) +#define AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK ( 15<< 0) +#define AW882XX_BIT_HAGCCFG4_GAIN_SHIFT (8) +#define AW882XX_BIT_HAGCCFG4_GAIN_MASK (0x00ff) + +struct aw882xx_low_vol { + uint32_t vol; + uint8_t ipeak; + uint8_t gain; +}; + +struct aw882xx_low_temp { + int16_t temp; + uint8_t ipeak; + uint8_t gain; + uint32_t vmax; +}; + +struct aw882xx_monitor{ + struct hrtimer timer; + uint32_t timer_val; + struct work_struct work; + uint32_t is_enable; + uint16_t pre_vol; + uint16_t vol_count; + uint8_t first_entry; + + struct aw882xx_low_vol vol_cfg; + struct aw882xx_low_temp temp_cfg; + int vol_up_num; + int vol_down_num; + int temp_up_num; + int temp_down_num; + struct aw882xx_low_vol *vol_up_table; + struct aw882xx_low_vol *vol_down_table; + struct aw882xx_low_temp *temp_up_table; + struct aw882xx_low_temp *temp_down_table; + +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/*control runin test function*/ +#define AW882XX_RUNIN_TEST +enum AWINIC_PROFILE{ + AW_PROFILE_MUSIC = 0, + AW_PROFILE_RINGTONE, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_VOICE, + AW_PROFILE_MAX, +}; + +#define VERSION_MAX 4 +#define PROJECT_NAME_MAX 24 +#define VOLUME_STEP_DB (6) +#define VOLUME_MIN_NEG_90_DB (90) +#define FADE_STEP_DB (6) + +typedef struct awinic_afe_param_header{ + uint8_t fw[VERSION_MAX]; + uint8_t cfg[VERSION_MAX]; + uint8_t project[PROJECT_NAME_MAX]; + uint32_t start; + uint32_t params_len; + uint8_t check_sum; + uint8_t profile_num; + uint8_t reserve[2]; +}aw_afe_params_hdr_t; + +struct profile_info { + struct mutex lock; + int cur_profile; + int status; + int len; + char* data[AW_PROFILE_MAX]; +}; + +struct aw882xx { + struct regmap *regmap; + struct i2c_client *i2c; + struct snd_soc_component *component; + struct device *dev; + struct mutex lock; + + struct aw882xx_monitor monitor; +#ifdef AW882XX_RUNIN_TEST + struct delayed_work adsp_status; +#endif + int sysclk; + int rate; + int pstream; + int cstream; + + int reset_gpio; + int irq_gpio; + int power_flag; + + unsigned char reg_addr; + + unsigned int flags; + unsigned int chipid; + unsigned int init; + unsigned int spk_rcv_mode; + uint32_t cali_re; + uint32_t default_re; + uint8_t cur_gain; + uint8_t need_fade; + unsigned int cfg_num; + unsigned int afe_rx_portid; + unsigned int afe_tx_portid; + unsigned int afe_profile; + struct profile_info profile; +}; + +struct aw882xx_container { + int len; + unsigned char data[]; +}; +#endif diff --git a/asoc/codecs/aw882xx/aw882xx_reg.h b/asoc/codecs/aw882xx/aw882xx_reg.h new file mode 100644 index 000000000000..4d680beec550 --- /dev/null +++ b/asoc/codecs/aw882xx/aw882xx_reg.h @@ -0,0 +1,1570 @@ +#ifndef __AW882XX_REG_H__ +#define __AW882XX_REG_H__ + +/* registers list */ +#define AW882XX_ID_REG (0x00) +#define AW882XX_SYSST_REG (0x01) +#define AW882XX_SYSINT_REG (0x02) +#define AW882XX_SYSINTM_REG (0x03) +#define AW882XX_SYSCTRL_REG (0x04) +#define AW882XX_SYSCTRL2_REG (0x05) +#define AW882XX_I2SCTRL_REG (0x06) +#define AW882XX_I2SCFG1_REG (0x07) +#define AW882XX_I2SCFG2_REG (0x08) +#define AW882XX_HAGCCFG1_REG (0x09) +#define AW882XX_HAGCCFG2_REG (0x0A) +#define AW882XX_HAGCCFG3_REG (0x0B) +#define AW882XX_HAGCCFG4_REG (0x0C) +#define AW882XX_HAGCCFG5_REG (0x0D) +#define AW882XX_HAGCCFG6_REG (0x0E) +#define AW882XX_HAGCCFG7_REG (0x0F) +#define AW882XX_HAGCST_REG (0x10) +#define AW882XX_PRODID_REG (0x11) +#define AW882XX_VBAT_REG (0x12) +#define AW882XX_TEMP_REG (0x13) +#define AW882XX_PVDD_REG (0x14) +#define AW882XX_DBGCTRL_REG (0x20) +#define AW882XX_I2SINT_REG (0x21) +#define AW882XX_I2SCAPCNT_REG (0x22) +#define AW882XX_CRCIN_REG (0x38) +#define AW882XX_CRCOUT_REG (0x39) +#define AW882XX_VSNCTRL1_REG (0x50) +#define AW882XX_ISNCTRL1_REG (0x52) +#define AW882XX_ISNCTRL2_REG (0x53) +#define AW882XX_VTMCTRL1_REG (0x54) +#define AW882XX_VTMCTRL2_REG (0x55) +#define AW882XX_VTMCTRL3_REG (0x56) +#define AW882XX_ISNDAT_REG (0x57) +#define AW882XX_VSNDAT_REG (0x58) +#define AW882XX_PWMCTRL_REG (0x59) +#define AW882XX_PWMCTRL2_REG (0x5A) +#define AW882XX_BSTCTRL1_REG (0x60) +#define AW882XX_BSTCTRL2_REG (0x61) +#define AW882XX_BSTCTRL3_REG (0x62) +#define AW882XX_BSTDBG1_REG (0x63) +#define AW882XX_BSTDBG2_REG (0x64) +#define AW882XX_BSTDBG3_REG (0x65) +#define AW882XX_PLLCTRL1_REG (0x66) +#define AW882XX_PLLCTRL2_REG (0x67) +#define AW882XX_PLLCTRL3_REG (0x68) +#define AW882XX_CDACTRL1_REG (0x69) +#define AW882XX_CDACTRL2_REG (0x6A) +#define AW882XX_SADCCTRL_REG (0x6B) +#define AW882XX_TESTCTRL1_REG (0x70) +#define AW882XX_TESTCTRL2_REG (0x71) +#define AW882XX_EFCTRL1_REG (0x72) +#define AW882XX_EFCTRL2_REG (0x73) +#define AW882XX_EFWH_REG (0x74) +#define AW882XX_EFWM2_REG (0x75) +#define AW882XX_EFWM1_REG (0x76) +#define AW882XX_EFWL_REG (0x77) +#define AW882XX_EFRH_REG (0x78) +#define AW882XX_EFRM2_REG (0x79) +#define AW882XX_EFRM1_REG (0x7A) +#define AW882XX_EFRL_REG (0x7B) +#define AW882XX_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW882XX_REG_MAX (0x7D) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw882xx_reg_access[AW882XX_REG_MAX] = { + [AW882XX_ID_REG] = (REG_RD_ACCESS), + [AW882XX_SYSST_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINT_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINTM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCST_REG] = (REG_RD_ACCESS), + [AW882XX_PRODID_REG] = (REG_RD_ACCESS), + [AW882XX_VBAT_REG] = (REG_RD_ACCESS), + [AW882XX_TEMP_REG] = (REG_RD_ACCESS), + [AW882XX_PVDD_REG] = (REG_RD_ACCESS), + [AW882XX_DBGCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SINT_REG] = (REG_RD_ACCESS), + [AW882XX_I2SCAPCNT_REG] = (REG_RD_ACCESS), + [AW882XX_CRCIN_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CRCOUT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_PWMCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PWMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SADCCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFRH_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM2_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM1_REG] = (REG_RD_ACCESS), + [AW882XX_EFRL_REG] = (REG_RD_ACCESS), + [AW882XX_TESTDET_REG] = (REG_RD_ACCESS), +}; + +/******************************************** + * Register Detail + *******************************************/ +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW882XX_IDCODE_START_BIT (0) +#define AW882XX_IDCODE_BITS_LEN (16) +#define AW882XX_IDCODE_MASK \ + (~(((1<= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_debug("%s port_id %d, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; +} +EXPORT_SYMBOL(aw_adm_param_enable); +#endif /** * adm_param_enable - diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 3ab1499c8e32..a48078ecbf4f 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -24,6 +24,13 @@ #include "adsp_err.h" #include "q6afecal-hwdep.h" +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX +#define AFE_MODULE_ID_AWDSP_TX (0x10013D00) +#define AFE_MODULE_ID_AWDSP_RX (0x10013D01) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) +#endif /* #ifdef CONFIG_SND_SOC_AWINIC_AW882XX */ #define WAKELOCK_TIMEOUT 5000 #define AFE_CLK_TOKEN 1024 #define AFE_NOWAIT_TOKEN 2048 @@ -275,6 +282,10 @@ struct afe_ctl { uint32_t cps_ch_mask; struct afe_cps_hw_intf_cfg *cps_config; int lsm_afe_ports[MAX_LSM_SESSIONS]; +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + struct rtac_cal_block_data aw_cal; + atomic_t aw_state; +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ }; struct afe_clkinfo_per_port { @@ -1117,7 +1128,19 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) payload, data->token); return -EINVAL; } - +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + if (atomic_read(&this_afe.aw_state) == 1) { + if (!payload[0]) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: status: %d", __func__, payload[0]); + atomic_set(&this_afe.state, -1); + } + atomic_set(&this_afe.aw_state, 0); + wake_up(&this_afe.wait[data->token]); + return 0; + } +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ if (rtac_make_afe_callback(data->payload, data->payload_size)) return 0; @@ -2511,6 +2534,15 @@ static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, case AFE_PARAM_ID_SP_V4_EX_VI_FTM_CFG: param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI; break; +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + case AFE_PARAM_ID_AWDSP_RX_SET_ENABLE: + case AFE_PARAM_ID_AWDSP_RX_PARAMS: + param_info.module_id = AFE_MODULE_ID_AWDSP_RX; + break; + case AFE_PARAM_ID_AWDSP_TX_SET_ENABLE: + param_info.module_id = AFE_MODULE_ID_AWDSP_TX; + break; +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ default: pr_err("%s: default case 0x%x\n", __func__, param_id); goto fail_cmd; @@ -3920,6 +3952,216 @@ done: return ret; } +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX +int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + + if (afe_spk_prot_prepare(rx_port_id, 0, + AFE_PARAM_ID_AWDSP_RX_SET_ENABLE, &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: set bypass failed \n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(aw_send_afe_rx_module_enable); +int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + + if (afe_spk_prot_prepare(tx_port_id, 0, + AFE_PARAM_ID_AWDSP_TX_SET_ENABLE, &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: set bypass failed \n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(aw_send_afe_tx_module_enable); + +int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write) +{ + int32_t result = 0; + uint32_t port_id = rx_port_id; + int32_t module_id = AFE_MODULE_ID_AWDSP_RX; + uint32_t port_index = 0; + uint32_t payload_size = 0; + size_t len; + struct rtac_cal_block_data *aw_cal = &(this_afe.aw_cal); + struct mem_mapping_hdr mem_hdr; + struct param_hdr_v3 param_hdr; + + pr_debug("%s: enter\n", __func__); + + if (param_id == AFE_PARAM_ID_AWDSP_TX_SET_ENABLE) { + port_id = tx_port_id; + module_id = AFE_MODULE_ID_AWDSP_TX; + } + + if (aw_cal->map_data.dma_buf == 0) { + /*Minimal chunk size is 4K*/ + aw_cal->map_data.map_size = SZ_4K; + result = msm_audio_ion_alloc(&(aw_cal->map_data.dma_buf), + aw_cal->map_data.map_size, + &(aw_cal->cal_data.paddr),&len, + &(aw_cal->cal_data.kvaddr)); + if (result < 0) { + pr_err("%s: allocate buffer failed! ret = %d\n", + __func__, result); + goto err; + } + } + + if (aw_cal->map_data.map_handle == 0) { + result = afe_map_rtac_block(aw_cal); + if (result < 0) { + pr_err("%s: map buffer failed! ret = %d\n", + __func__, result); + goto err; + } + } + + port_index = q6audio_get_port_index(port_id); + if (port_index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port = 0x%x\n", __func__, port_id); + goto err; + } + + if (cmd_size > (SZ_4K - sizeof(struct param_hdr_v3))) { + pr_err("%s: Invalid payload size = %d\n", __func__, cmd_size); + result = -EINVAL; + goto err; + } + + /* Pack message header with data */ + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_size = cmd_size; + + if (write) { + param_hdr.param_id = param_id; + q6common_pack_pp_params(aw_cal->cal_data.kvaddr, + ¶m_hdr, + buf, + &payload_size); + aw_cal->cal_data.size = payload_size; + } else { + param_hdr.param_id = param_id; + aw_cal->cal_data.size = cmd_size + sizeof(struct param_hdr_v3); + } + + /*Send/Get package to/from ADSP*/ + mem_hdr.data_payload_addr_lsw = + lower_32_bits(aw_cal->cal_data.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(aw_cal->cal_data.paddr); + mem_hdr.mem_map_handle = + aw_cal->map_data.map_handle; + + pr_debug("%s: Sending aw_cal port = 0x%x, cal size = %zd, cal addr = 0x%pK\n", + __func__, port_id, aw_cal->cal_data.size, &aw_cal->cal_data.paddr); + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, result); + goto err; + } + + if (write) { + if (q6common_is_instance_id_supported()) + result = q6afe_set_params_v3(port_id, port_index, &mem_hdr, NULL, payload_size); + else + result = q6afe_set_params_v2(port_id, port_index, &mem_hdr, NULL, payload_size); + } else { + int8_t *resp = (int8_t *)aw_cal->cal_data.kvaddr; + + atomic_set(&this_afe.aw_state, 1); + if (q6common_is_instance_id_supported()) { + result = q6afe_get_params_v3(port_id, port_index, &mem_hdr, ¶m_hdr); + resp += sizeof(struct param_hdr_v3); + } else { + result = q6afe_get_params_v2(port_id, port_index, &mem_hdr, ¶m_hdr); + resp += sizeof(struct param_hdr_v1); + } + + if (result) { + pr_err("%s: get response from port 0x%x failed %d\n", + __func__, port_id, result); + goto err; + } + else { + /*Copy response data to command buffer*/ + memcpy(buf, resp, cmd_size); + } + } +err: + return result; +} +EXPORT_SYMBOL(aw_send_afe_cal_apr); + +void aw_cal_unmap_memory(void) +{ + int result = 0; + + if (this_afe.aw_cal.map_data.map_handle) { + result = afe_unmap_rtac_block(&this_afe.aw_cal.map_data.map_handle); + + /*Force to remap after unmap failed*/ + if (result) + this_afe.aw_cal.map_data.map_handle = 0; + } +} +EXPORT_SYMBOL(aw_cal_unmap_memory); + +int aw_send_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + if (afe_spk_prot_prepare(rx_port_id, 0, + AFE_PARAM_ID_AWDSP_RX_SET_ENABLE, + &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: AW set rx bypass failed\n", + __func__); + } + return 0; +} +EXPORT_SYMBOL(aw_send_rx_module_enable); + +int aw_send_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + if (afe_spk_prot_prepare(tx_port_id, 0, + AFE_PARAM_ID_AWDSP_TX_SET_ENABLE, + &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: AW set tx module enable failed\n", + __func__); + } + return 0; +} +EXPORT_SYMBOL(aw_send_tx_module_enable); + +#endif + + static int afe_init_cdc_reg_config(void) { struct param_hdr_v3 param_hdr; @@ -12078,6 +12320,9 @@ void afe_exit(void) q6core_destroy_uevent_data(this_afe.uevent_data); +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + aw_cal_unmap_memory(); +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ afe_delete_cal_data(); config_debug_fs_exit(); From 73006e69d77de5f84f1c67d2cc6db87cb438c369 Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 30 Oct 2020 10:04:01 +0800 Subject: [PATCH 21/79] denver:add sec i2s routing 1.add sec i2s routing/ 2.update latest aw driver v0.1.17 3.add sec_mi2s_tx_hotless_dai_links for aw vi-feedback Change-Id: I33692eef95754428bf258014ea79cbb428f865f3 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/1789134 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Reviewed-by: Yuesheng Huang Submit-Approved: Jira Key --- asoc/codecs/aw882xx/aw882xx.c | 551 ++++++++++++++++++++++------------ asoc/codecs/aw882xx/aw882xx.h | 5 + asoc/holi.c | 26 +- asoc/msm-pcm-routing-v2.c | 21 ++ asoc/msm_holi_dailink.h | 5 + 5 files changed, 412 insertions(+), 196 deletions(-) diff --git a/asoc/codecs/aw882xx/aw882xx.c b/asoc/codecs/aw882xx/aw882xx.c index 6e6231d2382c..42b21533bd38 100644 --- a/asoc/codecs/aw882xx/aw882xx.c +++ b/asoc/codecs/aw882xx/aw882xx.c @@ -1,7 +1,7 @@ /* * aw882xx.c aw882xx codec module * - * Version: v0.1.12 + * Version: v0.1.17 * * keep same with AW882XX_VERSION * @@ -14,9 +14,6 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ - -#define DEBUG - #ifdef CONFIG_AW882XX_CODEC #include @@ -45,25 +42,6 @@ #include "aw882xx.h" #include "aw882xx_reg.h" - - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - - /****************************************************** * * Marco @@ -71,7 +49,7 @@ ******************************************************/ #define AW882XX_I2C_NAME "aw882xx_smartpa" -#define AW882XX_VERSION "v0.1.12" +#define AW882XX_VERSION "v0.1.17" #define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 #define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -86,6 +64,7 @@ #define CALI_BUF_MAX 100 +#define DELAY_TIME_MAX 300 #define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" #ifdef CONFIG_AW882XX_DSP @@ -116,12 +95,13 @@ static int aw_adm_param_enable(int port_id, int module_id, int param_id, int ena } #endif -//static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re); +static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re); static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re); static int aw882xx_load_profile_params(struct aw882xx *aw882xx); static int aw882xx_skt_set_dsp(int value); static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade); -static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in); +static void aw882xx_fade_in_out(struct aw882xx *aw882xx); +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value); /*monitor voltage and temperature table*/ static struct aw882xx_low_vol vol_down_table[] = { @@ -313,11 +293,15 @@ static int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, ******************************************************/ static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) { - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); if (mute) { - if (aw882xx->need_fade) - aw882xx_fade_in_out(aw882xx, false); + if (aw882xx->need_fade) { + aw882xx->is_fade_in = 0; + aw882xx->fade_work_start = 1; + cancel_delayed_work_sync(&aw882xx->fade_work); + aw882xx_fade_in_out(aw882xx); + } aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, AW882XX_HMUTE_MASK, AW882XX_HMUTE_ENABLE_VALUE); @@ -325,8 +309,13 @@ static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, AW882XX_HMUTE_MASK, AW882XX_HMUTE_DISABLE_VALUE); - if (aw882xx->need_fade) - aw882xx_fade_in_out(aw882xx, true); + if (aw882xx->need_fade) { + aw882xx->is_fade_in = 1; + aw882xx->fade_work_start = 1; + aw882xx_volume_set(aw882xx, VOLUME_MIN_NEG_90_DB); + schedule_delayed_work(&aw882xx->fade_work, + msecs_to_jiffies(aw882xx->delayed_time)); + } } } @@ -335,7 +324,7 @@ static bool aw882xx_get_power_status(struct aw882xx *aw882xx) { unsigned int reg_value = 0; int ret = 0; - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL_REG, ®_value); if (ret < 0) { @@ -353,7 +342,7 @@ static bool aw882xx_get_power_status(struct aw882xx *aw882xx) static void aw882xx_run_pwd(struct aw882xx *aw882xx, bool pwd) { - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); if (pwd) { aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, @@ -379,13 +368,13 @@ static int aw882xx_sysst_check(struct aw882xx *aw882xx) ret = 0; break; } else { - pr_err("%s: check fail, cnt=%d, reg_val=0x%04x\n", + pr_debug("%s: check fail, cnt=%d, reg_val=0x%04x\n", __func__, i, reg_val); msleep(2); } } if (ret < 0) - pr_err("%s: check fail\n", __func__); + pr_info("%s: check fail\n", __func__); return ret; } @@ -398,7 +387,7 @@ static int aw882xx_get_sysint(struct aw882xx *aw882xx, unsigned int *sysint) ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); if (ret < 0) - pr_err("%s: read sysint fail, ret=%d\n", __func__, ret); + pr_info("%s: read sysint fail, ret=%d\n", __func__, ret); else *sysint = reg_val; @@ -410,7 +399,7 @@ static int aw882xx_get_iis_status(struct aw882xx *aw882xx) int ret = -1; unsigned int reg_val = 0; - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); if (reg_val & AW882XX_PLLS_LOCKED_VALUE) @@ -474,7 +463,7 @@ static int aw882xx_set_vcalb(struct aw882xx *aw882xx) vcalb = AW882XX_VCAL_FACTOR * icalk / vcalk; reg_val = (unsigned int)vcalb; - pr_err("%s: icalk=%d, vcalk=%d, vcalb=%d, reg_val=%d\n", + pr_debug("%s: icalk=%d, vcalk=%d, vcalb=%d, reg_val=%d\n", __func__, icalk, vcalk, vcalb, reg_val); ret = aw882xx_i2c_write(aw882xx, AW882XX_VTMCTRL3_REG, reg_val); @@ -489,7 +478,7 @@ static void aw882xx_send_cali_re_to_dsp(struct aw882xx *aw882xx) ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->default_re, sizeof(int32_t), false); - pr_err("aw882xx: default_re:%d\n", aw882xx->default_re); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); } if ((aw882xx != NULL) && (aw882xx->cali_re != ERRO_CALI_VALUE)) { ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, @@ -504,21 +493,19 @@ static void aw882xx_start(struct aw882xx *aw882xx) { int ret = -1; uint32_t cali_re; - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); - - - //ret = aw882xx_get_cali_re_from_nv(&cali_re); - //if (ret < 0) { + ret = aw882xx_get_cali_re_from_nv(&cali_re); + if (ret < 0) { cali_re = ERRO_CALI_VALUE; pr_err("%s: use default vaule %d", __func__ , ERRO_CALI_VALUE); - //} - //ret = aw882xx_set_cali_re(aw882xx, cali_re); - //if (ret < 0) + } + ret = aw882xx_set_cali_re(aw882xx, cali_re); + if (ret < 0) pr_err("%s: set cali re failed: %d\n", __func__, ret); mutex_lock(&aw882xx->lock); - pr_err("%s: monitor is_enable %d,spk_rcv_mode %d\n", + pr_info("%s: monitor is_enable %d,spk_rcv_mode %d\n", __func__, aw882xx->monitor.is_enable, aw882xx->spk_rcv_mode); if (aw882xx->monitor.is_enable && (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE)) { @@ -534,7 +521,7 @@ static void aw882xx_start(struct aw882xx *aw882xx) aw882xx->profile.cur_profile = 0; } } - //aw882xx_send_cali_re_to_dsp(aw882xx); + aw882xx_send_cali_re_to_dsp(aw882xx); aw882xx_run_pwd(aw882xx, false); ret = aw882xx_sysst_check(aw882xx); @@ -556,7 +543,7 @@ static void aw882xx_start(struct aw882xx *aw882xx) static void aw882xx_stop(struct aw882xx *aw882xx) { - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); mutex_lock(&aw882xx->lock); aw882xx->power_flag = AW882XX_POWER_DOWN; @@ -575,7 +562,6 @@ static void aw882xx_stop(struct aw882xx *aw882xx) * aw882xx config * ******************************************************/ -#if 0 static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re) { /*custom add, if success return value is 0 , else -1*/ @@ -597,7 +583,7 @@ static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re) set_fs(KERNEL_DS); /*read file*/ - vfs_read(fp, buf, CALI_BUF_MAX - 1, &pos); + kernel_read(fp, buf, CALI_BUF_MAX - 1, &pos); /*get cali re value*/ if (sscanf(buf, "%d", &read_re) != 1) { @@ -612,12 +598,10 @@ static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re) filp_close(fp, NULL); *cali_re = read_re; - pr_err("%s: %d", __func__, read_re); + pr_info("%s: %d", __func__, read_re); return 0; } -#endif -#if 1 static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re) { if (aw882xx == NULL) @@ -625,7 +609,7 @@ static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re) aw882xx->cali_re = cali_re; return 0; } -#endif + static int aw882xx_reg_container_update(struct aw882xx *aw882xx, struct aw882xx_container *aw882xx_cont) { @@ -634,14 +618,14 @@ static int aw882xx_reg_container_update(struct aw882xx *aw882xx, int reg_val = 0; int ret = -1; - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); for (i = 0; i < aw882xx_cont->len; i += 4) { reg_addr = (aw882xx_cont->data[i+1]<<8) + aw882xx_cont->data[i+0]; reg_val = (aw882xx_cont->data[i+3]<<8) + aw882xx_cont->data[i+2]; - pr_err("%s: reg=0x%04x, val = 0x%04x\n", + pr_debug("%s: reg=0x%04x, val = 0x%04x\n", __func__, reg_addr, reg_val); ret = aw882xx_i2c_write(aw882xx, (unsigned char)reg_addr, @@ -650,7 +634,7 @@ static int aw882xx_reg_container_update(struct aw882xx *aw882xx, break; } - pr_err("%s: exit\n", __func__); + pr_debug("%s: exit\n", __func__); return ret; } @@ -668,7 +652,7 @@ static void aw882xx_reg_loaded(const struct firmware *cont, void *context) return; } - pr_err("%s: loaded %s - size: %zu\n", __func__, + pr_info("%s: loaded %s - size: %zu\n", __func__, aw882xx_cfg_name[aw882xx->cfg_num], cont ? cont->size : 0); aw882xx_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); @@ -687,25 +671,28 @@ static void aw882xx_reg_loaded(const struct firmware *cont, void *context) pr_err("%s: reg update fail\n", __func__); } else { pr_err("%s: reg update sucess\n", __func__); - aw882xx_run_mute(aw882xx, true); + //aw882xx_run_mute(aw882xx, true); + aw882xx_volume_set(aw882xx, VOLUME_MIN_NEG_90_DB); + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); aw882xx_set_vcalb(aw882xx); } mutex_unlock(&aw882xx->lock); kfree(aw882xx_cfg); if (aw882xx->afe_profile) { aw882xx_load_profile_params(aw882xx); - aw882xx->need_fade = 0; } + aw882xx->need_fade = 0; aw882xx_start(aw882xx); - - if (aw882xx->afe_profile) { + if(aw882xx->afe_profile || aw882xx->fade_flag) { aw882xx->need_fade = 1; } } static int aw882xx_load_reg(struct aw882xx *aw882xx) { - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, aw882xx_cfg_name[aw882xx->cfg_num], @@ -716,14 +703,14 @@ static int aw882xx_load_reg(struct aw882xx *aw882xx) static void aw882xx_get_cfg_shift(struct aw882xx *aw882xx) { aw882xx->cfg_num = aw882xx_mode_cfg_shift[aw882xx->spk_rcv_mode]; - pr_err("%s: cfg_num=%d\n", __func__, aw882xx->cfg_num); + pr_debug("%s: cfg_num=%d\n", __func__, aw882xx->cfg_num); } static void aw882xx_cold_start(struct aw882xx *aw882xx) { int ret = -1; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); aw882xx_get_cfg_shift(aw882xx); @@ -735,12 +722,12 @@ static void aw882xx_cold_start(struct aw882xx *aw882xx) static void aw882xx_smartpa_cfg(struct aw882xx *aw882xx, bool flag) { - pr_err("%s: flag = %d\n", __func__, flag); + pr_info("%s: flag = %d\n", __func__, flag); if (flag == true) { if ((aw882xx->init == AW882XX_INIT_ST) || (aw882xx->init == AW882XX_INIT_NG)) { - pr_err("%s: init = %d\n", __func__, aw882xx->init); + pr_info("%s: init = %d\n", __func__, aw882xx->init); aw882xx_cold_start(aw882xx); } else { aw882xx_start(aw882xx); @@ -853,7 +840,7 @@ static struct snd_kcontrol_new aw882xx_volume = { static int aw882xx_spk_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - pr_err("%s: aw882xx_spk_control=%d\n", + pr_debug("%s: aw882xx_spk_control=%d\n", __func__, aw882xx_spk_control); ucontrol->value.integer.value[0] = aw882xx_spk_control; return 0; @@ -865,7 +852,7 @@ static int aw882xx_spk_set(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", __func__, ucontrol->value.integer.value[0]); if (ucontrol->value.integer.value[0] == aw882xx_spk_control) @@ -884,7 +871,7 @@ static int aw882xx_spk_set(struct snd_kcontrol *kcontrol, static int aw882xx_rcv_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - pr_err("%s: aw882xx_rcv_control=%d\n", __func__, aw882xx_rcv_control); + pr_debug("%s: aw882xx_rcv_control=%d\n", __func__, aw882xx_rcv_control); ucontrol->value.integer.value[0] = aw882xx_rcv_control; return 0; } @@ -895,7 +882,7 @@ static int aw882xx_rcv_set(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", __func__, ucontrol->value.integer.value[0]); if (ucontrol->value.integer.value[0] == aw882xx_rcv_control) @@ -914,7 +901,7 @@ static int aw882xx_rcv_set(struct snd_kcontrol *kcontrol, static int aw882xx_algo_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - pr_err("%s: aw882xx_algo enable=%d\n", + pr_debug("%s: aw882xx_algo enable=%d\n", __func__, atomic_read(&g_algo_rx_enable)); ucontrol->value.integer.value[0] = atomic_read(&g_algo_rx_enable); @@ -928,7 +915,7 @@ static int aw882xx_algo_set(struct snd_kcontrol *kcontrol, int ret = -EINVAL; uint32_t ctrl_value = 0; - pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", __func__, ucontrol->value.integer.value[0]); ctrl_value = ucontrol->value.integer.value[0]; @@ -943,7 +930,7 @@ static int aw882xx_algo_set(struct snd_kcontrol *kcontrol, static int aw882xx_tx_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - pr_err("%s: aw882xx_tx_control=%d\n", __func__, + pr_debug("%s: aw882xx_tx_control=%d\n", __func__, atomic_read(&g_algo_tx_enable)); ucontrol->value.integer.value[0] = atomic_read(&g_algo_tx_enable); @@ -957,7 +944,7 @@ static int aw882xx_tx_set(struct snd_kcontrol *kcontrol, int ret = -EINVAL; uint32_t ctrl_value = 0; - pr_err("%s: ucontrol->value.integer.value[0]=%ld\n", + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", __func__, ucontrol->value.integer.value[0]); ctrl_value = ucontrol->value.integer.value[0]; @@ -974,7 +961,7 @@ static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - pr_err("%s: profile : %s \n", __func__, + pr_debug("%s: profile : %s \n", __func__, awinic_profile[aw882xx->profile.cur_profile]); if (aw882xx->afe_profile) { ucontrol->value.integer.value[0] = aw882xx->profile.cur_profile; @@ -989,7 +976,7 @@ static int aw882xx_profile_firmware_parse(struct aw882xx *aw882xx, const struct uint32_t param_size = 0; char *param_data = NULL; int i; - pr_err("%s: loaded %s - size: %zu\n", __func__, + pr_info("%s: loaded %s - size: %zu\n", __func__, awinic_profile_param_file_name[0], cont ? cont->size : 0); if (cont->size == 0) { @@ -1005,12 +992,12 @@ static int aw882xx_profile_firmware_parse(struct aw882xx *aw882xx, const struct /*output bin info*/ hdr = (aw_afe_params_hdr_t *)fw_data; - pr_err("%s: fw v_%d.%d.%d \n", __func__, + pr_info("%s: fw v_%d.%d.%d \n", __func__, hdr->fw[0], hdr->fw[1], hdr->fw[2]); - pr_err("%s: param v_%d.%d.%d \n", __func__, + pr_info("%s: param v_%d.%d.%d \n", __func__, hdr->cfg[0], hdr->cfg[1], hdr->cfg[2]); - pr_err("%s: project name : %s \n", __func__, hdr->project); - pr_err("%s: param len: %d", __func__, hdr->params_len); + pr_info("%s: project name : %s \n", __func__, hdr->project); + pr_info("%s: param len: %d", __func__, hdr->params_len); param_data = fw_data + sizeof(aw_afe_params_hdr_t); param_size = cont->size - sizeof(aw_afe_params_hdr_t); @@ -1077,7 +1064,7 @@ static int aw882xx_load_profile_params(struct aw882xx *aw882xx) { const struct firmware *fw = NULL; int ret; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); ret = request_firmware(&fw, awinic_profile_param_file_name[0], @@ -1096,25 +1083,30 @@ static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value) unsigned int reg_value = 0; unsigned int real_value = ((value / VOLUME_STEP_DB) << 4) + (value % VOLUME_STEP_DB) * 2; - if(real_value > aw882xx->cur_gain) { - /* cal real value */ - aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + if(real_value >= aw882xx->cur_gain) { real_value = (real_value << 8) | (reg_value & 0x00ff); - /* write value */ - aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); + } else { + real_value = (aw882xx->cur_gain << 8) | (reg_value & 0x00ff); } + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); } -static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) +static void aw882xx_fade_in_out(struct aw882xx *aw882xx) { int i = 0; + if(aw882xx == NULL) { + aw882xx->fade_work_start = 0; + return; + } //volume up - if (is_fade_in) { + if (aw882xx->is_fade_in) { i = VOLUME_MIN_NEG_90_DB; do { aw882xx_volume_set(aw882xx, i); - i -= FADE_STEP_DB; + i -= FADE_STEP_DB; //6 usleep_range(1400,1600); } while (i >= 0); } else { // volume down @@ -1124,6 +1116,24 @@ static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) usleep_range(1400,1600); } while (i <= VOLUME_MIN_NEG_90_DB); } + aw882xx->fade_work_start = 0; +} + +static void aw882xx_fade_work_func(struct work_struct *work) +{ + struct delayed_work *d_work; + struct aw882xx *aw882xx; + + d_work = to_delayed_work(work); + aw882xx = container_of(d_work, struct aw882xx, fade_work); + if(aw882xx == NULL) { + pr_debug("%s: can not get aw882xx from fade_work\n", __func__); + return; + } + pr_debug("%s: enter in: %d\n", __func__, aw882xx->is_fade_in); + + aw882xx_fade_in_out(aw882xx); + pr_debug("%s: exit\n", __func__); } static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade) @@ -1165,7 +1175,7 @@ static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profi } mutex_unlock(&aw882xx->profile.lock); - pr_err("%s: set params done", __func__); + pr_debug("%s: set params done", __func__); return 0; } @@ -1183,7 +1193,7 @@ static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, pr_err("%s: unsuport profile %d", __func__, ctl_value); return -EINVAL; } - pr_err("%s: profile switch to %s \n", + pr_debug("%s: profile switch to %s \n", __func__, awinic_profile[ctl_value]); mutex_lock(&aw882xx->lock); @@ -1210,7 +1220,7 @@ static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, static int aw882xx_skt_disable_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - pr_err("%s: aw882xx_skt_disable %d\n", + pr_debug("%s: aw882xx_skt_disable %d\n", __func__, atomic_read(&g_skt_disable)); ucontrol->value.integer.value[0] = atomic_read(&g_skt_disable); @@ -1231,7 +1241,7 @@ static int aw882xx_skt_set_dsp(int value) return -EINVAL; } - pr_err("%s: set skt %s", __func__, value == 1 ? "enable" : "disable"); + pr_info("%s: set skt %s", __func__, value == 1 ? "enable" : "disable"); return 0; } @@ -1240,7 +1250,7 @@ static int aw882xx_skt_disable_set(struct snd_kcontrol *kcontrol, { int value = ucontrol->value.integer.value[0]; - pr_err("%s: aw882xx_skt_disable %d \n", __func__, value); + pr_debug("%s: aw882xx_skt_disable %d \n", __func__, value); aw882xx_skt_set_dsp(!value); atomic_set(&g_skt_disable, value); @@ -1254,7 +1264,7 @@ static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - pr_err("%s: aw882xx_monitor_get %d\n", + pr_debug("%s: aw882xx_monitor_get %d\n", __func__, aw882xx->monitor.is_enable); ucontrol->value.integer.value[0] = aw882xx->monitor.is_enable; @@ -1270,7 +1280,7 @@ static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, int enable = ucontrol->value.integer.value[0]; - pr_err("%s: aw882xx_monitor_set %d \n", __func__, enable); + pr_debug("%s: aw882xx_monitor_set %d \n", __func__, enable); if (enable == aw882xx->monitor.is_enable) return 1; @@ -1282,11 +1292,39 @@ static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_fade_dealyed_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + + pr_debug("%s: aw882xx_fade_dealyed_time_get %d\n", + __func__, aw882xx->delayed_time); + + ucontrol->value.integer.value[0] = aw882xx->delayed_time; + + return 0; +} + +static int aw882xx_fade_dealyed_time_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + aw882xx->delayed_time = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_fade_dealyed_time_set %d ms \n", __func__, aw882xx->delayed_time); + return 0; +} + + #ifdef AW882XX_RUNIN_TEST static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - pr_err("%s: aw882xx_runin test %d\n", + pr_debug("%s: aw882xx_runin test %d\n", __func__, atomic_read(&g_runin_test)); ucontrol->value.integer.value[0] = atomic_read(&g_runin_test); @@ -1299,7 +1337,7 @@ static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, { int value = ucontrol->value.integer.value[0]; - pr_err("%s: aw882xx_runin_test %d \n", __func__, value); + pr_debug("%s: aw882xx_runin_test %d \n", __func__, value); if (value > 1 ) { value = 0; @@ -1323,7 +1361,7 @@ static void aw882xx_set_adsp_module_status(struct work_struct *work) /*set afe rx module*/ ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &set_value, sizeof(uint32_t)); if (ret) { - pr_err("%s: disable afe rx module %d falied , ret=%d\n", + pr_info("%s: disable afe rx module %d falied , ret=%d\n", __func__, set_value, ret); } @@ -1333,7 +1371,7 @@ static void aw882xx_set_adsp_module_status(struct work_struct *work) pr_err("%s: disable skt failed !\n", __func__); } - pr_err("%s: disable skt and afe module \n", __func__); + pr_info("%s: disable skt and afe module \n", __func__); } #endif @@ -1356,6 +1394,8 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_skt_disable_get, aw882xx_skt_disable_set), SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[1], aw882xx_monitor_get, aw882xx_monitor_set), + SOC_SINGLE_EXT("aw882xx_fade_dealyed_time", SND_SOC_NOPM, 0, DELAY_TIME_MAX, 0, + aw882xx_fade_dealyed_time_get, aw882xx_fade_dealyed_time_set), #ifdef AW882XX_RUNIN_TEST SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[0], aw882xx_runin_test_get, aw882xx_runin_test_set), @@ -1369,7 +1409,7 @@ static struct snd_kcontrol_new aw882xx_profile_controls[] = { static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) { - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); snd_soc_add_component_controls(aw882xx->component, aw882xx_controls, ARRAY_SIZE(aw882xx_controls)); @@ -1394,11 +1434,11 @@ static int aw882xx_startup(struct snd_pcm_substream *substream, struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - pr_err("%s: playback enter\n", __func__); + pr_info("%s: playback enter\n", __func__); /*aw882xx_run_pwd(aw882xx, false);*/ aw882xx->power_flag = AW882XX_POWER_ING; } else { - pr_err("%s: capture enter\n", __func__); + pr_info("%s: capture enter\n", __func__); } return 0; @@ -1409,15 +1449,15 @@ static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(dai->codec);*/ struct snd_soc_component *component = dai->component; - pr_err("%s: fmt=0x%x\n", __func__, fmt); + pr_info("%s: fmt=0x%x\n", __func__, fmt); /* Supported mode: regular I2S, slave, or PDM */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { -// dev_err(component->dev, "%s: invalid codec master mode\n", -// __func__); + dev_err(component->dev, "%s: invalid codec master mode\n", + __func__); return -EINVAL; } break; @@ -1434,7 +1474,7 @@ static int aw882xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec_dai->component); - pr_err("%s: freq=%d\n", __func__, freq); + pr_info("%s: freq=%d\n", __func__, freq); aw882xx->sysclk = freq; return 0; @@ -1452,13 +1492,13 @@ static int aw882xx_hw_params(struct snd_pcm_substream *substream, int width = 0; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - pr_err("%s: requested rate: %d, sample size: %d\n", + pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, rate, snd_pcm_format_width(params_format(params))); return 0; } /* get rate param */ aw882xx->rate = rate = params_rate(params); - pr_err("%s: requested rate: %d, sample size: %d\n", + pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, rate, snd_pcm_format_width(params_format(params))); /* match rate */ @@ -1508,7 +1548,7 @@ static int aw882xx_hw_params(struct snd_pcm_substream *substream, /* get bit width */ width = params_width(params); - pr_err("%s: width = %d\n", __func__, width); + pr_debug("%s: width = %d\n", __func__, width); switch (width) { case 16: reg_value = AW882XX_I2SFS_16_BITS_VALUE; @@ -1542,7 +1582,7 @@ static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) struct snd_soc_component *component = dai->component; struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); - pr_err("%s: mute state=%d\n", __func__, mute); + pr_info("%s: mute state=%d\n", __func__, mute); if (!(aw882xx->flags & AW882XX_FLAG_START_ON_MUTE)) return 0; @@ -1619,7 +1659,7 @@ static int aw882xx_probe(struct snd_soc_component *component) struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); int ret = -1; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); aw882xx->component = component; @@ -1628,7 +1668,7 @@ static int aw882xx_probe(struct snd_soc_component *component) //if (component->dev->of_node) //dev_set_name(component->dev, "%s", "aw882xx_smartpa"); - pr_err("%s: exit\n", __func__); + pr_info("%s: exit\n", __func__); ret = 0; return ret; @@ -1637,7 +1677,7 @@ static int aw882xx_probe(struct snd_soc_component *component) static void aw882xx_remove(struct snd_soc_component *component) { /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec);*/ - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); return; } @@ -1649,14 +1689,14 @@ static unsigned int aw882xx_codec_read(struct snd_soc_component *component, unsigned int value = 0; int ret = -1; - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); if (aw882xx_reg_access[reg] & REG_RD_ACCESS) { ret = aw882xx_i2c_read(aw882xx, reg, &value); if (ret < 0) - pr_err("%s: read register failed\n", __func__); + pr_debug("%s: read register failed\n", __func__); } else { - pr_err("%s: register 0x%x no read access\n", __func__, reg); + pr_debug("%s: register 0x%x no read access\n", __func__, reg); } return ret; } @@ -1667,42 +1707,21 @@ static int aw882xx_codec_write(struct snd_soc_component *component, int ret = -1; struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); - pr_err("%s: enter ,reg is 0x%x value is 0x%x\n", + pr_debug("%s: enter ,reg is 0x%x value is 0x%x\n", __func__, reg, value); if (aw882xx_reg_access[reg]®_WR_ACCESS) { ret = aw882xx_i2c_write(aw882xx, reg, value); } else { - pr_err("%s: register 0x%x no write access\n", + pr_debug("%s: register 0x%x no write access\n", __func__, reg); } return ret; } -static const struct snd_soc_dapm_widget aw882xx_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN("AW_DAI_RX", "Speaker_Playback", 0, 0, 0, 0), - SND_SOC_DAPM_AIF_OUT("AW_DAI_TX", "Speaker_Capture", 0, 0, 0, 0), - SND_SOC_DAPM_OUTPUT("SPK_OUT"), - SND_SOC_DAPM_INPUT("CAPTURE_IN"), -}; - -static const struct snd_soc_dapm_route aw882xx_audio_map[] = { - {"AW_DAI_RX", NULL, "Speaker_Playback"}, - {"Speaker_Capture", NULL, "AW_DAI_TX"}, - {"AW_DAI_TX", NULL, "CAPTURE_IN"}, - {"SPK_OUT", NULL, "AW_DAI_RX"}, -}; - - static struct snd_soc_component_driver soc_component_dev_aw882xx = { .probe = aw882xx_probe, - - .dapm_widgets = aw882xx_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aw882xx_dapm_widgets), - .dapm_routes = aw882xx_audio_map, - .num_dapm_routes = ARRAY_SIZE(aw882xx_audio_map), - .remove = aw882xx_remove, .read = aw882xx_codec_read, .write = aw882xx_codec_write, @@ -1719,7 +1738,7 @@ static void aw882xx_interrupt_setup(struct aw882xx *aw882xx) { unsigned int reg_val; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); reg_val &= (~AW882XX_UVLS_VDD_BELOW_2P8V_VALUE); @@ -1732,27 +1751,27 @@ static void aw882xx_interrupt_clear(struct aw882xx *aw882xx) { unsigned int reg_val = 0; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); - pr_err("%s: reg SYSST=0x%x\n", __func__, reg_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); - pr_err("%s: reg SYSINT=0x%x\n", __func__, reg_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); - pr_err("%s: reg SYSINTM=0x%x\n", __func__, reg_val); + pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val); } static irqreturn_t aw882xx_irq(int irq, void *data) { struct aw882xx *aw882xx = data; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); aw882xx_interrupt_clear(aw882xx); - pr_err("%s: exit\n", __func__); + pr_info("%s: exit\n", __func__); return IRQ_HANDLED; } @@ -1965,6 +1984,15 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, __func__, aw882xx->afe_profile); } + ret = of_property_read_u32(np, "fade-flag", &aw882xx->fade_flag); + if (ret) { + aw882xx->fade_flag = 0; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + /*get low vol table cfg*/ ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "up", &monitor->vol_up_table, &monitor->vol_up_num); @@ -2070,7 +2098,7 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, int aw882xx_hw_reset(struct aw882xx *aw882xx) { - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { gpio_set_value_cansleep(aw882xx->reset_gpio, 0); @@ -2104,15 +2132,15 @@ int aw882xx_read_chipid(struct aw882xx *aw882xx) } switch (reg) { case AW882XX_ID: - pr_err("%s: aw882xx detected\n", __func__); + pr_info("%s: aw882xx detected\n", __func__); aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; aw882xx->flags |= AW882XX_FLAG_START_ON_MUTE; aw882xx->chipid = AW882XX_ID; - pr_err("%s: aw882xx->flags=0x%x\n", + pr_info("%s: aw882xx->flags=0x%x\n", __func__, aw882xx->flags); return 0; default: - pr_err("%s: unsupported device revision (0x%x)\n", + pr_info("%s: unsupported device revision (0x%x)\n", __func__, reg); break; } @@ -2286,8 +2314,7 @@ static ssize_t aw882xx_mec_store(struct device *dev, if (ret < 0) return ret; - - pr_err("%s: mec_ctr=%d\n", __func__, mec_ctr); + pr_info("%s: mec_ctr=%d\n", __func__, mec_ctr); ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, param_id, &mec_ctr, sizeof(uint32_t), true); @@ -2296,8 +2323,6 @@ static ssize_t aw882xx_mec_store(struct device *dev, mdelay(2); - - return count; } @@ -2358,6 +2383,54 @@ static ssize_t aw882xx_default_re_show(struct device *dev, return len; } +static ssize_t aw882xx_spk_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int ret = -1; + int16_t data_len = _IOC_SIZE(AW882XX_IOCTL_GET_CALI_DATA); + int32_t spk_temp = 0; + char *data_ptr = NULL; + unsigned int reg_value = 0; + + if(aw882xx == NULL) { + goto fail_temp; + } + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + goto fail_temp; + } + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK\n", __func__); + goto exit_temp; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, + aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + goto exit_temp; + } + + memcpy(&spk_temp, data_ptr + sizeof(int32_t), sizeof(int32_t)); + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_temp: %d\n", spk_temp); + return len; + +exit_temp: + kfree(data_ptr); +fail_temp: + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_temp: fail\n"); + return len; +} + #ifdef AW_DEBUG static ssize_t aw882xx_vol_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -2373,7 +2446,7 @@ static ssize_t aw882xx_vol_store(struct device *dev, if (ret < 0) return ret; - pr_err("%s: vol set =%d\n", __func__, vol); + pr_info("%s: vol set =%d\n", __func__, vol); aw882xx->monitor.test_vol = vol; return count; @@ -2404,7 +2477,7 @@ static ssize_t aw882xx_temp_store(struct device *dev, if (ret < 0) return ret; - pr_err("%s: temp set =%d\n", __func__, temp); + pr_info("%s: temp set =%d\n", __func__, temp); aw882xx->monitor.test_temp = temp; return count; @@ -2428,6 +2501,8 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, int16_t data_len = _IOC_SIZE(cmd); int ret = 0; char *data_ptr = NULL; + struct ptr_params_data *p_params; + int32_t *p_data; data_ptr = kmalloc(data_len, GFP_KERNEL); @@ -2436,7 +2511,7 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, return -EFAULT; } - pr_err("cmd : %d, data_len%d\n", cmd , data_len); + pr_info("cmd : %d, data_len%d\n", cmd , data_len); switch (cmd) { case AW882XX_IOCTL_ENABLE_CALI: { if (copy_from_user(data_ptr, @@ -2445,7 +2520,7 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, goto exit; } g_aw882xx_cali_flag = (int8_t)data_ptr[0]; - pr_err("%s:set cali %s", __func__, + pr_info("%s:set cali %s", __func__, (g_aw882xx_cali_flag == 0) ? ("disable") : ("enable")); } break; case AW882XX_IOCTL_SET_CALI_CFG: { @@ -2537,7 +2612,7 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, if(aw882xx->default_re == 0) { ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->default_re, sizeof(int32_t), false); - pr_err("aw882xx: default_re:%d\n", aw882xx->default_re); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); } ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, AFE_PARAM_ID_AWDSP_RX_RE_L, @@ -2556,7 +2631,7 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, data_ptr, data_len, false); if(aw882xx->default_re == 0) { aw882xx->default_re = *((uint32_t *)data_ptr); - pr_err("aw882xx: default_re:%d\n", aw882xx->default_re); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); } if (ret) { pr_err("%s: dsp_msg_read error: 0x%x\n", @@ -2617,7 +2692,48 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, ret = -EFAULT; goto exit; } - pr_err("%s: set params done", __func__); + pr_debug("%s: set params done", __func__); + } break; + case AW882XX_IOCTL_SET_PTR_PARAM_NUM: { + if (copy_from_user(data_ptr, (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + p_params = (struct ptr_params_data *)data_ptr; + if (p_params->data == NULL || (!p_params->len)) { + pr_err("%s: p_params error\n", __func__); + ret = -EFAULT; + goto exit; + } + p_data = kzalloc(p_params->len, GFP_KERNEL); + if (!p_data) { + pr_err("%s: error allocating memory\n", __func__); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(p_data, (void __user *)p_params->data, + p_params->len)) { + kfree(p_data); + p_data = NULL; + ret = -EFAULT; + goto exit; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, + aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_PARAMS, + p_data, p_params->len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + kfree(p_data); + p_data = NULL; + ret =-EFAULT; + goto exit; + } + kfree(p_data); + p_data = NULL; } break; default: { pr_err("%s : cmd %d\n", __func__, cmd); @@ -2640,6 +2756,8 @@ static DEVICE_ATTR(mec, S_IWUSR | S_IRUGO, aw882xx_mec_show, aw882xx_mec_store); static DEVICE_ATTR(default_re, S_IWUSR | S_IRUGO, aw882xx_default_re_show, NULL); +static DEVICE_ATTR(spk_temp, S_IWUSR | S_IRUGO, + aw882xx_spk_temp_show, NULL); #ifdef AW_DEBUG static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, aw882xx_vol_show, aw882xx_vol_store); @@ -2654,6 +2772,7 @@ static struct attribute *aw882xx_attributes[] = { &dev_attr_spk_rcv.attr, &dev_attr_mec.attr, &dev_attr_default_re.attr, + &dev_attr_spk_temp.attr, #ifdef AW_DEBUG &dev_attr_vol.attr, &dev_attr_temp.attr, @@ -2672,7 +2791,7 @@ static int aw882xx_file_open(struct inode *inode, struct file *file) return -ENODEV; file->private_data = (void *)g_aw882xx; - pr_err("open success"); + pr_debug("open success"); return 0; } @@ -2680,7 +2799,7 @@ static int aw882xx_file_release(struct inode *inode, struct file *file) { file->private_data = (void *)NULL; - pr_err("release successi\n"); + pr_debug("release successi\n"); return 0; } @@ -2724,16 +2843,16 @@ void init_aw882xx_misc_driver(struct aw882xx *aw882xx) pr_err("%s: misc register fail: %d\n", __func__, ret); return; } - pr_err("%s: misc register success", __func__); + pr_debug("%s: misc register success", __func__); } /*monitor*/ static int aw882xx_monitor_start(struct aw882xx_monitor *monitor) { - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); if (!hrtimer_active(&monitor->timer)) { - pr_err("%s: start monitor\n", __func__); + pr_info("%s: start monitor\n", __func__); hrtimer_start(&monitor->timer, ktime_set(monitor->timer_val/1000, (monitor->timer_val%1000)*1000000), HRTIMER_MODE_REL); @@ -2744,10 +2863,10 @@ static int aw882xx_monitor_start(struct aw882xx_monitor *monitor) static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor) { - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); if (hrtimer_active(&monitor->timer)) { - pr_err("%s: stop monitor\n", __func__); + pr_info("%s: stop monitor\n", __func__); hrtimer_cancel(&monitor->timer); } return 0; @@ -2759,7 +2878,7 @@ static enum hrtimer_restart struct aw882xx_monitor *monitor = container_of(timer, struct aw882xx_monitor, timer); - pr_err("%s : enter\n", __func__); + pr_debug("%s : enter\n", __func__); if (monitor->is_enable) schedule_work(&monitor->work); @@ -2793,7 +2912,7 @@ static int aw882xx_monitor_get_voltage(struct aw882xx *aw882xx, local_vol) / AW_VOL_COUNT; } aw882xx->monitor.pre_vol = *vol; - pr_err("%s: cur voltage is %d, vol is %d\n", __func__, local_vol, *vol); + pr_debug("%s: cur voltage is %d, vol is %d\n", __func__, local_vol, *vol); return 0; } static int aw882xx_monitor_voltage(struct aw882xx *aw882xx, @@ -2872,7 +2991,7 @@ static int aw882xx_monitor_get_temperature(struct aw882xx *aw882xx, int *temp) local_temp = local_temp | AW882XX_MONITOR_TEMP_NEG_MASK; *temp = (int)local_temp; - pr_err("%s: chip temperature = %d\n", __func__, local_temp); + pr_debug("%s: chip temperature = %d\n", __func__, local_temp); return 0; } @@ -2983,7 +3102,7 @@ static void aw882xx_monitor_set_ipeak(struct aw882xx *aw882xx, uint8_t ipeak) read_reg_val &= AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK; if (read_reg_val == ipeak) { - pr_err("%s: ipeak = 0x%x, no change\n", + pr_debug("%s: ipeak = 0x%x, no change\n", __func__, read_reg_val); return; } @@ -2996,7 +3115,7 @@ static void aw882xx_monitor_set_ipeak(struct aw882xx *aw882xx, uint8_t ipeak) pr_err("%s: write ipeak failed\n", __func__); return; } - pr_err("%s: set reg val = 0x%x, ipeak = 0x%x\n", + pr_debug("%s: set reg val = 0x%x, ipeak = 0x%x\n", __func__, reg_val, ipeak); } static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) @@ -3005,7 +3124,11 @@ static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) unsigned int read_reg_val; int ret; - if (gain == GAIN_NONE) + if(aw882xx == NULL) { + return; + } + + if (gain == GAIN_NONE || (aw882xx->fade_work_start == 1)) return; ret = aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); @@ -3017,7 +3140,7 @@ static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) read_reg_val = reg_val >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; aw882xx->cur_gain = gain; if (read_reg_val == gain) { - pr_err("%s: gain = 0x%x, no change\n", + pr_debug("%s: gain = 0x%x, no change\n", __func__, read_reg_val); return; } @@ -3030,7 +3153,7 @@ static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) pr_err("%s: write gain failed\n", __func__); return; } - pr_err("%s: set reg val = 0x%x, gain = 0x%x\n", + pr_debug("%s: set reg val = 0x%x, gain = 0x%x\n", __func__, reg_val, gain); } static void aw882xx_monitor_set_vmax(struct aw882xx *aw882xx, uint32_t vmax) @@ -3048,7 +3171,7 @@ static void aw882xx_monitor_set_vmax(struct aw882xx *aw882xx, uint32_t vmax) pr_err("%s: dsp_msg_write error: 0x%x\n", __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); - pr_err("%s: set vmax = 0x%x\n", __func__, vmax); + pr_debug("%s: set vmax = 0x%x\n", __func__, vmax); } static void aw882xx_monitor_work(struct aw882xx *aw882xx) { @@ -3062,7 +3185,7 @@ static void aw882xx_monitor_work(struct aw882xx *aw882xx) return; } if (g_aw882xx_cali_flag != 0 || g_aw882xx_profile_flag != 0) { - pr_err("%s: done nothing while start cali", __func__); + pr_info("%s: done nothing while start cali", __func__); return; } @@ -3077,9 +3200,9 @@ static void aw882xx_monitor_work(struct aw882xx *aw882xx) pr_err("%s: monitor temperature failed\n", __func__); return; } - pr_err("%s: vol: ipeak = 0x%x, gain = 0x%x\n", + pr_debug("%s: vol: ipeak = 0x%x, gain = 0x%x\n", __func__, vol_cfg->ipeak, vol_cfg->gain); - pr_err("%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + pr_debug("%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", __func__, temp_cfg->ipeak, temp_cfg->gain, temp_cfg->vmax); memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); @@ -3093,12 +3216,42 @@ static void aw882xx_monitor_work(struct aw882xx *aw882xx) aw882xx_monitor_set_vmax(aw882xx, set_cfg.vmax); } +static void aw882xx_monitor_init_gain(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol *vol_cfg = &aw882xx->monitor.vol_cfg; + struct aw882xx_low_temp *temp_cfg = &aw882xx->monitor.temp_cfg; + struct aw882xx_low_temp set_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + + memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); + + aw882xx_monitor_get_cfg(&set_cfg, vol_cfg); + aw882xx->cur_gain = set_cfg.gain; +} + static int aw882xx_get_hmute(struct aw882xx *aw882xx) { unsigned int reg_val = 0; int ret; - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); if ((~AW882XX_HMUTE_MASK) & reg_val) @@ -3115,7 +3268,7 @@ static void aw882xx_monitor_work_func(struct work_struct *work) struct aw882xx *aw882xx = container_of(monitor, struct aw882xx, monitor); - pr_err("%s: enter\n", __func__); + pr_debug("%s: enter\n", __func__); mutex_lock(&aw882xx->lock); if (!aw882xx_get_hmute(aw882xx)) { aw882xx_monitor_work(aw882xx); @@ -3126,7 +3279,7 @@ static void aw882xx_monitor_work_func(struct work_struct *work) void init_aw882xx_monitor(struct aw882xx_monitor *monitor) { - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); hrtimer_init(&monitor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); monitor->timer.function = aw882xx_monitor_timer_func; INIT_WORK(&monitor->work, aw882xx_monitor_work_func); @@ -3137,7 +3290,6 @@ void init_aw882xx_monitor(struct aw882xx_monitor *monitor) monitor->temp_cfg.ipeak = monitor->temp_up_table[0].ipeak; monitor->temp_cfg.gain = monitor->temp_up_table[0].gain; monitor->temp_cfg.vmax = monitor->temp_up_table[0].vmax; - #ifdef AW_DEBUG monitor->test_vol = 0; monitor->test_temp = 0; @@ -3159,7 +3311,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, int ret = -1; int i; - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { dev_err(&i2c->dev, "check_functionality failed\n"); @@ -3234,7 +3386,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, goto err_dai_kzalloc; memcpy(dai, aw882xx_dai, sizeof(aw882xx_dai)); - pr_err("%s: dai->name(%s)\n", __func__, dai->name); + pr_info("%s: dai->name(%s)\n", __func__, dai->name); ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_aw882xx, dai, ARRAY_SIZE(aw882xx_dai)); @@ -3275,12 +3427,16 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, } init_aw882xx_monitor(&aw882xx->monitor); - + aw882xx_monitor_init_gain(aw882xx); init_aw882xx_misc_driver(aw882xx); g_aw882xx = aw882xx; aw882xx->default_re = 0; aw882xx->need_fade = 0; + aw882xx->fade_work_start = 0; + aw882xx->delayed_time = 0; + aw882xx->is_fade_in = 0; + /*init profile*/ mutex_init(&aw882xx->profile.lock); aw882xx->profile.cur_profile = 0; @@ -3290,6 +3446,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, aw882xx->profile.data[i] = NULL; } + INIT_DELAYED_WORK(&aw882xx->fade_work, aw882xx_fade_work_func); /*global init*/ atomic_set(&g_algo_rx_enable, 0); atomic_set(&g_algo_tx_enable, 0); @@ -3298,7 +3455,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, atomic_set(&g_runin_test, 0); INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); #endif - pr_err("%s: probe completed successfully!\n", __func__); + pr_info("%s: probe completed successfully!\n", __func__); return 0; @@ -3328,7 +3485,7 @@ static int aw882xx_i2c_remove(struct i2c_client *i2c) { struct aw882xx *aw882xx = i2c_get_clientdata(i2c); - pr_err("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); misc_deregister(&aw882xx_misc); if (gpio_to_irq(aw882xx->irq_gpio)) devm_free_irq(&i2c->dev, @@ -3370,12 +3527,11 @@ static struct i2c_driver aw882xx_i2c_driver = { .id_table = aw882xx_i2c_id, }; - static int __init aw882xx_i2c_init(void) { int ret = -1; - pr_err("%s: aw882xx driver version %s\n", __func__, AW882XX_VERSION); + pr_info("%s: aw882xx driver version %s\n", __func__, AW882XX_VERSION); ret = i2c_add_driver(&aw882xx_i2c_driver); if (ret) @@ -3383,6 +3539,11 @@ static int __init aw882xx_i2c_init(void) return ret; } + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + module_init(aw882xx_i2c_init); diff --git a/asoc/codecs/aw882xx/aw882xx.h b/asoc/codecs/aw882xx/aw882xx.h index b5941cf8d7dd..f8e1cb28db24 100644 --- a/asoc/codecs/aw882xx/aw882xx.h +++ b/asoc/codecs/aw882xx/aw882xx.h @@ -246,6 +246,10 @@ struct aw882xx { #ifdef AW882XX_RUNIN_TEST struct delayed_work adsp_status; #endif + struct delayed_work fade_work; + int delayed_time; + int fade_work_start; + int is_fade_in; int sysclk; int rate; int pstream; @@ -269,6 +273,7 @@ struct aw882xx { unsigned int afe_rx_portid; unsigned int afe_tx_portid; unsigned int afe_profile; + unsigned int fade_flag; struct profile_info profile; }; diff --git a/asoc/holi.c b/asoc/holi.c index 5cf2b1ad56f5..4126b65455a5 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -5214,6 +5214,21 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { }, }; +static struct snd_soc_dai_link msm_sec_mi2s_tx_hotless_dai_links[] = { + { + .name = "Secondary MI2S_TX Hostless", + .stream_name = "Secondary MI2S_TX Hostless Capture", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + SND_SOC_DAILINK_REG(sec_mi2s_tx_hostless), + }, +}; + static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { {/* hw:x,33 */ .name = MSM_DAILINK_NAME(ASM Loopback), @@ -5923,7 +5938,8 @@ static struct snd_soc_dai_link msm_holi_dai_links[ ARRAY_SIZE(msm_rx_tx_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link) + - ARRAY_SIZE(msm_wcn_btfm_be_dai_links)]; + ARRAY_SIZE(msm_wcn_btfm_be_dai_links) + + ARRAY_SIZE(msm_sec_mi2s_tx_hotless_dai_links)]; static int msm_populate_dai_link_component_of_node( struct snd_soc_card *card) @@ -6272,6 +6288,14 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) ARRAY_SIZE(msm_wcn_btfm_be_dai_links); } } + + if (mi2s_audio_intf) { + memcpy(msm_holi_dai_links + total_links, + msm_sec_mi2s_tx_hotless_dai_links, + sizeof(msm_sec_mi2s_tx_hotless_dai_links)); + total_links += ARRAY_SIZE(msm_sec_mi2s_tx_hotless_dai_links); + } + dailink = msm_holi_dai_links; } else if (!strcmp(match->data, "stub_codec")) { card = &snd_soc_card_stub_msm; diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 053262d07c87..7e42abcc6928 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -33273,6 +33273,10 @@ static const char * const mi2s_rx_vi_fb_tx_mux_text[] = { "ZERO", "SENARY_TX" }; +static const char * const sec_mi2s_rx_vi_fb_tx_mux_text[] = { + "ZERO", "SEC_MI2S_TX" +}; + static const char * const int4_mi2s_rx_vi_fb_tx_mono_mux_text[] = { "ZERO", "INT5_MI2S_TX" }; @@ -33305,6 +33309,9 @@ static const int wsa_rx_0_vi_fb_tx_rch_value[] = { MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0 }; +static const int sec_mi2s_rx_vi_fb_tx_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SECONDARY_MI2S_TX +}; static const int mi2s_rx_vi_fb_tx_value[] = { MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SENARY_MI2S_TX @@ -33353,6 +33360,11 @@ static const struct soc_enum mi2s_rx_vi_fb_mux_enum = ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text), mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value); +static const struct soc_enum sec_mi2s_rx_vi_fb_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SEC_I2S_RX, 0, 0, + ARRAY_SIZE(sec_mi2s_rx_vi_fb_tx_mux_text), + sec_mi2s_rx_vi_fb_tx_mux_text, sec_mi2s_rx_vi_fb_tx_value); + static const struct soc_enum int4_mi2s_rx_vi_fb_mono_ch_mux_enum = SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0, ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_mono_mux_text), @@ -33400,6 +33412,11 @@ static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux = mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, spkr_prot_put_vi_lch_port); +static const struct snd_kcontrol_new sec_mi2s_rx_vi_fb_mux = + SOC_DAPM_ENUM_EXT("SEC_MI2S_RX_VI_FB_MUX", + sec_mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_mono_ch_mux = SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", int4_mi2s_rx_vi_fb_mono_ch_mux_enum, spkr_prot_get_vi_lch_port, @@ -34401,6 +34418,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets_mi2s[] = { ARRAY_SIZE(int4_mi2s_rx_port_mixer_controls)), SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, &mi2s_rx_vi_fb_mux), + SND_SOC_DAPM_MUX("SEC_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, + &sec_mi2s_rx_vi_fb_mux), SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", SND_SOC_NOPM, 0, 0, &int4_mi2s_rx_vi_fb_mono_ch_mux), SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", SND_SOC_NOPM, 0, 0, @@ -42083,9 +42102,11 @@ static const struct snd_soc_dapm_route intercon_mi2s[] = { {"SENARY_MI2S_TX", NULL, "BE_IN"}, {"PRI_MI2S_RX_VI_FB_MUX", "SENARY_TX", "SENARY_TX"}, + {"SEC_MI2S_RX_VI_FB_MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"INT4_MI2S_RX_VI_FB_MONO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, {"INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_VI_FB_MUX"}, {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_MONO_CH_MUX"}, {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_STEREO_CH_MUX"}, }; diff --git a/asoc/msm_holi_dailink.h b/asoc/msm_holi_dailink.h index dd2290347b0c..7ac3e51891fe 100644 --- a/asoc/msm_holi_dailink.h +++ b/asoc/msm_holi_dailink.h @@ -481,3 +481,8 @@ SND_SOC_DAILINK_DEFS(proxy_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-dev.8194")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); + +SND_SOC_DAILINK_DEFS(sec_mi2s_tx_hostless, + DAILINK_COMP_ARRAY(COMP_CPU("SEC_MI2S_TX_HOSTLESS")), + DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-hostless"))); From 9a5ed361746820bdcddbe97779a40c32e66e904e Mon Sep 17 00:00:00 2001 From: liaohj Date: Tue, 22 Dec 2020 11:53:26 +0800 Subject: [PATCH 22/79] ASoc:sm4350(holi):fix VOL_UP and media buttons trigger assistant propagated from (CR) Google defines impedance range 110-180 Ohms for VOICECOMMAND key. In qcom implementation the actual range that will trigger assitant is 90-195 Ohms. Some headsets and selfie sticks users are reporting assistant being triggered incorrectly. Since a dedicated assistant button is a much more rare usecase, we fix this issue by reducing the range associated with VOICECOMMAND from 90-195 to 110-180 to match CDD without any extra tolerance. With this change range 0-90 will trigger MEDIA key and 180-325 will trigger VOLUME_UP key Change-Id: I9c2330b16af81f74ccf6b85ed67fa6c8a57242a3 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/1834281 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/holi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/holi.c b/asoc/holi.c index 4126b65455a5..4297fe03cee8 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -4758,8 +4758,8 @@ static void *def_wcd_mbhc_cal(void) btn_high = ((void *)&btn_cfg->_v_btn_low) + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); - btn_high[0] = 75; - btn_high[1] = 150; + btn_high[0] = 88; + btn_high[1] = 138; btn_high[2] = 237; btn_high[3] = 500; btn_high[4] = 500; From 57f297e0ba53545b13f809527d938ad96f4e17a0 Mon Sep 17 00:00:00 2001 From: zhouwl Date: Thu, 31 Dec 2020 18:40:49 +0800 Subject: [PATCH 23/79] ASoC: add some necessary mbhc logs for debugging MBHC is Qcom headset detection, add some necessary logs in order to help debug. Change-Id: Idd6a106c6abaab6d13df1dda00536e9e3fefe9f2 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/1840645 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/wcd-mbhc-v2.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 61b1eaf16a1c..761d541b7350 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -604,7 +604,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->zl = mbhc->zr = 0; - pr_debug("%s: Reporting removal %d(%x)\n", __func__, + pr_info("%s: Reporting removal %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status, WCD_MBHC_JACK_MASK); @@ -647,7 +647,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->zl = mbhc->zr = 0; if (!mbhc->force_linein) { - pr_debug("%s: Reporting removal (%x)\n", + pr_info("%s: Reporting removal (%x)\n", __func__, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, 0, WCD_MBHC_JACK_MASK); @@ -711,6 +711,8 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, &mbhc->zl, &mbhc->zr); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); + pr_info("%s,compute impedance,zl = %d,zr = %d\n", + __func__, mbhc->zl, mbhc->zr); if ((mbhc->zl > mbhc->mbhc_cfg->linein_th) && (mbhc->zr > mbhc->mbhc_cfg->linein_th) && (jack_type == SND_JACK_HEADPHONE)) { @@ -731,7 +733,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_status, WCD_MBHC_JACK_MASK); } - pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n", + pr_info("%s: Marking jack type as SND_JACK_LINEOUT\n", __func__); } } @@ -766,7 +768,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->mbhc_cb->mbhc_micb_ramp_control) mbhc->mbhc_cb->mbhc_micb_ramp_control(component, false); - pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + pr_info("%s: Reporting insertion %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, (mbhc->hph_status | SND_JACK_MECHANICAL), @@ -789,7 +791,7 @@ void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) else pr_info("%s: hs_detect_plug work not cancelled\n", __func__); - pr_debug("%s: Report extension cable\n", __func__); + pr_info("%s: Report extension cable\n", __func__); wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); /* * If PA is enabled HPHL schmitt trigger can @@ -1096,7 +1098,7 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) int r = IRQ_HANDLED; struct wcd_mbhc *mbhc = data; - pr_debug("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); if (mbhc == NULL) { pr_err("%s: NULL irq data\n", __func__); return IRQ_NONE; @@ -1279,7 +1281,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { ret = wcd_cancel_btn_work(mbhc); if (ret == 0) { - pr_debug("%s: Reporting long button release event\n", + pr_info("%s: Reporting long button release event\n", __func__); wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0, mbhc->buttons_pressed); @@ -1288,7 +1290,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) pr_debug("%s: Switch irq kicked in, ignore\n", __func__); } else { - pr_debug("%s: Reporting btn press\n", + pr_info("%s: Reporting btn press\n", __func__); wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, From 0350528d20fc13f738fa6c968aa65b12631ca9ac Mon Sep 17 00:00:00 2001 From: zhouwl Date: Wed, 6 Jan 2021 15:54:03 +0800 Subject: [PATCH 24/79] ASoC: FAI only can process mono channel Qcom fluence AI algorithm only can process mono TX data, In USB-C headset VoIP scenario, adm_open transfer stereo channel to adsp, FAI can't work correctly, it will cause USB-C uplink sound lost. Change-Id: Ic3ba7236806f2eb8701d632f4144c62e521dd785 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/1844487 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yuesheng Huang Submit-Approved: Jira Key --- dsp/q6adm.c | 3 ++- include/dsp/apr_audio-v2.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 79587f3909a5..8145d3d40f73 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -3512,7 +3512,8 @@ int adm_open_v2(int port_id, int path, int rate, int channel_mode, int topology, if (topology == VPM_TX_VOICE_SMECNS_V2_COPP_TOPOLOGY || topology == VPM_TX_VOICE_FLUENCE_SM_COPP_TOPOLOGY || topology == VPM_TX_VOICE_FLUENCE_NN_COPP_TOPOLOGY || - topology == AUDIO_RX_MONO_VOIP_COPP_TOPOLOGY) + topology == AUDIO_RX_MONO_VOIP_COPP_TOPOLOGY || + topology == VOICE_TX_FNN_COPP_TOPOLOGY) channel_mode = 1; /* diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index ee0bfd5f2f23..3b2b5dde6870 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -5798,6 +5798,7 @@ struct afe_param_id_lpass_core_shared_clk_cfg { #define VPM_TX_DM_FLUENCE_EF_COPP_TOPOLOGY 0x10000005 #define AUDIO_RX_MONO_VOIP_COPP_TOPOLOGY 0x11000101 #define VPM_TX_VOICE_FLUENCE_NN_COPP_TOPOLOGY 0x10028008 +#define VOICE_TX_FNN_COPP_TOPOLOGY 0x10010B0F /* Memory map regions command payload used by the * #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS From 131ac575c69db7b300dfed5b425103751d2131dc Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 9 Apr 2021 10:29:13 +0800 Subject: [PATCH 25/79] mbhc: set the type to lineout when zl or zr is zero. surge will cause zl or zr is zero. this kind of surge happened on aux. so set this kind as lineout to avoid aux params wrong. Change-Id: I96425117ea0c60d0171919da8ef434b2b2f318c0 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/1920430 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Yuesheng Huang Submit-Approved: Jira Key --- asoc/codecs/wcd-mbhc-v2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 761d541b7350..966aa9b586c8 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -713,8 +713,10 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, fsm_en); pr_info("%s,compute impedance,zl = %d,zr = %d\n", __func__, mbhc->zl, mbhc->zr); - if ((mbhc->zl > mbhc->mbhc_cfg->linein_th) && - (mbhc->zr > mbhc->mbhc_cfg->linein_th) && + if ((((mbhc->zl > mbhc->mbhc_cfg->linein_th) && + (mbhc->zr > mbhc->mbhc_cfg->linein_th)) || + (mbhc->zl == 0) || + (mbhc->zr == 0)) && (jack_type == SND_JACK_HEADPHONE)) { jack_type = SND_JACK_LINEOUT; mbhc->force_linein = true; From c8b0d260ccca5b101ed5d5b2de9b770836182f67 Mon Sep 17 00:00:00 2001 From: zhouwl Date: Wed, 23 Jun 2021 19:44:04 +0800 Subject: [PATCH 26/79] q6afe: enlarge payload size for AW calibration After upgrading AW SKT v0.2.1, speaker calibration is failed, need to enlarge this payload size to 8K, verifies on v0.2.0, aw882xx calibration works fine. - kernel failure log: aw_send_afe_cal_apr: Invalid payload size = 4520 Change-Id: I7d5b9b26c52e4bea06ee74169dcfec27bcdba554 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/1989533 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- dsp/q6afe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index a48078ecbf4f..4fd4edf335ae 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -4009,8 +4009,8 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, } if (aw_cal->map_data.dma_buf == 0) { - /*Minimal chunk size is 4K*/ - aw_cal->map_data.map_size = SZ_4K; + /*Minimal chunk size is 8K*/ + aw_cal->map_data.map_size = SZ_8K; result = msm_audio_ion_alloc(&(aw_cal->map_data.dma_buf), aw_cal->map_data.map_size, &(aw_cal->cal_data.paddr),&len, @@ -4037,7 +4037,7 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, goto err; } - if (cmd_size > (SZ_4K - sizeof(struct param_hdr_v3))) { + if (cmd_size > (SZ_8K - sizeof(struct param_hdr_v3))) { pr_err("%s: Invalid payload size = %d\n", __func__, cmd_size); result = -EINVAL; goto err; From 17bbd21243696fe16daff7e178817d94eafb9a83 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 24 Jun 2021 09:38:17 +0800 Subject: [PATCH 27/79] audio-kernel:update aw882xx driver to 0.1.18 add real time temperature acquisition interface. for >= 0.2.0 algo. Change-Id: I6f0681b79471f169e5f25180c53ae397c6efb9ba Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/1990140 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xx/aw882xx.c | 168 +++++++++++++++++++++++++++++++++- asoc/codecs/aw882xx/aw882xx.h | 33 +++++++ 2 files changed, 199 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw882xx/aw882xx.c b/asoc/codecs/aw882xx/aw882xx.c index 42b21533bd38..9e093450fb0a 100644 --- a/asoc/codecs/aw882xx/aw882xx.c +++ b/asoc/codecs/aw882xx/aw882xx.c @@ -1,7 +1,7 @@ /* * aw882xx.c aw882xx codec module * - * Version: v0.1.17 + * Version: v0.1.18 * * keep same with AW882XX_VERSION * @@ -49,7 +49,7 @@ ******************************************************/ #define AW882XX_I2C_NAME "aw882xx_smartpa" -#define AW882XX_VERSION "v0.1.17" +#define AW882XX_VERSION "v0.1.18" #define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 #define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -67,6 +67,8 @@ #define DELAY_TIME_MAX 300 #define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" +static DEFINE_MUTEX(g_msg_dsp_lock); + #ifdef CONFIG_AW882XX_DSP extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, uint32_t param_id, void *buf, int cmd_size, bool write); @@ -2495,6 +2497,163 @@ static ssize_t aw882xx_temp_show(struct device *dev, return len; } #endif + +static int aw_write_msg_to_dsp(struct aw882xx *aw882xx, int inline_id, + char *data, int data_size) +{ + int32_t *dsp_msg = NULL; + struct aw_dsp_msg_hdr *hdr = NULL; + int ret; + + dsp_msg = kzalloc(sizeof(struct aw_dsp_msg_hdr) + data_size, + GFP_KERNEL); + if (!dsp_msg) { + pr_err("%s: inline_id:0x%x kzalloc dsp_msg error\n", + __func__, inline_id); + return -ENOMEM; + } + hdr = (struct aw_dsp_msg_hdr *)dsp_msg; + hdr->type = DSP_MSG_TYPE_DATA; + hdr->opcode_id = inline_id; + hdr->version = AWINIC_DSP_MSG_HDR_VER; + + memcpy(((char *)dsp_msg) + sizeof(struct aw_dsp_msg_hdr), + data, data_size); + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_MSG, (void *)dsp_msg, + sizeof(struct aw_dsp_msg_hdr) + data_size, true); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, write data failed\n", + __func__, inline_id); + kfree(dsp_msg); + dsp_msg = NULL; + return ret; + } + + kfree(dsp_msg); + dsp_msg = NULL; + return 0; +} + +static int aw_read_msg_from_dsp(struct aw882xx *aw882xx, int inline_id, + char *data, int data_size) +{ + struct aw_dsp_msg_hdr dsp_msg; + int ret; + + dsp_msg.type = DSP_MSG_TYPE_CMD; + dsp_msg.opcode_id = inline_id; + dsp_msg.version = AWINIC_DSP_MSG_HDR_VER; + + mutex_lock(&g_msg_dsp_lock); + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_MSG, + &dsp_msg, sizeof(struct aw_dsp_msg_hdr), true); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, write cmd to dsp failed\n", + __func__, inline_id); + goto dsp_msg_failed; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_MSG, + data, data_size, false); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, read data from dsp failed\n", + __func__, inline_id); + goto dsp_msg_failed; + } + + mutex_unlock(&g_msg_dsp_lock); + return 0; + +dsp_msg_failed: + mutex_unlock(&g_msg_dsp_lock); + return ret; +} + +static int aw_misc_ops_read_dsp(struct aw882xx *aw882xx, aw_ioctl_msg_t *msg) +{ + char __user* user_data = (char __user*)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + pr_err("%s : malloc failed !\n", __func__); + return -ENOMEM; + } + + ret = aw_read_msg_from_dsp(aw882xx, dsp_msg_id, data_ptr, data_len); + if (ret) { + pr_err("%s : write failed\n", __func__); + goto exit; + } + + if (copy_to_user((void __user *)user_data, + data_ptr, data_len)) { + ret = -EFAULT; + } +exit: + kfree(data_ptr); + return ret; +} + +static int aw_misc_ops_write_dsp(struct aw882xx *aw882xx, aw_ioctl_msg_t *msg) +{ + char __user* user_data = (char __user*)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + pr_err("%s : malloc failed !\n", __func__); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)user_data, data_len)) { + pr_err("%s : copy data failed\n", __func__); + ret = -EFAULT; + goto exit; + } + + ret = aw_write_msg_to_dsp(aw882xx, dsp_msg_id, data_ptr, data_len); + if (ret) { + pr_err("%s : write failed\n", __func__); + } +exit: + kfree(data_ptr); + return ret; +} + +static int aw_misc_ops_msg(struct aw882xx *aw882xx, unsigned long arg) +{ + aw_ioctl_msg_t ioctl_msg; + + if (copy_from_user(&ioctl_msg, (void __user *)arg, sizeof(aw_ioctl_msg_t))) { + return -EFAULT; + } + + if(ioctl_msg.version != AW_IOCTL_MSG_VERSION) { + pr_err("%s:unsupported msg version %d\n", __func__, ioctl_msg.version); + return -EINVAL; + } + + if (ioctl_msg.type == AW_IOCTL_MSG_RD_DSP) { + return aw_misc_ops_read_dsp(aw882xx, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_WR_DSP) { + return aw_misc_ops_write_dsp(aw882xx, &ioctl_msg); + } else { + pr_err("%s:unsupported msg type %d\n", __func__, ioctl_msg.type); + return -EINVAL; + } +} + static int aw882xx_cali_operation(struct aw882xx *aw882xx, unsigned int cmd, unsigned long arg) { @@ -2735,6 +2894,11 @@ static int aw882xx_cali_operation(struct aw882xx *aw882xx, kfree(p_data); p_data = NULL; } break; + case AW882XX_IOCTL_MSG: + ret = aw_misc_ops_msg(aw882xx, arg); + if (ret < 0) + goto exit; + break; default: { pr_err("%s : cmd %d\n", __func__, cmd); } break; diff --git a/asoc/codecs/aw882xx/aw882xx.h b/asoc/codecs/aw882xx/aw882xx.h index f8e1cb28db24..0cbf46a30da3 100644 --- a/asoc/codecs/aw882xx/aw882xx.h +++ b/asoc/codecs/aw882xx/aw882xx.h @@ -26,6 +26,8 @@ #define AW_MODULE_ID_COPP (0X10013D02) /*SKT module id*/ #define AW_MODULE_PARAMS_ID_COPP_ENABLE (0X10013D14) /*SKT enable param id*/ +#define AW_IOCTL_MSG_VERSION (0) + #define DEFAULT_CALI_VALUE (7) #define ERRO_CALI_VALUE (0) #define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) @@ -43,12 +45,19 @@ #define AFE_PARAM_ID_AWDSP_RX_F0_R (0X10013D20) #define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L (0X10013D21) #define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_R (0X10013D22) +#define AFE_PARAM_ID_AWDSP_RX_MSG (0X10013D2A) enum { AW_FIRST_ENTRY = 0, AW_NOT_FIRST_ENTRY = 1, }; +enum { + AW_IOCTL_MSG_IOCTL = 0, + AW_IOCTL_MSG_RD_DSP, + AW_IOCTL_MSG_WR_DSP +}; + enum AWINIC_CALI_CMD{ AW_CALI_CMD_NONE = 0, AW_CALI_CMD_START, @@ -65,9 +74,16 @@ enum AWINIC_CALI_CMD{ AW_CALI_CMD_MAX, }; +enum aw882xx_dsp_msg_type { + DSP_MSG_TYPE_DATA = 0, + DSP_MSG_TYPE_CMD = 1, +}; + #define AW882XX_CALI_CFG_NUM 3 #define AW882XX_CALI_DATA_NUM 6 #define AW882XX_PARAMS_NUM 400 +#define AWINIC_DSP_MSG_HDR_VER (1) + struct cali_cfg{ int32_t data[AW882XX_CALI_CFG_NUM]; }; @@ -82,6 +98,22 @@ struct ptr_params_data { int32_t *data; }; +typedef struct { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t data_len; + char *data_buf; + int32_t reseriver[2]; +}aw_ioctl_msg_t; + +struct aw_dsp_msg_hdr { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reserver[3]; +}; + #define AW882XX_IOCTL_MAGIC 'a' #define AW882XX_IOCTL_SET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 1, struct cali_cfg) @@ -96,6 +128,7 @@ struct ptr_params_data { #define AW882XX_IOCTL_SET_PARAM _IOWR(AW882XX_IOCTL_MAGIC, 10,struct params_data) #define AW882XX_IOCTL_ENABLE_CALI _IOWR(AW882XX_IOCTL_MAGIC, 11,int8_t) #define AW882XX_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW882XX_IOCTL_MAGIC, 12, struct ptr_params_data) +#define AW882XX_IOCTL_MSG _IOWR(AW882XX_IOCTL_MAGIC, 16, aw_ioctl_msg_t) enum aw882xx_init { From ecd318fbada9147844dd8f8cf7d8d973be51dedc Mon Sep 17 00:00:00 2001 From: zhouwl Date: Tue, 6 Jul 2021 11:26:38 +0800 Subject: [PATCH 28/79] ASoC: add aw882xx v1.4.0.1 driver Upgrade aw882xx driver to v1.4.0.1 for supporting aw88261, at the meantime, use different devicetree compatible name to compat the prior v0.1.18. - v0.1.18 supports aw88258, names aw882xx_k419.ko; - v1.4.0.1 supports aw88261+aw88258, names aw882xx_acf.ko, because need *_acf.bin config file. Change-Id: Iefcc513dcce232c3a783a5eb0a169049995a7c76 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/2003285 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/Kbuild | 1 + asoc/codecs/aw882xxacf/Android.mk | 8 + asoc/codecs/aw882xxacf/Kbuild | 16 + asoc/codecs/aw882xxacf/aw882xx.c | 2282 ++++++ asoc/codecs/aw882xxacf/aw882xx.h | 170 + asoc/codecs/aw882xxacf/aw_bin_parse.c | 512 ++ asoc/codecs/aw882xxacf/aw_bin_parse.h | 73 + asoc/codecs/aw882xxacf/aw_calib.c | 2243 ++++++ asoc/codecs/aw882xxacf/aw_calib.h | 144 + asoc/codecs/aw882xxacf/aw_data_type.h | 125 + asoc/codecs/aw882xxacf/aw_device.c | 1557 ++++ asoc/codecs/aw882xxacf/aw_device.h | 283 + asoc/codecs/aw882xxacf/aw_dsp.c | 1051 +++ asoc/codecs/aw882xxacf/aw_dsp.h | 60 + asoc/codecs/aw882xxacf/aw_init.c | 1612 ++++ asoc/codecs/aw882xxacf/aw_log.h | 34 + asoc/codecs/aw882xxacf/aw_monitor.c | 1204 +++ asoc/codecs/aw882xxacf/aw_monitor.h | 173 + asoc/codecs/aw882xxacf/aw_pid_1852_reg.h | 1589 ++++ asoc/codecs/aw882xxacf/aw_pid_2013_reg.h | 6550 ++++++++++++++++ asoc/codecs/aw882xxacf/aw_pid_2032_reg.h | 1777 +++++ asoc/codecs/aw882xxacf/aw_pid_2055_reg.h | 6792 +++++++++++++++++ asoc/codecs/aw882xxacf/aw_pid_2071_reg.h | 7202 ++++++++++++++++++ asoc/codecs/aw882xxacf/aw_pid_2113_reg.h | 8777 ++++++++++++++++++++++ 24 files changed, 44235 insertions(+) create mode 100644 asoc/codecs/aw882xxacf/Android.mk create mode 100644 asoc/codecs/aw882xxacf/Kbuild create mode 100644 asoc/codecs/aw882xxacf/aw882xx.c create mode 100644 asoc/codecs/aw882xxacf/aw882xx.h create mode 100644 asoc/codecs/aw882xxacf/aw_bin_parse.c create mode 100644 asoc/codecs/aw882xxacf/aw_bin_parse.h create mode 100644 asoc/codecs/aw882xxacf/aw_calib.c create mode 100644 asoc/codecs/aw882xxacf/aw_calib.h create mode 100644 asoc/codecs/aw882xxacf/aw_data_type.h create mode 100644 asoc/codecs/aw882xxacf/aw_device.c create mode 100644 asoc/codecs/aw882xxacf/aw_device.h create mode 100644 asoc/codecs/aw882xxacf/aw_dsp.c create mode 100644 asoc/codecs/aw882xxacf/aw_dsp.h create mode 100644 asoc/codecs/aw882xxacf/aw_init.c create mode 100644 asoc/codecs/aw882xxacf/aw_log.h create mode 100644 asoc/codecs/aw882xxacf/aw_monitor.c create mode 100644 asoc/codecs/aw882xxacf/aw_monitor.h create mode 100644 asoc/codecs/aw882xxacf/aw_pid_1852_reg.h create mode 100644 asoc/codecs/aw882xxacf/aw_pid_2013_reg.h create mode 100644 asoc/codecs/aw882xxacf/aw_pid_2032_reg.h create mode 100644 asoc/codecs/aw882xxacf/aw_pid_2055_reg.h create mode 100644 asoc/codecs/aw882xxacf/aw_pid_2071_reg.h create mode 100644 asoc/codecs/aw882xxacf/aw_pid_2113_reg.h diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index c1776112b0f6..505adf9807cb 100755 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -275,6 +275,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += wsa883x/ obj-y += rouleur/ obj-y += aw882xx/ + obj-y += aw882xxacf/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/asoc/codecs/aw882xxacf/Android.mk b/asoc/codecs/aw882xxacf/Android.mk new file mode 100644 index 000000000000..b82a1f9a359a --- /dev/null +++ b/asoc/codecs/aw882xxacf/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := $(TOP)/device/qcom/common/dlkm +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw882xx_acf.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/asoc/codecs/aw882xxacf/Kbuild b/asoc/codecs/aw882xxacf/Kbuild new file mode 100644 index 000000000000..1472548a2bb7 --- /dev/null +++ b/asoc/codecs/aw882xxacf/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ + +EXTRA_CFLAGS += -DCONFIG_AW882XX_CODEC -DCONFIG_AW882XX_DSP + +aw882xx_acf-objs += aw882xx.o +aw882xx_acf-objs += aw_bin_parse.o +aw882xx_acf-objs += aw_calib.o +aw882xx_acf-objs += aw_device.o +aw882xx_acf-objs += aw_dsp.o +aw882xx_acf-objs += aw_init.o +aw882xx_acf-objs += aw_monitor.o + +obj-m += aw882xx_acf.o diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c new file mode 100644 index 000000000000..8e3b32c3ae8b --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -0,0 +1,2282 @@ +/* + * aw882xx.c aw882xx codec module + * + * + * Copyright (c) 2020 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_log.h" +#include "aw_dsp.h" + +#define AW882XX_DRIVER_VERSION "v1.4.0.1" +#define AW882XX_I2C_NAME "aw882xxacf_smartpa" + +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + +static unsigned int g_aw882xx_dev_cnt = 0; +static unsigned int g_print_dbg = 0; +static unsigned int g_algo_rx_en = true; +static unsigned int g_algo_tx_en = true; +static unsigned int g_algo_copp_en = true; +#ifdef AW_SPIN_ENABLE +static unsigned int g_spin_value = 0; +#endif + +static DEFINE_MUTEX(g_aw882xx_lock); +struct aw_container *g_awinic_cfg = NULL; + +static const char *const aw882xx_switch[] = {"Disable", "Enable"}; +#ifdef AW_SPIN_ENABLE +static const char *const aw882xx_spin[] = {"spin_0", "spin_90", + "spin_180", "spin_270"}; +#endif + +/****************************************************** + * + * aw882xx distinguish between codecs and components by version + * + ******************************************************/ +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .kcontrol_codec = snd_soc_kcontrol_component, + .codec_get_drvdata = snd_soc_component_get_drvdata, + .add_codec_controls = snd_soc_add_component_controls, + .unregister_codec = snd_soc_unregister_component, + .register_codec = snd_soc_register_component, +}; +#else +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .kcontrol_codec = snd_soc_kcontrol_codec, + .codec_get_drvdata = snd_soc_codec_get_drvdata, + .add_codec_controls = snd_soc_add_codec_controls, + .unregister_codec = snd_soc_unregister_codec, + .register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef AW_KERNEL_VER_OVER_4_19_1 + return dai->component; +#else + return dai->codec; +#endif +} + + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +int aw882xx_get_version(char *buf, int size) +{ + if (size > strlen(AW882XX_DRIVER_VERSION)) { + memcpy(buf, AW882XX_DRIVER_VERSION, strlen(AW882XX_DRIVER_VERSION)); + return strlen(AW882XX_DRIVER_VERSION); + } else { + return -ENOMEM; + } +} + +int aw882xx_get_dev_num(void) { + return g_aw882xx_dev_cnt; +} + +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + aw_dev_err(aw882xx->dev, "can not allocate memory"); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + aw_dev_err(aw882xx->dev, "i2c master send error"); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "transfer failed."); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + aw_dev_err(aw882xx->dev, "transfer failed(size error)."); + return -ENXIO; + } + + return 0; +} + +int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c_write cnt=%d error=%d", + cnt, ret); + } else { + if (g_print_dbg) + aw_dev_info(aw882xx->dev, "reg_addr: 0x%04x, reg_data :0x%04x", + (uint16_t)reg_addr, (uint16_t)reg_data); + break; + } + cnt++; + } + + return ret; +} + +int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c_read cnt=%d error=%d", + cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + if (g_print_dbg) + aw_dev_info(aw882xx->dev, "reg_addr: 0x%04x, reg_data :0x%04x", + (uint16_t)reg_addr, (uint16_t)(*reg_data)); + break; + } + cnt++; + } + + return ret; +} + +int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + + return 0; +} +#if 0 +static void *aw882xx_devm_kstrdup(struct device *dev, char *buf) +{ + char *str; + + str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!str) + return str; + + memcpy(str, buf, strlen(buf)); + return str; +} +#endif +/***************************************************** + * + * snd_soc_dai_driver ops + * + *****************************************************/ +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_info(aw882xx->dev, "playback enter"); + /*load cali re*/ + aw_dev_init_cali_re(aw882xx->aw_pa); + } else { + aw_dev_info(aw882xx->dev, "capture enter"); + } + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = aw_snd_soc_codec_get_drvdata(dai->codec);*/ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + + aw_dev_info(codec->dev, "fmt=0x%x", fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + aw_dev_err(codec->dev, "invalid codec master mode"); + return -EINVAL; + } + break; + default: + aw_dev_err(codec->dev, "unsupported DAI format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_info(aw882xx->dev, "freq=%d", freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_dev_dbg(aw882xx->dev, "stream capture requested rate: %d, sample size: %d", + params_rate(params), params_width(params)); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_dbg(aw882xx->dev, "stream playback requested rate: %d, sample size: %d", + params_rate(params), params_width(params)); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw_dev_info(aw882xx->dev, "stream playback"); + } else { + aw_dev_info(aw882xx->dev, "stream capture"); + } +} + +static void aw882xx_start_pa(struct aw882xx *aw882xx) +{ + int ret; + int i; + + aw_dev_info(aw882xx->dev, "enter"); + + if (aw882xx->fw_status == AW_DEV_FW_OK) { + if (aw882xx->allow_pw == false) { + aw_dev_info(aw882xx->dev, "dev can not allow power "); + return; + } + + for (i = 0; i < AW_START_RETRIES; i++) { + /*if PA already power ,stop PA then start*/ + if (aw882xx->aw_pa->status) + aw_device_stop(aw882xx->aw_pa); + + ret = aw_dev_reg_update(aw882xx->aw_pa, aw882xx->phase_sync); + if (ret) { + aw_dev_err(aw882xx->dev, "fw update failed, cnt:%d", i); + continue; + } + + ret = aw_device_start(aw882xx->aw_pa); + if (ret) { + aw_dev_err(aw882xx->dev, "start failed, cnt:%d", i); + continue; + } else { + if (aw882xx->dc_flag) + queue_delayed_work(aw882xx->work_queue, + &aw882xx->dc_work, + msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); + aw_dev_info(aw882xx->dev, "start success"); +#ifdef AW_SPIN_ENABLE + if (aw882xx->index == 0) { + ret = aw_dev_set_spin(g_spin_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } +#endif + break; + } + } + } else { + aw_dev_info(aw882xx->dev, "dev acf load failed"); + } + +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_info(aw882xx->dev, "mute state=%d", mute); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_info(aw882xx->dev, "capture"); + return 0; + } + + if (mute) { + aw882xx->pstream = false; + cancel_delayed_work_sync(&aw882xx->dc_work); + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); + } else { + if (aw882xx->fw_status == AW_DEV_FW_FAILED) { + aw_dev_info(aw882xx->dev, "fw_load failed ,can not start PA"); + return 0; + } + aw882xx->pstream = true; + mutex_lock(&aw882xx->lock); + /*aw882xx_start_pa(aw882xx);*/ + queue_delayed_work(aw882xx->work_queue, + &aw882xx->start_work, 0); + mutex_unlock(&aw882xx->lock); + } + + return 0; +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +/***************************************************** + * + * snd_soc_codec_driver | snd_soc_component_driver| + * + *****************************************************/ +static int aw882xx_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count, ret; + char *name = NULL; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw_dev_get_profile_count(aw882xx->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + aw_dev_err(aw882xx->dev, "get count[%d] failed ", count); + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + ret = aw_dev_get_profile_name(aw882xx->aw_pa, name, count); + if (ret) { + strlcpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); + return 0; + } + + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw_dev_get_profile_index(aw882xx->aw_pa); + aw_dev_dbg(codec->dev, "profile index [%d]", aw_dev_get_profile_index(aw882xx->aw_pa)); + return 0; + +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ret; + int cur_index; + + if (aw882xx->dbg_en_prof == false) { + aw_dev_info(codec->dev, "profile close "); + return 0; + } + + /* check value valid */ + ret = aw_dev_check_profile_index(aw882xx->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + aw_dev_info(codec->dev, "unsupported index %d", + (int)ucontrol->value.integer.value[0]); + return 0; + } + + /*check cur_index == set value*/ + cur_index = aw_dev_get_profile_index(aw882xx->aw_pa); + if (cur_index == ucontrol->value.integer.value[0]) { + aw_dev_info(codec->dev, "index no change"); + return 0; + } + + mutex_lock(&aw882xx->lock); + aw_dev_set_profile_index(aw882xx->aw_pa, ucontrol->value.integer.value[0]); + /*pstream = 0 no pcm just set status*/ + if (aw882xx->pstream && aw882xx->allow_pw) + aw_dev_prof_update(aw882xx->aw_pa, aw882xx->phase_sync); + mutex_unlock(&aw882xx->lock); + aw_dev_info(codec->dev, "prof id %d", (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = 2; + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw882xx_switch[uinfo->value.enumerated.item], + strlen(aw882xx_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw882xx_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw882xx->allow_pw; + + return 0; + +} + +static int aw882xx_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (aw882xx->pstream) { + if (ucontrol->value.integer.value[0] == 0) { + cancel_delayed_work_sync(&aw882xx->dc_work); + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + aw882xx->allow_pw = false; + mutex_unlock(&aw882xx->lock); + aw_dev_info(aw882xx->dev, "stop pa"); + } else { + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw882xx->allow_pw = true; + if (aw882xx->fw_status == AW_DEV_FW_OK) + aw882xx_start_pa(aw882xx); + else + aw_dev_info(aw882xx->dev, "fw_load failed ,can not start PA"); + mutex_unlock(&aw882xx->lock); + } + } else { + mutex_lock(&aw882xx->lock); + if (ucontrol->value.integer.value[0]) + aw882xx->allow_pw = true; + else + aw882xx->allow_pw = false; + mutex_unlock(&aw882xx->lock); + } + + return 0; +} + +static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) +{ + struct snd_kcontrol_new *aw882xx_dev_control = NULL; + char *kctl_name; + + aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 2, GFP_KERNEL); + if (aw882xx_dev_control == NULL) { + aw_dev_err(aw882xx->codec->dev, "kcontrol malloc failed!"); + return -ENOMEM; + } + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_prof", aw882xx->index); + + aw882xx_dev_control[0].name = kctl_name; + aw882xx_dev_control[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[0].info = aw882xx_profile_info; + aw882xx_dev_control[0].get = aw882xx_profile_get; + aw882xx_dev_control[0].put = aw882xx_profile_set; + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_switch", aw882xx->index); + + aw882xx_dev_control[1].name = kctl_name; + aw882xx_dev_control[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[1].info = aw882xx_switch_info; + aw882xx_dev_control[1].get = aw882xx_switch_get; + aw882xx_dev_control[1].put = aw882xx_switch_set; + + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, + aw882xx_dev_control, 2); + + return 0; +} + +static void aw882xx_request_firmware(struct work_struct *work) +{ + struct aw882xx *aw882xx = + container_of(work, struct aw882xx, fw_work.work); + const struct firmware *cont = NULL; + struct aw_container *aw_cfg = NULL; + int ret = -1; + + aw882xx->fw_status = AW_DEV_FW_FAILED; + ret = request_firmware(&cont, aw882xx->aw_pa->acf_name, aw882xx->dev); + if ((ret) || (!cont)) { + aw_dev_info(aw882xx->dev, "load [%s] failed!", aw882xx->aw_pa->acf_name); + if (aw882xx->fw_retry_cnt == AW_READ_CHIPID_RETRIES) { + aw882xx->fw_retry_cnt = 0; + } else { + aw882xx->fw_retry_cnt++; + /* sleep 1s */ + msleep(1000); + aw_dev_info(aw882xx->dev, "load [%s] try [%d]!", + aw882xx->aw_pa->acf_name, aw882xx->fw_retry_cnt); + aw882xx_request_firmware(work); + } + return; + } + + aw_dev_info(aw882xx->dev, "load [%s] , file size: [%zu]", + aw882xx->aw_pa->acf_name, cont ? cont->size : 0); + + mutex_lock(&g_aw882xx_lock); + if (g_awinic_cfg == NULL) { + aw_cfg = vzalloc(cont->size + sizeof(int)); + if (aw_cfg == NULL) { + release_firmware(cont); + aw_dev_err(aw882xx->dev, "malloc failed"); + mutex_unlock(&g_aw882xx_lock); + return; + } + aw_cfg->len = cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + ret = aw_dev_load_acf_check(aw_cfg); + if (ret) { + aw_dev_err(aw882xx->dev, "Load [%s] failed ....!", aw882xx->aw_pa->acf_name); + vfree(aw_cfg); + aw_cfg = NULL; + mutex_unlock(&g_aw882xx_lock); + return; + } + g_awinic_cfg = aw_cfg; + } else { + aw_cfg = g_awinic_cfg; + release_firmware(cont); + aw_dev_info(aw882xx->dev, "[%s] already loaded...", aw882xx->aw_pa->acf_name); + } + mutex_unlock(&g_aw882xx_lock); + + mutex_lock(&aw882xx->lock); + /*aw device init*/ + ret = aw_device_init(aw882xx->aw_pa, aw_cfg); + if (ret < 0) { + aw_dev_info(aw882xx->dev, "dev init failed"); + mutex_unlock(&aw882xx->lock); + return; + } + + /*create kcontrol by profile*/ + aw882xx_dynamic_create_controls(aw882xx); + + aw882xx->fw_status = AW_DEV_FW_OK; + aw882xx->fw_retry_cnt = 0; + + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_startup_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, start_work.work); + + aw_dev_info(aw882xx->dev, "enter"); + + mutex_lock(&aw882xx->lock); + aw882xx_start_pa(aw882xx); + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_dc_prot_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, dc_work.work); + int dc_status = -1; + int dev_status = aw_dev_status(aw882xx->aw_pa); + + if (aw882xx->dc_flag) { + if (dev_status) { + dc_status = aw_dev_dc_status(aw882xx->aw_pa); + if (dc_status > 0) { + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); + } else { + queue_delayed_work(aw882xx->work_queue, + &aw882xx->dc_work, + msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); + } + } + } +} + +static void aw882xx_irq_restart(struct aw882xx *aw882xx) +{ + int ret; + + aw_dev_dbg(aw882xx->dev, "enter"); + + mutex_lock(&aw882xx->lock); + + /*stop pa*/ + aw_device_stop(aw882xx->aw_pa); + + /*hw reset*/ + aw882xx_hw_reset(aw882xx); + + /*aw reinit*/ + if (aw882xx->fw_status == AW_DEV_FW_OK) { + ret = aw_device_irq_reinit(aw882xx->aw_pa); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "irq reinit failed"); + goto failed_exit; + } + + if (aw882xx->allow_pw && aw882xx->pstream) { + ret = aw_device_start(aw882xx->aw_pa); + if (ret) { + aw_dev_err(aw882xx->dev, "start failed"); + goto failed_exit; + } + } else { + aw_dev_info(aw882xx->dev, "allow_pw [%d] ,pstream[%d], not start", + aw882xx->allow_pw, aw882xx->pstream); + } + } else { + aw_dev_err(aw882xx->dev, "fw not load ,cannot init device"); + } +failed_exit: + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_interrupt_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, interrupt_work.work); + int16_t reg_value; + int ret; + + aw_dev_info(aw882xx->dev, "enter"); + + /*read reg value*/ + ret = aw_dev_get_int_status(aw882xx->aw_pa, ®_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "get init_reg value failed"); + } else { + aw_dev_info(aw882xx->dev, "int value 0x%x", reg_value); + if (aw882xx->aw_pa->ops.aw_get_irq_type) { + ret = aw882xx->aw_pa->ops.aw_get_irq_type(aw882xx->aw_pa, reg_value); + if (ret != INT_TYPE_NONE) { + aw882xx_irq_restart(aw882xx); + return; + } + } + } + + /*clear init reg*/ + aw_dev_clear_int_status(aw882xx->aw_pa); + + /*unmask interrupt*/ + aw_dev_set_intmask(aw882xx->aw_pa, true); +} + +static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + ctrl_value = ucontrol->value.integer.value[0]; + + + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + return 0; +} + +static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = g_algo_rx_en; + aw_dev_info(aw882xx->dev, "no stream, use record value"); + } + + aw_dev_dbg(aw882xx->dev, "aw882xx_rx_enable %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_tx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_afe_module_en(AW_TX_MODULE, ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + return 0; +} + +static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = g_algo_tx_en; + aw_dev_info(aw882xx->dev, "no streamm, use record value"); + } + + aw_dev_dbg(aw882xx->dev, "aw882xx_tx_enable %ld", + ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_copp_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_copp_module_en(ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + + return 0; +} + +static int aw882xx_get_copp_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = g_algo_copp_en; + + aw_dev_dbg(aw882xx->dev, "done nothing"); + return 0; +} + +void aw882xx_kcontorl_set(struct aw882xx *aw882xx) +{ + int ret; + + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, g_algo_rx_en); + if (ret) + aw_dev_err(aw882xx->dev, "afe set, ret=%d", ret); + + ret = aw_dev_set_copp_module_en(g_algo_copp_en); + if (ret) + aw_dev_err(aw882xx->dev, "copp set error, ret=%d", ret); +} + +#ifdef AW_SPIN_ENABLE +static int aw882xx_set_spin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + if (aw882xx->pstream) { + ret = aw_dev_set_spin(ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } else { + aw_dev_info(aw882xx->dev, "stream no start only record"); + } + + g_spin_value = ctrl_value; + return 0; +} + +static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev = aw882xx->aw_pa; + + aw_dev_get_spin(&ctrl_value); + + ucontrol->value.integer.value[0] = ctrl_value; + + aw_dev_dbg(aw882xx->dev, "done nothing"); + return 0; +} +#endif + +static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, true); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, false); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + + aw_dev_set_fade_time(ucontrol->value.integer.value[0], false); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), +#ifdef AW_SPIN_ENABLE + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_spin), aw882xx_spin), +#endif +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[0], + aw882xx_get_rx_en, aw882xx_set_rx_en), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[0], + aw882xx_get_tx_en, aw882xx_set_tx_en), + SOC_ENUM_EXT("aw882xx_copp_switch", aw882xx_snd_enum[0], + aw882xx_get_copp_en, aw882xx_set_copp_en), +#ifdef AW_SPIN_ENABLE + SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[1], + aw882xx_get_spin, aw882xx_set_spin), +#endif + SOC_SINGLE_EXT("aw882xx_fadein_us", 0, 0, 1000000, 0, + aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), + SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, + aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + aw_dev_info(aw882xx->dev, "enter"); + + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, + &aw882xx_controls[0], ARRAY_SIZE(aw882xx_controls)); +} + +#ifdef AW_MTK_PLATFORM_WITH_DSP +static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name) +{ + char buf[50]; + int i2cbus = aw882xx->i2c->adapter->nr; + int i2caddr = aw882xx->i2c->addr; + + snprintf(buf, 50, "%s-%x-%x", *name, i2cbus, i2caddr); + (*name) = aw882xx_devm_kstrdup(aw882xx->dev, buf); + if (!(*name)) + return -ENOMEM; + + aw_dev_info(aw882xx->dev, "name is %s", (*name)); + return 0; +} + +static const struct snd_soc_dapm_widget aw882xx_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("audio_out"), + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("iv_in"), +}; + +static const struct snd_soc_dapm_route aw882xx_audio_map[] = { + {"audio_out", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "iv_in"}, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +static struct snd_soc_dapm_context *snd_soc_codec_get_dapm(struct snd_soc_codec *codec) +{ + return &codec->dapm; +} +#endif +#endif + +static void aw882xx_add_widgets(struct aw882xx *aw882xx) +{ +#ifdef AW_MTK_PLATFORM_WITH_DSP + int i = 0; + int ret; + struct snd_soc_dapm_widget *aw_widgets = NULL; + struct snd_soc_dapm_route *aw_route = NULL; +#ifdef AW_KERNEL_VER_OVER_4_19_1 + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(aw882xx->codec); +#else + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(aw882xx->codec); +#endif + + + /*add widgets*/ + aw_widgets = devm_kzalloc(aw882xx->dev, + sizeof(struct snd_soc_dapm_widget) * ARRAY_SIZE(aw882xx_dapm_widgets), + GFP_KERNEL); + if (!aw_widgets) { + aw_dev_err(aw882xx->dev, "alloc widget memory failed!"); + return; + } + memcpy(aw_widgets, aw882xx_dapm_widgets, + sizeof(struct snd_soc_dapm_widget) * ARRAY_SIZE(aw882xx_dapm_widgets)); + + for (i = 0; i < ARRAY_SIZE(aw882xx_dapm_widgets); i++) { + if (aw_widgets[i].name) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_widgets[i].name); + if (ret) { + aw_dev_err(aw882xx->dev, "append widget name suffix failed!"); + return; + } + } + + if (aw_widgets[i].sname) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_widgets[i].sname); + if (ret) { + aw_dev_err(aw882xx->dev, "append widget sname suffix failed!"); + return; + } + } + } + + snd_soc_dapm_new_controls(dapm, aw_widgets, ARRAY_SIZE(aw882xx_dapm_widgets)); + + /*add route*/ + aw_route = devm_kzalloc(aw882xx->dev, + sizeof(struct snd_soc_dapm_route) * ARRAY_SIZE(aw882xx_audio_map), + GFP_KERNEL); + if (!aw_route) { + aw_dev_err(aw882xx->dev, "alloc route memory failed!"); + return; + } + memcpy(aw_route, aw882xx_audio_map, + sizeof(struct snd_soc_dapm_route) * ARRAY_SIZE(aw882xx_audio_map)); + + for (i = 0; i < ARRAY_SIZE(aw882xx_audio_map); i++) { + if (aw_route[i].sink) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_route[i].sink); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "append sink name suffix failed!"); + return; + } + } + + if (aw_route[i].source) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_route[i].source); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "append source name suffix failed!"); + return; + } + } + } + snd_soc_dapm_add_routes(dapm, aw_route, ARRAY_SIZE(aw882xx_audio_map)); +#endif +} + +static void aw882xx_load_fw(struct aw882xx *aw882xx) +{ +#ifdef CONFIG_AW882XX_DSP + aw882xx_request_firmware(&aw882xx->fw_work.work); +#else + queue_delayed_work(aw882xx->work_queue, + &aw882xx->fw_work, + msecs_to_jiffies(AW882XX_LOAD_FW_DELAY_TIME)); +#endif +} + +static int aw882xx_codec_probe(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + aw_dev_info(aw882xx->dev, "enter"); + + aw882xx->work_queue = create_singlethread_workqueue("aw882xx"); + if (!aw882xx->work_queue) { + aw_dev_err(aw882xx->dev, "create workqueue failed !"); + return -EINVAL; + } + + INIT_DELAYED_WORK(&aw882xx->start_work, aw882xx_startup_work); + INIT_DELAYED_WORK(&aw882xx->interrupt_work, aw882xx_interrupt_work); + INIT_DELAYED_WORK(&aw882xx->dc_work, aw882xx_dc_prot_work); + INIT_DELAYED_WORK(&aw882xx->fw_work, aw882xx_request_firmware); + + aw882xx->codec = aw_codec; + + if (aw882xx->index == 0) + aw882xx_add_codec_controls(aw882xx); + + aw882xx_add_widgets(aw882xx); + + /*load fw bin*/ + aw882xx_load_fw(aw882xx); + + /*load cali re*/ + aw_dev_init_cali_re(aw882xx->aw_pa); + + return 0; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static void aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + aw_dev_info(aw882xx->dev, "enter"); + aw_dev_deinit(aw882xx->aw_pa); + destroy_workqueue(aw882xx->work_queue); + aw882xx->work_queue = NULL; +} +#else +static int aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + + aw_dev_info(aw882xx->dev, "enter"); + aw_dev_deinit(aw882xx->aw_pa); + destroy_workqueue(aw882xx->work_queue); + aw882xx->work_queue = NULL; + return 0; +} +#endif + +#if 0 +static int aw882xx_dai_drv_append_suffix(struct aw882xx *aw882xx, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + char buf[50]; + int i; + int i2cbus = aw882xx->i2c->adapter->nr; + int addr = aw882xx->i2c->addr; + + if ((dai_drv != NULL) && (num_dai > 0)) + for (i = 0; i < num_dai; i++) { + snprintf(buf, 50, "%s-%x-%x", dai_drv[i].name, i2cbus, + addr); + dai_drv[i].name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].playback.stream_name, + i2cbus, addr); + dai_drv[i].playback.stream_name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].capture.stream_name, + i2cbus, addr); + dai_drv[i].capture.stream_name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + aw_dev_info(aw882xx->dev, "dai name [%s]", dai_drv[i].name); + aw_dev_info(aw882xx->dev, "pstream_name name [%s]", dai_drv[i].playback.stream_name); + aw_dev_info(aw882xx->dev, "cstream_name name [%s]", dai_drv[i].capture.stream_name); + } + + return 0; +} +#endif + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE), + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &aw882xx_dai_ops, + /* .symmetric_rates = 1,*/ + }, +}; + + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct snd_soc_component_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_codec_probe, + .remove = aw882xx_codec_remove, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_codec_probe, + .remove = aw882xx_codec_remove, +}; +#endif + +int aw_componet_codec_register(struct aw882xx *aw882xx) +{ + struct snd_soc_dai_driver *dai_drv; + int ret; + dai_drv = devm_kzalloc(aw882xx->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (dai_drv == NULL) { + aw_dev_err(aw882xx->dev, "dai_driver malloc failed"); + return -ENOMEM; + } + + memcpy(dai_drv, aw882xx_dai, sizeof(aw882xx_dai)); + + /*aw882xx_dai_drv_append_suffix(aw882xx, dai_drv, ARRAY_SIZE(aw882xx_dai));*/ + + ret = aw882xx->codec_ops->register_codec(aw882xx->dev, + &soc_codec_dev_aw882xx, + dai_drv, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "failed to register aw882xx: %d", ret); + return -EINVAL; + } + return 0; +} +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw882xx_parse_gpio_dt(struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret = 0;; + + if (!np) { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + return -EINVAL; + } + + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + aw_dev_info(aw882xx->dev, "no reset gpio provided, will not HW reset device"); + } else { + aw_dev_info(aw882xx->dev, "reset gpio provided ok"); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) { + aw_dev_err(aw882xx->dev, "no irq gpio provided."); + ret = -EFAULT; + } else { + aw_dev_info(aw882xx->dev, "irq gpio provided ok."); + } + + return ret; +} + +static struct aw882xx *aw882xx_malloc_init(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) { + dev_err(&i2c->dev, "devm_kzalloc failed."); + return NULL; + } + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + aw882xx->aw_pa = NULL; + aw882xx->codec = NULL; + aw882xx->codec_ops = &aw_componet_codec_ops; + aw882xx->fw_status = AW_DEV_FW_FAILED; + aw882xx->fw_retry_cnt = 0; + aw882xx->dbg_en_prof = true; + aw882xx->allow_pw = true; + aw882xx->work_queue = NULL; + + mutex_init(&aw882xx->lock); + + return aw882xx; +} + +static int aw882xx_gpio_request(struct aw882xx *aw882xx) +{ + int ret; + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(aw882xx->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + aw_dev_err(aw882xx->dev, "rst request failed"); + return ret; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(aw882xx->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + aw_dev_err(aw882xx->dev, "int request failed"); + return ret; + } + } + + return 0; +} + +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret; + int32_t dc_enable = 0; + int32_t sync_enable = 0; + + /*gpio dts parser*/ + ret = aw882xx_parse_gpio_dt(aw882xx, np); + if (ret) + return ret; + + ret = of_property_read_u32(np, "dc-flag", &dc_enable); + if (ret) { + dc_enable = false; + aw_dev_info(aw882xx->dev, "close dc protect!\n"); + } else { + aw_dev_info(aw882xx->dev, "dc-flag = %d\n", dc_enable); + } + + aw882xx->dc_flag = dc_enable; + + + ret = of_property_read_u32(np, "sync-flag", &sync_enable); + if (ret < 0) { + aw_dev_info(aw882xx->dev, + "read sync flag failed,default phase sync off\n"); + sync_enable = false; + + } else { + aw_dev_info(aw882xx->dev, + "sync flag is %d", sync_enable); + } + + aw882xx->phase_sync = sync_enable; + + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + aw_dev_info(aw882xx->dev, "enter"); + + if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + mdelay(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + mdelay(2); + } else { + aw_dev_info(aw882xx->dev, "has no reset gpio"); + } + return 0; +} + +static int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg_value = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_CHIP_ID_REG, ®_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "failed to read REG_ID: %d", ret); + return -EIO; + } + switch (reg_value) { + case PID_1852_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 1852 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2013_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2013 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2032_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2032 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2055_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2055 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2071_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2071 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2113_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2113 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + default: + aw_dev_info(aw882xx->dev, "unsupported device revision (0x%x)", + reg_value); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)data; + + if (!aw882xx) { + aw_pr_err("pointer is NULL"); + return -EINVAL; + } + + aw_dev_info(aw882xx->dev, "enter"); + + /* mask all irq */ + aw_dev_set_intmask(aw882xx->aw_pa, false); + + /* upload workqueue */ + if (aw882xx->work_queue) + queue_delayed_work(aw882xx->work_queue, &aw882xx->interrupt_work, 0); + + return IRQ_HANDLED; +} + +static int aw882xx_interrupt_init(struct aw882xx *aw882xx) +{ + int irq_flags; + int ret; + + if (gpio_is_valid(aw882xx->irq_gpio)) { + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(aw882xx->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + aw_dev_err(aw882xx->dev, "Failed to request IRQ %d: %d", + gpio_to_irq(aw882xx->irq_gpio), ret); + return ret; + } + } else { + aw_dev_info(aw882xx->dev, "gpio invalid"); + /* disable interrupt */ + } + + return 0; +} + +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + int reg_num = aw882xx->aw_pa->ops.aw_get_reg_num(); + + for (i = 0; i < reg_num; i++) { + if (aw882xx->aw_pa->ops.aw_check_rd_access(i)) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->rw_reg_addr = (unsigned char)databuf[0]; + if (aw882xx->aw_pa->ops.aw_check_rd_access(databuf[0])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->rw_reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx->aw_pa->ops.aw_check_rd_access(aw882xx->rw_reg_addr)) { + aw882xx_i2c_read(aw882xx, aw882xx->rw_reg_addr, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", aw882xx->rw_reg_addr, reg_val); + } + return len; +} + + int aw882xx_awrw_write(struct aw882xx *aw882xx, const char *buf, size_t count) +{ + int i, ret; + char *data_buf = NULL; + int str_len, data_len, temp_data; + char *reg_data; + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + + aw_dev_info(aw882xx->dev, "write:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + + data_len = AWRW_DATA_BYTES * packet->reg_num; + + str_len = count - AWRW_HDR_LEN - 1; + + if ((data_len * 5 - 1) > str_len) { + aw_dev_err(aw882xx->dev, "data_str_len [%d], requeset len [%d]", + str_len, (data_len * 5 - 1)); + return -EINVAL; + } + + data_buf = kmalloc(data_len + 1, GFP_KERNEL); + if (data_buf == NULL) { + aw_dev_err(aw882xx->dev, "alloc memory failed"); + return -ENOMEM; + } + + data_buf[0] = packet->reg_addr; + reg_data = data_buf + 1; + + aw_dev_dbg(aw882xx->dev, "reg_addr: 0x%02x", data_buf[0]); + + for (i = 0; i < data_len; i++) { + sscanf(buf + AWRW_HDR_LEN + 1 + i * 5, "0x%02x", &temp_data); + reg_data[i] = temp_data; + aw_dev_dbg(aw882xx->dev, "[%d] : 0x%02x", i, reg_data[i]); + } + + ret = i2c_master_send(aw882xx->i2c, data_buf, data_len + 1); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "write failed"); + kfree(data_buf); + data_buf = NULL; + return -EFAULT; + } + + kfree(data_buf); + data_buf = NULL; + + aw_dev_info(aw882xx->dev, "write success"); + return 0; +} + +static int aw882xx_awrw_data_check(struct aw882xx *aw882xx, int *data) +{ + int reg_num_max = aw882xx->aw_pa->ops.aw_get_reg_num(); + + if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || + (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { + aw_dev_err(aw882xx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", + data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); + return -EINVAL; + } + + if (data[AWRW_HDR_REG_ADDR] >= reg_num_max) { + aw_dev_err(aw882xx->dev, "reg_addr[%d] > reg_max[%d]", + data[AWRW_HDR_REG_ADDR], reg_num_max); + return -EINVAL; + } + + return 0; +} + +/* flag addr_bytes data_bytes reg_num reg_addr*/ +static int aw882xx_awrw_parse_buf(struct aw882xx *aw882xx, + const char *buf, size_t count) +{ + int data[AWRW_HDR_MAX] = {0}; + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + int ret; + + if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + &data[AWRW_HDR_WR_FLAG], + &data[AWRW_HDR_ADDR_BYTES], + &data[AWRW_HDR_DATA_BYTES], + &data[AWRW_HDR_REG_NUM], + &data[AWRW_HDR_REG_ADDR]) == 5) { + ret = aw882xx_awrw_data_check(aw882xx, data); + if (ret < 0) + return ret; + + packet->reg_addr = data[AWRW_HDR_REG_ADDR]; + packet->reg_num = data[AWRW_HDR_REG_NUM]; + + if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { + return aw882xx_awrw_write(aw882xx, buf, count); + } else if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_READ) { + packet->status = AWRW_I2C_ST_READ; + aw_dev_info(aw882xx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + return 0; + } else { + aw_dev_err(aw882xx->dev, "please check str format, unsupport flag %d", + data[AWRW_HDR_WR_FLAG]); + return -EINVAL; + } + } else { + aw_dev_err(aw882xx->dev, "can not parse string"); + return -EINVAL; + } + +} + +static ssize_t aw882xx_awrw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int ret; + + if (count < AWRW_HDR_LEN) { + aw_dev_err(dev, "data count too smaller, please check write format"); + aw_dev_err(dev, "string %s", buf); + return -EINVAL; + } + + ret = aw882xx_awrw_parse_buf(aw882xx, buf, count); + if (ret) + return -EINVAL; + + return count; +} + +static ssize_t aw882xx_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + int data_len, len = 0; + int ret, i; + char *reg_data = NULL; + + if (packet->status != AWRW_I2C_ST_READ) { + aw_dev_err(aw882xx->dev, "please write read cmd first"); + return -EINVAL; + } + + data_len = AWRW_DATA_BYTES * packet->reg_num; + reg_data = (char *)kmalloc(data_len, GFP_KERNEL); + if (reg_data == NULL) { + aw_dev_err(aw882xx->dev, "memory alloc failed"); + ret = -EINVAL; + goto exit; + } + + ret = aw882xx_i2c_reads(aw882xx, packet->reg_addr, (char *)reg_data, data_len); + if (ret < 0) { + ret = -EFAULT; + goto exit; + } + + aw_dev_info(aw882xx->dev, "reg_addr 0x%02x, reg_num %d", + packet->reg_addr, packet->reg_num); + + for (i = 0; i < data_len; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + aw_dev_dbg(aw882xx->dev, "0x%02x", reg_data[i]); + } + + ret = len; + +exit: + if (reg_data) + kfree(reg_data); + + packet->status = AWRW_I2C_ST_NONE; + return ret; +} + + +static ssize_t aw882xx_drv_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "driver_ver: %s \n", AW882XX_DRIVER_VERSION); + + return len; +} + +static ssize_t aw882xx_dsp_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int cali_re; + int ret; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + ret = aw_dev_get_cali_re(aw882xx->aw_pa, &cali_re); + if (ret) { + len += snprintf(buf+len, PAGE_SIZE-len, + "read dsp_re failed!\n"); + return len; + } + + len += snprintf(buf+len, PAGE_SIZE-len, + "%d \n", cali_re); + + return len; +} + +static ssize_t aw882xx_fade_step_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + /*step 0 - 12*/ + if (1 == sscanf(buf, "%d", &databuf[0])) { + if (databuf[0] > (aw882xx->aw_pa->volume_desc.mute_volume)) { + aw_dev_info(aw882xx->dev, "step overflow %d Db", databuf[0]); + return count; + } + aw_dev_set_fade_vol_step(aw882xx->aw_pa, databuf[0]); + } + aw_dev_info(aw882xx->dev, "set step %d DB Done", databuf[0]); + + return count; +} + +static ssize_t aw882xx_fade_step_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "step: %d \n", aw_dev_get_fade_vol_step(aw882xx->aw_pa)); + + return len; +} + +static ssize_t aw882xx_dbg_prof_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (1 == sscanf(buf, "%d", &databuf[0])) { + if (databuf[0]) + aw882xx->dbg_en_prof = true; + else + aw882xx->dbg_en_prof = false; + } + aw_dev_info(aw882xx->dev, "en_prof %d Done", databuf[0]); + + return count; +} + +static ssize_t aw882xx_dbg_prof_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + " %d \n", aw882xx->dbg_en_prof); + + return len; +} + +static ssize_t aw882xx_sync_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int flag = 0; + int ret; + + ret = kstrtouint(buf, 0, &flag); + if (ret < 0) + return ret; + + flag = ((flag == false) ? false : true); + + aw_dev_info(aw882xx->dev, "set phase sync flag : [%d]", flag); + + aw882xx->phase_sync = flag; + + return count; +} + +static ssize_t aw882xx_sync_flag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "sync flag : %d\n", aw882xx->phase_sync); + + return len; +} + +static ssize_t aw882xx_print_dbg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int ret; + + ret = kstrtouint(buf, 0, &g_print_dbg); + if (ret < 0) + return ret; + + g_print_dbg = ((g_print_dbg == false) ? false : true); + + aw_dev_info(aw882xx->dev, "set g_print_dbg : [%d]", g_print_dbg); + + return count; +} + +static ssize_t aw882xx_print_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "g_print_dbg : %d\n", g_print_dbg); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, + aw882xx_awrw_show, aw882xx_awrw_store); +static DEVICE_ATTR(drv_ver, S_IRUGO, + aw882xx_drv_ver_show, NULL); +static DEVICE_ATTR(dsp_re, S_IRUGO, + aw882xx_dsp_re_show, NULL); +static DEVICE_ATTR(fade_step, S_IWUSR | S_IRUGO, + aw882xx_fade_step_show, aw882xx_fade_step_store); +static DEVICE_ATTR(dbg_prof, S_IWUSR | S_IRUGO, + aw882xx_dbg_prof_show, aw882xx_dbg_prof_store); +static DEVICE_ATTR(phase_sync, S_IWUSR | S_IRUGO, + aw882xx_sync_flag_show, aw882xx_sync_flag_store); +static DEVICE_ATTR(print_dbg, S_IWUSR | S_IRUGO, + aw882xx_print_dbg_show, aw882xx_print_dbg_store); + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_awrw.attr, + &dev_attr_drv_ver.attr, + &dev_attr_fade_step.attr, + &dev_attr_dbg_prof.attr, + &dev_attr_dsp_re.attr, + &dev_attr_phase_sync.attr, + &dev_attr_print_dbg.attr, + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes, +}; + +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int ret; + + aw_pr_info("enter addr=0x%x", i2c->addr); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + aw_dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + /*dev free all auto free*/ + aw882xx = aw882xx_malloc_init(i2c); + if (aw882xx == NULL) { + aw_dev_err(&i2c->dev, "malloc aw882xx failed"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, aw882xx); + + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + aw_dev_err(&i2c->dev, "failed to parse device tree node"); + return ret; + } + + /*get gpio resource*/ + ret = aw882xx_gpio_request(aw882xx); + if (ret) + return ret; + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + aw_dev_err(&i2c->dev, "aw882xx_read_chipid failed ret=%d", ret); + return ret; + } + + /*aw pa init*/ + ret = aw882xx_init(aw882xx, g_aw882xx_dev_cnt); + if (ret) + return ret; + + /*aw882xx irq*/ + aw882xx_interrupt_init(aw882xx); + + /*codec register*/ + ret = aw_componet_codec_register(aw882xx); + if (ret) { + aw_dev_err(&i2c->dev, "codec register failde"); + return ret; + } + + /*create attr*/ + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "error creating sysfs attr files"); + goto err_sysfs; + } + + /*set aw882xx to dev private*/ + dev_set_drvdata(&i2c->dev, aw882xx); + + /*i2c packet init*/ + aw882xx->i2c_packet.status = AWRW_I2C_ST_NONE; + aw882xx->i2c_packet.reg_num = 0; + aw882xx->i2c_packet.reg_addr = 0xff; + aw882xx->i2c_packet.reg_data = NULL; + + aw882xx->index = g_aw882xx_dev_cnt; + /*add device to total list*/ + mutex_lock(&g_aw882xx_lock); + g_aw882xx_dev_cnt++; + mutex_unlock(&g_aw882xx_lock); + aw_dev_info(&i2c->dev, "%s: dev_cnt %d \n", __func__, g_aw882xx_dev_cnt); + return ret; +err_sysfs: + aw_componet_codec_ops.unregister_codec(&i2c->dev); + + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw882xx->dev, "enter"); + + /*rm irq*/ + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + /*free gpio*/ + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + /*rm attr node*/ + sysfs_remove_group(&i2c->dev.kobj, &aw882xx_attribute_group); + + /*free device resource */ + aw_device_remove(aw882xx->aw_pa); + + /*unregister codec*/ + aw882xx->codec_ops->unregister_codec(&i2c->dev); + + /*remove device to total list*/ + mutex_lock(&g_aw882xx_lock); + g_aw882xx_dev_cnt--; + if (g_aw882xx_dev_cnt == 0) { + if (g_awinic_cfg) { + vfree(g_awinic_cfg); + g_awinic_cfg = NULL; + } + } + mutex_unlock(&g_aw882xx_lock); + + return 0; + +} + +static void aw882xx_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw882xx->dev, "enter"); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xxacf_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .shutdown = aw882xx_i2c_shutdown, + .id_table = aw882xx_i2c_id, +}; + + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + aw_pr_info("aw882xx driver version %s", AW882XX_DRIVER_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + aw_pr_err("fail to add aw882xx device into i2c"); + + return ret; +} +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h new file mode 100644 index 000000000000..debdd986d6aa --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -0,0 +1,170 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ +#include +#include +#include "aw_device.h" + +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define AW882XX_CHIP_ID_REG (0x00) +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 +#define AW882XX_DC_DELAY_TIME (1000) +#define AW882XX_LOAD_FW_DELAY_TIME (0) +#define AW_START_RETRIES (5) + + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ + + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +enum { + AW882XX_STREAM_CLOSE = 0, + AW882XX_STREAM_OPEN, +}; + +enum aw882xx_chipid { + PID_1852_ID = 0x1852, + PID_2013_ID = 0x2013, + PID_2032_ID = 0x2032, + PID_2055_ID = 0x2055, + PID_2071_ID = 0x2071, + PID_2113_ID = 0x2113, +}; + +#define AW882XX_SOFT_RESET_VALUE (0x55aa) + +enum aw882xx_int_type { + INT_TYPE_NONE = 0, + INT_TYPE_UVLO = 0x1, + INT_TYPE_BSTOC = 0x2, + INT_TYPE_OCDI = 0x4, + INT_TYPE_OTHI = 0x8, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*kcontrol_codec)(struct snd_kcontrol *kcontrol); + void *(*codec_get_drvdata)(aw_snd_soc_codec_t *codec); + int (*add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*unregister_codec)(struct device *dev); + int (*register_codec)(struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai); +}; + +enum { + AWRW_I2C_ST_NONE = 0, + AWRW_I2C_ST_READ, + AWRW_I2C_ST_WRITE, +}; + +#define AWRW_ADDR_BYTES (1) +#define AWRW_DATA_BYTES (2) +#define AWRW_HDR_LEN (24) + +enum { + AWRW_FLAG_WRITE = 0, + AWRW_FLAG_READ, +}; + +enum { + AW_BSTCFG_DISABLE = 0, + AW_BSTCFG_ENABLE, +}; + +enum { + AW_FRCPWM_DISABLE = 0, + AW_FRCPWM_ENABLE, +}; + +enum { + AWRW_HDR_WR_FLAG = 0, + AWRW_HDR_ADDR_BYTES, + AWRW_HDR_DATA_BYTES, + AWRW_HDR_REG_NUM, + AWRW_HDR_REG_ADDR, + AWRW_HDR_MAX, +}; + +struct aw882xx_i2c_packet{ + char status; + unsigned int reg_num; + unsigned int reg_addr; + char *reg_data; +}; + +/******************************************** + * struct aw882xx + *******************************************/ +struct aw882xx { + int sysclk; + int rate; + int pstream; + int cstream; + + unsigned char index; + unsigned char phase_sync; /* phase sync */ + unsigned char dc_flag; + unsigned char dbg_en_prof; /* debug enable/disable profile function */ + unsigned char allow_pw; /* allow power */ + int reset_gpio; + int irq_gpio; + unsigned int chip_id; + unsigned char fw_status; + unsigned char fw_retry_cnt; + unsigned char rw_reg_addr; /* rw attr node store read addr */ + + aw_snd_soc_codec_t *codec; + struct aw_componet_codec_ops *codec_ops; + + struct i2c_client *i2c; + struct device *dev; + struct aw882xx_i2c_packet i2c_packet; + struct aw_device *aw_pa; + + struct workqueue_struct *work_queue; + struct delayed_work start_work; + struct delayed_work interrupt_work; + struct delayed_work dc_work; + struct delayed_work fw_work; + + struct mutex lock; +}; + +void aw882xx_kcontorl_set(struct aw882xx *aw882xx); +int aw882xx_get_version(char *buf, int size); +int aw882xx_get_dev_num(void); +int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data); +int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data); +int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data); +int aw882xx_init(struct aw882xx *aw882xx, int index); +int aw882xx_hw_reset(struct aw882xx *aw882xx); + + +#endif + diff --git a/asoc/codecs/aw882xxacf/aw_bin_parse.c b/asoc/codecs/aw882xxacf/aw_bin_parse.c new file mode 100644 index 000000000000..d7cfcb555df3 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_bin_parse.c @@ -0,0 +1,512 @@ +/* +* aw_bin_parse.c +* +* Copyright (c) 2020 AWINIC Technology CO., LTD +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw_bin_parse.h" + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +int aw_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} diff --git a/asoc/codecs/aw882xxacf/aw_bin_parse.h b/asoc/codecs/aw882xxacf/aw_bin_parse.h new file mode 100644 index 000000000000..e8fa6dd84199 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_bin_parse.h @@ -0,0 +1,73 @@ +#ifndef __AW_BIN_PARSE_H__ +#define __AW_BIN_PARSE_H__ + +#define NULL ((void *)0) +#define GET_32_DATA(w, x, y, z) ((unsigned int)((((uint32_t)w) << 24) | (((uint32_t)x) << 16) | (((uint32_t)y) << 8) | ((uint32_t)z))) +#define BIN_NUM_MAX 100 +#define HEADER_LEN 60 +/********************************************************* + * + * header information + * + ********************************************************/ +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[8]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +/************************************************************ +* +* function define +* +************************************************************/ +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + unsigned char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +int aw_parsing_bin_file(struct aw_bin *bin); +int aw_parse_bin_header_1_0_0(struct aw_bin *bin); +#endif diff --git a/asoc/codecs/aw882xxacf/aw_calib.c b/asoc/codecs/aw882xxacf/aw_calib.c new file mode 100644 index 000000000000..480850acc220 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_calib.c @@ -0,0 +1,2243 @@ +/* + * aw_calibration.c cali_module + * + * + * Copyright (c) 2020 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_dsp.h" +#include "aw_log.h" +#include "aw_calib.h" + +static bool is_single_cali = false; /*if mutli_dev cali false, single dev true*/ + +static const char *cali_str[CALI_STR_MAX] = {"none", "start_cali", "cali_re", + "cali_f0", "store_re", "show_re", "show_r0", "show_cali_f0", "show_f0", + "show_te", "show_st", "dev_sel", "get_ver", "get_dev_num", + "cali_q_f0", "show_q_f0" +}; + +static char *ch_name[AW_DEV_CH_MAX] = {"pri_l", "pri_r", "sec_l", "sec_r"}; +static unsigned int g_cali_re_time = AW_CALI_RE_DEFAULT_TIMER; +static unsigned int g_msic_wr_flag = CALI_STR_NONE; +static unsigned int g_dev_select = AW_DEV_CH_PRI_L; +static unsigned int g_cali_status = false; +static struct miscdevice *g_misc_dev = NULL; +static DEFINE_MUTEX(g_cali_lock); + + +#ifdef AW_CALI_STORE_EXAMPLE + /*write cali to persist file example*/ +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" +#define AW_INT_DEC_DIGIT 10 + +static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) +{ + struct file *fp = NULL; + char buf[50] = {0}; + loff_t pos = 0; + mm_segment_t fs; + + fp = filp_open(AWINIC_CALI_FILE, O_RDWR | O_CREAT, 0644); + if (IS_ERR(fp)) { + pr_err("%s:channel:%d open %s failed!\n", + __func__, channel, AWINIC_CALI_FILE); + return -EINVAL; + } + + pos = AW_INT_DEC_DIGIT * channel; + + snprintf(buf, sizeof(buf), "%10d", cali_re); + + fs = get_fs(); + set_fs(KERNEL_DS); + + kernel_write(fp, buf, strlen(buf), &pos); + + set_fs(fs); + + pr_info("%s: channel:%d buf:%s cali_re:%d\n", + __func__, channel, buf, cali_re); + + filp_close(fp, NULL); + return 0; +} + +static int aw_cali_get_read_cali_re(int32_t *cali_re, int channel) +{ + struct file *fp = NULL; + /*struct inode *node;*/ + int f_size; + char *buf = NULL; + int32_t int_cali_re = 0; + loff_t pos = 0; + mm_segment_t fs; + + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s:channel:%d open %s failed!\n", + __func__, channel, AWINIC_CALI_FILE); + return -EINVAL; + } + + pos = AW_INT_DEC_DIGIT * channel; + + /*node = fp->f_dentry->d_inode;*/ + /*f_size = node->i_size;*/ + f_size = AW_INT_DEC_DIGIT; + + buf = kzalloc(f_size + 1, GFP_ATOMIC); + if (!buf) { + pr_err("%s: channel:%d malloc mem %d failed!\n", + __func__, channel, f_size); + filp_close(fp, NULL); + return -EINVAL; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + + kernel_read(fp, buf, f_size, &pos); + + set_fs(fs); + + if (sscanf(buf, "%d", &int_cali_re) == 1) + *cali_re = int_cali_re; + else + *cali_re = AW_ERRO_CALI_VALUE; + + pr_info("%s: channel:%d buf:%s int_cali_re: %d\n", + __func__, channel, buf, int_cali_re); + + kfree(buf); + buf = NULL; + filp_close(fp, NULL); + + return 0; +} +#endif + +/*custom need add to set/get cali_re form/to nv*/ +int aw_cali_write_re_to_nvram(int32_t cali_re, int32_t channel) +{ +#ifdef AW_CALI_STORE_EXAMPLE + if (channel >= AW_DEV_CH_MAX) { + pr_err("%s: unsupported channel [%d] \n", __func__, channel); + return -EINVAL; + } + return aw_cali_write_cali_re_to_file(cali_re, channel); +#else + return -EBUSY; +#endif +} + +int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t channel) +{ + /*custom add, if success return value is 0 , else -1*/ +#ifdef AW_CALI_STORE_EXAMPLE + if (channel >= AW_DEV_CH_MAX) { + pr_err("%s: unsupported channel [%d] \n", __func__, channel); + return -EINVAL; + } + return aw_cali_get_read_cali_re(cali_re, channel); +#else + return -EBUSY; +#endif +} + +int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) +{ + int ret = 0; + + if ((re >= aw_dev->re_min) && (re <= aw_dev->re_max)) { + aw_dev->cali_desc.cali_re = re; + ret = aw_cali_write_re_to_nvram(re, aw_dev->channel); + if (ret) + aw_dev_err(aw_dev->dev, "write re to nvram failed!"); + } else { + aw_dev_err(aw_dev->dev, "invalid cali re %d !", re); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "store cali re is %d", re); + return ret; +} + +/*********************************aw_cali_sevice*********************************************************/ +static void aw_cali_svc_set_cali_status(struct aw_device *aw_dev, int status) +{ + if (status) + g_cali_status = true; + else + g_cali_status = false; + + aw_dev_info(aw_dev->dev, "cali %s", + (status == 0) ? ("disable") : ("enable")); +} + +int aw_cali_svc_get_cali_status(void) +{ + return g_cali_status; +} + +static int aw_cali_svc_dev_get_re(struct aw_device *aw_dev) +{ + int32_t re[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_r0(aw_dev, &re[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get re failed!"); + return ret; + } + sum += re[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + re[0] = sum / AW_CALI_READ_TIMES; + + aw_dev->cali_desc.cali_re = re[0]; + + if ((aw_dev->cali_desc.cali_re >= aw_dev->re_min) && + (aw_dev->cali_desc.cali_re <= aw_dev->re_max)) { + ret = aw_cali_write_re_to_nvram(aw_dev->cali_desc.cali_re, aw_dev->channel); + if (ret) + aw_dev_err(aw_dev->dev, "write re to nvram failed!"); + } + return 0; +} + +static int aw_cali_svc_dev_get_f0(struct aw_device *aw_dev) +{ + int32_t f0[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_f0(aw_dev, &f0[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get f0 failed!"); + return ret; + } + sum += f0[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + f0[0] = sum / AW_CALI_READ_TIMES; + + aw_dev->cali_desc.cali_f0 = f0[0]; + return 0; +} + +static int aw_cali_svc_dev_get_f0_q(struct aw_device *aw_dev) +{ + int32_t f0[AW_CALI_READ_TIMES], q[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum_f0 = 0, sum_q = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_f0_q(aw_dev, &f0[i], &q[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get f0 failed!"); + return ret; + } + sum_f0 += f0[i]; + sum_q += q[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + f0[0] = sum_f0 / AW_CALI_READ_TIMES; + q[0] = sum_q / AW_CALI_READ_TIMES; + aw_dev->cali_desc.cali_f0 = f0[0]; + aw_dev->cali_desc.cali_q = q[0]; + aw_dev_dbg(aw_dev->dev, "cali f0 is %d, q is %d", + aw_dev->cali_desc.cali_f0, aw_dev->cali_desc.cali_q); + return 0; +} + + +static int aw_cali_svc_dev_cali_mode_en(struct aw_device *aw_dev, int type, bool is_enable, unsigned int flag) +{ + int ret; + + /* open cali mode */ + if (is_enable) { + aw_cali_svc_set_cali_status(aw_dev, true); + + if (type == CALI_TYPE_RE) { + if (flag & CALI_OPS_HMUTE) { + ret = aw_dsp_hmute_en(aw_dev, true); + if (ret < 0) + return ret; + } + } else { + if (flag & CALI_OPS_NOISE) { + ret = aw_dsp_noise_en(aw_dev, true); + if (ret < 0) + return ret; + } + } + + ret = aw_dsp_cali_en(aw_dev, true); + if (ret < 0) + return ret; + } else { + aw_dsp_cali_en(aw_dev, false); + + if (type == CALI_TYPE_RE) { + /*close mute*/ + if (flag & CALI_OPS_HMUTE) + aw_dsp_hmute_en(aw_dev, false); + } else { + /*close mute*/ + if (flag & CALI_OPS_NOISE) + aw_dsp_noise_en(aw_dev, false); + } + + /*close cali mode*/ + aw_cali_svc_set_cali_status(aw_dev, false); + } + return 0; +} + +static int aw_cali_svc_dev_cali_re(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_RE, true, flag); + if (ret < 0) + goto exit; + + /*wait time*/ + msleep(g_cali_re_time); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_re(aw_dev); + if (ret < 0) + goto exit; +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_RE, false, flag); + + return ret; +} + +static int aw_cali_svc_devs_cali_re(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos; + struct aw_device *local_dev; + int ret; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + /* enable all dev cali mode */ + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_RE, true, flag); + if (ret < 0) + goto exit; + } + + /* wait time */ + msleep(g_cali_re_time); + + /* dev get echo cali re */ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_re(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /* close cali mode */ + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_RE, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_re(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_re(aw_dev, flag); + else + return aw_cali_svc_devs_cali_re(aw_dev, flag); + return 0; +} + +static int aw_cali_svc_dev_cali_f0(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_f0(aw_dev); + if (ret < 0) + goto exit; + +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, false, flag); + + return ret; +} + +static int aw_cali_svc_devs_cali_f0(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, " get dev list failed"); + return ret; + } + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /* set mode status */ + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + } + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_f0(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /*close cali*/ + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_f0(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_f0(aw_dev, flag); + else + return aw_cali_svc_devs_cali_f0(aw_dev, flag); + return 0; +} + +static int aw_cali_svc_dev_cali_f0_q(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_f0_q(aw_dev); + if (ret < 0) + goto exit; + +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, false, flag); + + return ret; + +} + +static int aw_cali_svc_devs_cali_f0_q(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /*set mode status*/ + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + } + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_f0_q(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_f0_q(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_f0_q(aw_dev, flag); + else + return aw_cali_svc_devs_cali_f0_q(aw_dev, flag); + + return 0; +} + +static int aw_cali_svc_cali_re_f0(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_cali_re(aw_dev, is_single, flag); + if (ret) + return ret; + + ret = aw_cali_svc_cali_f0(aw_dev, is_single, flag); + if (ret) + return ret; + + return 0; +} + +static int aw_cali_svc_cali_re_f0_q(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_cali_re(aw_dev, is_single, flag); + if (ret) + return ret; + + ret = aw_cali_svc_cali_f0_q(aw_dev, is_single, flag); + if (ret) + return ret; + + return 0; +} + +int aw_cali_svc_cali_cmd(struct aw_device *aw_dev, int cali_cmd, bool is_single, unsigned int flag) +{ + switch (cali_cmd) { + case AW_CALI_CMD_RE: + return aw_cali_svc_cali_re(aw_dev, is_single, flag); + case AW_CALI_CMD_F0: + return aw_cali_svc_cali_f0(aw_dev, is_single, flag); + case AW_CALI_CMD_F0_Q: + return aw_cali_svc_cali_f0_q(aw_dev, is_single, flag); + case AW_CALI_CMD_RE_F0: + return aw_cali_svc_cali_re_f0(aw_dev, is_single, flag); + case AW_CALI_CMD_RE_F0_Q: + return aw_cali_svc_cali_re_f0_q(aw_dev, is_single, flag); + default: + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cali_cmd); + return -EINVAL; + } + return 0; +} + +int aw_cali_svc_get_devs_cali_re(struct aw_device *aw_dev, int32_t *re_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + re_buf[local_dev->channel] = local_dev->cali_desc.cali_re; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + return -EINVAL; + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_r0(struct aw_device *aw_dev, int32_t *re_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_r0(local_dev, &re_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d] ", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_te(struct aw_device *aw_dev, int32_t *te_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_te(local_dev, &te_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_st(struct aw_device *aw_dev, int32_t *st_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_st(local_dev, &st_buf[local_dev->channel << 1], &st_buf[(local_dev->channel << 1) + 1]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_cali_f0(struct aw_device *aw_dev, int32_t *f0_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + f0_buf[local_dev->channel] = local_dev->cali_desc.cali_f0; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_f0(struct aw_device *aw_dev, int32_t *f0_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_f0(local_dev, &f0_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_cali_f0_q(struct aw_device *aw_dev, + int32_t *f0_buf, int32_t *q_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + f0_buf[local_dev->channel] = local_dev->cali_desc.cali_f0; + q_buf[local_dev->channel] = local_dev->cali_desc.cali_q; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +static int aw_cali_svc_set_devs_re_str(struct aw_device *aw_dev, const char *re_str) +{ + struct list_head *dev_list, *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + int re_data[AW_DEV_CH_MAX] = {0}; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + ret = sscanf(re_str, "pri_l:%d pri_r:%d sec_l:%d sec_r:%d", + &re_data[AW_DEV_CH_PRI_L], + &re_data[AW_DEV_CH_PRI_R], + &re_data[AW_DEV_CH_SEC_L], + &re_data[AW_DEV_CH_SEC_R]); + + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "unsupport str[%s]", re_str); + return ret; + } + + aw_dev_dbg(aw_dev->dev, "pri_l:%d pri_r:%d sec_l:%d sec_r:%d", + re_data[AW_DEV_CH_PRI_L], + re_data[AW_DEV_CH_PRI_R], + re_data[AW_DEV_CH_SEC_L], + re_data[AW_DEV_CH_SEC_R]); + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < AW_DEV_CH_MAX) { + aw_cali_store_cali_re(local_dev, re_data[local_dev->channel]); + cnt++; + } + } + return cnt; + +} + +static int aw_cali_svc_get_cmd_form_str(struct aw_device *aw_dev, const char *buf) +{ + int i; + + for (i = 0; i < CALI_STR_MAX; i++) { + if (!strncmp(cali_str[i], buf, strlen(cali_str[i]))) { + break; + } + } + + if (i == CALI_STR_MAX) { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "find str [%s]", cali_str[i]); + return i; +} + +/*********************************aw_cali_sevice*********************************************************/ + + +/*****************************attr start***************************************************/ +static ssize_t aw_cali_attr_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + uint32_t time; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + + ret = kstrtoint(buf, 0, &time); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read buf %s failed\n", + buf); + return ret; + } + + if (time < 400) { + aw_dev_err(aw_dev->dev, "time:%d is too short, no set", + time); + return -EINVAL; + } + + g_cali_re_time = time; + aw_dev_dbg(aw_dev->dev, "time:%d", time); + + return count; +} + +static ssize_t aw_cali_attr_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "time: %d \n", g_cali_re_time); + + return len; +} + +static ssize_t aw_cali_attr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int ret; + + ret = aw_cali_svc_get_cmd_form_str(aw_dev, buf); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + return -EPERM; + } + + if (ret == CALI_STR_CALI_RE_F0) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + return count; + } else if (ret == CALI_STR_CALI_RE) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, + is_single_cali, CALI_OPS_HMUTE); + return count; + } else if (ret == CALI_STR_CALI_F0) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, + is_single_cali, CALI_OPS_NOISE); + return count; + } else { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + } + + return -EPERM; + +} + +static ssize_t aw_cali_attr_re_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int ret; + int data; + + if (is_single_cali) { + ret = kstrtoint(buf, 0, &data); + if (ret < 0) { + aw_dev_err(aw882xx->dev, " read buf %s failed", buf); + return ret; + } + aw_cali_store_cali_re(aw_dev, data); + } else { + ret = aw_cali_svc_set_devs_re_str(aw_dev, buf); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "set re str %s failed", buf); + return -EPERM; + } + } + + return count; +} + +static ssize_t aw_cali_attr_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, is_single_cali, CALI_OPS_HMUTE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali Re cmd failed\n"); + return len; + } + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_re); + } else { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get re failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get re failed\n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +static ssize_t aw_cali_attr_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, is_single_cali, CALI_OPS_NOISE); + return count; +} + +static ssize_t aw_cali_attr_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, is_single_cali, CALI_OPS_NOISE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali f0 cmd failed\n"); + return len; + } + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_f0); + } else { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get f0 failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +static ssize_t aw_cali_attr_show_re(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_re); + } else { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + } + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_attr_show_f0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_f0); + } else { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +/*set cali time*/ +static DEVICE_ATTR(cali_time, S_IWUSR | S_IRUGO, + aw_cali_attr_time_show, aw_cali_attr_time_store); +/*start cali*/ +static DEVICE_ATTR(cali, S_IWUSR, + NULL, aw_cali_attr_store); +/*cali re*/ +static DEVICE_ATTR(cali_re, S_IRUGO | S_IWUSR, + aw_cali_attr_re_show, aw_cali_attr_re_store); +/*cali_f0*/ +static DEVICE_ATTR(cali_f0, S_IRUGO | S_IWUSR, + aw_cali_attr_f0_show, aw_cali_attr_f0_store); +/*show cali_re*/ +static DEVICE_ATTR(re_show, S_IRUGO, + aw_cali_attr_show_re, NULL); +/*show cali_f0*/ +static DEVICE_ATTR(f0_show, S_IRUGO, + aw_cali_attr_show_f0, NULL); + + +static struct attribute *aw_cali_attr[] = { + &dev_attr_cali_time.attr, + &dev_attr_cali.attr, + &dev_attr_cali_re.attr, + &dev_attr_cali_f0.attr, + &dev_attr_re_show.attr, + &dev_attr_f0_show.attr, + NULL +}; + +static struct attribute_group aw_cali_attr_group = { + .attrs = aw_cali_attr +}; + +static void aw_cali_attr_init(struct aw_device *aw_dev) +{ + int ret; + + aw_dev_info(aw_dev->dev, "enter"); + + ret = sysfs_create_group(&aw_dev->dev->kobj, &aw_cali_attr_group); + if (ret < 0) + aw_dev_info(aw_dev->dev, "error creating sysfs cali attr files"); +} + +static void aw_cali_attr_deinit(struct aw_device *aw_dev) +{ + sysfs_remove_group(&aw_dev->dev->kobj, &aw_cali_attr_group); + aw_dev_info(aw_dev->dev, "attr files deinit"); +} + +/*****************************attr end***************************************************/ + +/*****************************class node******************************************************/ +static ssize_t aw_cali_class_time_show(struct class *class, struct class_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "time: %d \n", g_cali_re_time); + + return len; +} + +static ssize_t aw_cali_class_time_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + int ret; + uint32_t time; + + ret = kstrtoint(buf, 0, &time); + if (ret < 0) { + pr_err("[Awinic] %s, read buf %s failed\n", + __func__, buf); + return ret; + } + + if (time < 400) { + pr_err("[Awinic] %s:time:%d is too short, no set\n", + __func__, time); + return -EINVAL; + } + + g_cali_re_time = time; + pr_debug("%s:time:%d\n", __func__, time); + + return len; +} + +static ssize_t aw_cali_class_cali_re_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_RE, false, CALI_OPS_HMUTE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali Re cmd failed\n"); + return len; + } + + ret = aw_cali_svc_get_devs_cali_re(local_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get re failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_cali_re_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_set_devs_re_str(local_dev, buf); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "set re str %s failed", buf); + return -EPERM; + } + + return len; +} + +static ssize_t aw_cali_class_cali_f0_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_F0, false, CALI_OPS_NOISE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali f0 cmd failed\n"); + return len; + } + + ret = aw_cali_svc_get_devs_cali_f0(local_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get f0 failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get f0 failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_cali_f0_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_F0, false, CALI_OPS_NOISE); + + return len; +} + +static ssize_t aw_cali_class_f0_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_get_devs_cali_f0(local_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_re_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_get_devs_cali_re(local_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static struct class_attribute class_attr_cali_time = \ + __ATTR(cali_time, S_IWUSR | S_IRUGO, \ + aw_cali_class_time_show, aw_cali_class_time_store); + +static struct class_attribute class_attr_re25_calib = \ + __ATTR(re25_calib, S_IWUSR | S_IRUGO, \ + aw_cali_class_cali_re_show, aw_cali_class_cali_re_store); + +static struct class_attribute class_attr_f0_calib = \ + __ATTR(f0_calib, S_IWUSR | S_IRUGO, \ + aw_cali_class_cali_f0_show, aw_cali_class_cali_f0_store); + +static struct class_attribute class_attr_re_show = \ + __ATTR(re_show, S_IRUGO, \ + aw_cali_class_re_show, NULL); + +static struct class_attribute class_attr_f0_show = \ + __ATTR(f0_show, S_IRUGO, \ + aw_cali_class_f0_show, NULL); + + +static struct class aw_cali_class = { + .name = "smartpa", + .owner = THIS_MODULE, +}; + +static void aw_cali_class_attr_init(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->index != 0) { + aw_dev_info(aw_dev->dev, "class node already register"); + return; + } + + ret = class_register(&aw_cali_class); + if (ret < 0) { + aw_dev_info(aw_dev->dev, "error creating class node"); + return; + } + ret = class_create_file(&aw_cali_class, &class_attr_re25_calib); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re25_calib fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_f0_calib); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re25_calib fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_cali_time); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_cali_time fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_re_show); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re_show fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_f0_show); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_f0_show fail"); + } +} + +static void aw_cali_class_attr_deinit(struct aw_device *aw_dev) +{ + class_remove_file(&aw_cali_class, &class_attr_re25_calib); + class_remove_file(&aw_cali_class, &class_attr_f0_calib); + class_remove_file(&aw_cali_class, &class_attr_cali_time); + class_remove_file(&aw_cali_class, &class_attr_re_show); + class_remove_file(&aw_cali_class, &class_attr_f0_show); + + class_unregister(&aw_cali_class); + aw_dev_info(aw_dev->dev, "unregister class node"); +} +/*****************************class node******************************************************/ + +/*****************************misc node******************************************************/ +static int aw_cali_misc_open(struct inode *inode, struct file *file) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + file->private_data = NULL; + return -EINVAL; + } + + /* find select dev */ + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel == g_dev_select) { + break; + } + } + + /* cannot find sel dev, use list first dev */ + if (local_dev->channel != g_dev_select) { + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + aw_dev_dbg(local_dev->dev, "can not find [%s], use default", ch_name[g_dev_select]); + } + + file->private_data = (void *)local_dev; + + aw_dev_dbg(local_dev->dev, "misc open success\n"); + return 0; +} + +static int aw_cali_misc_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_debug("misc release successi\n"); + return 0; +} + +static int aw_cali_misc_params_ptr(struct aw_device *aw_dev, struct ptr_params_data *p_params) +{ + char *p_data = NULL; + int ret = 0; + + if (p_params->data == NULL || (!p_params->len)) { + aw_dev_err(aw_dev->dev, "p_params error"); + ret = -EFAULT; + return ret; + } + + p_data = kzalloc(p_params->len, GFP_KERNEL); + if (p_data == NULL) { + aw_dev_err(aw_dev->dev, "error allocating memory"); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(p_data, + (void __user *)p_params->data, + p_params->len)) { + ret = -EFAULT; + goto exit; + } + + ret = aw_dsp_write_params(aw_dev, p_data, p_params->len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "write params failed "); + ret = -EFAULT; + goto exit; + } + +exit: + if (p_data != NULL) { + kfree(p_data); + p_data = NULL; + } + + return ret; +} + +static int aw_cali_misc_ops_write(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + unsigned int data_len = _IOC_SIZE(cmd); + char *data_ptr = NULL; + int32_t data = 0; + int ret = 0; + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc ops write"); + return -EINVAL; + } + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + + switch (cmd) { + case AW_IOCTL_ENABLE_CALI: { + aw_cali_svc_set_cali_status(aw_dev, data_ptr[0]); + } break; + case AW_IOCTL_SET_CALI_CFG: { + ret = aw_dsp_write_cali_cfg(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_NOISE: { + data = *(int32_t *)data_ptr; + ret = aw_dsp_noise_en(aw_dev, data); + } break; + case AW_IOCTL_SET_VMAX: { + ret = aw_dsp_write_vmax(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_PARAM: { + ret = aw_dsp_write_params(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_PTR_PARAM_NUM: { + ret = aw_cali_misc_params_ptr(aw_dev, (struct ptr_params_data *)data_ptr); + } break; + case AW_IOCTL_SET_CALI_RE: { + aw_cali_store_cali_re(aw_dev, *((int32_t *)data_ptr)); + /*ret = aw_dsp_write_cali_re(aw_dev, *(int32_t *)data_ptr);*/ + } break; + case AW_IOCTL_SET_DSP_HMUTE: { + data = *(int32_t *)data_ptr; + ret = aw_dsp_hmute_en(aw_dev, data); + } break; + case AW_IOCTL_SET_CALI_CFG_FLAG: { + data = *(int32_t *)data_ptr; + /*aw_cali_svc_set_cali_status(aw_dev, data);*/ + ret = aw_dsp_cali_en(aw_dev, data); + } break; + default:{ + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + } break; + } + +exit: + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_ops_read(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + char *data_ptr = NULL; + int32_t *data_32_ptr = NULL; + int ret = 0; + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc ops read"); + return -EINVAL; + } + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + switch (cmd) { + case AW_IOCTL_GET_CALI_CFG: { + ret = aw_dsp_read_cali_cfg(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_CALI_DATA: { + ret = aw_dsp_read_cali_data(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_F0: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_f0(aw_dev, data_32_ptr); + } break; + case AW_IOCTL_GET_CALI_RE: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_cali_re(aw_dev, data_32_ptr); + } break; + case AW_IOCTL_GET_VMAX: { + ret = aw_dsp_read_vmax(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_F0_Q: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_f0_q(aw_dev, &data_32_ptr[0], &data_32_ptr[1]); + } break; + default:{ + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + } break; + } + + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + } + + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_read_dsp(struct aw_device *aw_dev, aw_ioctl_msg_t *msg) +{ + char __user *user_data = (char __user *)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + ret = aw_dsp_read_msg(aw_dev, dsp_msg_id, data_ptr, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, " write failed"); + goto exit; + } + + if (copy_to_user((void __user *)user_data, + data_ptr, data_len)) + ret = -EFAULT; +exit: + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_write_dsp(struct aw_device *aw_dev, aw_ioctl_msg_t *msg) +{ + char __user *user_data = (char __user *)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !\n"); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)user_data, data_len)) { + aw_dev_err(aw_dev->dev, "copy data failed"); + ret = -EFAULT; + goto exit; + } + + ret = aw_dsp_write_msg(aw_dev, dsp_msg_id, data_ptr, data_len); + if (ret) + aw_dev_err(aw_dev->dev, "write failed"); + +exit: + kfree(data_ptr); + return ret; +} + + +static int aw_cali_misc_ops(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg); +static int aw_cali_misc_ops_msg(struct aw_device *aw_dev, unsigned long arg) +{ + aw_ioctl_msg_t ioctl_msg; + + if (copy_from_user(&ioctl_msg, (void __user *)arg, sizeof(aw_ioctl_msg_t))) + return -EFAULT; + + if (ioctl_msg.version != AW_IOCTL_MSG_VERSION) { + aw_dev_err(aw_dev->dev, "unsupported msg version %d", ioctl_msg.version); + return -EINVAL; + } + + if (ioctl_msg.type == AW_IOCTL_MSG_RD_DSP) { + return aw_cali_misc_read_dsp(aw_dev, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_WR_DSP) { + return aw_cali_misc_write_dsp(aw_dev, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_IOCTL) { + return aw_cali_misc_ops(aw_dev, ioctl_msg.opcode_id, (unsigned long)ioctl_msg.data_buf); + } else { + aw_dev_err(aw_dev->dev, "unsupported msg type %d", ioctl_msg.type); + return -EINVAL; + } +} + +static int aw_cali_misc_ops(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case AW_IOCTL_ENABLE_CALI: + case AW_IOCTL_SET_CALI_CFG: + case AW_IOCTL_SET_NOISE: + case AW_IOCTL_SET_VMAX: + case AW_IOCTL_SET_PARAM: + case AW_IOCTL_SET_PTR_PARAM_NUM: + case AW_IOCTL_SET_CALI_RE: + case AW_IOCTL_SET_DSP_HMUTE: + case AW_IOCTL_SET_CALI_CFG_FLAG: + ret = aw_cali_misc_ops_write(aw_dev, cmd, arg); + break; + case AW_IOCTL_GET_CALI_CFG: + case AW_IOCTL_GET_CALI_DATA: + case AW_IOCTL_GET_F0: + case AW_IOCTL_GET_CALI_RE: + case AW_IOCTL_GET_VMAX: + case AW_IOCTL_GET_F0_Q: + ret = aw_cali_misc_ops_read(aw_dev, cmd, arg); + break; + case AW_IOCTL_MSG: { + ret = aw_cali_misc_ops_msg(aw_dev, arg); + } break; + default: + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +static long aw_cali_misc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw_device *aw_dev = NULL; + + if (((_IOC_TYPE(cmd)) != (AW_IOCTL_MAGIC))) { + aw_dev_err(aw_dev->dev, " cmd magic err"); + return -EINVAL; + } + aw_dev = (struct aw_device *)file->private_data; + ret = aw_cali_misc_ops(aw_dev, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +#ifdef CONFIG_COMPAT +static long aw_cali_misc_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw_device *aw_dev = NULL; + + if (((_IOC_TYPE(cmd)) != (AW_IOCTL_MAGIC))) { + aw_dev_err(aw_dev->dev, "cmd magic err"); + return -EINVAL; + } + aw_dev = (struct aw_device *)file->private_data; + ret = aw_cali_misc_ops(aw_dev, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} +#endif + +static ssize_t aw_cali_misc_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + int i, ret; + struct aw_device *aw_dev = (struct aw_device *)filp->private_data; + char local_buf[128] = { 0 }; + unsigned int dev_num; + int32_t temp_data[AW_DEV_CH_MAX << 1] = {0}; + int32_t temp_data1[AW_DEV_CH_MAX << 1] = {0}; + + aw_dev_info(aw_dev->dev, "enter"); + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc read"); + return -EINVAL; + } + + if (*pos) { + *pos = 0; + return 0; + } + + switch (g_msic_wr_flag) { + case CALI_STR_SHOW_RE: { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get cali_re failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d mOhms ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_CALI_F0: { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get cali f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_R0: { + ret = aw_cali_svc_get_devs_r0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get r0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d mOhms ", ch_name[i], temp_data[i]); + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_TE: { + ret = aw_cali_svc_get_devs_te(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get te failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_ST: { + ret = aw_cali_svc_get_devs_st(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get spkr status failed"); + return ret; + } else { + for (i = 0; i < ret; i++) { + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:R0 %d mOhms Te %d ", + ch_name[i], temp_data[i << 1], temp_data[(i << 1) + 1]); + } + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_F0: { + ret = aw_cali_svc_get_devs_f0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } + } break; + case CALI_STR_VER: { + if (aw_dev->ops.aw_get_version) { + len = aw_dev->ops.aw_get_version(local_buf, sizeof(local_buf)); + if (len < 0) { + aw_dev_err(aw_dev->dev, "get version failed"); + return -EINVAL; + } + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } else { + aw_dev_err(aw_dev->dev, "get version is NULL"); + return -EINVAL; + } + } break; + case CALI_STR_DEV_NUM: { + if (aw_dev->ops.aw_get_dev_num) { + dev_num = aw_dev->ops.aw_get_dev_num(); + len += snprintf(local_buf + len, sizeof(local_buf) - len, "dev_num:%d\n", dev_num); + } else { + aw_dev_err(aw_dev->dev, "get dev num is NULL"); + return -EINVAL; + } + break; + } + case CALI_STR_SHOW_F0_Q: { + ret = aw_cali_svc_get_devs_cali_f0_q(aw_dev, + temp_data, temp_data1, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get q f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:f0:%d q:%d", ch_name[i], temp_data[i], temp_data1[i]); + + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } + } break; + default: { + if (g_msic_wr_flag == CALI_STR_NONE) { + aw_dev_info(aw_dev->dev, "please write cmd first"); + return -EINVAL; + } else { + aw_dev_err(aw_dev->dev, "unsupported flag [%d]", g_msic_wr_flag); + g_msic_wr_flag = CALI_STR_NONE; + return -EINVAL; + } + } break; + } + + if (copy_to_user((void __user *)buf, local_buf, len)) { + aw_dev_err(aw_dev->dev, "copy_to_user error"); + g_msic_wr_flag = CALI_STR_NONE; + return -EFAULT; + } + + g_msic_wr_flag = CALI_STR_NONE; + *pos += len; + return len; +} + +static int aw_cali_misc_switch_dev(struct file *filp, struct aw_device *aw_dev, char *cmd_buf) +{ + int i; + char dev_select[50]; + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + /* get sel dev str */ + sscanf(cmd_buf, "dev_sel:%s", dev_select); + + for (i = 0; i < AW_DEV_CH_MAX; i++) { + if (strnstr(dev_select, ch_name[i], strlen(ch_name[i]))) + break; + } + + if (i == AW_DEV_CH_MAX) { + aw_dev_err(aw_dev->dev, "unsupport dev [%s]", dev_select); + return -EINVAL; + } + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + /* find sel dev */ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel == i) { + filp->private_data = (void *)local_dev; + g_dev_select = i; + aw_dev_info(local_dev->dev, "switch dev to [%s]", ch_name[i]); + return 0; + } + } + aw_dev_err(aw_dev->dev, " unsupport [%s]", dev_select); + return -EINVAL; +} + +static ssize_t aw_cali_misc_write(struct file *filp, const char __user *buf, size_t size, loff_t *pos) +{ + char *kernel_buf = NULL; + struct aw_device *aw_dev = (struct aw_device *)filp->private_data; + int ret = 0; + + aw_dev_info(aw_dev->dev, "enter, write size:%d", (int)size); + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc write"); + return -EINVAL; + } + + kernel_buf = kzalloc(size + 1, GFP_KERNEL); + if (kernel_buf == NULL) { + aw_dev_err(aw_dev->dev, "kzalloc failed !"); + return -ENOMEM; + } + + if (copy_from_user(kernel_buf, + (void __user *)buf, + size)) { + ret = -EFAULT; + goto exit; + } + + ret = aw_cali_svc_get_cmd_form_str(aw_dev, kernel_buf); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "upported cmd [%s]! ", kernel_buf); + ret = -EINVAL; + goto exit; + } + + switch (ret) { + case CALI_STR_CALI_RE_F0: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_CALI_RE: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, + is_single_cali, CALI_OPS_HMUTE); + } break; + case CALI_STR_CALI_F0: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_SET_RE: { + /*skip store_re*/ + ret = aw_cali_svc_set_devs_re_str(aw_dev, + kernel_buf + strlen(cali_str[CALI_STR_SET_RE]) + 1); + } break; + case CALI_STR_DEV_SEL: { + ret = aw_cali_misc_switch_dev(filp, aw_dev, kernel_buf); + } break; + case CALI_STR_CALI_F0_Q: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0_Q, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_SHOW_RE: /*show cali_re*/ + case CALI_STR_SHOW_R0: /*show real r0*/ + case CALI_STR_SHOW_CALI_F0: /*GET DEV CALI_F0*/ + case CALI_STR_SHOW_F0: /*SHOW REAL F0*/ + case CALI_STR_SHOW_TE: + case CALI_STR_SHOW_ST: + case CALI_STR_VER: + case CALI_STR_DEV_NUM: + case CALI_STR_SHOW_F0_Q:{ + g_msic_wr_flag = ret; + ret = 0; + } break; + default: { + aw_dev_err(aw_dev->dev, "unsupported [%s]! ", kernel_buf); + ret = -EINVAL; + } break; + }; + +exit: + aw_dev_info(aw_dev->dev, "cmd [%s]! ", kernel_buf); + if (kernel_buf) { + kfree(kernel_buf); + kernel_buf = NULL; + } + if (ret < 0) + return -EINVAL; + else + return size; +} + +static const struct file_operations aw_cali_misc_fops = { + .owner = THIS_MODULE, + .open = aw_cali_misc_open, + .read = aw_cali_misc_read, + .write = aw_cali_misc_write, + .release = aw_cali_misc_release, + .unlocked_ioctl = aw_cali_misc_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = aw_cali_misc_compat_ioctl, +#endif + +}; + +struct miscdevice misc_cali = { + .name = "aw882xx_smartpa", + .minor = MISC_DYNAMIC_MINOR, + .fops = &aw_cali_misc_fops, +}; + +static int aw_cali_misc_init(struct aw_device *aw_dev) +{ + int ret; + + mutex_lock(&g_cali_lock); + if (g_misc_dev == NULL) { + ret = misc_register(&misc_cali); + if (ret) { + aw_dev_err(aw_dev->dev, "misc register fail: %d\n", ret); + mutex_unlock(&g_cali_lock); + return -EINVAL; + } + g_misc_dev = &misc_cali; + aw_dev_dbg(aw_dev->dev, "misc register success"); + } else { + aw_dev_dbg(aw_dev->dev, "misc already register"); + } + mutex_unlock(&g_cali_lock); + + return 0; +} + +static void aw_cali_misc_deinit(struct aw_device *aw_dev) +{ + mutex_lock(&g_cali_lock); + if (g_misc_dev) { + misc_deregister(g_misc_dev); + g_misc_dev = NULL; + } + mutex_unlock(&g_cali_lock); + aw_dev_dbg(aw_dev->dev, " misc unregister done"); +} + +/*****************************misc node******************************************************/ +static void aw_cali_parse_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + int ret = -1; + const char *cali_mode_str; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + ret = of_property_read_string(np, "aw-cali-mode", &cali_mode_str); + if (ret < 0) { + aw_dev_info(aw_dev->dev, " aw-cali-mode get failed ,user default misc way"); + desc->mode = AW_CALI_MODE_MISC; + return; + } + + if (!strcmp(cali_mode_str, "none")) + desc->mode = AW_CALI_MODE_NONE; + else if (!strcmp(cali_mode_str, "aw_class")) + desc->mode = AW_CALI_MODE_CLASS; + else if (!strcmp(cali_mode_str, "aw_attr")) + desc->mode = AW_CALI_MODE_ATTR; + else if (!strcmp(cali_mode_str, "aw_cali_all")) + desc->mode = AW_CALI_MODE_ALL; + else + desc->mode = AW_CALI_MODE_MISC; /*default misc*/ + + aw_dev_info(aw_dev->dev, "cali mode str:%s num:%d", + cali_mode_str, desc->mode); +} + +int aw_cali_parse_re_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-re-min", &aw_dev->re_min); + if (ret < 0) { + aw_dev->re_min = AW_CALI_RE_DEFAULT_MIN; + aw_dev_info(aw_dev->dev, "read aw-re-min failed, use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-re-max", &aw_dev->re_max); + if (ret < 0) { + aw_dev->re_max = AW_CALI_RE_DEFAULT_MAX; + aw_dev_info(aw_dev->dev, "read aw-re-max failed, use default"); + } + + if (aw_dev->re_min >= aw_dev->re_max) { + aw_dev_err(aw_dev->dev, "re max must be greater than re min"); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "re min: %d, re max: %d", + aw_dev->re_min, aw_dev->re_max); + return 0; +} + +int aw_cali_init(struct aw_cali_desc *cali_desc) +{ + int ret; + + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + cali_desc->cali_f0 = 0; + cali_desc->cali_re = 0; + cali_desc->cali_q = 0; + cali_desc->status = 0; + + aw_cali_parse_dt(aw_dev); + + if (cali_desc->mode == AW_CALI_MODE_ATTR) { + aw_cali_attr_init(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { + aw_cali_class_attr_init(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_init(aw_dev); + aw_cali_class_attr_init(aw_dev); + } + + aw_cali_misc_init(aw_dev); + + ret = aw_cali_parse_re_dt(aw_dev); + + return ret; +} + +void aw_cali_deinit(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + if (cali_desc->mode == AW_CALI_MODE_ATTR) { + aw_cali_attr_deinit(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { + aw_cali_class_attr_deinit(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_deinit(aw_dev); + aw_cali_class_attr_deinit(aw_dev); + } + + aw_cali_misc_deinit(aw_dev); +} + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif diff --git a/asoc/codecs/aw882xxacf/aw_calib.h b/asoc/codecs/aw882xxacf/aw_calib.h new file mode 100644 index 000000000000..d7b0099c29a6 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_calib.h @@ -0,0 +1,144 @@ +#ifndef __AWINIC_CALIBRATION_H__ +#define __AWINIC_CALIBRATION_H__ + +#define AW_CALI_STORE_EXAMPLE + +#define AW_CALI_READ_TIMES (3) +#define AW_ERRO_CALI_VALUE (0) +#define AW_CALI_RE_DEFAULT_TIMER (3000) + +#define AW_CALI_RE_MAX (15000) +#define AW_CALI_RE_MIN (4000) + +#define AW_CALI_CFG_NUM (3) +#define AW_CALI_DATA_NUM (6) +#define AW_PARAMS_NUM (600) +#define AW_KILO_PARAMS_NUM (1000) + +#define AW_CALI_RE_DEFAULT_MAX (50000) +#define AW_CALI_RE_DEFAULT_MIN (1000) + +struct aw_device; + +enum afe_module_type { + AW_RX_MODULE = 0, + AW_TX_MODULE = 1, +}; + +struct cali_cfg { + int32_t data[AW_CALI_CFG_NUM]; +}; + +struct cali_data { + int32_t data[AW_CALI_DATA_NUM]; +}; + +struct params_data { + int32_t data[AW_PARAMS_NUM]; +}; + +struct ptr_params_data { + int len; + int32_t *data; +}; + +struct f0_q_data { + int32_t data[4]; +}; + +enum { + AW_IOCTL_MSG_IOCTL = 0, + AW_IOCTL_MSG_RD_DSP, + AW_IOCTL_MSG_WR_DSP +}; + +#define AW_IOCTL_MSG_VERSION (0) +typedef struct { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t data_len; + char *data_buf; + int32_t reseriver[2]; +} aw_ioctl_msg_t; + +#define AW_IOCTL_MAGIC 'a' +#define AW_IOCTL_SET_CALI_CFG _IOWR(AW_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW_IOCTL_GET_CALI_CFG _IOWR(AW_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW_IOCTL_GET_CALI_DATA _IOWR(AW_IOCTL_MAGIC, 3, struct cali_data) +#define AW_IOCTL_SET_NOISE _IOWR(AW_IOCTL_MAGIC, 4, int32_t) +#define AW_IOCTL_GET_F0 _IOWR(AW_IOCTL_MAGIC, 5, int32_t) +#define AW_IOCTL_SET_CALI_RE _IOWR(AW_IOCTL_MAGIC, 6, int32_t) +#define AW_IOCTL_GET_CALI_RE _IOWR(AW_IOCTL_MAGIC, 7, int32_t) +#define AW_IOCTL_SET_VMAX _IOWR(AW_IOCTL_MAGIC, 8, int32_t) +#define AW_IOCTL_GET_VMAX _IOWR(AW_IOCTL_MAGIC, 9, int32_t) +#define AW_IOCTL_SET_PARAM _IOWR(AW_IOCTL_MAGIC, 10, struct params_data) +#define AW_IOCTL_ENABLE_CALI _IOWR(AW_IOCTL_MAGIC, 11, int8_t) +#define AW_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW_IOCTL_MAGIC, 12, struct ptr_params_data) +#define AW_IOCTL_GET_F0_Q _IOWR(AW_IOCTL_MAGIC, 13, struct f0_q_data) +#define AW_IOCTL_SET_DSP_HMUTE _IOWR(AW_IOCTL_MAGIC, 14, int32_t) +#define AW_IOCTL_SET_CALI_CFG_FLAG _IOWR(AW_IOCTL_MAGIC, 15, int32_t) +#define AW_IOCTL_MSG _IOWR(AW_IOCTL_MAGIC, 16, aw_ioctl_msg_t) + +enum{ + AW_CALI_MODE_NONE = 0, + AW_CALI_MODE_ATTR, + AW_CALI_MODE_CLASS, + AW_CALI_MODE_MISC, + AW_CALI_MODE_ALL, + AW_CALI_MODE_MAX, +}; + +enum { + AW_CALI_CMD_RE = 0, + AW_CALI_CMD_F0, + AW_CALI_CMD_RE_F0, + AW_CALI_CMD_F0_Q, + AW_CALI_CMD_RE_F0_Q, +}; + +enum { + CALI_OPS_HMUTE = 0X0001, + CALI_OPS_NOISE = 0X0002, +}; + +enum { + CALI_TYPE_RE = 0, + CALI_TYPE_F0, +}; + +enum { + CALI_STR_NONE = 0, + CALI_STR_CALI_RE_F0, + CALI_STR_CALI_RE, + CALI_STR_CALI_F0, + CALI_STR_SET_RE, + CALI_STR_SHOW_RE, /*show cali_re*/ + CALI_STR_SHOW_R0, /*show real r0*/ + CALI_STR_SHOW_CALI_F0, /*GET DEV CALI_F0*/ + CALI_STR_SHOW_F0, /*SHOW REAL F0*/ + CALI_STR_SHOW_TE, + CALI_STR_SHOW_ST, + CALI_STR_DEV_SEL, /*switch device*/ + CALI_STR_VER, + CALI_STR_DEV_NUM, + CALI_STR_CALI_F0_Q, + CALI_STR_SHOW_F0_Q, + CALI_STR_MAX, +}; + +struct aw_cali_desc { + unsigned char status; + unsigned char mode; /*0:NONE 1:ATTR 2:CLASS 3:MISC */ + int32_t cali_re; /*set cali_re*/ + int32_t cali_f0; /*store cali_f0*/ + int32_t cali_q; /*store cali q*/ +}; + +int aw_cali_init(struct aw_cali_desc *cali_desc); +void aw_cali_deinit(struct aw_cali_desc *cali_desc); +int aw_cali_svc_get_cali_status(void); +int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t ch_index); + + +#endif diff --git a/asoc/codecs/aw882xxacf/aw_data_type.h b/asoc/codecs/aw882xxacf/aw_data_type.h new file mode 100644 index 000000000000..36aaf4f077ce --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_data_type.h @@ -0,0 +1,125 @@ +#ifndef __AWINIC_DATA_TYPE_H__ +#define __AWINIC_DATA_TYPE_H__ + +#define AW_NAME_BUF_MAX (50) + +struct aw_msg_hdr { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reseriver[3]; +}; + + +/****************************************************************** +* aw profile +*******************************************************************/ +#define PROJECT_NAME_MAX (24) +#define CUSTOMER_NAME_MAX (16) +#define CFG_VERSION_MAX (4) +#define DEV_NAME_MAX (16) + +#define ACF_FILE_ID (0xa15f908) + + +enum aw_cfg_hdr_version { + AW_CFG_HDR_VER_0_0_0_1 = 0x00000001, +}; + +enum aw_cfg_dde_type { + AW_DEV_TYPE_ID = 0x00000000, + AW_SKT_TYPE_ID = 0x00000001, + AW_DEV_DEFAULT_TYPE_ID = 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG = 0, + ACF_SEC_TYPE_DSP, + ACF_SEC_TYPE_DSP_CFG, + ACF_SEC_TYPE_DSP_FW, + ACF_SEC_TYPE_HDR_REG, + ACF_SEC_TYPE_HDR_DSP_CFG, + ACF_SEC_TYPE_HDR_DSP_FW, + ACF_SEC_TYPE_MUTLBIN, + ACF_SEC_TYPE_SKT_PROJECT, + ACF_SEC_TYPE_DSP_PROJECT, + ACF_SEC_TYPE_MONITOR, + ACF_SEC_TYPE_MAX, +}; + +enum profile_data_type { + AW_PROFILE_DATA_TYPE_REG = 0, + AW_PROFILE_DATA_TYPE_DSP, + AW_PROFILE_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW_PROFILE_MUSIC = 0, + AW_PROFILE_VOICE, + AW_PROFILE_VOIP, + AW_PROFILE_RINGTONE, + AW_PROFILE_RINGTONE_HS, + AW_PROFILE_LOWPOWER, + AW_PROFILE_BYPASS, + AW_PROFILE_MMI, + AW_PROFILE_FM, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_RECEIVER, + AW_PROFILE_MAX, +}; + +enum aw_profile_status { + AW_PROFILE_WAIT = 0, + AW_PROFILE_OK, +}; + +struct aw_cfg_hdr { + uint32_t a_id; /*acf file ID 0xa15f908*/ + char a_project[PROJECT_NAME_MAX]; /*project name*/ + char a_custom[CUSTOMER_NAME_MAX]; /*custom name :huawei xiaomi vivo oppo*/ + char a_version[CFG_VERSION_MAX]; /*author update version*/ + uint32_t a_author_id; /*author id*/ + uint32_t a_ddt_size; /*sub section table entry size*/ + uint32_t a_ddt_num; /*sub section table entry num*/ + uint32_t a_hdr_offset; /*sub section table offset in file*/ + uint32_t a_hdr_version; /*sub section table version*/ + uint32_t reserve[3]; +}; + +struct aw_cfg_dde { + uint32_t type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + uint16_t dev_index; /*dev id*/ + uint16_t dev_bus; /*dev bus id*/ + uint16_t dev_addr; /*dev addr id*/ + uint16_t dev_profile; /*dev profile id*/ + uint32_t data_type; /*data type id*/ + uint32_t data_size; + uint32_t data_offset; + uint32_t data_crc; + uint32_t reserve[5]; +}; + +struct aw_sec_data_desc { + uint32_t len; + unsigned char *data; +}; + +struct aw_prof_desc { + uint32_t id; + uint32_t prof_st; + struct aw_sec_data_desc sec_desc[AW_PROFILE_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + struct aw_prof_desc *prof_desc; +}; + + +#endif + diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c new file mode 100644 index 000000000000..3067527e415d --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -0,0 +1,1557 @@ +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "aw_data_type.h" +#include "aw_log.h" +#include "aw_device.h" +#include "aw_dsp.h" +/*#include "aw_afe.h"*/ +#include "aw_bin_parse.h" + +#define AW_DEV_SYSST_CHECK_MAX (10) + +enum { + AW_EXT_DSP_WRITE_NONE = 0, + AW_EXT_DSP_WRITE, +}; + +static char *profile_name[AW_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", "Ringtone_hs", + "Lowpower", "Bypass", "Mmi", "Fm", "Notification", "Receiver" + }; + + +static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; +static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ +static unsigned int g_fade_in_time = AW_1000_US / 10; +static unsigned int g_fade_out_time = AW_1000_US >> 1; +static LIST_HEAD(g_dev_list); +static DEFINE_MUTEX(g_dev_lock); + +/*********************************awinic acf*************************************/ +static void aw_dev_reg_dump(struct aw_device *aw_dev) +{ + int reg_num = aw_dev->ops.aw_get_reg_num(); + uint8_t i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < reg_num; i++) { + if (aw_dev->ops.aw_check_rd_access(i)) { + aw_dev->ops.aw_i2c_read(aw_dev, i, ®_val); + aw_dev_info(aw_dev->dev, "read: reg = 0x%02x, val = 0x%04x", + i, reg_val); + } + } +} + +static uint8_t aw_dev_crc8_check(unsigned char *data, uint32_t data_size) +{ + uint8_t crc_value = 0x00; + uint8_t pdatabuf = 0; + int i; + + while (data_size--) { + pdatabuf = *data++; + for (i = 0; i < 8; i++) { + /*if the lowest bit is 1*/ + if ((crc_value ^ (pdatabuf)) & 0x01) { + /*Xor multinomial*/ + crc_value ^= 0x18; + crc_value >>= 1; + crc_value |= 0x80; + } else { + crc_value >>= 1; + } + pdatabuf >>= 1; + } + } + return crc_value; +} + +static int aw_dev_check_cfg_by_hdr(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_cfg_dde *cfg_dde = NULL; + unsigned int end_data_offset = 0; + unsigned int act_data = 0; + unsigned int hdr_ddt_len = 0; + uint8_t act_crc8 = 0; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /*check file type id is awinic acf file*/ + if (cfg_hdr->a_id != ACF_FILE_ID) { + aw_pr_err("not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->a_hdr_offset + cfg_hdr->a_ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + aw_pr_err("hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->a_hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /*check data size*/ + cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->a_hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->a_ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + aw_pr_err("act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + aw_pr_err("a_ddt_num[%d] end_data_offset[%d] overflow file size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = aw_dev_crc8_check(aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size); + if (act_crc8 != cfg_dde[i].data_crc) { + aw_pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (uint32_t)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + aw_pr_info("project name [%s]", cfg_hdr->a_project); + aw_pr_info("custom name [%s]", cfg_hdr->a_custom); + aw_pr_info("version name [%d.%d.%d.%d]", cfg_hdr->a_version[3], cfg_hdr->a_version[2], + cfg_hdr->a_version[1], cfg_hdr->a_version[0]); + aw_pr_info("author id %d", cfg_hdr->a_author_id); + + return 0; +} + +int aw_dev_load_acf_check(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + + if (aw_cfg == NULL) { + aw_pr_err("aw_prof is NULL"); + return -ENOMEM; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + aw_pr_err("cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->a_hdr_version) { + case AW_CFG_HDR_VER_0_0_0_1: + return aw_dev_check_cfg_by_hdr(aw_cfg); + default: + aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + return 0; +} + + +static int aw_dev_parse_raw_reg(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + if (data_len % 4) { + aw_dev_err(aw_dev->dev, "bin data len get error!"); + return -EINVAL; + } + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].len = data_len; + + return 0; +} + +static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin = NULL; + int ret; + + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (aw_bin == NULL) { + aw_dev_err(aw_dev->dev, "devm_kzalloc aw_bin failed"); + return -ENOMEM; + } + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_bin); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + aw_dev_err(aw_dev->dev, "bin num or type error"); + goto parse_bin_failed; + } + + if (aw_bin->header_info[0].valid_data_len % 4) { + aw_dev_err(aw_dev->dev, "bin data len get error!"); + return -EINVAL; + } + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW_PROFILE_OK; + + kfree(aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + kfree(aw_bin); + aw_bin = NULL; + return ret; +} + +static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, + struct aw_cfg_dde *prof_hdr, struct aw_prof_desc *scene_prof_desc) +{ + switch (prof_hdr->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg(aw_dev, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size, + scene_prof_desc); + break; + case ACF_SEC_TYPE_HDR_REG: + return aw_dev_parse_reg_bin_with_hdr(aw_dev, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size, + scene_prof_desc); + break; + case ACF_SEC_TYPE_MONITOR: + return aw_monitor_parse_fw(&aw_dev->monitor_desc, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size); + break; + } + + return 0; +} + +static int aw_dev_parse_dev_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (cfg_dde[i].type == AW_DEV_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dev driver bin data failed"); + return ret; + } + sec_num++; + } else { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], NULL); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse monitor bin data failed"); + return ret; + } + } + } + } + + if (sec_num == 0) { + aw_dev_info(aw_dev->dev, "get dev type num is %d, please use default", sec_num); + return AW_DEV_TYPE_NONE; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->index == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_DEV_DEFAULT_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dev driver bin data failed"); + return ret; + } + sec_num++; + } else { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], NULL); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse monitor bin data failed"); + return ret; + } + } + } + } + + if (sec_num == 0) { + aw_dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_skt_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->index == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_SKT_TYPE_ID)) { + if (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) { + ret = aw_dev_parse_raw_dsp(aw_dev, + (uint8_t *)prof_hdr + cfg_dde[i].data_offset, + cfg_dde[i].data_size, + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dsp bin data failed"); + return ret; + } + sec_num++; + } + } + } + + aw_dev_info(aw_dev->dev, "get dsp data prof cnt is %d ", sec_num); + return 0; +} + +static int aw_dev_acf_load_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int ret; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + return ret; + } else if (ret == AW_DEV_TYPE_NONE) { + aw_dev_info(aw_dev->dev, "get dev type num is0, parse default dev type"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + return ret; + } + + ret = aw_dev_parse_skt_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + return ret; + + return 0; +} + + +static int aw_dev_cfg_get_vaild_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + int i; + int num = 0; + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) + aw_dev->prof_info.count++; + } + + aw_dev_info(aw_dev->dev, "get vaild profile:%d", aw_dev->prof_info.count); + + if (!aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = kzalloc(prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) { + if (num >= prof_info->count) { + aw_dev_err(aw_dev->dev, "get scene num[%d] overflow count[%d]", + num, prof_info->count); + return -ENOMEM; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + + return 0; +} + +static int aw_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_all_prof_info all_prof_info; + int ret; + + aw_dev_info(aw_dev->dev, "enter"); + memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info)); + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->a_hdr_version) { + case AW_CFG_HDR_VER_0_0_0_1: + ret = aw_dev_acf_load_by_hdr(aw_dev, cfg_hdr, &all_prof_info); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + break; + default: + aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + ret = aw_dev_cfg_get_vaild_prof(aw_dev, all_prof_info); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "parse cfg success"); + return 0; +} + +static struct aw_sec_data_desc *aw_dev_get_prof_data(struct aw_device *aw_dev, int index, int data_type) +{ + struct aw_sec_data_desc *sec_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + + if (index >= aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + if (data_type >= AW_PROFILE_DATA_TYPE_MAX) { + aw_dev_err(aw_dev->dev, "unsupport data type id [%d]", data_type); + return NULL; + } + + sec_data = &prof_desc->sec_desc[data_type]; + + aw_dev_dbg(aw_dev->dev, "get prof[%s] data len[%d]", profile_name[prof_desc->id], sec_data->len); + + return sec_data; +} + + +/*****************************awinic device*************************************/ +static char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_desc *prof_desc = NULL; + + if (index < 0) { + aw_dev_err(aw_dev->dev, "index[%d] error", index); + return NULL; + } + + if (index >= aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return profile_name[prof_desc->id]; +} + +/*static int aw_dev_dsp_fw_update(struct aw_device *aw_dev); +{ + int ret; + struct aw_sec_data_desc *dsp_data; + + char *prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + + if (prof_name == NULL) { + aw_dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + dsp_data = aw_dev_get_prof_data(aw_dev, + aw_dev->set_prof, AW_PROFILE_DATA_TYPE_DSP); + if (dsp_data == NULL || + dsp_data->data == NULL || + dsp_data->len == 0) { + aw_dev_info(aw_dev->dev, "dsp data is NULL"); + return 0; + } + + mutex_lock(&g_ext_dsp_prof_wr_lock); + if (ext_dsp_prof_write == AW_EXT_DSP_WRITE_NONE) { + ret = aw_dsp_write_params(aw_dev, dsp_data->data, dsp_data->len); + if (ret) { + aw_dev_err(aw_dev->dev, "dsp params update failed !"); + mutex_unlock(&g_ext_dsp_prof_wr_lock); + return ret; + } + ext_dsp_prof_write = AW_EXT_DSP_WRITE; + } else { + aw_dev_dbg(aw_dev->dev, "dsp params already update !"); + } + mutex_unlock(&g_ext_dsp_prof_wr_lock); + + aw_dev_info(aw_dev->dev, "load %s done", prof_name); + return 0; +}*/ + +/*pwd enable update reg*/ +static int aw_dev_reg_fw_update(struct aw_device *aw_dev) +{ + int i = 0; + unsigned int reg_addr = 0; + unsigned int reg_val = 0; + unsigned int read_val; + int ret = -1; + unsigned int init_volume = 0; + struct aw_int_desc *int_desc = &aw_dev->int_desc; + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + struct aw_sec_data_desc *reg_data; + int16_t *data; + int data_len; + + char *prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + + if (prof_name == NULL) { + aw_dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + reg_data = aw_dev_get_prof_data(aw_dev, aw_dev->set_prof, AW_PROFILE_DATA_TYPE_REG); + if (reg_data == NULL) + return -EINVAL; + + data = (int16_t *)reg_data->data; + data_len = reg_data->len >> 1; + + for (i = 0; i < data_len; i += 2) { + reg_addr = data[i]; + reg_val = data[i + 1]; + + if (reg_addr == int_desc->mask_reg) { + int_desc->int_mask = reg_val; + reg_val = int_desc->mask_default; + } + + if (aw_dev->bstcfg_enable) { + if (reg_addr == profctrl_desc->reg) { + profctrl_desc->cfg_prof_mode = + reg_val & (~profctrl_desc->mask); + } + + if (reg_addr == bstctrl_desc->reg) { + bstctrl_desc->cfg_bst_type = + reg_val & (~bstctrl_desc->mask); + } + } + + /*keep amppd status*/ + if (reg_addr == aw_dev->amppd_desc.reg) { + aw_dev->amppd_st = reg_val & (~aw_dev->amppd_desc.mask); + aw_dev_info(aw_dev->dev, "amppd_st=0x%04x", aw_dev->amppd_st); + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->amppd_desc.mask); + reg_val &= aw_dev->amppd_desc.mask; + reg_val |= read_val; + } + + /*keep pwd status*/ + if (reg_addr == aw_dev->pwd_desc.reg) { + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->pwd_desc.mask); + reg_val &= aw_dev->pwd_desc.mask; + reg_val |= read_val; + } + /*keep mute status*/ + if (reg_addr == aw_dev->mute_desc.reg) { + /*get bin value*/ + aw_dev->mute_st = reg_val & (~aw_dev->mute_desc.mask); + aw_dev_info(aw_dev->dev, "mute_st=0x%04x", aw_dev->mute_st); + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->mute_desc.mask); + reg_val &= aw_dev->mute_desc.mask; + reg_val |= read_val; + } + + if (reg_addr == aw_dev->vcalb_desc.vcalb_reg) + continue; + + aw_dev_dbg(aw_dev->dev, "reg=0x%04x, val = 0x%04x", + (uint16_t)reg_addr, (uint16_t)reg_val); + ret = aw_dev->ops.aw_i2c_write(aw_dev, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + aw_dev->ops.aw_get_volume(aw_dev, &init_volume); + aw_dev->volume_desc.init_volume = init_volume; + + /*keep min volume*/ + aw_dev->ops.aw_set_volume(aw_dev, aw_dev->volume_desc.mute_volume); + + aw_dev_info(aw_dev->dev, "load %s done", prof_name); + + return ret; +} + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + int i = 0; + int fade_step = aw_dev->vol_step; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + if (fade_step == 0 || g_fade_in_time == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + if (i < desc->init_volume) + i = desc->init_volume; + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_in_time, g_fade_in_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + int i = 0; + unsigned start_volume = 0; + int fade_step = aw_dev->vol_step; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + if (fade_step == 0 || g_fade_out_time == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); + return; + } + + aw_dev->ops.aw_get_volume(aw_dev, &start_volume); + i = start_volume; + for (i = start_volume; i <= desc->mute_volume; i += fade_step) { + if (i > desc->mute_volume) + i = desc->mute_volume; + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_out_time, g_fade_out_time + 10); + } + if (i != desc->mute_volume) { + aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); + usleep_range(g_fade_out_time, g_fade_out_time + 10); + } +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + struct aw_pwd_desc *pwd_desc = &aw_dev->pwd_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (pwd) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, pwd_desc->reg, + pwd_desc->mask, + pwd_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, pwd_desc->reg, + pwd_desc->mask, + pwd_desc->disable); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + struct aw_amppd_desc *amppd_desc = &aw_dev->amppd_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (amppd) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, amppd_desc->reg, + amppd_desc->mask, + amppd_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, amppd_desc->reg, + amppd_desc->mask, + amppd_desc->disable); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static void aw_dev_mute(struct aw_device *aw_dev, bool mute) +{ + struct aw_mute_desc *mute_desc = &aw_dev->mute_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (mute) { + aw_dev_fade_out(aw_dev); + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + uint16_t reg_icalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); + reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); + reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); + } + + if (reg_icalk & (~desc->icalk_sign_mask)) + reg_icalk = reg_icalk | (~desc->icalk_neg_mask); + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + uint16_t reg_vcalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->vcalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); + reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); + } + + if (reg_vcalk & (~desc->vcalk_sign_mask)) + reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { + aw_dev_info(aw_dev->dev, "REG None!"); + return 0; + } + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret < 0) + return ret; + + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + + icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; + if (!vcalk) { + aw_dev_err(aw_dev->dev, "vcalk is 0"); + return -EINVAL; + } + + vcalb = desc->vcal_factor * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); + + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw_dev->ops.aw_i2c_read(aw_dev, aw_dev->int_desc.st_reg, ®_val); + if (ret < 0) + aw_dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + aw_dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); + return ret; +} + +void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + uint16_t int_status = 0; + + /*read int status and clear*/ + aw_dev_get_int_status(aw_dev, &int_status); + /*make suer int status is clear*/ + aw_dev_get_int_status(aw_dev, &int_status); + aw_dev_info(aw_dev->dev, "done"); +} + +int aw_dev_set_intmask(struct aw_device *aw_dev, bool flag) +{ + struct aw_int_desc *desc = &aw_dev->int_desc; + int ret = -1; + + if (flag) + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->mask_reg, + desc->int_mask); + else + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->mask_reg, + desc->mask_default); + aw_dev_info(aw_dev->dev, "done"); + return ret; +} + +static int aw_dev_mode1_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (reg_val & desc->pll_check) { + ret = 0; + break; + } else { + aw_dev_dbg(aw_dev->dev, "check pll lock fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + if (ret < 0) + aw_dev_err(aw_dev->dev, "check fail"); + else + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +static int aw_dev_mode2_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_cco_mux_desc *cco_mux_desc = &aw_dev->cco_mux_desc; + + aw_dev->ops.aw_i2c_read(aw_dev, cco_mux_desc->reg, ®_val); + reg_val &= (~cco_mux_desc->mask); + aw_dev_dbg(aw_dev->dev, "REG_PLLCTRL1_bit14 = 0x%04x", reg_val); + if (reg_val == cco_mux_desc->divided_val) { + aw_dev_dbg(aw_dev->dev, "CCO_MUX is already divided"); + return ret; + } + + /* change mode2 */ + aw_dev->ops.aw_i2c_write_bits(aw_dev, cco_mux_desc->reg, + cco_mux_desc->mask, cco_mux_desc->bypass_val); + ret = aw_dev_mode1_pll_check(aw_dev); + + /* change mode1 */ + aw_dev->ops.aw_i2c_write_bits(aw_dev, cco_mux_desc->reg, + cco_mux_desc->mask, cco_mux_desc->divided_val); + if (ret == 0) { + usleep_range(AW_2000_US, AW_2000_US + 10); + ret = aw_dev_mode1_pll_check(aw_dev); + } + + return ret; +} + +static int aw_dev_syspll_check(struct aw_device *aw_dev) +{ + int ret = -1; + + ret = aw_dev_mode1_pll_check(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, + "mode1 check iis failed try switch to mode2 check"); + + ret= aw_dev_mode2_pll_check(aw_dev); + if (ret < 0) + aw_dev_err(aw_dev->dev, "mode2 check iis failed"); + } + + return ret; +} + +static int aw_dev_sysst_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (((reg_val & (~desc->mask)) & desc->st_check) == desc->st_check) { + ret = 0; + break; + } else { + aw_dev_info(aw_dev->dev, "check fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + if (ret < 0) + aw_dev_err(aw_dev->dev, "check fail"); + else + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +int aw_dev_get_profile_count(struct aw_device *aw_dev) +{ + if (aw_dev == NULL) { + aw_dev_err(aw_dev->dev, "aw_dev is NULL"); + return -ENOMEM; + } + + return aw_dev->prof_info.count; +} + +int aw_dev_get_profile_name(struct aw_device *aw_dev, char *name, int index) +{ + int dev_profile_id; + + if (index < 0) { + aw_dev_err(aw_dev->dev, "index[%d] error", index); + return -EINVAL; + } + + if (index > aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow dev prof num[%d]", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + if (aw_dev->prof_info.prof_desc[index].id >= AW_PROFILE_MAX) { + aw_dev_err(aw_dev->dev, "can not find match id "); + return -EINVAL; + } + + dev_profile_id = aw_dev->prof_info.prof_desc[index].id; + + strlcpy(name, profile_name[dev_profile_id], + strlen(profile_name[dev_profile_id]) + 1); + /*aw_dev_dbg(aw_dev->dev, "%s: get name [%s]", + __func__, profile_name[dev_profile_id]);*/ + return 0; +} + +int aw_dev_check_profile_index(struct aw_device *aw_dev, int index) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + else + return 0; +} + +int aw_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->set_prof; +} + +int aw_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + if (index >= aw_dev->prof_info.count || index < 0) { + return -EINVAL; + } else { + aw_dev->set_prof = index; + aw_dev_info(aw_dev->dev, "set prof[%s]", + profile_name[aw_dev->prof_info.prof_desc[index].id]); + } + mutex_lock(&g_ext_dsp_prof_wr_lock); + ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; + mutex_unlock(&g_ext_dsp_prof_wr_lock); + + return 0; +} + +int aw_dev_get_fade_vol_step(struct aw_device *aw_dev) +{ + return aw_dev->vol_step; +} + +void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step) +{ + aw_dev->vol_step = step; +} + +void aw_dev_get_fade_time(unsigned int *time, bool fade_in) +{ + if (fade_in) + *time = g_fade_in_time; + else + *time = g_fade_out_time; +} + +void aw_dev_set_fade_time(unsigned int time, bool fade_in) +{ + if (fade_in) + g_fade_in_time = time; + else + g_fade_out_time = time; +} + +/*init aw_device*/ +void aw_dev_deinit(struct aw_device *aw_dev) +{ + if (aw_dev == NULL) + return; + + if (aw_dev->prof_info.prof_desc != NULL) { + kfree(aw_dev->prof_info.prof_desc); + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + } +} + +int aw_dev_get_cali_re(struct aw_device *aw_dev, int32_t *cali_re) +{ + + return aw_dsp_read_cali_re(aw_dev, cali_re); +} + +int aw_dev_dc_status(struct aw_device *aw_dev) +{ + return aw_dsp_get_dc_status(aw_dev); +} + +int aw_dev_status(struct aw_device *aw_dev) +{ + return aw_dev->status; +} + +int aw_dev_init_cali_re(struct aw_device *aw_dev) +{ + int ret = 0; + + if (aw_dev->cali_desc.mode) { + if (aw_dev->cali_desc.cali_re == AW_ERRO_CALI_VALUE) { + ret = aw_cali_read_re_from_nvram(&aw_dev->cali_desc.cali_re, aw_dev->channel); + if (ret) { + aw_dev_info(aw_dev->dev, "read nvram cali failed, use default Re"); + aw_dev->cali_desc.cali_re = AW_ERRO_CALI_VALUE; + } + } + } else { + aw_dev_info(aw_dev->dev, "no cali, needn't init cali re"); + } + return ret; +} + +static void aw_dev_soft_reset(struct aw_device *aw_dev) +{ + struct aw_soft_rst *reset = &aw_dev->soft_rst; + + aw_dev->ops.aw_i2c_write(aw_dev, reset->reg, reset->reg_value); + aw_dev_info(aw_dev->dev, "soft reset done"); +} + +int aw_device_irq_reinit(struct aw_device *aw_dev) +{ + int ret; + + /*reg re load*/ + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + + /*update vcalb*/ + aw_dev_set_vcalb(aw_dev); + + return 0; +} + +int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + /*acf_hdr_t *hdr;*/ + int ret; + + if (aw_dev == NULL || aw_cfg == NULL) { + aw_dev_err(aw_dev->dev, "pointer is NULL"); + return -ENOMEM; + } + + ret = aw_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + aw_dev_deinit(aw_dev); + aw_dev_err(aw_dev->dev, "aw_dev acf load failed"); + return -EINVAL; + } + + aw_dev_soft_reset(aw_dev); + + aw_dev->cur_prof = AW_PROFILE_MUSIC; + aw_dev->set_prof = AW_PROFILE_MUSIC; + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + + ret = aw_dev_set_vcalb(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "can't set vcalb"); + return ret; + } + + aw_dev->status = AW_DEV_PW_ON; + aw_device_stop(aw_dev); + + aw_dev_info(aw_dev->dev, "init done"); + return 0; +} + +int aw_dev_reg_update(struct aw_device *aw_dev, bool force) +{ + int ret; + + if (force) { + aw_dev_soft_reset(aw_dev); + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + } else { + if (aw_dev->cur_prof != aw_dev->set_prof) { + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + } + } + + aw_dev->cur_prof = aw_dev->set_prof; + + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +static void aw_dev_cali_re_update(struct aw_device *aw_dev) +{ + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + if (desc->mode) { + if ((desc->cali_re >= aw_dev->re_min) && + (desc->cali_re <= aw_dev->re_max)) + aw_dsp_write_cali_re(aw_dev, desc->cali_re); + } +} + +int aw_dev_prof_update(struct aw_device *aw_dev, bool force) +{ + int ret; + + /*if power on need off -- load -- on*/ + if (aw_dev->status == AW_DEV_PW_ON) { + aw_device_stop(aw_dev); + + ret = aw_dev_reg_update(aw_dev, force); + if (ret) { + aw_dev_err(aw_dev->dev, "fw update failed "); + return ret; + } + + ret = aw_device_start(aw_dev); + if (ret) { + aw_dev_err(aw_dev->dev, "start failed "); + return ret; + } + } else { + /*if pa off , only update set_prof value*/ + aw_dev_info(aw_dev->dev, "set prof[%d] done !", aw_dev->set_prof); + } + + aw_dev_info(aw_dev->dev, "update done !"); + return 0; +} + +static void aw_dev_boost_type_set(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bstcfg_enable) { + /*set spk mode*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, profctrl_desc->reg, + profctrl_desc->mask, profctrl_desc->spk_mode); + + /*force boost*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->frc_bst); + + aw_dev_dbg(aw_dev->dev, "boost type set done"); + } +} + +static void aw_dev_boost_type_recover(struct aw_device *aw_dev) +{ + + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bstcfg_enable) { + /*set transprant*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->tsp_type); + + usleep_range(AW_5000_US, AW_5000_US + 50); + /*set cfg boost type*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->cfg_bst_type); + + /*set cfg prof mode*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, profctrl_desc->reg, + profctrl_desc->mask, profctrl_desc->cfg_prof_mode); + + aw_dev_dbg(aw_dev->dev, "boost type recover done"); + } +} + +static void aw_dev_set_frcpwm_mode(struct aw_device *aw_dev) +{ + struct aw_frcpwm_desc *frcpwm_desc = &aw_dev->frcpwm_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + aw_dev->ops.aw_i2c_write_bits(aw_dev, frcpwm_desc->reg, + frcpwm_desc->mask, frcpwm_desc->frcpwm_val); + + aw_dev_dbg(aw_dev->dev, "frcpwm_mode set done"); +} + +int aw_device_start(struct aw_device *aw_dev) +{ + int ret; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->status == AW_DEV_PW_ON) { + aw_dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /*set froce boost*/ + aw_dev_boost_type_set(aw_dev); + + /*power on*/ + aw_dev_pwd(aw_dev, false); + usleep_range(AW_2000_US, AW_2000_US + 10); + + ret = aw_dev_syspll_check(aw_dev); + if (ret < 0) { + aw_dev_reg_dump(aw_dev); + aw_dev_pwd(aw_dev, true); + aw_dev_dbg(aw_dev->dev, "pll check failed cannot start"); + return ret; + } + + /*amppd on*/ + aw_dev_amppd(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 50); + + /*check i2s status*/ + ret = aw_dev_sysst_check(aw_dev); + if (ret < 0) { + aw_dev_reg_dump(aw_dev); + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, false); + /*clear interrupt*/ + aw_dev_clear_int_status(aw_dev); + /*close amppd*/ + aw_dev_amppd(aw_dev, true); + /*power down*/ + aw_dev_pwd(aw_dev, true); + return -EINVAL; + } + + /*boost type recover*/ + aw_dev_boost_type_recover(aw_dev); + + /*enable tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, true); + + if (aw_dev->amppd_st) { + aw_dev_amppd(aw_dev, true); + } + + if (aw_dev->frcpwm_en) + aw_dev_set_frcpwm_mode(aw_dev); + + if (!aw_dev->mute_st) { + /*close mute*/ + aw_dev_mute(aw_dev, false); + } + + /*clear inturrupt*/ + aw_dev_clear_int_status(aw_dev); + /*set inturrupt mask*/ + aw_dev_set_intmask(aw_dev, true); + + aw_monitor_start(&aw_dev->monitor_desc); + aw_dev_cali_re_update(aw_dev); + + aw_dev->status = AW_DEV_PW_ON; + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +int aw_device_stop(struct aw_device *aw_dev) +{ + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->status == AW_DEV_PW_OFF) { + aw_dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW_DEV_PW_OFF; + + aw_monitor_stop(&aw_dev->monitor_desc); + /*clear interrupt*/ + aw_dev_clear_int_status(aw_dev); + + /*set defaut int mask*/ + aw_dev_set_intmask(aw_dev, false); + + /*set mute*/ + aw_dev_mute(aw_dev, true); + + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, false); + + usleep_range(AW_1000_US, AW_1000_US + 100); + + /*enable amppd*/ + aw_dev_amppd(aw_dev, true); + + /*set power down*/ + aw_dev_pwd(aw_dev, true); + + ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +int aw_dev_set_afe_module_en(int type, int enable) +{ + return aw_dsp_set_afe_module_en(type, enable); +} + +int aw_dev_get_afe_module_en(int type, int *status) +{ + return aw_dsp_get_afe_module_en(type, status); +} + +int aw_dev_set_copp_module_en(bool enable) +{ + return aw_dsp_set_copp_module_en(enable); +} + +int aw_dev_set_spin(int spin_mode) +{ + return aw_dsp_write_spin(spin_mode); +} + +int aw_dev_get_spin(int *spin_mode) +{ + return aw_dsp_read_spin(spin_mode); +} + +static void aw_device_parse_sound_channel_dt(struct aw_device *aw_dev) +{ + int ret; + uint32_t channel_value; + + aw_dev->channel = AW_DEV_CH_PRI_L; + ret = of_property_read_u32(aw_dev->dev->of_node, "sound-channel", &channel_value); + if (ret < 0) { + aw_dev_info(aw_dev->dev, "read sound-channel failed,use default"); + return; + } + + aw_dev_dbg(aw_dev->dev, "read sound-channel value is : %d", channel_value); + if (channel_value >= AW_DEV_CH_MAX) { + channel_value = AW_DEV_CH_PRI_L; + } + aw_dev->channel = channel_value; +} + +static void aw_device_parse_dt(struct aw_device *aw_dev) +{ + aw_device_parse_sound_channel_dt(aw_dev); + aw_device_parse_topo_id_dt(aw_dev); + aw_device_parse_port_id_dt(aw_dev); +} + +int aw_dev_get_list_head(struct list_head **head) +{ + if (list_empty(&g_dev_list)) + return -EINVAL; + + *head = &g_dev_list; + + return 0; +} + +int aw_device_probe(struct aw_device *aw_dev) +{ + int ret; + INIT_LIST_HEAD(&aw_dev->list_node); + + aw_device_parse_dt(aw_dev); + ret = aw_cali_init(&aw_dev->cali_desc); + if (ret) + return ret; + + aw_monitor_init(&aw_dev->monitor_desc); + /*aw_afe_init();*/ + + mutex_lock(&g_dev_lock); + list_add(&aw_dev->list_node, &g_dev_list); + mutex_unlock(&g_dev_lock); + + return 0; +} + +int aw_device_remove(struct aw_device *aw_dev) +{ + aw_monitor_deinit(&aw_dev->monitor_desc); + aw_cali_deinit(&aw_dev->cali_desc); + /*aw_afe_deinit();*/ + return 0; +} + diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h new file mode 100644 index 000000000000..22f5819dbc88 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -0,0 +1,283 @@ +#ifndef __AWINIC_DEVICE_FILE_H__ +#define __AWINIC_DEVICE_FILE_H__ +#include "aw_data_type.h" +#include "aw_calib.h" +#include "aw_monitor.h" + + +#define AW_VOLUME_STEP_DB (6 * 2) +#define AW_REG_NONE (0xFF) +#define AW_NAME_MAX (50) + +enum { + AW_1000_US = 1000, + AW_2000_US = 2000, + AW_3000_US = 3000, + AW_4000_US = 4000, + AW_5000_US = 5000, + AW_10000_US = 10000, +}; + +struct aw_device; + +enum { + AW_DEV_TYPE_OK = 0, + AW_DEV_TYPE_NONE = 1, +}; + +enum { + AW_DEV_CH_PRI_L = 0, + AW_DEV_CH_PRI_R = 1, + AW_DEV_CH_SEC_L = 2, + AW_DEV_CH_SEC_R = 3, + AW_DEV_CH_MAX, +}; + +enum AW_DEV_INIT { + AW_DEV_INIT_ST = 0, + AW_DEV_INIT_OK = 1, + AW_DEV_INIT_NG = 2, +}; + +enum AW_DEV_STATUS { + AW_DEV_PW_OFF = 0, + AW_DEV_PW_ON, +}; + +enum AW_DEV_FW_STATUS { + AW_DEV_FW_FAILED = 0, + AW_DEV_FW_OK, +}; + +struct aw_device_ops { + int (*aw_i2c_write)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int reg_data); + int (*aw_i2c_read)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int *reg_data); + int (*aw_i2c_write_bits)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int mask, unsigned int reg_data); + int (*aw_set_volume)(struct aw_device *aw_dev, unsigned int value); + int (*aw_get_volume)(struct aw_device *aw_dev, unsigned int *value); + unsigned int (*aw_reg_val_to_db)(unsigned int value); + void (*aw_i2s_enable)(struct aw_device *aw_dev, bool flag); + bool (*aw_check_wr_access)(int reg); + bool (*aw_check_rd_access)(int reg); + int (*aw_get_reg_num)(void); + int (*aw_get_version)(char *buf, int size); + int (*aw_get_dev_num)(void); + void (*aw_set_algo)(struct aw_device *aw_dev); + unsigned int (*aw_get_irq_type)(struct aw_device *aw_dev, unsigned int value); +}; + +struct aw_int_desc { + unsigned int mask_reg; /*interrupt mask reg*/ + unsigned int st_reg; /*interrupt status reg*/ + unsigned int mask_default; /*default mask close all*/ + unsigned int int_mask; /*set mask*/ +}; + +struct aw_frcpwm_desc { + unsigned int reg; + unsigned int mask; + unsigned int frcpwm_val; +}; + +struct aw_soft_rst { + int reg; + int reg_value; +}; + +struct aw_pwd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_amppd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + + +struct aw_vcalb_desc { + unsigned int icalk_reg; + unsigned int icalk_reg_mask; + unsigned int icalk_shift; + unsigned int icalkl_reg; + unsigned int icalkl_reg_mask; + unsigned int icalkl_shift; + unsigned int icalk_sign_mask; + unsigned int icalk_neg_mask; + int icalk_value_factor; + + unsigned int vcalk_reg; + unsigned int vcalk_reg_mask; + unsigned int vcalk_shift; + unsigned int vcalkl_reg; + unsigned int vcalkl_reg_mask; + unsigned int vcalkl_shift; + unsigned int vcalk_sign_mask; + unsigned int vcalk_neg_mask; + int vcalk_value_factor; + + unsigned int vcalb_reg; + int cabl_base_value; + int vcal_factor; +}; + +struct aw_mute_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_sysst_desc { + unsigned int reg; + unsigned int mask; + unsigned int st_check; + unsigned int pll_check; +}; + +struct aw_profctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int spk_mode; + unsigned int cfg_prof_mode; +}; + +struct aw_bstctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int frc_bst; + unsigned int tsp_type; + unsigned int cfg_bst_type; +}; + +struct aw_cco_mux_desc { + unsigned int reg; + unsigned int mask; + unsigned int divided_val; + unsigned int bypass_val; +}; + +struct aw_volume_desc { + unsigned int reg; + unsigned int mask; + unsigned int shift; + int init_volume; + int mute_volume; +}; + +struct aw_voltage_desc { + unsigned int reg; + unsigned int vbat_range; + unsigned int int_bit; +}; + +struct aw_temperature_desc { + unsigned int reg; + unsigned int sign_mask; + unsigned int neg_mask; +}; + +struct aw_ipeak_desc { + unsigned int reg; + unsigned int mask; +}; + +struct aw_container { + int len; + uint8_t data[]; +}; + +struct aw_device { + int index; + int status; + int bstcfg_enable; + int frcpwm_en; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char cur_prof; /*current profile index*/ + unsigned char set_prof; /*set profile index*/ + unsigned int channel; /*pa channel select*/ + unsigned int vol_step; + unsigned int re_max; + unsigned int re_min; + + struct device *dev; + struct i2c_client *i2c; + char acf_name[AW_NAME_MAX]; + char monitor_name[AW_NAME_MAX]; + void *private_data; + + struct aw_int_desc int_desc; + struct aw_pwd_desc pwd_desc; + struct aw_amppd_desc amppd_desc; + struct aw_mute_desc mute_desc; + struct aw_vcalb_desc vcalb_desc; + struct aw_sysst_desc sysst_desc; + struct aw_profctrl_desc profctrl_desc; + struct aw_bstctrl_desc bstctrl_desc; + struct aw_cco_mux_desc cco_mux_desc; + struct aw_voltage_desc voltage_desc; + struct aw_temperature_desc temp_desc; + struct aw_ipeak_desc ipeak_desc; + struct aw_volume_desc volume_desc; + struct aw_prof_info prof_info; + struct aw_cali_desc cali_desc; + struct aw_monitor_desc monitor_desc; + struct aw_soft_rst soft_rst; + struct aw_frcpwm_desc frcpwm_desc; + struct aw_device_ops ops; + struct list_head list_node; +}; + + +int aw_dev_load_acf_check(struct aw_container *aw_cfg); +void aw_dev_deinit(struct aw_device *aw_dev); +int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw_device_start(struct aw_device *aw_dev); +int aw_device_stop(struct aw_device *aw_dev); +int aw_dev_reg_update(struct aw_device *aw_dev, bool force); +int aw_device_irq_reinit(struct aw_device *aw_dev); + +/*profile*/ +int aw_dev_prof_update(struct aw_device *aw_dev, bool force); +int aw_dev_get_profile_count(struct aw_device *aw_dev); +int aw_dev_get_profile_name(struct aw_device *aw_dev, char *name, int index); +int aw_dev_check_profile_index(struct aw_device *aw_dev, int index); +int aw_dev_get_profile_index(struct aw_device *aw_dev); +int aw_dev_set_profile_index(struct aw_device *aw_dev, int index); + +/*re*/ +int aw_dev_get_cali_re(struct aw_device *aw_dev, int32_t *cali_re); +int aw_dev_init_cali_re(struct aw_device *aw_dev); +int aw_dev_dc_status(struct aw_device *aw_dev); + +/*interrupt*/ +int aw_dev_status(struct aw_device *aw_dev); +int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status); +void aw_dev_clear_int_status(struct aw_device *aw_dev); +int aw_dev_set_intmask(struct aw_device *aw_dev, bool flag); + +/*fade int / out*/ +void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step); +int aw_dev_get_fade_vol_step(struct aw_device *aw_dev); +void aw_dev_get_fade_time(unsigned int *time, bool fade_in); +void aw_dev_set_fade_time(unsigned int time, bool fade_in); + +/*dsp kcontrol*/ +int aw_dev_set_afe_module_en(int type, int enable); +int aw_dev_get_afe_module_en(int type, int *status); +int aw_dev_set_copp_module_en(bool enable); +int aw_dev_set_spin(int spin_mode); +int aw_dev_get_spin(int *spin_mode); + +int aw_device_probe(struct aw_device *aw_dev); +int aw_device_remove(struct aw_device *aw_dev); +int aw_dev_get_list_head(struct list_head **head); + +#endif + diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c new file mode 100644 index 000000000000..ced5e746e81f --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -0,0 +1,1051 @@ +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw_device.h" +#include "aw_dsp.h" +#include "aw_log.h" +/*#include "aw_afe.h"*/ + +static DEFINE_MUTEX(g_aw_dsp_msg_lock); +static DEFINE_MUTEX(g_aw_dsp_lock); + + +#define AW_MSG_ID_ENABLE_CALI (0x00000001) +#define AW_MSG_ID_ENABLE_HMUTE (0x00000002) +#define AW_MSG_ID_F0_Q (0x00000003) +#define AW_MSG_ID_DIRECT_CUR_FLAG (0x00000006) +#define AW_MSG_ID_SPK_STATUS (0x00000007) + +/*dsp params id*/ +#define AW_MSG_ID_RX_SET_ENABLE (0x10013D11) +#define AW_MSG_ID_PARAMS (0x10013D12) +#define AW_MSG_ID_TX_SET_ENABLE (0x10013D13) +#define AW_MSG_ID_VMAX_L (0X10013D17) +#define AW_MSG_ID_VMAX_R (0X10013D18) +#define AW_MSG_ID_CALI_CFG_L (0X10013D19) +#define AW_MSG_ID_CALI_CFG_R (0x10013d1A) +#define AW_MSG_ID_RE_L (0x10013d1B) +#define AW_MSG_ID_RE_R (0X10013D1C) +#define AW_MSG_ID_NOISE_L (0X10013D1D) +#define AW_MSG_ID_NOISE_R (0X10013D1E) +#define AW_MSG_ID_F0_L (0X10013D1F) +#define AW_MSG_ID_F0_R (0X10013D20) +#define AW_MSG_ID_REAL_DATA_L (0X10013D21) +#define AW_MSG_ID_REAL_DATA_R (0X10013D22) + +#define AFE_MSG_ID_MSG_0 (0X10013D2A) +#define AFE_MSG_ID_MSG_1 (0X10013D2B) +#define AW_MSG_ID_PARAMS_1 (0x10013D2D) + +#define AW_MSG_ID_SPIN (0x10013D2E) + +int g_tx_topo_id = AW_TX_DEFAULT_TOPO_ID; +int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; +int g_tx_port_id = AW_TX_DEFAULT_PORT_ID; +int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + +enum { + MSG_PARAM_ID_0 = 0, + MSG_PARAM_ID_1, + MSG_PARAM_ID_MAX, +}; + +static uint32_t afe_param_msg_id[MSG_PARAM_ID_MAX] = { + AFE_MSG_ID_MSG_0, + AFE_MSG_ID_MSG_1, +}; + +/***************dsp communicate**************/ +#ifdef CONFIG_AW882XX_DSP +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 1)) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#endif + +#define AW_COPP_MODULE_ID (0X10013D02) /*SKT module id*/ +#define AW_COPP_PARAMS_ID_AWDSP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#ifdef AW_MTK_PLATFORM_WITH_DSP +extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); +extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, int16_t size, uint32_t *buf_len); +#elif defined CONFIG_AW882XX_DSP +//extern int afe_get_topology(int port_id); +extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id,uint32_t param_id, + void *buf, int cmd_size, bool write); +extern int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size); +extern int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + return 0; +} +static int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size) +{ + return 0; +} +static int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size) +{ + return 0; +} +//static int afe_get_topology(int port_id) +//{ + //return 0; +//} + +#endif + +/*#ifdef AW_QCOM_PLATFORM +extern void aw_set_port_id(int tx_port_id, int rx_port_id); +#else +static void aw_set_port_id(int tx_port_id, int rx_port_id) { + return; +} +#endif*/ + +#if 0 +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + /*for v3*/ + int copp_idx = 0; + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s port_id %d, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + + copp_idx = adm_get_default_copp_idx(port_id); + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_debug("%s port_id %d, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; + + return 0; +} +#endif + +static int aw_get_msg_num(int dev_ch, int *msg_num) +{ + switch (dev_ch) { + case AW_DEV_CH_PRI_L: + *msg_num = MSG_PARAM_ID_0; + break; + case AW_DEV_CH_PRI_R: + *msg_num = MSG_PARAM_ID_0; + break; + case AW_DEV_CH_SEC_L: + *msg_num = MSG_PARAM_ID_1; + break; + case AW_DEV_CH_SEC_R: + *msg_num = MSG_PARAM_ID_1; + break; + default: + aw_pr_err("can not find msg num, channel %d ", dev_ch); + return -EINVAL; + } + + aw_pr_dbg("msg num[%d] ", *msg_num); + return 0; +} + +/*****************qcom dsp communication function start**********************/ +#if 0 +static int aw_afe_get_topology(uint32_t param_id) +{ + if (param_id == AW_MSG_ID_TX_SET_ENABLE) + return afe_get_topology(g_tx_port_id); + else + return afe_get_topology(g_rx_port_id); +} + +static int aw_check_dsp_ready(uint32_t param_id) +{ + int ret = 0; + + //ret = aw_afe_get_topology(param_id); + + aw_pr_dbg("topo_id 0x%x ", ret); + + if (param_id == AW_MSG_ID_TX_SET_ENABLE) { + if (ret != g_tx_topo_id) + return false; + else + return true; + } else { + if (ret != g_rx_topo_id) + return false; + else + return true; + } +} +#endif + +static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int size) +{ + int ret; + //int try = 0; + + mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + //if (aw_check_dsp_ready(param_id)) { + ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, true); + mutex_unlock(&g_aw_dsp_lock); + return ret; + //} else { + // try++; + // usleep_range(AW_10000_US, AW_10000_US + 10); + // aw_pr_info("afe topo not ready try again"); + //} + //} + //mutex_unlock(&g_aw_dsp_lock); + + //return -EINVAL; +} + +static int aw_qcom_read_data_from_dsp(uint32_t param_id, void *data, int size) +{ + int ret; + //int try = 0; + + mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + // if (aw_check_dsp_ready(param_id)) { + ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, false); + mutex_unlock(&g_aw_dsp_lock); + return ret; +/* } else { + try++; + usleep_range(AW_10000_US, AW_10000_US + 10); + aw_pr_info("afe topo not ready try again"); + } + } + mutex_unlock(&g_aw_dsp_lock); + + return -EINVAL; +*/ +} + +static int aw_qcom_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + int32_t *dsp_msg; + int ret = 0; + int msg_len = (int)(sizeof(aw_dsp_msg_t) + size); + + mutex_lock(&g_aw_dsp_msg_lock); + dsp_msg = kzalloc(msg_len, GFP_KERNEL); + if (!dsp_msg) { + aw_pr_err("msg_id:0x%x kzalloc dsp_msg error", + msg_id); + ret = -ENOMEM; + goto w_mem_err; + } + dsp_msg[0] = AW_DSP_MSG_TYPE_DATA; + dsp_msg[1] = msg_id; + dsp_msg[2] = AW_DSP_MSG_HDR_VER; + + memcpy(dsp_msg + (sizeof(aw_dsp_msg_t) / sizeof(int32_t)), + data_ptr, size); + + ret = aw_qcom_write_data_to_dsp(afe_param_msg_id[msg_num], + (void *)dsp_msg, msg_len); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, write data to dsp failed", msg_id); + kfree(dsp_msg); + goto w_mem_err; + } + + aw_pr_dbg("msg_id:0x%x, write data[%d] to dsp success", msg_id, msg_len); + mutex_unlock(&g_aw_dsp_msg_lock); + kfree(dsp_msg); + return 0; +w_mem_err: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +static int aw_qcom_read_msg_from_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + aw_dsp_msg_t cmd_msg; + int ret; + + mutex_lock(&g_aw_dsp_msg_lock); + cmd_msg.type = AW_DSP_MSG_TYPE_CMD; + cmd_msg.opcode_id = msg_id; + cmd_msg.version = AW_DSP_MSG_HDR_VER; + + ret = aw_qcom_write_data_to_dsp(afe_param_msg_id[msg_num], + &cmd_msg, sizeof(aw_dsp_msg_t)); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, write cmd to dsp failed", msg_id); + goto dsp_msg_failed; + } + + ret = aw_qcom_read_data_from_dsp(afe_param_msg_id[msg_num], + data_ptr, (int)size); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, read data from dsp failed", msg_id); + goto dsp_msg_failed; + } + + aw_pr_dbg("msg_id:0x%x, read data[%d] from dsp success", msg_id, size); + mutex_unlock(&g_aw_dsp_msg_lock); + return 0; +dsp_msg_failed: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +/******************* afe module communication function ************************/ +static int aw_dsp_set_afe_rx_module_enable(void *buf, int size) +{ + return aw_send_afe_rx_module_enable(g_rx_port_id, buf, size); +} + +static int aw_dsp_set_afe_tx_module_enable(void *buf, int size) +{ + return aw_send_afe_tx_module_enable(g_tx_port_id, buf, size); +} + +static int aw_dsp_get_afe_rx_module_enable(void *buf, int size) +{ + return aw_qcom_read_data_from_dsp(AW_MSG_ID_RX_SET_ENABLE, buf, size); +} + +static int aw_dsp_get_afe_tx_module_enable(void *buf, int size) +{ + return aw_qcom_read_data_from_dsp(AW_MSG_ID_TX_SET_ENABLE, buf, size); +} + +/******************* read/write msg communication function ***********************/ +static int aw_read_msg_from_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + return aw_qcom_read_msg_from_dsp(msg_num, msg_id, data_ptr, size); +} + +static int aw_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + return aw_qcom_write_msg_to_dsp(msg_num, msg_id, data_ptr, size); +} + +/******************* read/write data communication function ***********************/ +static int aw_read_data_from_dsp(uint32_t param_id, void *data, int size) +{ + return aw_qcom_read_data_from_dsp(param_id, data, size); +} + +static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) +{ + return aw_qcom_write_data_to_dsp(param_id, data, size); +} + +/************************* dsp communication function *****************************/ +int aw_dsp_set_afe_module_en(int type, int enable) +{ + int ret; + + switch (type) { + case AW_RX_MODULE: + ret = aw_dsp_set_afe_rx_module_enable(&enable, sizeof(int32_t)); + break; + case AW_TX_MODULE: + ret = aw_dsp_set_afe_tx_module_enable(&enable, sizeof(int32_t)); + break; + default: + pr_err("%s: unsupported type %d\n", __func__, type); + return -EINVAL; + } + + return ret; +} + +int aw_dsp_get_afe_module_en(int type, int *status) +{ + int ret; + + switch (type) { + case AW_RX_MODULE: + ret = aw_dsp_get_afe_rx_module_enable(status, sizeof(int32_t)); + break; + case AW_TX_MODULE: + ret = aw_dsp_get_afe_tx_module_enable(status, sizeof(int32_t)); + break; + default: + pr_err("%s: unsupported type %d\n", __func__, type); + return -EINVAL; + } + + return ret; +} + +int aw_dsp_read_te(struct aw_device *aw_dev, int32_t *te) +{ + int ret; + int msg_num; + int32_t data[8]; /*[re:r0:Te:r0_te]*/ + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SPK_STATUS, + (char *)data, sizeof(int32_t) * 8); + if (ret) { + aw_dev_err(aw_dev->dev, " read Te failed "); + return ret; + } + + if ((aw_dev->channel % 2) == 0) + *te = data[2]; + else + *te = data[6]; + + aw_dev_dbg(aw_dev->dev, "read Te %d", *te); + return 0; +} + +int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te) +{ + int ret; + int msg_num; + int32_t data[8]; /*[re:r0:Te:r0_te]*/ + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SPK_STATUS, + (char *)data, sizeof(int32_t) * 8); + if (ret) { + aw_dev_err(aw_dev->dev, "read spk st failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) { + *r0 = AW_DSP_RE_TO_SHOW_RE(data[0]); + *te = data[2]; + } else { + *r0 = AW_DSP_RE_TO_SHOW_RE(data[4]); + *te = data[6]; + } + aw_dev_dbg(aw_dev->dev, "read Re %d , Te %d", *r0, *te); + return 0; +} + +int aw_dsp_read_spin(int *spin_mode) +{ + int ret; + int32_t spin = 0; + + ret = aw_read_data_from_dsp(AW_MSG_ID_SPIN, &spin, sizeof(int32_t)); + if (ret) { + *spin_mode = 0; + aw_pr_err("read spin failed "); + return ret; + } + *spin_mode = spin; + aw_pr_dbg("read spin done"); + return 0; +} + +int aw_dsp_write_spin(int spin_mode) +{ + int ret; + int32_t spin = spin_mode; + + if (spin >= AW_SPIN_MAX) { + aw_pr_err("spin [%d] unsupported ", spin); + return -EINVAL; + } + + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN, &spin, sizeof(int32_t)); + if (ret) { + aw_pr_err("write spin failed "); + return ret; + } + aw_pr_dbg("write spin done"); + return 0; +} + +int aw_dsp_read_r0(struct aw_device *aw_dev, int32_t *r0) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t data[6]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_REAL_DATA_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_REAL_DATA_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)data, sizeof(int32_t) * 6); + if (ret) { + aw_dev_err(aw_dev->dev, "read real re failed "); + return ret; + } + + *r0 = AW_DSP_RE_TO_SHOW_RE(data[0]); + aw_dev_dbg(aw_dev->dev, "read r0 %d\n", *r0); + return 0; +} + +int aw_dsp_read_cali_data(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_REAL_DATA_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_REAL_DATA_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali dara failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read cali_data"); + return 0; +} + +int aw_dsp_get_dc_status(struct aw_device *aw_dev) +{ + int ret; + int msg_num; + int32_t data[2]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_DIRECT_CUR_FLAG, (char *)data, sizeof(int32_t) * 2); + if (ret) { + aw_dev_err(aw_dev->dev, "read dc flag failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) + ret = data[0]; + else + ret = data[1]; + + aw_dev_dbg(aw_dev->dev, "read direct current status:%d", ret); + return ret; +} + +int aw_dsp_read_f0_q(struct aw_device *aw_dev, int32_t *f0, int32_t *q) +{ + int ret; + int msg_num; + int32_t data[4]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_F0_Q, (char *)data, sizeof(int32_t) * 4); + if (ret) { + aw_dev_err(aw_dev->dev, "read f0 & q failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) { + *f0 = data[0]; + *q = data[1]; + } else { + *f0 = data[2]; + *q = data[3]; + } + aw_dev_dbg(aw_dev->dev, "read f0 is %d, q is %d", *f0, *q); + return ret; +} + +int aw_dsp_read_f0(struct aw_device *aw_dev, int32_t *f0) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_F0_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_F0_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)f0, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "read f0 failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read f0"); + return 0; +} + +int aw_dsp_cali_en(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + int msg_num; + int32_t enable = is_enable; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ENABLE_CALI, (char *)&enable, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali en failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write cali_en[%d]", is_enable); + return 0; +} + +int aw_dsp_hmute_en(struct aw_device *aw_dev, bool is_hmute) +{ + int32_t hmute = is_hmute; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ENABLE_HMUTE, (char *)&hmute, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write hmue failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write hmute[%d]", is_hmute); + return 0; +} + +int aw_dsp_read_cali_re(struct aw_device *aw_dev, int32_t *cali_re) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t read_re = 0; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_RE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_RE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)&read_re, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali re failed "); + return ret; + } + *cali_re = AW_DSP_RE_TO_SHOW_RE(read_re); + aw_dev_dbg(aw_dev->dev, "read cali re done"); + return 0; +} + +int aw_dsp_write_cali_re(struct aw_device *aw_dev, int32_t cali_re) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t local_re = AW_SHOW_RE_TO_DSP_RE(cali_re); + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_RE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_RE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, (char *)&local_re, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali re failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write cali re done"); + return 0; +} + +int aw_dsp_write_params(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (msg_num == MSG_PARAM_ID_0) + msg_id = AW_MSG_ID_PARAMS; + else + msg_id = AW_MSG_ID_PARAMS_1; + + ret = aw_write_data_to_dsp(msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write params failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write params done"); + return 0; +} + +int aw_dsp_read_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_VMAX_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_VMAX_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read vmax failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read vmax done"); + return 0; +} + +int aw_dsp_write_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_VMAX_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_VMAX_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write vmax failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write vmax done"); + return 0; +} + +int aw_dsp_noise_en(struct aw_device *aw_dev, bool is_noise) +{ + int32_t noise = is_noise; + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_NOISE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_NOISE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, (char *)&noise, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write noise failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write noise[%d] done", noise); + return 0; +} + +int aw_dsp_read_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_CALI_CFG_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_CALI_CFG_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali_cfg failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read cali_cfg done"); + return 0; +} + +int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_CALI_CFG_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_CALI_CFG_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali_cfg failed "); + return ret; + } + + aw_dev_dbg(aw_dev->dev, "write cali_cfg done"); + return 0; +} + +int aw_dsp_read_msg(struct aw_device *aw_dev, + uint32_t msg_id, char *data_ptr, unsigned int data_size) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + return aw_read_msg_from_dsp(msg_num, msg_id, data_ptr, data_size); +} + +int aw_dsp_write_msg(struct aw_device *aw_dev, + uint32_t msg_id, char *data_ptr, unsigned int data_size) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + return aw_write_msg_to_dsp(msg_num, msg_id, data_ptr, data_size); +} + +int aw_dsp_set_copp_module_en(bool enable) +{ + int ret; + + ret = aw_adm_param_enable(g_rx_port_id, AW_COPP_MODULE_ID, + AW_COPP_PARAMS_ID_AWDSP_ENABLE, enable); + if (ret) + return -EINVAL; + + aw_pr_info("set skt %s", enable == 1 ? "enable" : "disable"); + return 0; +} + +void aw_device_parse_topo_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-tx-topo-id", &g_tx_topo_id); + if (ret < 0) { + g_tx_topo_id = AW_TX_DEFAULT_TOPO_ID; + aw_dev_info(aw_dev->dev, "read aw-tx-topo-id failed,use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-topo-id", &g_rx_topo_id); + if (ret < 0) { + g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; + aw_dev_info(aw_dev->dev, "read aw-rx-topo-id failed,use default"); + } + + aw_dev_info(aw_dev->dev, "tx-topo-id: 0x%x, rx-topo-id: 0x%x", + g_tx_topo_id, g_rx_topo_id); +} + +void aw_device_parse_port_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-tx-port-id", &g_tx_port_id); + if (ret < 0) { + g_tx_port_id = AW_TX_DEFAULT_PORT_ID; + aw_dev_info(aw_dev->dev, "read aw-tx-port-id failed,use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-port-id", &g_rx_port_id); + if (ret < 0) { + g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + aw_dev_info(aw_dev->dev, "read aw-rx-port-id failed,use default"); + } + + aw_dev_info(aw_dev->dev, "tx-port-id: 0x%x, rx-port-id: 0x%x", + g_tx_port_id, g_rx_port_id); + +} + diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h new file mode 100644 index 000000000000..8789c48e4cec --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -0,0 +1,60 @@ +#ifndef __AWINIC_DSP_H__ +#define __AWINIC_DSP_H__ + +/*factor form 12bit(4096) to 1000*/ +#define AW_DSP_RE_TO_SHOW_RE(re) (((re) * (1000)) >> (12)) +#define AW_SHOW_RE_TO_DSP_RE(re) (((re) << 12) / (1000)) + +#define AW_DSP_TRY_TIME (3) +#define AW_DSP_SLEEP_TIME (10) + +#define AW_TX_DEFAULT_TOPO_ID (0x1000FF00) +#define AW_RX_DEFAULT_TOPO_ID (0x1000FF01) +#define AW_TX_DEFAULT_PORT_ID (0x1007) +#define AW_RX_DEFAULT_PORT_ID (0x1006) + +enum aw_dsp_msg_type { + AW_DSP_MSG_TYPE_DATA = 0, + AW_DSP_MSG_TYPE_CMD = 1, +}; + +enum { + AW_SPIN_0 = 0, + AW_SPIN_90, + AW_SPIN_180, + AW_SPIN_270, + AW_SPIN_MAX, +}; + +#define AW_DSP_MSG_HDR_VER (1) +typedef struct aw_msg_hdr aw_dsp_msg_t; + +int aw_dsp_write_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); +int aw_dsp_read_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); +int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_read_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_noise_en(struct aw_device *aw_dev, bool is_noise); +int aw_dsp_write_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_read_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_write_params(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_write_cali_re(struct aw_device *aw_dev, int32_t cali_re); +int aw_dsp_read_cali_re(struct aw_device *aw_dev, int32_t *cali_re); +int aw_dsp_read_r0(struct aw_device *aw_dev, int32_t *r0); +int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te); +int aw_dsp_read_te(struct aw_device *aw_dev, int32_t *te); +int aw_dsp_get_dc_status(struct aw_device *aw_dev); +int aw_dsp_hmute_en(struct aw_device *aw_dev, bool is_hmute); +int aw_dsp_cali_en(struct aw_device *aw_dev, bool is_enable); +int aw_dsp_read_f0(struct aw_device *aw_dev, int32_t *f0); +int aw_dsp_read_f0_q(struct aw_device *aw_dev, int32_t *f0, int32_t *q); +int aw_dsp_read_cali_data(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_set_afe_module_en(int type, int enable); +int aw_dsp_get_afe_module_en(int type, int *status); +int aw_dsp_set_copp_module_en(bool enable); +int aw_dsp_write_spin(int spin_mode); +int aw_dsp_read_spin(int *spin_mode); +void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); +void aw_device_parse_port_id_dt(struct aw_device *aw_dev); + +#endif + diff --git a/asoc/codecs/aw882xxacf/aw_init.c b/asoc/codecs/aw882xxacf/aw_init.c new file mode 100644 index 000000000000..12a05e59ca55 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_init.c @@ -0,0 +1,1612 @@ +/* + * aw_init.c aw882xx codec module + * + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_pid_1852_reg.h" +#include "aw_pid_2013_reg.h" +#include "aw_pid_2032_reg.h" +#include "aw_pid_2055_reg.h" +#include "aw_pid_2071_reg.h" +#include "aw_pid_2113_reg.h" +#include "aw_log.h" + +int aw882xx_dev_i2c_write_bits(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_write_bits(aw882xx, reg_addr, mask, reg_data); +} + +int aw882xx_dev_i2c_write(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_write(aw882xx, reg_addr, reg_data); +} + +int aw882xx_dev_i2c_read(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int *reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_read(aw882xx, reg_addr, reg_data); +} + +void aw882xx_dev_set_algo_en(struct aw_device *aw_dev) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + aw882xx_kcontorl_set(aw882xx); +} + +/* [7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1 */ +static unsigned int aw_pid_1852_reg_val_to_db(unsigned int value) +{ + return ((value >> 4) * AW_PID_1852_VOL_STEP_DB + (value & 0x0f)); +} + +/* [7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2 */ +static unsigned int aw_pid_1852_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_1852_VOL_STEP_DB) << 4) + (value % AW_PID_1852_VOL_STEP_DB)); +} + +static int aw_pid_1852_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_1852_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_1852_HAGCCFG4_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* 15 : 8] volume */ + real_value = (real_value << 8) | (reg_value & 0x00ff); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_1852_HAGCCFG4_REG, real_value); + return 0; +} + +static int aw_pid_1852_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_1852_HAGCCFG4_REG, ®_value); + + /* [15 : 8] volume */ + real_value = reg_value >> 8; + + real_value = aw_pid_1852_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_1852_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_1852_I2SCFG1_REG, + AW_PID_1852_I2STXEN_MASK, + AW_PID_1852_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_1852_I2SCFG1_REG, + AW_PID_1852_I2STXEN_MASK, + AW_PID_1852_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_1852_check_rd_access(int reg) +{ + if (reg >= AW_PID_1852_REG_MAX) + return false; + + + if (aw_pid_1852_reg_access[reg] & AW_PID_1852_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_1852_check_wr_access(int reg) +{ + if (reg >= AW_PID_1852_REG_MAX) + return false; + + + if (aw_pid_1852_reg_access[reg] & AW_PID_1852_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_1852_get_reg_num(void) +{ + return AW_PID_1852_REG_MAX; +} + +static unsigned int aw_pid_1852_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + /* UVL0 */ + if (value & (~AW_PID_1852_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_1852_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_1852_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_1852_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /* call aw device init func */ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_1852_ACF_FILE, strlen(AW_PID_1852_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_1852_MONITOR_FILE, strlen(AW_PID_1852_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->vol_step = AW_PID_1852_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_1852_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_1852_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_1852_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_1852_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_1852_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_1852_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_1852_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_1852_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_1852_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_1852_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_1852_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_1852_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_1852_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_1852_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_1852_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_1852_PWDN_NORMAL_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_1852_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_1852_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_1852_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_1852_AMPPD_NORMAL_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_1852_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_1852_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_1852_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_1852_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_1852_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_1852_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_1852_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_1852_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_1852_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_1852_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_1852_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_1852_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_1852_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_1852_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_1852_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_1852_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_1852_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_1852_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_1852_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_1852_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_1852_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_1852_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_1852_I2S_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_1852_I2S_CCO_MUX_8_16_32KHZ_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_1852_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_1852_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_1852_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_1852_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_1852_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_1852_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_1852_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_1852_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_1852_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_1852_HAGCCFG4_REG; + aw_pa->volume_desc.mask = AW_PID_1852_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_1852_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_1852_MUTE_VOL; + + aw_pa->soft_rst.reg = AW_PID_1852_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/****************************************************** + * + * A2013 init + * + ******************************************************/ +/*[9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1*/ +static unsigned int aw_pid_2013_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2013_VOL_STEP_DB + (value & 0x003f)); +} + +/*[9 : 6]: -6DB ; [5 : 0]: -0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8*/ +static unsigned int aw_pid_2013_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2013_VOL_STEP_DB) << 6) + (value % AW_PID_2013_VOL_STEP_DB)); +} + + +static int aw_pid_2013_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2013_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2013_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*9 : 0] volume*/ + real_value = real_value | (reg_value & 0xfc00); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2013_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2013_get_volume(struct aw_device *aw_dev, unsigned int* value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2013_SYSCTRL2_REG, ®_value); + + /*[9 : 0] volume*/ + real_value = reg_value & 0x03ff; + + real_value = aw_pid_2013_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2013_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (flag) + aw882xx_i2c_write_bits(aw882xx, AW_PID_2013_I2SCTRL1_REG, + AW_PID_2013_I2STXEN_MASK, + AW_PID_2013_I2STXEN_ENABLE_VALUE); + else + aw882xx_i2c_write_bits(aw882xx, AW_PID_2013_I2SCFG1_REG, + AW_PID_2013_I2STXEN_MASK, + AW_PID_2013_I2STXEN_DISABLE_VALUE); +} + +static bool aw_pid_2013_check_rd_access(int reg) +{ + if (reg >= AW_PID_2013_REG_MAX) + return false; + + if (aw_pid_2013_reg_access[reg] & AW_PID_2013_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2013_check_wr_access(int reg) +{ + if (reg >= AW_PID_2013_REG_MAX) + return false; + + if (aw_pid_2013_reg_access[reg] & AW_PID_2013_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2013_get_reg_num(void) +{ + return AW_PID_2013_REG_MAX; +} + +static unsigned int aw_pid_2013_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /*UVL0*/ + if (value & (~AW_PID_2013_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /*BSTOCM*/ + if (value & (~AW_PID_2013_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /*OCDI*/ + if (value & (~AW_PID_2013_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /*OTHI*/ + if (value & (~AW_PID_2013_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static void aw_pid_2013_efver_check(struct aw_device *aw_dev) +{ + unsigned int reg_val; + unsigned int efverh; + unsigned int efverl; + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2013_EFRM1_REG, ®_val); + + efverh = (((reg_val & (~AW_PID_2013_EFVERH_MASK)) >> + AW_PID_2013_EFVERH_START_BIT) ^ + AW_PID_2013_EFVER_CHECK); + efverl = (((reg_val & (~AW_PID_2013_EFVERL_MASK)) >> + AW_PID_2013_EFVERL_START_BIT) ^ + AW_PID_2013_EFVER_CHECK); + + aw_dev_dbg(aw_dev->dev, "efverh: 0x%0x, efverl: 0x%0x", efverh, efverl); + + if (efverh && efverl) { + aw_dev->bstcfg_enable = AW_BSTCFG_ENABLE; + aw_dev_info(aw_dev->dev, "A2013 EFVER A"); + } else if (efverh && !efverl) { + aw_dev->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_dev_info(aw_dev->dev, "A2013 EFVER B"); + } else { + aw_dev->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_dev_info(aw_dev->dev, "unsupport A2013 EFVER"); + } + aw_dev_info(aw_dev->dev, "bstcfg enable: %d", aw_dev->bstcfg_enable); +} + +static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2013_ACF_FILE, strlen(AW_PID_2013_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2013_MONITOR_FILE, strlen(AW_PID_2013_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->vol_step = AW_PID_2013_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2013_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2013_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2013_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2013_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2013_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2013_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2013_get_reg_num; + aw_pa->ops.aw_get_irq_type =aw_pid_2013_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2013_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2013_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2013_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2013_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2013_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2013_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2013_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2013_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2013_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2013_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2013_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2013_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2013_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2013_VSNTM1_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2013_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2013_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2013_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2013_EFRH_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2013_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2013_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2013_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2013_EFRM2_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2013_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2013_EF_ISN_GESLP2_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2013_EF_ISN_GESLP2_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2013_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2013_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2013_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2013_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2013_PLLS_LOCKED_VALUE; + + aw_pa->profctrl_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->profctrl_desc.mask = AW_PID_2013_EN_TRAN_MASK; + aw_pa->profctrl_desc.spk_mode = AW_PID_2013_EN_TRAN_SPK_VALUE; + + aw_pa->bstctrl_desc.reg = AW_PID_2013_BSTCTRL2_REG; + aw_pa->bstctrl_desc.mask = AW_PID_2013_BST_MODE_MASK; + aw_pa->bstctrl_desc.frc_bst = AW_PID_2013_BST_MODE_FORCE_BOOST_VALUE; + aw_pa->bstctrl_desc.tsp_type = AW_PID_2013_BST_MODE_TRANSPARENT_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2013_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2013_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2013_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2013_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2013_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2013_MONITOR_TEMP_SIGN_MASK; + + aw_pa->cco_mux_desc.reg = AW_PID_2013_PLLCTRL3_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2013_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2013_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2013_CCO_MUX_BYPASS_VALUE; + + aw_pa->ipeak_desc.reg = AW_PID_2013_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2013_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2013_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2013_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2013_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2013_MUTE_VOL; + + aw_pa->soft_rst.reg = AW_PID_2013_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + aw_pid_2013_efver_check(aw_pa); + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/****************************************************** + * + * A2032 init + * + ******************************************************/ +/*[7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1*/ +static unsigned int aw_pid_2032_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2032_VOL_STEP_DB + (value & 0x3f)); +} + +/* [7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2 */ +static unsigned int aw_pid_2032_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2032_VOL_STEP_DB) << 6) + (value % AW_PID_2032_VOL_STEP_DB)); +} + + +static int aw_pid_2032_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2032_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2032_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << 6) | (reg_value & 0x003f); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2032_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2032_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2032_SYSCTRL2_REG, ®_value); + + /* [15 : 6] volume */ + real_value = reg_value >> 6; + + real_value = aw_pid_2032_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2032_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2032_I2SCFG1_REG, + AW_PID_2032_I2STXEN_MASK, + AW_PID_2032_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2032_I2SCFG1_REG, + AW_PID_2032_I2STXEN_MASK, + AW_PID_2032_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2032_check_rd_access(int reg) +{ + if (reg >= AW_PID_2032_REG_MAX) + return false; + + if (aw_pid_2032_reg_access[reg] & AW_PID_2032_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2032_check_wr_access(int reg) +{ + if (reg >= AW_PID_2032_REG_MAX) + return false; + + if (aw_pid_2032_reg_access[reg] & + AW_PID_2032_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2032_get_reg_num(void) +{ + return AW_PID_2032_REG_MAX; +} + +static unsigned int aw_pid_2032_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2032_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2032_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2032_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2032_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /* call aw device init func */ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2032_ACF_FILE, strlen(AW_PID_2032_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2032_MONITOR_FILE, strlen(AW_PID_2032_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->vol_step = AW_PID_2032_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2032_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2032_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2032_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2032_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2032_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2032_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2032_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2032_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2032_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2032_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2032_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2032_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2032_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2032_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2032_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2032_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2032_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2032_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2032_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2032_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_2032_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2032_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2032_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2032_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2032_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2032_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2032_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2032_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2032_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2032_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2032_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2032_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2032_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2032_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2032_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2032_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2032_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2032_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2032_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2032_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2032_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2032_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2032_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2032_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2032_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2032_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2032_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2032_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2032_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2032_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2032_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2032_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2032_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2032_MUTE_VOL; + + aw_pa->soft_rst.reg = AW_PID_2032_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2055_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2055_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2055_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2055_VOL_STEP_DB) << 6) + (value % AW_PID_2055_VOL_STEP_DB)); +} + +static int aw_pid_2055_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2055_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2055_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*[9 : 0] volume*/ + real_value = (real_value | (reg_value & 0xfc00)); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2055_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2055_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2055_SYSCTRL2_REG, ®_value); + + /* [9 : 0] volume */ + real_value = (reg_value & 0x03ff); + + real_value = aw_pid_2055_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static bool aw_pid_2055_check_rd_access(int reg) +{ + if (reg >= AW_PID_2055_REG_MAX) { + return false; + } + + if (aw_pid_2055_reg_access[reg] & AW_PID_2055_REG_RD_ACCESS) { + return true; + } else { + return false; + } +} + +static bool aw_pid_2055_check_wr_access(int reg) +{ + if (reg >= AW_PID_2055_REG_MAX) + return false; + + if (aw_pid_2055_reg_access[reg] & + AW_PID_2055_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2055_get_reg_num(void) +{ + return AW_PID_2055_REG_MAX; +} + +static unsigned int aw_pid_2055_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2055_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2055_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2055_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2055_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2055_ACF_FILE, strlen(AW_PID_2055_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2055_MONITOR_FILE, strlen(AW_PID_2055_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->vol_step = AW_PID_2055_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2055_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2055_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2055_reg_val_to_db; + aw_pa->ops.aw_check_rd_access = aw_pid_2055_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2055_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2055_get_reg_num; + aw_pa->ops.aw_get_irq_type =aw_pid_2055_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2055_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2055_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2055_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2055_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2055_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2055_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2055_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2055_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2055_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2055_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2055_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2055_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2055_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg = AW_REG_NONE; + + aw_pa->sysst_desc.reg = AW_PID_2055_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2055_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2055_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2055_PLLS_LOCKED_VALUE; + + aw_pa->voltage_desc.reg = AW_REG_NONE; + aw_pa->temp_desc.reg = AW_REG_NONE; + + aw_pa->ipeak_desc.reg = AW_PID_2055_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2055_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2055_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2055_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2055_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2055_MUTE_VOL; + + aw_pa->cco_mux_desc.reg = AW_PID_2055_PLLCTRL3_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2055_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2055_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2055_CCO_MUX_BYPASS_VALUE; + + aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2071_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2071_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2071_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2071_VOL_STEP_DB) << 6) + (value % AW_PID_2071_VOL_STEP_DB)); +} + + +static int aw_pid_2071_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2071_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2071_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << 6) | (reg_value & 0x003f); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2071_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2071_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2071_SYSCTRL2_REG, ®_value); + + /* [15 : 6] volume */ + real_value = reg_value >> 6; + + real_value = aw_pid_2071_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2071_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2071_I2SCFG1_REG, + AW_PID_2071_I2STXEN_MASK, + AW_PID_2071_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2071_I2SCFG1_REG, + AW_PID_2071_I2STXEN_MASK, + AW_PID_2071_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2071_check_rd_access(int reg) +{ + if (reg >= AW_PID_2071_REG_MAX) + return false; + + if (aw_pid_2071_reg_access[reg] & AW_PID_2071_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2071_check_wr_access(int reg) +{ + if (reg >= AW_PID_2071_REG_MAX) + return false; + + if (aw_pid_2071_reg_access[reg] & + AW_PID_2071_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2071_get_reg_num(void) +{ + return AW_PID_2071_REG_MAX; +} + +static unsigned int aw_pid_2071_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2071_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2071_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2071_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2071_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2071_ACF_FILE, strlen(AW_PID_2071_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2071_MONITOR_FILE, strlen(AW_PID_2071_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->vol_step = AW_PID_2071_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2071_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2071_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2071_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2071_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2071_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2071_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2071_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2071_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2071_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2071_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2071_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2071_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2071_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2071_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2071_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2071_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2071_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2071_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2071_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2071_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_2071_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2071_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2071_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2071_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2071_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2071_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2071_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2071_EFRH_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2071_EF_VSN_OFFSET_MASK; + aw_pa->vcalb_desc.icalk_shift = AW_PID_2071_ICALK_SHIFT; + aw_pa->vcalb_desc.icalkl_reg = AW_PID_2071_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg_mask = AW_PID_2071_EF_ISN_OFFSET_MASK; + aw_pa->vcalb_desc.icalkl_shift = AW_PID_2071_ICALKL_SHIFT; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2071_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2071_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2071_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2071_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2071_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2071_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2071_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2071_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2071_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2071_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2071_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2071_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2071_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2071_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2071_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2071_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2071_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2071_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2071_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2071_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2071_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2071_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2071_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2071_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2071_MUTE_VOL; + + aw_pa->soft_rst.reg = AW_PID_2071_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2113_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2113_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2113_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2113_VOL_STEP_DB) << 6) + (value % AW_PID_2113_VOL_STEP_DB)); +} + +static int aw_pid_2113_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2113_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2113_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*[9 : 0] volume*/ + real_value = (real_value | (reg_value & 0xfc00)); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2113_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2113_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2113_SYSCTRL2_REG, ®_value); + + /* [9 : 0] volume */ + real_value = (reg_value & 0x03ff); + + real_value = aw_pid_2113_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2113_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2113_I2SCTRL3_REG, + AW_PID_2113_I2STXEN_MASK, + AW_PID_2113_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2113_I2SCTRL3_REG, + AW_PID_2113_I2STXEN_MASK, + AW_PID_2113_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2113_check_rd_access(int reg) +{ + if (reg >= AW_PID_2113_REG_MAX) + return false; + + if (aw_pid_2113_reg_access[reg] & AW_PID_2113_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2113_check_wr_access(int reg) +{ + if (reg >= AW_PID_2113_REG_MAX) + return false; + + if (aw_pid_2113_reg_access[reg] & + AW_PID_2113_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2113_get_reg_num(void) +{ + return AW_PID_2113_REG_MAX; +} + +static unsigned int aw_pid_2113_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2113_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2113_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2113_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2113_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static void aw_pid_2113_efver_check(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + uint16_t temh = 0; + uint16_t teml = 0; + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2113_EFRH3_REG, ®_val); + temh = ((uint16_t)reg_val & (~AW_PID_2113_TEMH_MASK)); + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2113_EFRL3_REG, ®_val); + teml = ((uint16_t)reg_val & (~AW_PID_2113_TEML_MASK)); + + if ((temh | teml) == AW_PID_2113_FRCPWM_CFG) + aw_dev->frcpwm_en = AW_FRCPWM_ENABLE; + else + aw_dev->frcpwm_en = AW_FRCPWM_DISABLE; + + aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcpwm_en is %d", + (temh | teml), aw_dev->frcpwm_en); +} + +static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2113_ACF_FILE, strlen(AW_PID_2113_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2113_MONITOR_FILE, strlen(AW_PID_2113_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->vol_step = AW_PID_2113_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2113_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2113_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2113_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2113_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2113_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2113_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2113_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2113_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2113_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2113_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2113_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2113_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2113_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2113_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2113_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2113_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2113_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2113_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2113_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2113_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2113_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2113_VSNTM1_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2113_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2113_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_reg = AW_PID_2113_EFRH4_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2113_EF_ISN_GESLP_H_MASK; + aw_pa->vcalb_desc.icalk_shift = AW_PID_2113_ICALK_SHIFT; + aw_pa->vcalb_desc.icalkl_reg = AW_PID_2113_EFRL4_REG; + aw_pa->vcalb_desc.icalkl_reg_mask = AW_PID_2113_EF_ISN_GESLP_L_MASK; + aw_pa->vcalb_desc.icalkl_shift = AW_PID_2113_ICALKL_SHIFT; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2113_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2113_EF_ISN_GESLP_NEG; + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2113_ICABLK_FACTOR; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2113_EFRH3_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2113_EF_VSN_GESLP_H_MASK; + aw_pa->vcalb_desc.vcalk_shift = AW_PID_2113_VCALK_SHIFT; + aw_pa->vcalb_desc.vcalkl_reg = AW_PID_2113_EFRL3_REG; + aw_pa->vcalb_desc.vcalkl_reg_mask = AW_PID_2113_EF_VSN_GESLP_L_MASK; + aw_pa->vcalb_desc.vcalkl_shift = AW_PID_2113_VCALKL_SHIFT; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2113_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2113_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2113_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2113_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2113_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2113_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2113_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2113_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2113_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2113_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2113_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2113_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2113_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2113_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2113_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2113_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2113_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2113_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2113_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2113_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2113_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2113_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2113_MUTE_VOL; + + aw_pa->frcpwm_desc.reg = AW_PID_2113_BSTCTRL3_REG; + aw_pa->frcpwm_desc.mask = AW_PID_2113_FORCE_PWM_MASK; + aw_pa->frcpwm_desc.frcpwm_val = AW_PID_2113_FORCE_PWM_FORCEMINUS_PWM_VALUE; + + aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + aw_pid_2113_efver_check(aw_pa); + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +int aw882xx_init(struct aw882xx *aw882xx, int index) +{ + if (aw882xx->chip_id == PID_1852_ID) + return aw_pid_1852_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2013_ID) + return aw_pid_2013_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2032_ID) + return aw_pid_2032_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2055_ID) + return aw_pid_2055_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2071_ID) + return aw_pid_2071_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2113_ID) + return aw_pid_2113_dev_init(aw882xx, index); + + aw_dev_err(aw882xx->dev, "unsupported chip id %d", aw882xx->chip_id); + return -EINVAL; +} + diff --git a/asoc/codecs/aw882xxacf/aw_log.h b/asoc/codecs/aw882xxacf/aw_log.h new file mode 100644 index 000000000000..31e0f8e1e510 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_log.h @@ -0,0 +1,34 @@ +#ifndef __AWINIC_LOG_H__ +#define __AWINIC_LOG_H__ + +/******************************************** + * print information control + *******************************************/ +#define aw_dev_err(dev, format, ...) \ + do { \ + pr_err("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_dev_info(dev, format, ...) \ + do { \ + pr_info("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_dev_dbg(dev, format, ...) \ + do { \ + pr_debug("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) + +#define aw_pr_err(format, ...) \ + do { \ + pr_err("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_pr_info(format, ...) \ + do { \ + pr_info("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_pr_dbg(format, ...) \ + do { \ + pr_debug("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) + +#endif + diff --git a/asoc/codecs/aw882xxacf/aw_monitor.c b/asoc/codecs/aw882xxacf/aw_monitor.c new file mode 100644 index 000000000000..5ee8af842b70 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_monitor.c @@ -0,0 +1,1204 @@ +/* + * awinic_monitor.c monitor_module + * + * Version: v0.1.17 + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw_dsp.h" +#include "aw_monitor.h" +#include "aw_log.h" + + +/***************************************************** + * device monitor + *****************************************************/ +static int aw_get_hmute(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + int ret; + struct aw_mute_desc *desc = &aw_dev->mute_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) + return ret; + + if (reg_val & (~desc->mask)) + ret = 1; + else + ret = 0; + + return ret; +} + +static int aw_monitor_get_data_form_system(struct aw_device *aw_dev, + int *data, int data_type) +{ + char name[] = "battery"; + int ret; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, data_type, &prop); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get data failed"); + return -EINVAL; + } + *data = prop.intval; + aw_dev_dbg(aw_dev->dev, "data: %d", *data); + } else { + aw_dev_err(aw_dev->dev, "no struct power supply name : %s", name); + return -EINVAL; + } + return 0; + +} + +static int aw_monitor_get_voltage(struct aw_device *aw_dev, unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + struct aw_voltage_desc *desc = &aw_dev->voltage_desc; + + if (desc->reg == AW_REG_NONE) { + ret = aw_monitor_get_data_form_system(aw_dev, (int *)vol, POWER_SUPPLY_PROP_VOLTAGE_NOW); + if (ret) { + aw_dev_err(aw_dev->dev, "get voltage from system failed!"); + return ret; + } + *vol = (*vol) / 1000; + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, vol); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read voltage failed!"); + return ret; + } + local_vol = ((*vol) * desc->vbat_range) / desc->int_bit; + *vol = local_vol; + } + + aw_dev_info(aw_dev->dev, "chip voltage is %d", *vol); + + return 0; +} + +static int aw_monitor_get_temperature(struct aw_device *aw_dev, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + struct aw_temperature_desc *desc = &aw_dev->temp_desc; + + if (desc->reg == AW_REG_NONE) { + ret = aw_monitor_get_data_form_system(aw_dev, temp, POWER_SUPPLY_PROP_TEMP); + if (ret) { + aw_dev_err(aw_dev->dev, "get temperature from system failed!"); + return ret; + } + *temp = (*temp) / 10; + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get temperature failed!"); + return ret; + } + aw_dev_info(aw_dev->dev, "reg val is 0x%04x", reg_val); + local_temp = reg_val; + if (local_temp & (~desc->sign_mask)) + local_temp = local_temp | desc->neg_mask; + + *temp = (int16_t)local_temp; + } + + aw_dev_info(aw_dev->dev, "chip temperature = %d", *temp); + return 0; +} + +static int aw_monitor_get_temp_and_vol(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + unsigned int voltage = 0; + int current_temp = 0; + int ret = -1; + +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw_monitor_get_voltage(aw_dev, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } + + if (monitor->test_temp == 0) { + ret = aw_monitor_get_temperature(aw_dev, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw_monitor_get_voltage(aw_dev, &voltage); + if (ret < 0) + return ret; + + ret = aw_monitor_get_temperature(aw_dev, ¤t_temp); + if (ret < 0) + return ret; +#endif + + monitor->vol_trace.sum_val += voltage; + monitor->temp_trace.sum_val += current_temp; + monitor->samp_count++; + + return 0; +} + +static int aw_monitor_first_get_data_form_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw_dev->dev, "table_info.aw_table is null"); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + return 0; +} + +static int aw_monitor_trace_data_from_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw_dev->dev, "table_info.aw_table is null"); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val && + data_trace->sum_val <= table_info.aw_table[i].max_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + + return 0; +} + +static int aw_monitor_get_data_from_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace, + uint32_t aplha) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + + if (monitor->first_entry == AW_FIRST_ENTRY) { + return aw_monitor_first_get_data_form_table(aw_dev, + table_info, data_trace); + } else { + data_trace->sum_val = data_trace->sum_val / monitor->samp_count; + data_trace->sum_val = ((int32_t)aplha * data_trace->sum_val + + (1000 - (int32_t)aplha) * data_trace->pre_val) / 1000; + return aw_monitor_trace_data_from_table(aw_dev, + table_info, data_trace); + } + + return 0; +} + +static int aw_monitor_get_data(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_monitor_trace *vol_trace = &monitor->vol_trace; + struct aw_monitor_trace *temp_trace = &monitor->temp_trace; + int ret; + + if (monitor_cfg->vol_switch) { + ret = aw_monitor_get_data_from_table(aw_dev, + monitor_cfg->vol_info, vol_trace, + monitor_cfg->vol_aplha); + if (ret < 0) + return ret; + } else { + vol_trace->aw_table.ipeak = IPEAK_NONE; + vol_trace->aw_table.gain = GAIN_NONE; + vol_trace->aw_table.vmax = VMAX_NONE; + } + + if (monitor_cfg->temp_switch) { + ret = aw_monitor_get_data_from_table(aw_dev, + monitor_cfg->temp_info, temp_trace, + monitor_cfg->temp_aplha); + if (ret < 0) + return ret; + } else { + temp_trace->aw_table.ipeak = IPEAK_NONE; + temp_trace->aw_table.gain = GAIN_NONE; + temp_trace->aw_table.vmax = VMAX_NONE; + } + + aw_dev_dbg(aw_dev->dev, "filter_vol:%d, vol: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x", + monitor->vol_trace.sum_val, vol_trace->aw_table.ipeak, + vol_trace->aw_table.gain, vol_trace->aw_table.vmax); + + aw_dev_dbg(aw_dev->dev, "filter_temp:%d, temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x", + monitor->temp_trace.sum_val, temp_trace->aw_table.ipeak, + temp_trace->aw_table.gain, temp_trace->aw_table.vmax); + return 0; +} + +static void aw_monitor_get_cfg(struct aw_device *aw_dev, + struct aw_table *set_table) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_table *temp_data = &monitor->temp_trace.aw_table; + struct aw_table *vol_data = &monitor->vol_trace.aw_table; + + if (temp_data->ipeak == IPEAK_NONE && vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else if (temp_data->ipeak == IPEAK_NONE) { + memcpy(set_table, vol_data, sizeof(struct aw_table)); + } else if (vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else { + if (monitor->monitor_cfg.logic_switch == AW_MON_LOGIC_OR) { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + temp_data->ipeak : vol_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + vol_data->gain : temp_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + vol_data->vmax : temp_data->vmax); + } else { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + vol_data->ipeak : temp_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + temp_data->gain : vol_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + temp_data->vmax : vol_data->vmax); + } + } +} + +static void aw_monitor_set_ipeak(struct aw_device *aw_dev, + uint16_t ipeak) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + struct aw_ipeak_desc *desc = &aw_dev->ipeak_desc; + + if (ipeak == IPEAK_NONE || (!monitor_cfg->ipeak_switch)) + return; + + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read ipeak failed"); + return; + } + + read_reg_val = reg_val; + + read_reg_val &= (~desc->mask); + + if (read_reg_val == ipeak) { + aw_dev_dbg(aw_dev->dev, "ipeak = 0x%x, no change", + read_reg_val); + return; + } + + reg_val &= desc->mask; + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->reg, reg_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "write ipeak failed"); + return; + } + aw_dev_info(aw_dev->dev, "set reg val = 0x%x, ipeak = 0x%x", + reg_val, ipeak); + +} + +static void aw_monitor_set_gain(struct aw_device *aw_dev, uint16_t gain) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + unsigned int read_volume; + unsigned int set_volume; + int ret; + + if (gain == GAIN_NONE || (!monitor_cfg->gain_switch)) + return; + + ret = aw_dev->ops.aw_get_volume(aw_dev, &read_volume); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read volume failed"); + return; + } + + gain = aw_dev->ops.aw_reg_val_to_db(gain); + + /*add offset*/ + set_volume = gain + aw_dev->volume_desc.init_volume; + + if (read_volume == set_volume) { + aw_dev_dbg(aw_dev->dev, "gain = 0x%x, no change", read_volume); + return; + } + + ret = aw_dev->ops.aw_set_volume(aw_dev, set_volume); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "set gain failed"); + return; + } + aw_dev_info(aw_dev->dev, "set reg val = 0x%x, gain = 0x%x", + set_volume, gain); + +} + +static void aw_monitor_set_vmax(struct aw_device *aw_dev, + uint32_t vmax) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + int ret; + + if (vmax == VMAX_NONE || (!monitor_cfg->vmax_switch)) + return; + + if ((vmax == aw_dev->monitor_desc.pre_vmax) && + (aw_dev->monitor_desc.first_entry != AW_FIRST_ENTRY)) { + aw_dev_dbg(aw_dev->dev, "vmax = 0x%x, no change", vmax); + return; + } + + ret = aw_dsp_write_vmax(aw_dev, (char *)&vmax, sizeof(uint32_t)); + if (ret) + return; + + aw_dev->monitor_desc.pre_vmax = vmax; + aw_dev_dbg(aw_dev->dev, "set vmax = 0x%x", vmax); +} + +static int aw_monitor_work(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_table set_table; + int ret = -1; + + if (aw_cali_svc_get_cali_status()) { + aw_dev_info(aw_dev->dev, "done nothing during calibration"); + return 0; + } + + ret = aw_monitor_get_temp_and_vol(aw_dev); + if (ret < 0) + return ret; + + if (monitor->samp_count < monitor_cfg->monitor_count && + (monitor->first_entry == AW_NOT_FIRST_ENTRY)) + return 0; + + ret = aw_monitor_get_data(aw_dev); + if (ret < 0) + return ret; + + aw_monitor_get_cfg(aw_dev, &set_table); + + aw_dev_dbg(aw_dev->dev, "set_ipeak = 0x%x, set_gain = 0x%x, set_vmax = 0x%x", + set_table.ipeak, set_table.gain, set_table.vmax); + + aw_monitor_set_ipeak(aw_dev, set_table.ipeak); + + aw_monitor_set_gain(aw_dev, set_table.gain); + + aw_monitor_set_vmax(aw_dev, set_table.vmax); + + + monitor->samp_count = 0; + monitor->temp_trace.pre_val = monitor->temp_trace.sum_val; + monitor->temp_trace.sum_val = 0; + + monitor->vol_trace.pre_val = monitor->vol_trace.sum_val; + monitor->vol_trace.sum_val = 0; + + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + + return 0; +} + +void aw_monitor_work_func(struct work_struct *work) +{ + struct aw_device *aw_dev = container_of(work, + struct aw_device, monitor_desc.delay_work.work); + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + + aw_dev_dbg(aw_dev->dev, "scene_mode %d,monitor_status:%d, monitor_switch:%d", + aw_dev->cur_prof, monitor_cfg->monitor_status, + monitor_cfg->monitor_switch); + + if ((aw_dev->prof_info.prof_desc[aw_dev->cur_prof].id != AW_PROFILE_RECEIVER) && + (monitor_cfg->monitor_status == AW_MON_CFG_OK) && + monitor_cfg->monitor_switch) { + if (!aw_get_hmute(aw_dev)) { + aw_monitor_work(aw_dev); + queue_delayed_work(aw882xx->work_queue, + &monitor->delay_work, msecs_to_jiffies(monitor_cfg->monitor_time)); + } + } +} + +void aw_monitor_start(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + aw_dev_info(aw_dev->dev, "enter"); + + monitor_desc->first_entry = AW_FIRST_ENTRY; + monitor_desc->samp_count = 0; + monitor_desc->vol_trace.sum_val = 0; + monitor_desc->temp_trace.sum_val = 0; + + queue_delayed_work(aw882xx->work_queue, + &monitor_desc->delay_work, 0); +} + +int aw_monitor_stop(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + aw_dev_info(aw_dev->dev, "enter"); + if (delayed_work_pending(&monitor_desc->delay_work)) + cancel_delayed_work_sync(&monitor_desc->delay_work); + + return 0; + +} + +/***************************************************** +* load monitor config +*****************************************************/ +static int aw_monitor_param_check_sum(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int i, check_sum = 0; + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + + if (data_len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw_dev->dev, "data size smaller than hdr , please check monitor bin"); + return -ENOMEM; + } + + for (i = 4; i < data_len; i++) + check_sum += (uint8_t)data[i]; + + if (monitor_hdr->check_sum != check_sum) { + aw_dev_err(aw_dev->dev, "check_sum[%d] is not equal to actual check_sum[%d]", + monitor_hdr->check_sum, check_sum); + return -ENOMEM; + } + + return 0; +} + +static int aw_monitor_check_fw(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + int temp_size, vol_size; + + if (data_len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw_dev->dev, "params size[%d] < struct aw_monitor_hdr size[%d]!", + data_len, (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > data_len) { + aw_dev_err(aw_dev->dev, "temp_offset[%d] overflow file size[%d]!", + monitor_hdr->temp_offset, data_len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > data_len) { + aw_dev_err(aw_dev->dev, "vol_offset[%d] overflow file size[%d]!", + monitor_hdr->vol_offset, data_len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > data_len) { + aw_dev_err(aw_dev->dev, "temp_size:[%d] overflow file size[%d]!", + temp_size, data_len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > data_len) { + aw_dev_err(aw_dev->dev, "vol_size:[%d] overflow file size[%d]!", + vol_size, data_len); + return -ENOMEM; + } + + return 0; +} + +static int aw_monitor_check_fw_v_0_1_1(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + int temp_size, vol_size; + + if (data_len < sizeof(struct aw_monitor_hdr_v_0_1_1)) { + aw_dev_err(aw_dev->dev, "params size[%d] < struct aw_monitor_hdr size[%d]!", + data_len, (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > data_len) { + aw_dev_err(aw_dev->dev, "temp_offset[%d] overflow file size[%d]!", + monitor_hdr->temp_offset, data_len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > data_len) { + aw_dev_err(aw_dev->dev, "vol_offset[%d] overflow file size[%d]!", + monitor_hdr->vol_offset, data_len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > data_len) { + aw_dev_err(aw_dev->dev, "temp_size:[%d] overflow file size[%d]!", + temp_size, data_len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > data_len) { + aw_dev_err(aw_dev->dev, "vol_size:[%d] overflow file size[%d]!", + vol_size, data_len); + return -ENOMEM; + } + + return 0; +} + + +static void aw_monitor_parse_hdr(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_switch = monitor_hdr->monitor_switch; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = monitor_hdr->ipeak_switch; + monitor_cfg->gain_switch = monitor_hdr->gain_switch; + monitor_cfg->vmax_switch = monitor_hdr->vmax_switch; + monitor_cfg->temp_switch = monitor_hdr->temp_switch; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = monitor_hdr->vol_switch; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw_dev->dev, "chip name:%s", + monitor_hdr->chip_type); + aw_dev_info(aw_dev->dev, "ui ver:0x%x", + monitor_hdr->ui_ver); + + aw_dev_info(aw_dev->dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d", + monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw_dev->dev, "ipeak_switch:%d, gain_switch:%d, vmax_switch:%d", + monitor_cfg->ipeak_switch, monitor_cfg->gain_switch, + monitor_cfg->vmax_switch); + + aw_dev_info(aw_dev->dev, "temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d", + monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); +} + +static void aw_monitor_parse_hdr_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_switch = (monitor_hdr->enable_flag >> MONITOR_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = (monitor_hdr->enable_flag >> MONITOR_IPEAK_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->logic_switch = (monitor_hdr->enable_flag >> MONITOR_LOGIC_BIT) & MONITOR_EN_MASK; + monitor_cfg->gain_switch = (monitor_hdr->enable_flag >> MONITOR_GAIN_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vmax_switch = (monitor_hdr->enable_flag >> MONITOR_VMAX_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_switch = (monitor_hdr->enable_flag >> MONITOR_TEMP_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = (monitor_hdr->enable_flag >> MONITOR_VOL_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw_dev->dev, "chip name:%s", + monitor_hdr->chip_type); + aw_dev_info(aw_dev->dev, "ui ver:0x%x", + monitor_hdr->ui_ver); + + aw_dev_info(aw_dev->dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d", + monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw_dev->dev, "logic_switch:%d, ipeak_switch:%d, gain_switch:%d, vmax_switch:%d", + monitor_cfg->logic_switch, monitor_cfg->ipeak_switch, + monitor_cfg->gain_switch, monitor_cfg->vmax_switch); + + aw_dev_info(aw_dev->dev, "temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d", + monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); +} + +static void aw_monitor_write_data_to_table(struct aw_device *aw_dev, + struct aw_table_info *table_info, const char *offset_ptr) +{ + int i; + + for (i = 0; i < table_info->table_num * AW_TABLE_SIZE; i += AW_TABLE_SIZE) { + table_info->aw_table[i / AW_TABLE_SIZE].min_val = + AW_GET_16_DATA(offset_ptr[1 + i], offset_ptr[i]); + table_info->aw_table[i / AW_TABLE_SIZE].max_val = + AW_GET_16_DATA(offset_ptr[3 + i], offset_ptr[2 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].ipeak = + AW_GET_16_DATA(offset_ptr[5 + i], offset_ptr[4 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].gain = + AW_GET_16_DATA(offset_ptr[7 + i], offset_ptr[6 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].vmax = + AW_GET_32_DATA(offset_ptr[11 + i], offset_ptr[10 + i], + offset_ptr[9 + i], offset_ptr[8 + i]); + } + + for (i = 0; i < table_info->table_num; i++) + aw_dev_info(aw_dev->dev, + "min_val:%d, max_val:%d, ipeak:0x%x, gain:0x%x, vmax:0x%x", + table_info->aw_table[i].min_val, + table_info->aw_table[i].max_val, + table_info->aw_table[i].ipeak, + table_info->aw_table[i].gain, + table_info->aw_table[i].vmax); + +} + + +static int aw_monitor_parse_temp_data(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_table_info *temp_info = + &aw_dev->monitor_desc.monitor_cfg.temp_info; + + aw_dev_info(aw_dev->dev, "===parse temp start ==="); + + if (temp_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (temp_info->aw_table == NULL) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_monitor_write_data_to_table(aw_dev, temp_info, + &data[monitor_hdr->temp_offset]); + aw_dev_info(aw_dev->dev, "===parse temp end ==="); + return 0; +} + +static int aw_monitor_parse_temp_data_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_table_info *temp_info = + &aw_dev->monitor_desc.monitor_cfg.temp_info; + + aw_dev_info(aw_dev->dev, "===parse temp start ==="); + + if (temp_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (temp_info->aw_table == NULL) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_monitor_write_data_to_table(aw_dev, temp_info, + &data[monitor_hdr->temp_offset]); + aw_dev_info(aw_dev->dev, "===parse temp end ==="); + return 0; +} + + +static int aw_monitor_parse_vol_data(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_table_info *vol_info = + &aw_dev->monitor_desc.monitor_cfg.vol_info; + + aw_dev_info(aw_dev->dev, "===parse vol start ==="); + if (vol_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (vol_info->aw_table == NULL) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_monitor_write_data_to_table(aw_dev, vol_info, + &data[monitor_hdr->vol_offset]); + aw_dev_info(aw_dev->dev, "===parse vol end ==="); + return 0; +} + +static int aw_monitor_parse_vol_data_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_table_info *vol_info = + &aw_dev->monitor_desc.monitor_cfg.vol_info; + + aw_dev_info(aw_dev->dev, "===parse vol start ==="); + if (vol_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (vol_info->aw_table == NULL) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_monitor_write_data_to_table(aw_dev, vol_info, + &data[monitor_hdr->vol_offset]); + aw_dev_info(aw_dev->dev, "===parse vol end ==="); + return 0; +} + + +static int aw_monitor_parse_data(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + ret = aw_monitor_check_fw(aw_dev, data, data_len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "check monitor failed"); + return ret; + } + + aw_monitor_parse_hdr(aw_dev, data); + + ret = aw_monitor_parse_temp_data(aw_dev, data); + if (ret < 0) + return ret; + + ret = aw_monitor_parse_vol_data(aw_dev, data); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +static int aw_monitor_parse_data_v_0_1_1(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + ret = aw_monitor_check_fw_v_0_1_1(aw_dev, data, data_len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "check monitor failed"); + return ret; + } + + aw_monitor_parse_hdr_v_0_1_1(aw_dev, data); + + ret = aw_monitor_parse_temp_data_v_0_1_1(aw_dev, data); + if (ret < 0) + return ret; + + ret = aw_monitor_parse_vol_data_v_0_1_1(aw_dev, data); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +int aw_monitor_parse_fw(struct aw_monitor_desc *monitor_desc, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr *monitor_hdr = NULL; + struct aw_device *aw_dev = NULL; + int ret; + + if (monitor_desc == NULL || data == NULL) { + pr_err("monitor_desc or data is NULL"); + return -EINVAL; + } + + monitor_hdr = (struct aw_monitor_hdr *)data; + aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + ret = aw_monitor_param_check_sum(aw_dev, data, data_len); + if (ret < 0) + return ret; + + switch (monitor_hdr->monitor_ver) { + case AW_MONITOR_HDR_VER_0_1_0: + return aw_monitor_parse_data(aw_dev, data, data_len); + case AW_MONITOR_HDR_VER_0_1_1: + return aw_monitor_parse_data_v_0_1_1(aw_dev, data, data_len); + default: + aw_dev_err(aw_dev->dev, "cfg version:0x%x unsupported", + monitor_hdr->monitor_ver); + return -EINVAL; + } +} + +static void aw_monitor_free_firmware(struct aw_device *aw_dev) +{ + struct aw_monitor_cfg *monitor_cfg = + &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_status = AW_MON_CFG_ST; + + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + } + + if (monitor_cfg->vol_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->vol_info.aw_table); + monitor_cfg->vol_info.aw_table = NULL; + } + + memset(monitor_cfg, 0, sizeof(struct aw_monitor_cfg)); +} + +static int aw_monitor_real_time_update_monitor(struct aw_device *aw_dev) +{ + const struct firmware *cont = NULL; + struct aw_container *aw_monitor_cnt = NULL; + int ret; + + ret = request_firmware(&cont, aw_dev->monitor_name, aw_dev->dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "failed to read %s", aw_dev->monitor_name); + release_firmware(cont); + return ret; + } + + aw_monitor_cnt = devm_kzalloc(aw_dev->dev, + cont->size + sizeof(uint32_t), GFP_KERNEL); + if (aw_monitor_cnt == NULL) { + aw_dev_err(aw_dev->dev, "alloc failed!"); + release_firmware(cont); + return ret; + } + + aw_monitor_cnt->len = cont->size; + memcpy(aw_monitor_cnt->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw_monitor_parse_fw(&aw_dev->monitor_desc, + aw_monitor_cnt->data, aw_monitor_cnt->len); + if (ret < 0) + aw_dev_err(aw_dev->dev, "parse monitor firmware failed!"); + + devm_kfree(aw_dev->dev, aw_monitor_cnt); + aw_monitor_cnt = NULL; + + return ret; +} + +/***************************************************** + * monitor init + *****************************************************/ +#ifdef AW_DEBUG +static ssize_t aw_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "vol set =%d", vol); + aw_dev->monitor_desc.test_vol = vol; + + return count; +} + +static ssize_t aw_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "vol: %d\n", + aw_dev->monitor_desc.test_vol); + return len; +} + +static ssize_t aw_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "temp set =%d", temp); + + aw_dev->monitor_desc.test_temp = temp; + + return count; +} + +static ssize_t aw_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx temp: %d\n", + aw_dev->monitor_desc.test_temp); + + return len; +} + +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw_vol_show, aw_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw_temp_show, aw_temp_store); +#endif + +static ssize_t aw_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "monitor enable set =%d", enable); + + if (aw_dev->monitor_desc.monitor_cfg.monitor_switch == enable) { + return count; + } else { + aw_dev->monitor_desc.monitor_cfg.monitor_switch = enable; + if (enable) + aw_monitor_start(&aw_dev->monitor_desc); + } + + return count; +} + +static ssize_t aw_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + + len += snprintf(buf+len, PAGE_SIZE-len, + "monitor enable: %d\n", + aw_dev->monitor_desc.monitor_cfg.monitor_switch); + return len; +} + +static ssize_t aw_monitor_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t update = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &update); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "monitor update = %d", update); + + if (update) { + aw_monitor_stop(&aw_dev->monitor_desc); + aw_monitor_free_firmware(aw_dev); + ret = aw_monitor_real_time_update_monitor(aw_dev); + if (ret < 0) + return ret; + aw_monitor_start(&aw_dev->monitor_desc); + } + + return count; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw_monitor_show, aw_monitor_store); +static DEVICE_ATTR(monitor_update, S_IWUSR, + NULL, aw_monitor_update_store); + + +static struct attribute *aw_monitor_attr[] = { + &dev_attr_monitor.attr, + &dev_attr_monitor_update.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw_monitor_attr_group = { + .attrs = aw_monitor_attr, +}; + +void aw_monitor_init(struct aw_monitor_desc *monitor_desc) +{ + int ret; + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + aw_dev_info(aw_dev->dev, "enter"); +#ifdef AW_DEBUG + monitor_desc->test_vol = 0; + monitor_desc->test_temp = 0; +#endif + + INIT_DELAYED_WORK(&monitor_desc->delay_work, aw_monitor_work_func); + + ret = sysfs_create_group(&aw_dev->dev->kobj, + &aw_monitor_attr_group); + if (ret < 0) + aw_dev_err(aw_dev->dev, "error creating sysfs attr files"); +} + +void aw_monitor_deinit(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = + container_of(monitor_desc, struct aw_device, monitor_desc); + + aw_monitor_stop(monitor_desc); + + sysfs_remove_group(&aw_dev->dev->kobj, &aw_monitor_attr_group); +} + diff --git a/asoc/codecs/aw882xxacf/aw_monitor.h b/asoc/codecs/aw882xxacf/aw_monitor.h new file mode 100644 index 000000000000..ef42608b212f --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_monitor.h @@ -0,0 +1,173 @@ +#ifndef __AW_MONITOR_H__ +#define __AW_MONITOR_H__ + +/*#define AW_DEBUG*/ + +struct aw_table; + +#define AW_TABLE_SIZE sizeof(struct aw_table) +#define AW_MONITOR_DEFAULT_FLAG (0) + +#define IPEAK_NONE (0xFF) +#define GAIN_NONE (0xFF) +#define VMAX_NONE (0xFFFFFFFF) + + +#define AW_GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | (((uint8_t)y) << 8) | ((uint8_t)z))) +#define AW_GET_16_DATA(x, y) \ + ((uint16_t)((((uint8_t)x) << 8) | (uint8_t)y)) + +enum { + AW_MON_LOGIC_OR = 0, + AW_MON_LOGIC_AND = 1, +}; + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +enum aw_monitor_hdr_ver { + AW_MONITOR_HDR_VER_0_1_0 = 0x00010000, + AW_MONITOR_HDR_VER_0_1_1 = 0x00010100, +}; + +enum aw_monitor_init { + AW_MON_CFG_ST = 0, + AW_MON_CFG_OK = 1, +}; + +struct aw_monitor_hdr { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[8]; + uint32_t ui_ver; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; +}; + +#define MONITOR_EN_MASK (0x01) + +enum { + MONITOR_EN_BIT = 0, + MONITOR_LOGIC_BIT = 1, + MONITOR_IPEAK_EN_BIT = 2, + MONITOR_GAIN_EN_BIT = 3, + MONITOR_VMAX_EN_BIT = 4, + MONITOR_TEMP_EN_BIT = 5, + MONITOR_VOL_EN_BIT = 6, +}; + +struct aw_monitor_hdr_v_0_1_1 { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[16]; + uint32_t ui_ver; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t enable_flag; + /* [bit 31:7] */ + /* [bit 6: vol en] */ + /* [bit 5: temp en] */ + /* [bit 4: vmax en] */ + /* [bit 3: gain en] */ + /* [bit 2: ipeak en] */ + /* [bit 1: & or | flag] */ + /* [bit 0: monitor en] */ + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; + uint32_t reserver[3]; +}; + + +struct aw_table { + int16_t min_val; + int16_t max_val; + uint16_t ipeak; + uint16_t gain; + uint32_t vmax; +}; + +struct aw_table_info { + uint8_t table_num; + struct aw_table *aw_table; +}; + +struct aw_monitor_cfg { + uint8_t monitor_status; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t logic_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + struct aw_table_info temp_info; + struct aw_table_info vol_info; +}; + +struct aw_monitor_trace { + int32_t pre_val; + int32_t sum_val; + struct aw_table aw_table; +}; + + +/****************************************************************** +* struct aw882xx monitor +*******************************************************************/ +struct aw_monitor_desc { + struct delayed_work delay_work; + struct aw_monitor_cfg monitor_cfg; + + uint8_t first_entry; + uint8_t samp_count; + uint8_t db_offset; + + uint32_t pre_vmax; + struct aw_monitor_trace temp_trace; + struct aw_monitor_trace vol_trace; + +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/****************************************************************** +* aw882xx monitor functions +*******************************************************************/ +void aw_monitor_start(struct aw_monitor_desc *monitor_desc); +int aw_monitor_stop(struct aw_monitor_desc *monitor_desc); +void aw_monitor_init(struct aw_monitor_desc *monitor_desc); +void aw_monitor_deinit(struct aw_monitor_desc *monitor_desc); +int aw_monitor_parse_fw(struct aw_monitor_desc *monitor_desc, + uint8_t *data, uint32_t data_len); + +#endif + diff --git a/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h b/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h new file mode 100644 index 000000000000..e09c714e5176 --- /dev/null +++ b/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h @@ -0,0 +1,1589 @@ +#ifndef __AW_PID_1852_REG_H__ +#define __AW_PID_1852_REG_H__ + +#define AW_PID_1852_ACF_FILE "aw882xx_pid_1852_acf.bin" +#define AW_PID_1852_MONITOR_FILE "aw882xx_pid_1852_monitor.bin" + +/* registers list */ +#define AW_PID_1852_ID_REG (0x00) +#define AW_PID_1852_SYSST_REG (0x01) +#define AW_PID_1852_SYSINT_REG (0x02) +#define AW_PID_1852_SYSINTM_REG (0x03) +#define AW_PID_1852_SYSCTRL_REG (0x04) +#define AW_PID_1852_SYSCTRL2_REG (0x05) +#define AW_PID_1852_I2SCTRL_REG (0x06) +#define AW_PID_1852_I2SCFG1_REG (0x07) +#define AW_PID_1852_I2SCFG2_REG (0x08) +#define AW_PID_1852_HAGCCFG1_REG (0x09) +#define AW_PID_1852_HAGCCFG2_REG (0x0A) +#define AW_PID_1852_HAGCCFG3_REG (0x0B) +#define AW_PID_1852_HAGCCFG4_REG (0x0C) +#define AW_PID_1852_HAGCCFG5_REG (0x0D) +#define AW_PID_1852_HAGCCFG6_REG (0x0E) +#define AW_PID_1852_HAGCCFG7_REG (0x0F) +#define AW_PID_1852_HAGCST_REG (0x10) +#define AW_PID_1852_PRODID_REG (0x11) +#define AW_PID_1852_VBAT_REG (0x12) +#define AW_PID_1852_TEMP_REG (0x13) +#define AW_PID_1852_PVDD_REG (0x14) +#define AW_PID_1852_DBGCTRL_REG (0x20) +#define AW_PID_1852_I2SINT_REG (0x21) +#define AW_PID_1852_I2SCAPCNT_REG (0x22) +#define AW_PID_1852_CRCIN_REG (0x38) +#define AW_PID_1852_CRCOUT_REG (0x39) +#define AW_PID_1852_VSNCTRL1_REG (0x50) +#define AW_PID_1852_ISNCTRL1_REG (0x52) +#define AW_PID_1852_ISNCTRL2_REG (0x53) +#define AW_PID_1852_VTMCTRL1_REG (0x54) +#define AW_PID_1852_VTMCTRL2_REG (0x55) +#define AW_PID_1852_VTMCTRL3_REG (0x56) +#define AW_PID_1852_ISNDAT_REG (0x57) +#define AW_PID_1852_VSNDAT_REG (0x58) +#define AW_PID_1852_PWMCTRL_REG (0x59) +#define AW_PID_1852_PWMCTRL2_REG (0x5A) +#define AW_PID_1852_BSTCTRL1_REG (0x60) +#define AW_PID_1852_BSTCTRL2_REG (0x61) +#define AW_PID_1852_BSTCTRL3_REG (0x62) +#define AW_PID_1852_BSTDBG1_REG (0x63) +#define AW_PID_1852_BSTDBG2_REG (0x64) +#define AW_PID_1852_BSTDBG3_REG (0x65) +#define AW_PID_1852_PLLCTRL1_REG (0x66) +#define AW_PID_1852_PLLCTRL2_REG (0x67) +#define AW_PID_1852_PLLCTRL3_REG (0x68) +#define AW_PID_1852_CDACTRL1_REG (0x69) +#define AW_PID_1852_CDACTRL2_REG (0x6A) +#define AW_PID_1852_SADCCTRL_REG (0x6B) +#define AW_PID_1852_TESTCTRL1_REG (0x70) +#define AW_PID_1852_TESTCTRL2_REG (0x71) +#define AW_PID_1852_EFCTRL1_REG (0x72) +#define AW_PID_1852_EFCTRL2_REG (0x73) +#define AW_PID_1852_EFWH_REG (0x74) +#define AW_PID_1852_EFWM2_REG (0x75) +#define AW_PID_1852_EFWM1_REG (0x76) +#define AW_PID_1852_EFWL_REG (0x77) +#define AW_PID_1852_EFRH_REG (0x78) +#define AW_PID_1852_EFRM2_REG (0x79) +#define AW_PID_1852_EFRM1_REG (0x7A) +#define AW_PID_1852_EFRL_REG (0x7B) +#define AW_PID_1852_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW_PID_1852_REG_MAX (0x7D) + + +#define AW_PID_1852_REG_NONE_ACCESS (0) +#define AW_PID_1852_REG_RD_ACCESS (1 << 0) +#define AW_PID_1852_REG_WR_ACCESS (1 << 1) + + +static const unsigned char aw_pid_1852_reg_access[AW_PID_1852_REG_MAX] = { + [AW_PID_1852_ID_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSST_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSINT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSINTM_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SYSCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SYSCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCFG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCFG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG4_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG5_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG6_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG7_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCST_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PRODID_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VBAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_TEMP_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PVDD_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_DBGCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SINT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_I2SCAPCNT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_CRCIN_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CRCOUT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VSNCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNDAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VSNDAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PWMCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PWMCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CDACTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CDACTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SADCCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_TESTCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_TESTCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWH_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWM2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWM1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFRH_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRM2_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRM1_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRL_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_TESTDET_REG] = (AW_PID_1852_REG_RD_ACCESS), +}; + +/******************************************** + * Register Detail + *******************************************/ +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW_PID_1852_IDCODE_START_BIT (0) +#define AW_PID_1852_IDCODE_BITS_LEN (16) +#define AW_PID_1852_IDCODE_MASK \ + (~(((1< Date: Thu, 8 Jul 2021 19:54:26 +0800 Subject: [PATCH 29/79] audio-kernel:update aw882xxacf driver to v1.4.0.2 update aw882xxacf driver to v1.4.0.2. add aw882xx_runin_test controler for factory. Change-Id: I55513eb6b7841ec410b346157e143c9fdab6a805 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2006955 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 85 +++++++++++++++++++++++++++++--- asoc/codecs/aw882xxacf/aw882xx.h | 6 ++- asoc/codecs/aw882xxacf/aw_dsp.h | 2 + 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 8e3b32c3ae8b..05b485fda988 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.4.0.1" +#define AW882XX_DRIVER_VERSION "v1.4.0.2" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -49,10 +49,14 @@ static unsigned int g_algo_copp_en = true; #ifdef AW_SPIN_ENABLE static unsigned int g_spin_value = 0; #endif +#ifdef AW882XX_RUNIN_TEST +static unsigned int g_runin_test; +#endif static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; +static const char *const switch_status[] = { "Off", "On" }; static const char *const aw882xx_switch[] = {"Disable", "Enable"}; #ifdef AW_SPIN_ENABLE static const char *const aw882xx_spin[] = {"spin_0", "spin_90", @@ -387,6 +391,9 @@ static void aw882xx_start_pa(struct aw882xx *aw882xx) if (ret) aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); } +#endif +#ifdef AW882XX_RUNIN_TEST + schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); #endif break; } @@ -940,7 +947,7 @@ static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, return 0; } -static int aw882xx_set_copp_en(struct snd_kcontrol *kcontrol, +static int aw882xx_set_copp_dis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret = -EINVAL; @@ -957,7 +964,7 @@ static int aw882xx_set_copp_en(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; - ret = aw_dev_set_copp_module_en(ctrl_value); + ret = aw_dev_set_copp_module_en(!ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); @@ -966,7 +973,7 @@ static int aw882xx_set_copp_en(struct snd_kcontrol *kcontrol, return 0; } -static int aw882xx_get_copp_en(struct snd_kcontrol *kcontrol, +static int aw882xx_get_copp_dis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { aw_snd_soc_codec_t *codec = @@ -974,7 +981,7 @@ static int aw882xx_get_copp_en(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = g_algo_copp_en; + ucontrol->value.integer.value[0] = !g_algo_copp_en; aw_dev_dbg(aw882xx->dev, "done nothing"); return 0; @@ -1106,8 +1113,62 @@ static int aw882xx_set_fade_out_time(struct snd_kcontrol *kcontrol, return 0; } +#ifdef AW882XX_RUNIN_TEST +static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_pr_info("aw882xx_runin test get %d\n", g_runin_test); + + ucontrol->value.integer.value[0] = g_runin_test; + + return 0; +} + +static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + aw_pr_info("aw882xx_runin_test set %d \n", value); + + if (value > 1 ) { + value = 0; + } + + g_runin_test = value; + + return 0; +} + +static void aw882xx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + uint32_t ctrl_value = 0; + + /*no test reg set default value*/ + if (g_runin_test == 0) { + return; + } + + /*set afe rx module*/ + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); + if (ret) { + aw_pr_err("disable afe rx module %d falied , ret=%d\n", ctrl_value, ret); + } + + /*set skt module*/ + ret = aw_dev_set_copp_module_en(ctrl_value); + if (ret) { + aw_pr_err("disable skt failed !\n"); + } + + aw_pr_info("disable skt and afe module success\n"); +} +#endif + static const struct soc_enum aw882xx_snd_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), #ifdef AW_SPIN_ENABLE SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_spin), aw882xx_spin), #endif @@ -1118,11 +1179,15 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_rx_en, aw882xx_set_rx_en), SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[0], aw882xx_get_tx_en, aw882xx_set_tx_en), - SOC_ENUM_EXT("aw882xx_copp_switch", aw882xx_snd_enum[0], - aw882xx_get_copp_en, aw882xx_set_copp_en), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[1], + aw882xx_get_copp_dis, aw882xx_set_copp_dis), #ifdef AW_SPIN_ENABLE - SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[1], + SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[2], aw882xx_get_spin, aw882xx_set_spin), +#endif +#ifdef AW882XX_RUNIN_TEST + SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[1], + aw882xx_runin_test_get, aw882xx_runin_test_set), #endif SOC_SINGLE_EXT("aw882xx_fadein_us", 0, 0, 1000000, 0, aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), @@ -2167,6 +2232,10 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, aw882xx->i2c_packet.reg_data = NULL; aw882xx->index = g_aw882xx_dev_cnt; +#ifdef AW882XX_RUNIN_TEST + g_runin_test = 0; + INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); +#endif /*add device to total list*/ mutex_lock(&g_aw882xx_lock); g_aw882xx_dev_cnt++; diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h index debdd986d6aa..9d3b8aa2d785 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.h +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -14,7 +14,7 @@ #define AW882XX_DC_DELAY_TIME (1000) #define AW882XX_LOAD_FW_DELAY_TIME (0) #define AW_START_RETRIES (5) - +#define AW882XX_RUNIN_TEST #define AW_I2C_RETRIES 5 /* 5 times */ #define AW_I2C_RETRY_DELAY 5 /* 5 ms */ @@ -149,7 +149,9 @@ struct aw882xx { struct delayed_work interrupt_work; struct delayed_work dc_work; struct delayed_work fw_work; - +#ifdef AW882XX_RUNIN_TEST + struct delayed_work adsp_status; +#endif struct mutex lock; }; diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h index 8789c48e4cec..1396a88cc5fe 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.h +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -1,6 +1,8 @@ #ifndef __AWINIC_DSP_H__ #define __AWINIC_DSP_H__ +#define AW_QCOM_PLATFORM + /*factor form 12bit(4096) to 1000*/ #define AW_DSP_RE_TO_SHOW_RE(re) (((re) * (1000)) >> (12)) #define AW_SHOW_RE_TO_DSP_RE(re) (((re) << 12) / (1000)) From c7178e32292a1dfefc3cc2e0f33d04163a4f5854 Mon Sep 17 00:00:00 2001 From: zhouwl Date: Thu, 5 Aug 2021 19:59:27 +0800 Subject: [PATCH 30/79] ASoC: upgrade aw882xxacf driver to v1.6.0 upgrade kernel driver to latest v1.6.0. - add read algorithm version method; - change registers description; - optimize mixer kcontrols setting; - optimize rotation function. Change-Id: I23f1dcd6d30b5f44fbbcecd8a5da3cad70cb23c2 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/2033735 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 185 +- asoc/codecs/aw882xxacf/aw882xx.h | 10 +- asoc/codecs/aw882xxacf/aw_calib.c | 23 +- asoc/codecs/aw882xxacf/aw_device.c | 50 +- asoc/codecs/aw882xxacf/aw_device.h | 20 +- asoc/codecs/aw882xxacf/aw_dsp.c | 33 + asoc/codecs/aw882xxacf/aw_dsp.h | 1 + asoc/codecs/aw882xxacf/aw_init.c | 71 +- asoc/codecs/aw882xxacf/aw_monitor.c | 32 +- asoc/codecs/aw882xxacf/aw_pid_1852_reg.h | 2342 ++++---- asoc/codecs/aw882xxacf/aw_pid_2013_reg.h | 5392 ++---------------- asoc/codecs/aw882xxacf/aw_pid_2032_reg.h | 1459 ++--- asoc/codecs/aw882xxacf/aw_pid_2055_reg.h | 6326 +++------------------ asoc/codecs/aw882xxacf/aw_pid_2071_reg.h | 6148 ++------------------ asoc/codecs/aw882xxacf/aw_pid_2113_reg.h | 6509 +--------------------- 15 files changed, 4227 insertions(+), 24374 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 05b485fda988..d111210b27b2 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.4.0.2" +#define AW882XX_DRIVER_VERSION "v1.6.0" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -43,9 +43,9 @@ static unsigned int g_aw882xx_dev_cnt = 0; static unsigned int g_print_dbg = 0; -static unsigned int g_algo_rx_en = true; -static unsigned int g_algo_tx_en = true; -static unsigned int g_algo_copp_en = true; +static unsigned int g_algo_rx_en = false; +static unsigned int g_algo_tx_en = false; +static unsigned int g_algo_copp_en = false; #ifdef AW_SPIN_ENABLE static unsigned int g_spin_value = 0; #endif @@ -366,8 +366,10 @@ static void aw882xx_start_pa(struct aw882xx *aw882xx) for (i = 0; i < AW_START_RETRIES; i++) { /*if PA already power ,stop PA then start*/ - if (aw882xx->aw_pa->status) - aw_device_stop(aw882xx->aw_pa); + if (aw882xx->aw_pa->status) { + aw_dev_info(aw882xx->dev, "already start"); + return; + } ret = aw_dev_reg_update(aw882xx->aw_pa, aw882xx->phase_sync); if (ret) { @@ -385,13 +387,6 @@ static void aw882xx_start_pa(struct aw882xx *aw882xx) &aw882xx->dc_work, msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); aw_dev_info(aw882xx->dev, "start success"); -#ifdef AW_SPIN_ENABLE - if (aw882xx->index == 0) { - ret = aw_dev_set_spin(g_spin_value); - if (ret) - aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); - } -#endif #ifdef AW882XX_RUNIN_TEST schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); #endif @@ -409,6 +404,7 @@ static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) aw_snd_soc_codec_t *codec = aw_get_codec(dai); struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); + int ret = 0; aw_dev_info(aw882xx->dev, "mute state=%d", mute); @@ -434,10 +430,17 @@ static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) /*aw882xx_start_pa(aw882xx);*/ queue_delayed_work(aw882xx->work_queue, &aw882xx->start_work, 0); +#ifdef AW_SPIN_ENABLE + if (aw882xx->index == 0) { + ret = aw_dev_set_spin(g_spin_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } +#endif mutex_unlock(&aw882xx->lock); } - return 0; + return ret; } static const struct snd_soc_dai_ops aw882xx_dai_ops = { @@ -841,7 +844,6 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, { int ret = -EINVAL; uint32_t ctrl_value = 0; - struct aw_device *aw_dev; aw_snd_soc_codec_t *codec = aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = @@ -850,14 +852,13 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); - aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; - ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + g_algo_rx_en = ctrl_value; aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); return 0; } @@ -867,14 +868,12 @@ static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, { int ret = -EINVAL; uint32_t ctrl_value = 0; - struct aw_device *aw_dev; aw_snd_soc_codec_t *codec = aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); - aw_dev = aw882xx->aw_pa; if (aw882xx->pstream) { ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); @@ -883,8 +882,8 @@ static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = ctrl_value; } else { - ucontrol->value.integer.value[0] = g_algo_rx_en; - aw_dev_info(aw882xx->dev, "no stream, use record value"); + ucontrol->value.integer.value[0] = false; //g_algo_rx_en + aw_dev_info(aw882xx->dev, "no stream, rx disable"); } aw_dev_dbg(aw882xx->dev, "aw882xx_rx_enable %ld", @@ -897,7 +896,6 @@ static int aw882xx_set_tx_en(struct snd_kcontrol *kcontrol, { int ret = -EINVAL; uint32_t ctrl_value = 0; - struct aw_device *aw_dev; aw_snd_soc_codec_t *codec = aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = @@ -906,13 +904,13 @@ static int aw882xx_set_tx_en(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); - aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; ret = aw_dev_set_afe_module_en(AW_TX_MODULE, ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + g_algo_tx_en = ctrl_value; aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); return 0; } @@ -922,13 +920,11 @@ static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, { int ret = -EINVAL; uint32_t ctrl_value = 0; - struct aw_device *aw_dev; aw_snd_soc_codec_t *codec = aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); - aw_dev = aw882xx->aw_pa; if (aw882xx->pstream) { ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); @@ -938,8 +934,8 @@ static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, } ucontrol->value.integer.value[0] = ctrl_value; } else { - ucontrol->value.integer.value[0] = g_algo_tx_en; - aw_dev_info(aw882xx->dev, "no streamm, use record value"); + ucontrol->value.integer.value[0] = false; //g_algo_tx_en + aw_dev_info(aw882xx->dev, "no stream, tx disable"); } aw_dev_dbg(aw882xx->dev, "aw882xx_tx_enable %ld", @@ -952,7 +948,6 @@ static int aw882xx_set_copp_dis(struct snd_kcontrol *kcontrol, { int ret = -EINVAL; uint32_t ctrl_value = 0; - struct aw_device *aw_dev; aw_snd_soc_codec_t *codec = aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = @@ -961,14 +956,14 @@ static int aw882xx_set_copp_dis(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); - aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; ret = aw_dev_set_copp_module_en(!ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); - aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + g_algo_copp_en = !ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", g_algo_copp_en); return 0; } @@ -983,7 +978,7 @@ static int aw882xx_get_copp_dis(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = !g_algo_copp_en; - aw_dev_dbg(aw882xx->dev, "done nothing"); + aw_dev_info(aw882xx->dev, "copp_dis: %d", !g_algo_copp_en); return 0; } @@ -1039,14 +1034,23 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); int ctrl_value; + int ret = -EINVAL; aw_dev = aw882xx->aw_pa; - aw_dev_get_spin(&ctrl_value); + if (aw882xx->pstream) { + ret = aw_dev_get_spin(&ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = g_spin_value; + aw_dev_info(aw882xx->dev, "no stream, use record value"); - ucontrol->value.integer.value[0] = ctrl_value; - - aw_dev_dbg(aw882xx->dev, "done nothing"); + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); return 0; } #endif @@ -1166,6 +1170,47 @@ static void aw882xx_set_adsp_module_status(struct work_struct *work) } #endif +static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + pr_debug("%s: aw882xx_monitor_get %d\n", + __func__, monitor_cfg->monitor_switch); + + ucontrol->value.integer.value[0] = monitor_cfg->monitor_switch; + + return 0; +} + +static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_monitor_set %d \n", __func__, enable); + + mutex_lock(&aw882xx->lock); + if (enable == monitor_cfg->monitor_switch) { + mutex_unlock(&aw882xx->lock); + return 1; + } + + monitor_cfg->monitor_switch = enable; + if (enable) + aw_monitor_start(&aw_dev->monitor_desc); + mutex_unlock(&aw882xx->lock); + + return 0; +} + static const struct soc_enum aw882xx_snd_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), @@ -1189,6 +1234,8 @@ static struct snd_kcontrol_new aw882xx_controls[] = { SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[1], aw882xx_runin_test_get, aw882xx_runin_test_set), #endif + SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[0], + aw882xx_monitor_get, aw882xx_monitor_set), SOC_SINGLE_EXT("aw882xx_fadein_us", 0, 0, 1000000, 0, aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, @@ -1491,8 +1538,6 @@ int aw_componet_codec_register(struct aw882xx *aw882xx) static int aw882xx_parse_gpio_dt(struct aw882xx *aw882xx, struct device_node *np) { - int ret = 0;; - if (!np) { aw882xx->reset_gpio = -1; aw882xx->irq_gpio = -1; @@ -1507,13 +1552,12 @@ static int aw882xx_parse_gpio_dt(struct aw882xx *aw882xx, } aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); if (aw882xx->irq_gpio < 0) { - aw_dev_err(aw882xx->dev, "no irq gpio provided."); - ret = -EFAULT; + aw_dev_info(aw882xx->dev, "no irq gpio provided."); } else { aw_dev_info(aw882xx->dev, "irq gpio provided ok."); } - return ret; + return 0; } static struct aw882xx *aw882xx_malloc_init(struct i2c_client *i2c) @@ -1579,9 +1623,9 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, ret = of_property_read_u32(np, "dc-flag", &dc_enable); if (ret) { dc_enable = false; - aw_dev_info(aw882xx->dev, "close dc protect!\n"); + aw_dev_info(aw882xx->dev, "close dc protect!"); } else { - aw_dev_info(aw882xx->dev, "dc-flag = %d\n", dc_enable); + aw_dev_info(aw882xx->dev, "dc-flag = %d", dc_enable); } aw882xx->dc_flag = dc_enable; @@ -1590,9 +1634,8 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, ret = of_property_read_u32(np, "sync-flag", &sync_enable); if (ret < 0) { aw_dev_info(aw882xx->dev, - "read sync flag failed,default phase sync off\n"); + "read sync flag failed,default phase sync off"); sync_enable = false; - } else { aw_dev_info(aw882xx->dev, "sync flag is %d", sync_enable); @@ -1600,6 +1643,15 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, aw882xx->phase_sync = sync_enable; + ret = of_property_read_u32(np, "fade-flag", &aw882xx->fade_flag); + if (ret) { + aw882xx->fade_flag = 0; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + return 0; } @@ -1607,7 +1659,7 @@ int aw882xx_hw_reset(struct aw882xx *aw882xx) { aw_dev_info(aw882xx->dev, "enter"); - if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { + if (gpio_is_valid(aw882xx->reset_gpio)) { gpio_set_value_cansleep(aw882xx->reset_gpio, 0); mdelay(1); gpio_set_value_cansleep(aw882xx->reset_gpio, 1); @@ -1677,12 +1729,10 @@ static int aw882xx_read_chipid(struct aw882xx *aw882xx) static irqreturn_t aw882xx_irq(int irq, void *data) { struct aw882xx *aw882xx = (struct aw882xx *)data; - if (!aw882xx) { aw_pr_err("pointer is NULL"); return -EINVAL; } - aw_dev_info(aw882xx->dev, "enter"); /* mask all irq */ @@ -1778,7 +1828,7 @@ static ssize_t aw882xx_rw_show(struct device *dev, if (aw882xx->aw_pa->ops.aw_check_rd_access(aw882xx->rw_reg_addr)) { aw882xx_i2c_read(aw882xx, aw882xx->rw_reg_addr, ®_val); - len += snprintf(buf+len, PAGE_SIZE-len, + len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%04x\n", aw882xx->rw_reg_addr, reg_val); } return len; @@ -1969,7 +2019,7 @@ static ssize_t aw882xx_drv_ver_show(struct device *dev, { ssize_t len = 0; - len += snprintf(buf+len, PAGE_SIZE-len, + len += snprintf(buf + len, PAGE_SIZE - len, "driver_ver: %s \n", AW882XX_DRIVER_VERSION); return len; @@ -1985,12 +2035,12 @@ static ssize_t aw882xx_dsp_re_show(struct device *dev, ret = aw_dev_get_cali_re(aw882xx->aw_pa, &cali_re); if (ret) { - len += snprintf(buf+len, PAGE_SIZE-len, + len += snprintf(buf + len, PAGE_SIZE - len, "read dsp_re failed!\n"); return len; } - len += snprintf(buf+len, PAGE_SIZE-len, + len += snprintf(buf + len, PAGE_SIZE - len, "%d \n", cali_re); return len; @@ -2011,7 +2061,7 @@ static ssize_t aw882xx_fade_step_store(struct device *dev, } aw_dev_set_fade_vol_step(aw882xx->aw_pa, databuf[0]); } - aw_dev_info(aw882xx->dev, "set step %d DB Done", databuf[0]); + aw_dev_info(aw882xx->dev, "set step %d Done", databuf[0]); return count; } @@ -2022,7 +2072,7 @@ static ssize_t aw882xx_fade_step_show(struct device *dev, ssize_t len = 0; struct aw882xx *aw882xx = dev_get_drvdata(dev); - len += snprintf(buf+len, PAGE_SIZE-len, + len += snprintf(buf + len, PAGE_SIZE - len, "step: %d \n", aw_dev_get_fade_vol_step(aw882xx->aw_pa)); return len; @@ -2052,7 +2102,7 @@ static ssize_t aw882xx_dbg_prof_show(struct device *dev, struct aw882xx *aw882xx = dev_get_drvdata(dev); ssize_t len = 0; - len += snprintf(buf+len, PAGE_SIZE-len, + len += snprintf(buf + len, PAGE_SIZE - len, " %d \n", aw882xx->dbg_en_prof); return len; @@ -2104,7 +2154,7 @@ static ssize_t aw882xx_print_dbg_store(struct device *dev, g_print_dbg = ((g_print_dbg == false) ? false : true); - aw_dev_info(aw882xx->dev, "set g_print_dbg : [%d]", g_print_dbg); + aw_dev_info(aw882xx->dev, "set g_print_dbg : [%d]", g_print_dbg); return count; } @@ -2120,6 +2170,25 @@ static ssize_t aw882xx_print_dbg_show(struct device *dev, return len; } +static ssize_t aw882xx_algo_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret; + char algo_ver_buf[ALGO_VERSION_MAX] = { 0 }; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + ret = aw_get_algo_version(aw882xx->aw_pa, algo_ver_buf); + if (ret < 0) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read algo version failed!\n"); + return len; + } + len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", algo_ver_buf); + + return len; +} + static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw882xx_reg_show, aw882xx_reg_store); static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, @@ -2138,6 +2207,9 @@ static DEVICE_ATTR(phase_sync, S_IWUSR | S_IRUGO, aw882xx_sync_flag_show, aw882xx_sync_flag_store); static DEVICE_ATTR(print_dbg, S_IWUSR | S_IRUGO, aw882xx_print_dbg_show, aw882xx_print_dbg_store); +static DEVICE_ATTR(algo_ver, S_IRUGO, + aw882xx_algo_ver_show, NULL); + static struct attribute *aw882xx_attributes[] = { &dev_attr_reg.attr, @@ -2149,6 +2221,7 @@ static struct attribute *aw882xx_attributes[] = { &dev_attr_dsp_re.attr, &dev_attr_phase_sync.attr, &dev_attr_print_dbg.attr, + &dev_attr_algo_ver.attr, NULL }; @@ -2240,7 +2313,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, mutex_lock(&g_aw882xx_lock); g_aw882xx_dev_cnt++; mutex_unlock(&g_aw882xx_lock); - aw_dev_info(&i2c->dev, "%s: dev_cnt %d \n", __func__, g_aw882xx_dev_cnt); + aw_dev_info(&i2c->dev, "dev_cnt %d", g_aw882xx_dev_cnt); return ret; err_sysfs: aw_componet_codec_ops.unregister_codec(&i2c->dev); diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h index 9d3b8aa2d785..699248e58720 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.h +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -95,8 +95,13 @@ enum { }; enum { - AW_FRCPWM_DISABLE = 0, - AW_FRCPWM_ENABLE, + AW_FRCSET_DISABLE = 0, + AW_FRCSET_ENABLE, +}; + +enum { + AW_BOP_DISABLE = 0, + AW_BOP_ENABLE, }; enum { @@ -124,6 +129,7 @@ struct aw882xx { int pstream; int cstream; + unsigned int fade_flag; unsigned char index; unsigned char phase_sync; /* phase sync */ unsigned char dc_flag; diff --git a/asoc/codecs/aw882xxacf/aw_calib.c b/asoc/codecs/aw882xxacf/aw_calib.c index 480850acc220..7d6c6c78b2c6 100644 --- a/asoc/codecs/aw882xxacf/aw_calib.c +++ b/asoc/codecs/aw882xxacf/aw_calib.c @@ -48,7 +48,6 @@ static unsigned int g_cali_status = false; static struct miscdevice *g_misc_dev = NULL; static DEFINE_MUTEX(g_cali_lock); - #ifdef AW_CALI_STORE_EXAMPLE /*write cali to persist file example*/ #define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" @@ -150,7 +149,7 @@ int aw_cali_write_re_to_nvram(int32_t cali_re, int32_t channel) } return aw_cali_write_cali_re_to_file(cali_re, channel); #else - return -EBUSY; + return 0; #endif } @@ -164,7 +163,7 @@ int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t channel) } return aw_cali_get_read_cali_re(cali_re, channel); #else - return -EBUSY; + return 0; #endif } @@ -222,12 +221,12 @@ static int aw_cali_svc_dev_get_re(struct aw_device *aw_dev) aw_dev->cali_desc.cali_re = re[0]; - if ((aw_dev->cali_desc.cali_re >= aw_dev->re_min) && + /*if ((aw_dev->cali_desc.cali_re >= aw_dev->re_min) && (aw_dev->cali_desc.cali_re <= aw_dev->re_max)) { ret = aw_cali_write_re_to_nvram(aw_dev->cali_desc.cali_re, aw_dev->channel); if (ret) aw_dev_err(aw_dev->dev, "write re to nvram failed!"); - } + }*/ return 0; } @@ -839,7 +838,9 @@ static int aw_cali_svc_set_devs_re_str(struct aw_device *aw_dev, const char *re_ list_for_each (pos, dev_list) { local_dev = container_of(pos, struct aw_device, list_node); if (local_dev->channel < AW_DEV_CH_MAX) { - aw_cali_store_cali_re(local_dev, re_data[local_dev->channel]); + ret = aw_cali_store_cali_re(local_dev, re_data[local_dev->channel]); + if (ret < 0) + return ret; cnt++; } } @@ -947,7 +948,7 @@ static ssize_t aw_cali_attr_re_store(struct device *dev, struct aw882xx *aw882xx = dev_get_drvdata(dev); struct aw_device *aw_dev = aw882xx->aw_pa; int ret; - int data; + int data = 0; if (is_single_cali) { ret = kstrtoint(buf, 0, &data); @@ -955,7 +956,11 @@ static ssize_t aw_cali_attr_re_store(struct device *dev, aw_dev_err(aw882xx->dev, " read buf %s failed", buf); return ret; } - aw_cali_store_cali_re(aw_dev, data); + ret = aw_cali_store_cali_re(aw_dev, data); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "set re %d failed", data); + return -EPERM; + } } else { ret = aw_cali_svc_set_devs_re_str(aw_dev, buf); if (ret <= 0) { @@ -1554,7 +1559,7 @@ static int aw_cali_misc_ops_write(struct aw_device *aw_dev, ret = aw_cali_misc_params_ptr(aw_dev, (struct ptr_params_data *)data_ptr); } break; case AW_IOCTL_SET_CALI_RE: { - aw_cali_store_cali_re(aw_dev, *((int32_t *)data_ptr)); + ret = aw_cali_store_cali_re(aw_dev, *((int32_t *)data_ptr)); /*ret = aw_dsp_write_cali_re(aw_dev, *(int32_t *)data_ptr);*/ } break; case AW_IOCTL_SET_DSP_HMUTE: { diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index 3067527e415d..0dbeb9fb83ea 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -23,6 +23,7 @@ #include "aw_dsp.h" /*#include "aw_afe.h"*/ #include "aw_bin_parse.h" +#include "aw882xx.h" #define AW_DEV_SYSST_CHECK_MAX (10) @@ -36,7 +37,6 @@ static char *profile_name[AW_PROFILE_MAX] = { "Lowpower", "Bypass", "Mmi", "Fm", "Notification", "Receiver" }; - static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ static unsigned int g_fade_in_time = AW_1000_US / 10; @@ -194,15 +194,13 @@ static int aw_dev_parse_raw_reg(struct aw_device *aw_dev, return 0; } -static int aw_dev_parse_raw_dsp(struct aw_device *aw_dev, +static void aw_dev_parse_raw_dsp(struct aw_device *aw_dev, uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) { aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].data = data; prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].len = data_len; - - return 0; } static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, @@ -363,11 +361,10 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, return 0; } -static int aw_dev_parse_skt_type(struct aw_device *aw_dev, +static void aw_dev_parse_skt_type(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) { int i = 0; - int ret; int sec_num = 0; struct aw_cfg_dde *cfg_dde = (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); @@ -378,21 +375,16 @@ static int aw_dev_parse_skt_type(struct aw_device *aw_dev, if ((aw_dev->index == cfg_dde[i].dev_index) && (cfg_dde[i].type == AW_SKT_TYPE_ID)) { if (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) { - ret = aw_dev_parse_raw_dsp(aw_dev, + aw_dev_parse_raw_dsp(aw_dev, (uint8_t *)prof_hdr + cfg_dde[i].data_offset, cfg_dde[i].data_size, &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); - if (ret < 0) { - aw_dev_err(aw_dev->dev, "parse dsp bin data failed"); - return ret; - } sec_num++; } } } aw_dev_info(aw_dev->dev, "get dsp data prof cnt is %d ", sec_num); - return 0; } static int aw_dev_acf_load_by_hdr(struct aw_device *aw_dev, @@ -410,9 +402,7 @@ static int aw_dev_acf_load_by_hdr(struct aw_device *aw_dev, return ret; } - ret = aw_dev_parse_skt_type(aw_dev, prof_hdr, all_prof_info); - if (ret < 0) - return ret; + aw_dev_parse_skt_type(aw_dev, prof_hdr, all_prof_info); return 0; } @@ -690,16 +680,16 @@ static void aw_dev_fade_in(struct aw_device *aw_dev) { int i = 0; int fade_step = aw_dev->vol_step; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int fade_flag = aw882xx->fade_flag; struct aw_volume_desc *desc = &aw_dev->volume_desc; - if (fade_step == 0 || g_fade_in_time == 0) { + if (fade_step == 0 || g_fade_in_time == 0 || fade_flag == 0) { aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); return; } /*volume up*/ for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { - if (i < desc->init_volume) - i = desc->init_volume; aw_dev->ops.aw_set_volume(aw_dev, i); usleep_range(g_fade_in_time, g_fade_in_time + 10); } @@ -711,10 +701,12 @@ static void aw_dev_fade_out(struct aw_device *aw_dev) { int i = 0; unsigned start_volume = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int fade_flag = aw882xx->fade_flag; int fade_step = aw_dev->vol_step; struct aw_volume_desc *desc = &aw_dev->volume_desc; - if (fade_step == 0 || g_fade_out_time == 0) { + if (fade_step == 0 || g_fade_out_time == 0 || fade_flag == 0) { aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); return; } @@ -722,8 +714,6 @@ static void aw_dev_fade_out(struct aw_device *aw_dev) aw_dev->ops.aw_get_volume(aw_dev, &start_volume); i = start_volume; for (i = start_volume; i <= desc->mute_volume; i += fade_step) { - if (i > desc->mute_volume) - i = desc->mute_volume; aw_dev->ops.aw_set_volume(aw_dev, i); usleep_range(g_fade_out_time, g_fade_out_time + 10); } @@ -1031,7 +1021,7 @@ static int aw_dev_sysst_check(struct aw_device *aw_dev) int aw_dev_get_profile_count(struct aw_device *aw_dev) { if (aw_dev == NULL) { - aw_dev_err(aw_dev->dev, "aw_dev is NULL"); + aw_pr_err("aw_dev is NULL"); return -ENOMEM; } @@ -1338,18 +1328,6 @@ static void aw_dev_boost_type_recover(struct aw_device *aw_dev) } } -static void aw_dev_set_frcpwm_mode(struct aw_device *aw_dev) -{ - struct aw_frcpwm_desc *frcpwm_desc = &aw_dev->frcpwm_desc; - - aw_dev_dbg(aw_dev->dev, "enter"); - - aw_dev->ops.aw_i2c_write_bits(aw_dev, frcpwm_desc->reg, - frcpwm_desc->mask, frcpwm_desc->frcpwm_val); - - aw_dev_dbg(aw_dev->dev, "frcpwm_mode set done"); -} - int aw_device_start(struct aw_device *aw_dev) { int ret; @@ -1407,8 +1385,8 @@ int aw_device_start(struct aw_device *aw_dev) aw_dev_amppd(aw_dev, true); } - if (aw_dev->frcpwm_en) - aw_dev_set_frcpwm_mode(aw_dev); + if (aw_dev->ops.aw_reg_force_set) + aw_dev->ops.aw_reg_force_set(aw_dev); if (!aw_dev->mute_st) { /*close mute*/ diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 22f5819dbc88..95902e1c49e4 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -8,6 +8,8 @@ #define AW_VOLUME_STEP_DB (6 * 2) #define AW_REG_NONE (0xFF) #define AW_NAME_MAX (50) +#define ALGO_VERSION_MAX (80) + enum { AW_1000_US = 1000, @@ -64,6 +66,7 @@ struct aw_device_ops { int (*aw_get_dev_num)(void); void (*aw_set_algo)(struct aw_device *aw_dev); unsigned int (*aw_get_irq_type)(struct aw_device *aw_dev, unsigned int value); + void (*aw_reg_force_set)(struct aw_device *aw_dev); }; struct aw_int_desc { @@ -73,12 +76,6 @@ struct aw_int_desc { unsigned int int_mask; /*set mask*/ }; -struct aw_frcpwm_desc { - unsigned int reg; - unsigned int mask; - unsigned int frcpwm_val; -}; - struct aw_soft_rst { int reg; int reg_value; @@ -98,6 +95,12 @@ struct aw_amppd_desc { unsigned int disable; }; +struct aw_bop_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disbale; +}; struct aw_vcalb_desc { unsigned int icalk_reg; @@ -195,7 +198,8 @@ struct aw_device { int index; int status; int bstcfg_enable; - int frcpwm_en; + int frcset_en; + int bop_en; unsigned int mute_st; unsigned int amppd_st; @@ -229,7 +233,7 @@ struct aw_device { struct aw_cali_desc cali_desc; struct aw_monitor_desc monitor_desc; struct aw_soft_rst soft_rst; - struct aw_frcpwm_desc frcpwm_desc; + struct aw_bop_desc bop_desc; struct aw_device_ops ops; struct list_head list_node; }; diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c index ced5e746e81f..994b0f123913 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.c +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -25,6 +25,9 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_F0_Q (0x00000003) #define AW_MSG_ID_DIRECT_CUR_FLAG (0x00000006) #define AW_MSG_ID_SPK_STATUS (0x00000007) +#define AW_MSG_ID_VERSION (0x00000008) +#define AW_MSG_ID_VERSION_NEW (0x00000012) + /*dsp params id*/ #define AW_MSG_ID_RX_SET_ENABLE (0x10013D11) @@ -1008,6 +1011,36 @@ int aw_dsp_set_copp_module_en(bool enable) return 0; } +int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf) +{ + int ret; + unsigned int algo_ver = 0; + char *algo_data = NULL; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_VERSION, + (char *)&algo_ver, sizeof(uint32_t)); + if ((ret < 0) || (algo_ver == 0)) { + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_VERSION_NEW, + algo_ver_buf, ALGO_VERSION_MAX); + if (ret < 0) + return ret; + } else { + algo_data = (char *)&algo_ver; + snprintf(algo_ver_buf, ALGO_VERSION_MAX, "aw_algo_v%d.%d.%d.%d", + algo_data[3], algo_data[2], algo_data[1], algo_data[0]); + } + + aw_dev_dbg(aw_dev->dev, "%s", algo_ver_buf); + return 0; +} + void aw_device_parse_topo_id_dt(struct aw_device *aw_dev) { int ret; diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h index 1396a88cc5fe..dfa7fadab666 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.h +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -55,6 +55,7 @@ int aw_dsp_get_afe_module_en(int type, int *status); int aw_dsp_set_copp_module_en(bool enable); int aw_dsp_write_spin(int spin_mode); int aw_dsp_read_spin(int *spin_mode); +int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf); void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); void aw_device_parse_port_id_dt(struct aw_device *aw_dev); diff --git a/asoc/codecs/aw882xxacf/aw_init.c b/asoc/codecs/aw882xxacf/aw_init.c index 12a05e59ca55..8acd57c63f76 100644 --- a/asoc/codecs/aw882xxacf/aw_init.c +++ b/asoc/codecs/aw882xxacf/aw_init.c @@ -223,8 +223,8 @@ static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) aw_pa->prof_info.prof_desc = NULL; aw_pa->prof_info.count = 0; aw_pa->channel = 0; - aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; aw_pa->vol_step = AW_PID_1852_VOL_STEP; aw_pa->index = index; @@ -310,6 +310,8 @@ static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.shift = AW_PID_1852_VOL_START_BIT; aw_pa->volume_desc.mute_volume = AW_PID_1852_MUTE_VOL; + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->soft_rst.reg = AW_PID_1852_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -336,7 +338,6 @@ static unsigned int aw_pid_2013_db_val_to_reg(unsigned int value) return (((value / AW_PID_2013_VOL_STEP_DB) << 6) + (value % AW_PID_2013_VOL_STEP_DB)); } - static int aw_pid_2013_set_volume(struct aw_device *aw_dev, unsigned int value) { struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; @@ -498,7 +499,7 @@ static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) aw_pa->prof_info.prof_desc = NULL; aw_pa->prof_info.count = 0; aw_pa->channel = 0; - aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; aw_pa->vol_step = AW_PID_2013_VOL_STEP; aw_pa->index = index; @@ -593,6 +594,8 @@ static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.shift = AW_PID_2013_VOL_START_BIT; aw_pa->volume_desc.mute_volume = AW_PID_2013_MUTE_VOL; + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->soft_rst.reg = AW_PID_2013_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -761,7 +764,7 @@ static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) aw_pa->prof_info.count = 0; aw_pa->channel = 0; aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; - aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; aw_pa->vol_step = AW_PID_2032_VOL_STEP; aw_pa->index = index; @@ -847,6 +850,8 @@ static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.shift = AW_PID_2032_VOL_START_BIT; aw_pa->volume_desc.mute_volume = AW_PID_2032_MUTE_VOL; + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->soft_rst.reg = AW_PID_2032_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -986,8 +991,8 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->prof_info.prof_desc = NULL; aw_pa->prof_info.count = 0; aw_pa->channel = 0; - aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; aw_pa->vol_step = AW_PID_2055_VOL_STEP; aw_pa->index = index; @@ -1053,6 +1058,8 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->cco_mux_desc.divided_val = AW_PID_2055_CCO_MUX_DIVIDED_VALUE; aw_pa->cco_mux_desc.bypass_val = AW_PID_2055_CCO_MUX_BYPASS_VALUE; + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1213,8 +1220,8 @@ static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) aw_pa->prof_info.prof_desc = NULL; aw_pa->prof_info.count = 0; aw_pa->channel = 0; - aw_pa->frcpwm_en = AW_FRCPWM_DISABLE; aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; aw_pa->vol_step = AW_PID_2071_VOL_STEP; aw_pa->index = index; @@ -1303,6 +1310,8 @@ static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.shift = AW_PID_2071_VOL_START_BIT; aw_pa->volume_desc.mute_volume = AW_PID_2071_MUTE_VOL; + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->soft_rst.reg = AW_PID_2071_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1457,13 +1466,42 @@ static void aw_pid_2113_efver_check(struct aw_device *aw_dev) AW_PID_2113_EFRL3_REG, ®_val); teml = ((uint16_t)reg_val & (~AW_PID_2113_TEML_MASK)); - if ((temh | teml) == AW_PID_2113_FRCPWM_CFG) - aw_dev->frcpwm_en = AW_FRCPWM_ENABLE; + if ((temh | teml) == AW_PID_2113_DEFAULT_CFG) + aw_dev->frcset_en = AW_FRCSET_ENABLE; else - aw_dev->frcpwm_en = AW_FRCPWM_DISABLE; + aw_dev->frcset_en = AW_FRCSET_DISABLE; - aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcpwm_en is %d", - (temh | teml), aw_dev->frcpwm_en); + aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcset_en is %d", + (temh | teml), aw_dev->frcset_en); +} + +static void aw_pid_2113_reg_force_set(struct aw_device *aw_dev) +{ + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->frcset_en == AW_FRCSET_ENABLE) { + /*set FORCE_PWM*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL3_REG, + AW_PID_2113_FORCE_PWM_MASK, AW_PID_2113_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /*set BOOST_OC_WIDTH*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL5_REG, + AW_PID_2113_BST_OS_WIDTH_MASK, AW_PID_2113_BST_OS_WIDTH_50NS_VALUE); + /*set BURST_LOOPR*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL6_REG, + AW_PID_2113_BST_LOOPR_MASK, AW_PID_2113_BST_LOOPR_340K_VALUE); + /*set RSQN_DLY*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL7_REG, + AW_PID_2113_RSQN_DLY_MASK, AW_PID_2113_RSQN_DLY_35NS_VALUE); + /*set BURST_SSMODE*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL8_REG, + AW_PID_2113_BURST_SSMODE_MASK, AW_PID_2113_BURST_SSMODE_FAST_VALUE); + /*set BST_BURST*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL9_REG, + AW_PID_2113_BST_BURST_MASK, AW_PID_2113_BST_BURST_30MA_VALUE); + aw_dev_dbg(aw_dev->dev, "force set reg done!"); + }else { + aw_dev_info(aw_dev->dev, "needn't set reg value"); + } } static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) @@ -1486,6 +1524,7 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->prof_info.count = 0; aw_pa->channel = 0; aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; aw_pa->vol_step = AW_PID_2113_VOL_STEP; aw_pa->index = index; @@ -1506,6 +1545,7 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->ops.aw_check_wr_access = aw_pid_2113_check_wr_access; aw_pa->ops.aw_get_reg_num = aw_pid_2113_get_reg_num; aw_pa->ops.aw_get_irq_type = aw_pid_2113_get_irq_type; + aw_pa->ops.aw_reg_force_set = aw_pid_2113_reg_force_set; aw_pa->int_desc.mask_reg = AW_PID_2113_SYSINTM_REG; aw_pa->int_desc.mask_default = AW_PID_2113_SYSINTM_DEFAULT; @@ -1577,9 +1617,10 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.shift = AW_PID_2113_VOL_START_BIT; aw_pa->volume_desc.mute_volume = AW_PID_2113_MUTE_VOL; - aw_pa->frcpwm_desc.reg = AW_PID_2113_BSTCTRL3_REG; - aw_pa->frcpwm_desc.mask = AW_PID_2113_FORCE_PWM_MASK; - aw_pa->frcpwm_desc.frcpwm_val = AW_PID_2113_FORCE_PWM_FORCEMINUS_PWM_VALUE; + aw_pa->bop_desc.reg = AW_PID_2113_SADCCTRL3_REG; + aw_pa->bop_desc.mask = AW_PID_2113_BOP_EN_MASK; + aw_pa->bop_desc.enable = AW_PID_2113_BOP_EN_ENABLE_VALUE; + aw_pa->bop_desc.disbale = AW_PID_2113_BOP_EN_DISABLE_VALUE; aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1606,7 +1647,7 @@ int aw882xx_init(struct aw882xx *aw882xx, int index) else if (aw882xx->chip_id == PID_2113_ID) return aw_pid_2113_dev_init(aw882xx, index); - aw_dev_err(aw882xx->dev, "unsupported chip id %d", aw882xx->chip_id); + aw_dev_err(aw882xx->dev, "unsupported chip id 0x%04x", aw882xx->chip_id); return -EINVAL; } diff --git a/asoc/codecs/aw882xxacf/aw_monitor.c b/asoc/codecs/aw882xxacf/aw_monitor.c index 5ee8af842b70..2783fba1a9c6 100644 --- a/asoc/codecs/aw882xxacf/aw_monitor.c +++ b/asoc/codecs/aw882xxacf/aw_monitor.c @@ -480,9 +480,8 @@ void aw_monitor_work_func(struct work_struct *work) struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; - aw_dev_dbg(aw_dev->dev, "scene_mode %d,monitor_status:%d, monitor_switch:%d", - aw_dev->cur_prof, monitor_cfg->monitor_status, - monitor_cfg->monitor_switch); + aw_dev_dbg(aw_dev->dev, "scene_mode %d, monitor_status:%d, monitor_switch:%d", + aw_dev->cur_prof, monitor_cfg->monitor_status, monitor_cfg->monitor_switch); if ((aw_dev->prof_info.prof_desc[aw_dev->cur_prof].id != AW_PROFILE_RECEIVER) && (monitor_cfg->monitor_status == AW_MON_CFG_OK) && @@ -495,6 +494,26 @@ void aw_monitor_work_func(struct work_struct *work) } } +void aw_check_bop_status(struct aw_device *aw_dev) +{ + struct aw_bop_desc *bop_desc = &aw_dev->bop_desc; + unsigned int reg_val = 0; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bop_desc.reg == AW_REG_NONE) + return; + + aw_dev->ops.aw_i2c_read(aw_dev, bop_desc->reg, ®_val); + reg_val = (uint16_t)reg_val & (~bop_desc->mask); + if (reg_val == bop_desc->enable) + aw_dev->bop_en = AW_BOP_ENABLE; + else + aw_dev->bop_en = AW_BOP_DISABLE; + + aw_dev_dbg(aw_dev->dev, "check done! bop status is %d", aw_dev->bop_en); +} + void aw_monitor_start(struct aw_monitor_desc *monitor_desc) { struct aw_device *aw_dev = container_of(monitor_desc, @@ -508,6 +527,13 @@ void aw_monitor_start(struct aw_monitor_desc *monitor_desc) monitor_desc->vol_trace.sum_val = 0; monitor_desc->temp_trace.sum_val = 0; + aw_check_bop_status(aw_dev); + + if (aw_dev->bop_en == AW_BOP_ENABLE) { + aw_dev_info(aw_dev->dev, "bop status is enable, monitor can't start"); + return; + } + queue_delayed_work(aw882xx->work_queue, &monitor_desc->delay_work, 0); } diff --git a/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h b/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h index e09c714e5176..6f43a1e2693a 100644 --- a/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h +++ b/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h @@ -5,72 +5,72 @@ #define AW_PID_1852_MONITOR_FILE "aw882xx_pid_1852_monitor.bin" /* registers list */ -#define AW_PID_1852_ID_REG (0x00) -#define AW_PID_1852_SYSST_REG (0x01) -#define AW_PID_1852_SYSINT_REG (0x02) -#define AW_PID_1852_SYSINTM_REG (0x03) -#define AW_PID_1852_SYSCTRL_REG (0x04) -#define AW_PID_1852_SYSCTRL2_REG (0x05) -#define AW_PID_1852_I2SCTRL_REG (0x06) -#define AW_PID_1852_I2SCFG1_REG (0x07) -#define AW_PID_1852_I2SCFG2_REG (0x08) -#define AW_PID_1852_HAGCCFG1_REG (0x09) -#define AW_PID_1852_HAGCCFG2_REG (0x0A) -#define AW_PID_1852_HAGCCFG3_REG (0x0B) -#define AW_PID_1852_HAGCCFG4_REG (0x0C) -#define AW_PID_1852_HAGCCFG5_REG (0x0D) -#define AW_PID_1852_HAGCCFG6_REG (0x0E) -#define AW_PID_1852_HAGCCFG7_REG (0x0F) -#define AW_PID_1852_HAGCST_REG (0x10) -#define AW_PID_1852_PRODID_REG (0x11) -#define AW_PID_1852_VBAT_REG (0x12) -#define AW_PID_1852_TEMP_REG (0x13) -#define AW_PID_1852_PVDD_REG (0x14) -#define AW_PID_1852_DBGCTRL_REG (0x20) -#define AW_PID_1852_I2SINT_REG (0x21) -#define AW_PID_1852_I2SCAPCNT_REG (0x22) -#define AW_PID_1852_CRCIN_REG (0x38) -#define AW_PID_1852_CRCOUT_REG (0x39) -#define AW_PID_1852_VSNCTRL1_REG (0x50) -#define AW_PID_1852_ISNCTRL1_REG (0x52) -#define AW_PID_1852_ISNCTRL2_REG (0x53) -#define AW_PID_1852_VTMCTRL1_REG (0x54) -#define AW_PID_1852_VTMCTRL2_REG (0x55) -#define AW_PID_1852_VTMCTRL3_REG (0x56) -#define AW_PID_1852_ISNDAT_REG (0x57) -#define AW_PID_1852_VSNDAT_REG (0x58) -#define AW_PID_1852_PWMCTRL_REG (0x59) -#define AW_PID_1852_PWMCTRL2_REG (0x5A) -#define AW_PID_1852_BSTCTRL1_REG (0x60) -#define AW_PID_1852_BSTCTRL2_REG (0x61) -#define AW_PID_1852_BSTCTRL3_REG (0x62) -#define AW_PID_1852_BSTDBG1_REG (0x63) -#define AW_PID_1852_BSTDBG2_REG (0x64) -#define AW_PID_1852_BSTDBG3_REG (0x65) -#define AW_PID_1852_PLLCTRL1_REG (0x66) -#define AW_PID_1852_PLLCTRL2_REG (0x67) -#define AW_PID_1852_PLLCTRL3_REG (0x68) -#define AW_PID_1852_CDACTRL1_REG (0x69) -#define AW_PID_1852_CDACTRL2_REG (0x6A) -#define AW_PID_1852_SADCCTRL_REG (0x6B) -#define AW_PID_1852_TESTCTRL1_REG (0x70) -#define AW_PID_1852_TESTCTRL2_REG (0x71) -#define AW_PID_1852_EFCTRL1_REG (0x72) -#define AW_PID_1852_EFCTRL2_REG (0x73) -#define AW_PID_1852_EFWH_REG (0x74) -#define AW_PID_1852_EFWM2_REG (0x75) -#define AW_PID_1852_EFWM1_REG (0x76) -#define AW_PID_1852_EFWL_REG (0x77) -#define AW_PID_1852_EFRH_REG (0x78) -#define AW_PID_1852_EFRM2_REG (0x79) -#define AW_PID_1852_EFRM1_REG (0x7A) -#define AW_PID_1852_EFRL_REG (0x7B) -#define AW_PID_1852_TESTDET_REG (0x7C) +#define AW_PID_1852_ID_REG (0x00) +#define AW_PID_1852_SYSST_REG (0x01) +#define AW_PID_1852_SYSINT_REG (0x02) +#define AW_PID_1852_SYSINTM_REG (0x03) +#define AW_PID_1852_SYSCTRL_REG (0x04) +#define AW_PID_1852_SYSCTRL2_REG (0x05) +#define AW_PID_1852_I2SCTRL_REG (0x06) +#define AW_PID_1852_I2SCFG1_REG (0x07) +#define AW_PID_1852_I2SCFG2_REG (0x08) +#define AW_PID_1852_HAGCCFG1_REG (0x09) +#define AW_PID_1852_HAGCCFG2_REG (0x0A) +#define AW_PID_1852_HAGCCFG3_REG (0x0B) +#define AW_PID_1852_HAGCCFG4_REG (0x0C) +#define AW_PID_1852_HAGCCFG5_REG (0x0D) +#define AW_PID_1852_HAGCCFG6_REG (0x0E) +#define AW_PID_1852_HAGCCFG7_REG (0x0F) +#define AW_PID_1852_HAGCST_REG (0x10) +#define AW_PID_1852_PRODID_REG (0x11) +#define AW_PID_1852_VBAT_REG (0x12) +#define AW_PID_1852_TEMP_REG (0x13) +#define AW_PID_1852_PVDD_REG (0x14) +#define AW_PID_1852_DBGCTRL_REG (0x20) +#define AW_PID_1852_I2SINT_REG (0x21) +#define AW_PID_1852_I2SCAPCNT_REG (0x22) +#define AW_PID_1852_CRCIN_REG (0x38) +#define AW_PID_1852_CRCOUT_REG (0x39) +#define AW_PID_1852_VSNCTRL1_REG (0x50) +#define AW_PID_1852_ISNCTRL1_REG (0x52) +#define AW_PID_1852_ISNCTRL2_REG (0x53) +#define AW_PID_1852_VTMCTRL1_REG (0x54) +#define AW_PID_1852_VTMCTRL2_REG (0x55) +#define AW_PID_1852_VTMCTRL3_REG (0x56) +#define AW_PID_1852_ISNDAT_REG (0x57) +#define AW_PID_1852_VSNDAT_REG (0x58) +#define AW_PID_1852_PWMCTRL_REG (0x59) +#define AW_PID_1852_PWMCTRL2_REG (0x5A) +#define AW_PID_1852_BSTCTRL1_REG (0x60) +#define AW_PID_1852_BSTCTRL2_REG (0x61) +#define AW_PID_1852_BSTCTRL3_REG (0x62) +#define AW_PID_1852_BSTDBG1_REG (0x63) +#define AW_PID_1852_BSTDBG2_REG (0x64) +#define AW_PID_1852_BSTDBG3_REG (0x65) +#define AW_PID_1852_PLLCTRL1_REG (0x66) +#define AW_PID_1852_PLLCTRL2_REG (0x67) +#define AW_PID_1852_PLLCTRL3_REG (0x68) +#define AW_PID_1852_CDACTRL1_REG (0x69) +#define AW_PID_1852_CDACTRL2_REG (0x6A) +#define AW_PID_1852_SADCCTRL_REG (0x6B) +#define AW_PID_1852_TESTCTRL1_REG (0x70) +#define AW_PID_1852_TESTCTRL2_REG (0x71) +#define AW_PID_1852_EFCTRL1_REG (0x72) +#define AW_PID_1852_EFCTRL2_REG (0x73) +#define AW_PID_1852_EFWH_REG (0x74) +#define AW_PID_1852_EFWM2_REG (0x75) +#define AW_PID_1852_EFWM1_REG (0x76) +#define AW_PID_1852_EFWL_REG (0x77) +#define AW_PID_1852_EFRH_REG (0x78) +#define AW_PID_1852_EFRM2_REG (0x79) +#define AW_PID_1852_EFRM1_REG (0x7A) +#define AW_PID_1852_EFRL_REG (0x7B) +#define AW_PID_1852_TESTDET_REG (0x7C) /******************************************** * Register Access *******************************************/ -#define AW_PID_1852_REG_MAX (0x7D) +#define AW_PID_1852_REG_MAX (0x7D) #define AW_PID_1852_REG_NONE_ACCESS (0) @@ -142,263 +142,233 @@ static const unsigned char aw_pid_1852_reg_access[AW_PID_1852_REG_MAX] = { [AW_PID_1852_TESTDET_REG] = (AW_PID_1852_REG_RD_ACCESS), }; -/******************************************** - * Register Detail - *******************************************/ /* detail information of registers begin */ /* ID (0x00) detail */ /* IDCODE bit 15:0 (ID 0x00) */ -#define AW_PID_1852_IDCODE_START_BIT (0) -#define AW_PID_1852_IDCODE_BITS_LEN (16) -#define AW_PID_1852_IDCODE_MASK \ - (~(((1< Date: Thu, 16 Sep 2021 15:50:45 +0800 Subject: [PATCH 31/79] mbhc: change for linein threshold Add property "qcom,msm-hs-linein-threshold", give a chance to overlay linein threshold, Qcom mbhc default value is 5000. Change-Id: Ia44091c5ea787cf98b8c7b28149b31ee23b6f4d6 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/2068418 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/wcd-mbhc-v2.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 966aa9b586c8..3158cb513eee 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -1657,6 +1657,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) struct snd_soc_component *component; struct snd_soc_card *card; const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + const char *linein_th = "qcom,msm-hs-linein-threshold"; if (!mbhc || !mbhc_cfg) return -EINVAL; @@ -1683,6 +1684,12 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) "%s: skipping USB c analog configuration\n", __func__); } + if (of_find_property(card->dev->of_node, linein_th, NULL)) { + rc = of_property_read_u32(card->dev->of_node, linein_th, + &mbhc->mbhc_cfg->linein_th); + } + pr_info("%s, LINEIN threshold = %d\n", __func__, mbhc->mbhc_cfg->linein_th); + /* Parse fsa switch handle */ if (mbhc_cfg->enable_usbc_analog) { dev_dbg(mbhc->component->dev, "%s: usbc analog enabled\n", From 2a9421190eb69f1e63c83fb13e81c04ef11ce624 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 21 Oct 2021 18:20:46 +0800 Subject: [PATCH 32/79] audio:aw992xxacf:driver V1.7.0 1.seperate aw params from acdb. stored in aw_skt.bin use CONFIG_AW882XX_PARAMS_FROM_BIN macro 2.use profile for different scene. 3.need to match new skt algo V5.3.1.1 Change-Id: I155ddf8f1a8284443fa6564c9a1bc8ad1735a3e5 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2095737 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Hong Zhang Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 87 +++++++++++++++++++--- asoc/codecs/aw882xxacf/aw882xx.h | 6 ++ asoc/codecs/aw882xxacf/aw_calib.c | 51 +++---------- asoc/codecs/aw882xxacf/aw_calib.h | 5 +- asoc/codecs/aw882xxacf/aw_device.c | 29 +++++++- asoc/codecs/aw882xxacf/aw_device.h | 5 ++ asoc/codecs/aw882xxacf/aw_dsp.c | 115 +++++++++++++++++++---------- asoc/codecs/aw882xxacf/aw_dsp.h | 3 + dsp/Kbuild | 4 + dsp/q6afe.c | 3 + 10 files changed, 211 insertions(+), 97 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index d111210b27b2..271148fbf67f 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.6.0" +#define AW882XX_DRIVER_VERSION "v1.7.0" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -243,7 +243,7 @@ int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, return 0; } -#if 0 +#ifdef CONFIG_AW882XX_STEREO_SMARTPA static void *aw882xx_devm_kstrdup(struct device *dev, char *buf) { char *str; @@ -844,6 +844,7 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, { int ret = -EINVAL; uint32_t ctrl_value = 0; + struct aw_device *aw_dev = NULL; aw_snd_soc_codec_t *codec = aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = @@ -852,6 +853,7 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); + aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); @@ -860,6 +862,12 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, g_algo_rx_en = ctrl_value; aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + + if (ctrl_value) { + ret = aw_dev_set_algo_params_path(aw_dev); + if (ret < 0) + aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + } return 0; } @@ -874,6 +882,11 @@ static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); + /*ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + } + ucontrol->value.integer.value[0] = ctrl_value;*/ if (aw882xx->pstream) { ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); @@ -882,12 +895,12 @@ static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = ctrl_value; } else { - ucontrol->value.integer.value[0] = false; //g_algo_rx_en + ucontrol->value.integer.value[0] = false;//g_algo_rx_en; aw_dev_info(aw882xx->dev, "no stream, rx disable"); } - aw_dev_dbg(aw882xx->dev, "aw882xx_rx_enable %ld", - ucontrol->value.integer.value[0]); + aw_dev_dbg(aw882xx->dev, "aw882xx_rx_enable %d", + ctrl_value); return 0; } @@ -926,6 +939,12 @@ static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); + /*ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + } + ucontrol->value.integer.value[0] = ctrl_value;*/ + if (aw882xx->pstream) { ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); if (ret) { @@ -934,12 +953,12 @@ static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, } ucontrol->value.integer.value[0] = ctrl_value; } else { - ucontrol->value.integer.value[0] = false; //g_algo_tx_en + ucontrol->value.integer.value[0] = false;//g_algo_tx_en; aw_dev_info(aw882xx->dev, "no stream, tx disable"); } - aw_dev_dbg(aw882xx->dev, "aw882xx_tx_enable %ld", - ucontrol->value.integer.value[0]); + aw_dev_dbg(aw882xx->dev, "aw882xx_tx_enable %d", + ctrl_value); return 0; } @@ -1055,6 +1074,50 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, } #endif +static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + aw_dev_set_algo_prof(aw_dev, ctrl_value); + + return 0; +} + +static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev = aw882xx->aw_pa; + + aw_dev_get_algo_prof(aw_dev, &ctrl_value); + + ucontrol->value.integer.value[0] = ctrl_value; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + return 0; +} + static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1240,6 +1303,8 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), + SOC_SINGLE_EXT("aw882xx_algo_prof_switch", 0, 0, 100, 0, + aw882xx_get_prof_id, aw882xx_set_prof_id), }; static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) @@ -1433,7 +1498,7 @@ static int aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) } #endif -#if 0 +#ifdef CONFIG_AW882XX_STEREO_SMARTPA static int aw882xx_dai_drv_append_suffix(struct aw882xx *aw882xx, struct snd_soc_dai_driver *dai_drv, int num_dai) @@ -1519,7 +1584,9 @@ int aw_componet_codec_register(struct aw882xx *aw882xx) memcpy(dai_drv, aw882xx_dai, sizeof(aw882xx_dai)); - /*aw882xx_dai_drv_append_suffix(aw882xx, dai_drv, ARRAY_SIZE(aw882xx_dai));*/ +#ifdef CONFIG_AW882XX_STEREO_SMARTPA + aw882xx_dai_drv_append_suffix(aw882xx, dai_drv, ARRAY_SIZE(aw882xx_dai)); +#endif ret = aw882xx->codec_ops->register_codec(aw882xx->dev, &soc_codec_dev_aw882xx, diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h index 699248e58720..d50a66456c27 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.h +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -54,6 +54,12 @@ enum aw882xx_int_type { #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +#define AW_KERNEL_VER_OVER_5_4_0 +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + + #ifdef AW_KERNEL_VER_OVER_4_19_1 typedef struct snd_soc_component aw_snd_soc_codec_t; typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; diff --git a/asoc/codecs/aw882xxacf/aw_calib.c b/asoc/codecs/aw882xxacf/aw_calib.c index 7d6c6c78b2c6..3b9d36399d6f 100644 --- a/asoc/codecs/aw882xxacf/aw_calib.c +++ b/asoc/codecs/aw882xxacf/aw_calib.c @@ -36,8 +36,7 @@ static bool is_single_cali = false; /*if mutli_dev cali false, single dev true*/ static const char *cali_str[CALI_STR_MAX] = {"none", "start_cali", "cali_re", "cali_f0", "store_re", "show_re", "show_r0", "show_cali_f0", "show_f0", - "show_te", "show_st", "dev_sel", "get_ver", "get_dev_num", - "cali_q_f0", "show_q_f0" + "show_te", "show_st", "dev_sel", "get_ver", "get_dev_num" }; static char *ch_name[AW_DEV_CH_MAX] = {"pri_l", "pri_r", "sec_l", "sec_r"}; @@ -169,7 +168,7 @@ int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t channel) int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) { - int ret = 0; + /*int ret = 0; if ((re >= aw_dev->re_min) && (re <= aw_dev->re_max)) { aw_dev->cali_desc.cali_re = re; @@ -179,10 +178,10 @@ int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) } else { aw_dev_err(aw_dev->dev, "invalid cali re %d !", re); return -EINVAL; - } + }*/ aw_dev_info(aw_dev->dev, "store cali re is %d", re); - return ret; + return 0; } /*********************************aw_cali_sevice*********************************************************/ @@ -271,8 +270,6 @@ static int aw_cali_svc_dev_get_f0_q(struct aw_device *aw_dev) q[0] = sum_q / AW_CALI_READ_TIMES; aw_dev->cali_desc.cali_f0 = f0[0]; aw_dev->cali_desc.cali_q = q[0]; - aw_dev_dbg(aw_dev->dev, "cali f0 is %d, q is %d", - aw_dev->cali_desc.cali_f0, aw_dev->cali_desc.cali_q); return 0; } @@ -1805,7 +1802,6 @@ static ssize_t aw_cali_misc_read(struct file *filp, char __user *buf, size_t siz char local_buf[128] = { 0 }; unsigned int dev_num; int32_t temp_data[AW_DEV_CH_MAX << 1] = {0}; - int32_t temp_data1[AW_DEV_CH_MAX << 1] = {0}; aw_dev_info(aw_dev->dev, "enter"); @@ -1920,20 +1916,6 @@ static ssize_t aw_cali_misc_read(struct file *filp, char __user *buf, size_t siz } break; } - case CALI_STR_SHOW_F0_Q: { - ret = aw_cali_svc_get_devs_cali_f0_q(aw_dev, - temp_data, temp_data1, AW_DEV_CH_MAX); - if (ret <= 0) { - aw_dev_err(aw_dev->dev, "get q f0 failed"); - return ret; - } else { - for (i = 0; i < ret; i++) - len += snprintf(local_buf+len, sizeof(local_buf)-len, - "%s:f0:%d q:%d", ch_name[i], temp_data[i], temp_data1[i]); - - len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); - } - } break; default: { if (g_msic_wr_flag == CALI_STR_NONE) { aw_dev_info(aw_dev->dev, "please write cmd first"); @@ -2054,10 +2036,6 @@ static ssize_t aw_cali_misc_write(struct file *filp, const char __user *buf, siz case CALI_STR_DEV_SEL: { ret = aw_cali_misc_switch_dev(filp, aw_dev, kernel_buf); } break; - case CALI_STR_CALI_F0_Q: { - ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0_Q, - is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); - } break; case CALI_STR_SHOW_RE: /*show cali_re*/ case CALI_STR_SHOW_R0: /*show real r0*/ case CALI_STR_SHOW_CALI_F0: /*GET DEV CALI_F0*/ @@ -2065,8 +2043,7 @@ static ssize_t aw_cali_misc_write(struct file *filp, const char __user *buf, siz case CALI_STR_SHOW_TE: case CALI_STR_SHOW_ST: case CALI_STR_VER: - case CALI_STR_DEV_NUM: - case CALI_STR_SHOW_F0_Q:{ + case CALI_STR_DEV_NUM: { g_msic_wr_flag = ret; ret = 0; } break; @@ -2161,8 +2138,6 @@ static void aw_cali_parse_dt(struct aw_device *aw_dev) desc->mode = AW_CALI_MODE_CLASS; else if (!strcmp(cali_mode_str, "aw_attr")) desc->mode = AW_CALI_MODE_ATTR; - else if (!strcmp(cali_mode_str, "aw_cali_all")) - desc->mode = AW_CALI_MODE_ALL; else desc->mode = AW_CALI_MODE_MISC; /*default misc*/ @@ -2210,14 +2185,10 @@ int aw_cali_init(struct aw_cali_desc *cali_desc) aw_cali_parse_dt(aw_dev); - if (cali_desc->mode == AW_CALI_MODE_ATTR) { + if (cali_desc->mode == AW_CALI_MODE_ATTR) aw_cali_attr_init(aw_dev); - } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { + else if (cali_desc->mode == AW_CALI_MODE_CLASS) aw_cali_class_attr_init(aw_dev); - } else if (cali_desc->mode == AW_CALI_MODE_ALL) { - aw_cali_attr_init(aw_dev); - aw_cali_class_attr_init(aw_dev); - } aw_cali_misc_init(aw_dev); @@ -2231,14 +2202,10 @@ void aw_cali_deinit(struct aw_cali_desc *cali_desc) struct aw_device *aw_dev = container_of(cali_desc, struct aw_device, cali_desc); - if (cali_desc->mode == AW_CALI_MODE_ATTR) { + if (cali_desc->mode == AW_CALI_MODE_ATTR) aw_cali_attr_deinit(aw_dev); - } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { + else if (cali_desc->mode == AW_CALI_MODE_CLASS) aw_cali_class_attr_deinit(aw_dev); - } else if (cali_desc->mode == AW_CALI_MODE_ALL) { - aw_cali_attr_deinit(aw_dev); - aw_cali_class_attr_deinit(aw_dev); - } aw_cali_misc_deinit(aw_dev); } diff --git a/asoc/codecs/aw882xxacf/aw_calib.h b/asoc/codecs/aw882xxacf/aw_calib.h index d7b0099c29a6..ff3f15bebd98 100644 --- a/asoc/codecs/aw882xxacf/aw_calib.h +++ b/asoc/codecs/aw882xxacf/aw_calib.h @@ -85,8 +85,7 @@ enum{ AW_CALI_MODE_ATTR, AW_CALI_MODE_CLASS, AW_CALI_MODE_MISC, - AW_CALI_MODE_ALL, - AW_CALI_MODE_MAX, + AW_CALI_MODE_MAX }; enum { @@ -122,8 +121,6 @@ enum { CALI_STR_DEV_SEL, /*switch device*/ CALI_STR_VER, CALI_STR_DEV_NUM, - CALI_STR_CALI_F0_Q, - CALI_STR_SHOW_F0_Q, CALI_STR_MAX, }; diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index 0dbeb9fb83ea..25c036e22cd8 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -26,6 +26,7 @@ #include "aw882xx.h" #define AW_DEV_SYSST_CHECK_MAX (10) +#define AW_ALGO_PARAMS_PATH "/vendor/firmware/aw_skt.bin" enum { AW_EXT_DSP_WRITE_NONE = 0, @@ -1469,6 +1470,21 @@ int aw_dev_get_spin(int *spin_mode) return aw_dsp_read_spin(spin_mode); } +int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id) +{ + return aw_dsp_set_algo_prof(aw_dev, prof_id); +} + +int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id) +{ + return aw_dsp_get_algo_prof(aw_dev, prof_id); +} + +int aw_dev_set_algo_params_path(struct aw_device *aw_dev) +{ + return aw_dsp_set_algo_params_path(aw_dev); +} + static void aw_device_parse_sound_channel_dt(struct aw_device *aw_dev) { int ret; @@ -1505,19 +1521,28 @@ int aw_dev_get_list_head(struct list_head **head) return 0; } +void aw_device_algo_path_init(struct aw_device *aw_dev) +{ + + memcpy(aw_dev->algo_path, AW_ALGO_PARAMS_PATH, sizeof(AW_ALGO_PARAMS_PATH)); + aw_dev_dbg(aw_dev->dev, "aw_device_algo_path_init"); +} + int aw_device_probe(struct aw_device *aw_dev) { int ret; INIT_LIST_HEAD(&aw_dev->list_node); - + aw_dev_dbg(aw_dev->dev, "aw_device_probe 01"); aw_device_parse_dt(aw_dev); ret = aw_cali_init(&aw_dev->cali_desc); if (ret) return ret; - + aw_dev_dbg(aw_dev->dev, "aw_device_probe 02"); aw_monitor_init(&aw_dev->monitor_desc); /*aw_afe_init();*/ + aw_device_algo_path_init(aw_dev); + mutex_lock(&g_dev_lock); list_add(&aw_dev->list_node, &g_dev_list); mutex_unlock(&g_dev_lock); diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 95902e1c49e4..7b7315d24555 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -9,6 +9,7 @@ #define AW_REG_NONE (0xFF) #define AW_NAME_MAX (50) #define ALGO_VERSION_MAX (80) +#define AW_ALGO_PATH_MAX (256) enum { @@ -215,6 +216,7 @@ struct aw_device { char acf_name[AW_NAME_MAX]; char monitor_name[AW_NAME_MAX]; void *private_data; + char algo_path[AW_ALGO_PATH_MAX]; struct aw_int_desc int_desc; struct aw_pwd_desc pwd_desc; @@ -278,6 +280,9 @@ int aw_dev_get_afe_module_en(int type, int *status); int aw_dev_set_copp_module_en(bool enable); int aw_dev_set_spin(int spin_mode); int aw_dev_get_spin(int *spin_mode); +int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id); +int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id); +int aw_dev_set_algo_params_path(struct aw_device *aw_dev); int aw_device_probe(struct aw_device *aw_dev); int aw_device_remove(struct aw_device *aw_dev); diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c index 994b0f123913..8142b833d3ca 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.c +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -25,6 +25,8 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_F0_Q (0x00000003) #define AW_MSG_ID_DIRECT_CUR_FLAG (0x00000006) #define AW_MSG_ID_SPK_STATUS (0x00000007) +#define AW_MSG_ID_SWITCH_SCENE (0x00000013) +#define AW_MSG_ID_ALGO_PARAMS_PATH (0x00000014) #define AW_MSG_ID_VERSION (0x00000008) #define AW_MSG_ID_VERSION_NEW (0x00000012) @@ -45,6 +47,7 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_F0_R (0X10013D20) #define AW_MSG_ID_REAL_DATA_L (0X10013D21) #define AW_MSG_ID_REAL_DATA_R (0X10013D22) +#define AW_MSG_ID_SET_BIN_PARAMS (0X10013D30) #define AFE_MSG_ID_MSG_0 (0X10013D2A) #define AFE_MSG_ID_MSG_1 (0X10013D2B) @@ -91,7 +94,6 @@ static uint32_t afe_param_msg_id[MSG_PARAM_ID_MAX] = { extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, int16_t size, uint32_t *buf_len); #elif defined CONFIG_AW882XX_DSP -//extern int afe_get_topology(int port_id); extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id,uint32_t param_id, void *buf, int cmd_size, bool write); extern int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size); @@ -111,24 +113,15 @@ static int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size) { return 0; } -//static int afe_get_topology(int port_id) -//{ - //return 0; -//} #endif -/*#ifdef AW_QCOM_PLATFORM -extern void aw_set_port_id(int tx_port_id, int rx_port_id); +#ifdef CONFIG_AW882XX_DSP +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); #else -static void aw_set_port_id(int tx_port_id, int rx_port_id) { - return; -} -#endif*/ - -#if 0 static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) { +#if 0 /*for v3*/ int copp_idx = 0; uint32_t enable_param; @@ -165,7 +158,7 @@ static int aw_adm_param_enable(int port_id, int module_id, int param_id, int ena pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", __func__, module_id, INSTANCE_ID_0, enable, rc); return rc; - +#endif return 0; } #endif @@ -229,46 +222,21 @@ static int aw_check_dsp_ready(uint32_t param_id) static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int size) { int ret; - //int try = 0; mutex_lock(&g_aw_dsp_lock); - //while (try < AW_DSP_TRY_TIME) { - //if (aw_check_dsp_ready(param_id)) { ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, true); mutex_unlock(&g_aw_dsp_lock); return ret; - //} else { - // try++; - // usleep_range(AW_10000_US, AW_10000_US + 10); - // aw_pr_info("afe topo not ready try again"); - //} - //} - //mutex_unlock(&g_aw_dsp_lock); - - //return -EINVAL; } static int aw_qcom_read_data_from_dsp(uint32_t param_id, void *data, int size) { int ret; - //int try = 0; mutex_lock(&g_aw_dsp_lock); - //while (try < AW_DSP_TRY_TIME) { - // if (aw_check_dsp_ready(param_id)) { ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, false); mutex_unlock(&g_aw_dsp_lock); return ret; -/* } else { - try++; - usleep_range(AW_10000_US, AW_10000_US + 10); - aw_pr_info("afe topo not ready try again"); - } - } - mutex_unlock(&g_aw_dsp_lock); - - return -EINVAL; -*/ } static int aw_qcom_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) @@ -385,6 +353,75 @@ static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) } /************************* dsp communication function *****************************/ +int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) +{ + int ret; + int msg_num; + int32_t data = prof_id; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_SWITCH_SCENE, + (char *)&data, sizeof(int32_t)); + if (ret < 0) { + aw_pr_err("write prof_id failed "); + return ret; + } + aw_pr_dbg("write prof_id done"); + + return 0; + +} + +int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id) +{ + int ret; + int msg_num; + int32_t data = 0; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SWITCH_SCENE, + (char *)&data, sizeof(int32_t)); + if (ret < 0) { + *prof_id = 0; + aw_dev_err(aw_dev->dev, "read algo prof failed"); + return ret; + } + + *prof_id = data; + aw_pr_dbg("read prof_id:%d done", *prof_id); + return 0; +} + +int aw_dsp_set_algo_params_path(struct aw_device *aw_dev) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ALGO_PARAMS_PATH, + (char *)aw_dev->algo_path, + sizeof(aw_dev->algo_path)); + if (ret) + return -EINVAL; + + aw_pr_info("set algo params path: %s", aw_dev->algo_path); + return 0; +} int aw_dsp_set_afe_module_en(int type, int enable) { int ret; diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h index dfa7fadab666..aaf0089a0d92 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.h +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -56,6 +56,9 @@ int aw_dsp_set_copp_module_en(bool enable); int aw_dsp_write_spin(int spin_mode); int aw_dsp_read_spin(int *spin_mode); int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf); +int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id); +int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); +int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); void aw_device_parse_port_id_dt(struct aw_device *aw_dev); diff --git a/dsp/Kbuild b/dsp/Kbuild index 3b1e563efca9..dffcb6212a21 100755 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -15,6 +15,10 @@ endif EXTRA_CFLAGS += -DCONFIG_SND_SOC_AWINIC_AW882XX +ifeq ($(CONFIG_AW882XX_PARAMS_FROM_BIN), y) +CDEFINES += -DCONFIG_AW882XX_PARAMS_STORED_IN_BIN +endif + ifeq ($(CONFIG_SND_SOC_AUTO), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 4fd4edf335ae..55887af988e4 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -12045,6 +12045,9 @@ static int afe_unmap_cal_data(int32_t cal_type, ret = afe_cmd_memory_unmap( cal_block->map_data.q6map_handle); atomic_set(&this_afe.mem_map_cal_index, -1); +#ifdef CONFIG_AW882XX_PARAMS_STORED_IN_BIN + atomic_set(&this_afe.mem_map_cal_handles[cal_index],0); +#endif if (ret < 0) { pr_err("%s: unmap did not work! cal_type %i ret %d\n", __func__, cal_index, ret); From ceb08f3958bc1fbd819ab5135abe2a4374a3a923 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 11 Nov 2021 11:05:29 +0800 Subject: [PATCH 33/79] audio:aw882xxacf update driver to v1.8.0 released by aw liugezi. the main modification is aw_cali_store_cali_re function. tcmd code aw calibation will store cali value by tcmd code. and set_cali_re also will call aw_cali_store_cali_re in new driver. the final result is by aw_cali_store_cali_re function. it is different from aw88258 driver. aw88258 driver don't store cali value. after skt v0.2.0, algo need to read mohm value. don't need to convert through 4096. Change-Id: Ib357239b0e6db516c3a54d36270bfda999447895 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2112917 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 2 +- asoc/codecs/aw882xxacf/aw_calib.c | 49 +++++++++++++++++++++++-------- asoc/codecs/aw882xxacf/aw_calib.h | 3 +- asoc/codecs/aw882xxacf/aw_dsp.c | 45 +++++++++++++++++++++++++--- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 271148fbf67f..57da457fa5ec 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.7.0" +#define AW882XX_DRIVER_VERSION "v1.8.0" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ diff --git a/asoc/codecs/aw882xxacf/aw_calib.c b/asoc/codecs/aw882xxacf/aw_calib.c index 3b9d36399d6f..42fb249a8a28 100644 --- a/asoc/codecs/aw882xxacf/aw_calib.c +++ b/asoc/codecs/aw882xxacf/aw_calib.c @@ -52,6 +52,24 @@ static DEFINE_MUTEX(g_cali_lock); #define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" #define AW_INT_DEC_DIGIT 10 +static void aw_fs_read(struct file *file, char *buf, size_t count, loff_t *pos) +{ +#ifdef AW_KERNEL_VER_OVER_5_4_0 + kernel_read(file, buf, count, pos); +#else + vfs_read(file, buf, count, pos); +#endif +} + +static void aw_fs_write(struct file *file, char *buf, size_t count, loff_t *pos) +{ +#ifdef AW_KERNEL_VER_OVER_5_4_0 + kernel_write(file, buf, count, pos); +#else + vfs_write(file, buf, count, pos); +#endif +} + static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) { struct file *fp = NULL; @@ -73,7 +91,7 @@ static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) fs = get_fs(); set_fs(KERNEL_DS); - kernel_write(fp, buf, strlen(buf), &pos); + aw_fs_write(fp, buf, strlen(buf), &pos); set_fs(fs); @@ -118,7 +136,7 @@ static int aw_cali_get_read_cali_re(int32_t *cali_re, int channel) fs = get_fs(); set_fs(KERNEL_DS); - kernel_read(fp, buf, f_size, &pos); + aw_fs_read(fp, buf, f_size, &pos); set_fs(fs); @@ -168,7 +186,7 @@ int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t channel) int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) { - /*int ret = 0; + int ret = 0; if ((re >= aw_dev->re_min) && (re <= aw_dev->re_max)) { aw_dev->cali_desc.cali_re = re; @@ -178,10 +196,10 @@ int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) } else { aw_dev_err(aw_dev->dev, "invalid cali re %d !", re); return -EINVAL; - }*/ + } aw_dev_info(aw_dev->dev, "store cali re is %d", re); - return 0; + return ret; } /*********************************aw_cali_sevice*********************************************************/ @@ -2138,6 +2156,8 @@ static void aw_cali_parse_dt(struct aw_device *aw_dev) desc->mode = AW_CALI_MODE_CLASS; else if (!strcmp(cali_mode_str, "aw_attr")) desc->mode = AW_CALI_MODE_ATTR; + else if (!strcmp(cali_mode_str, "aw_cali_all")) + desc->mode = AW_CALI_MODE_ALL; else desc->mode = AW_CALI_MODE_MISC; /*default misc*/ @@ -2185,10 +2205,14 @@ int aw_cali_init(struct aw_cali_desc *cali_desc) aw_cali_parse_dt(aw_dev); - if (cali_desc->mode == AW_CALI_MODE_ATTR) + if (cali_desc->mode == AW_CALI_MODE_ATTR) { aw_cali_attr_init(aw_dev); - else if (cali_desc->mode == AW_CALI_MODE_CLASS) + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { aw_cali_class_attr_init(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_init(aw_dev); + aw_cali_class_attr_init(aw_dev); + } aw_cali_misc_init(aw_dev); @@ -2202,14 +2226,15 @@ void aw_cali_deinit(struct aw_cali_desc *cali_desc) struct aw_device *aw_dev = container_of(cali_desc, struct aw_device, cali_desc); - if (cali_desc->mode == AW_CALI_MODE_ATTR) + if (cali_desc->mode == AW_CALI_MODE_ATTR) { aw_cali_attr_deinit(aw_dev); - else if (cali_desc->mode == AW_CALI_MODE_CLASS) + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { aw_cali_class_attr_deinit(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_deinit(aw_dev); + aw_cali_class_attr_deinit(aw_dev); + } aw_cali_misc_deinit(aw_dev); } -#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE -MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); -#endif diff --git a/asoc/codecs/aw882xxacf/aw_calib.h b/asoc/codecs/aw882xxacf/aw_calib.h index ff3f15bebd98..fbcc2999282d 100644 --- a/asoc/codecs/aw882xxacf/aw_calib.h +++ b/asoc/codecs/aw882xxacf/aw_calib.h @@ -85,7 +85,8 @@ enum{ AW_CALI_MODE_ATTR, AW_CALI_MODE_CLASS, AW_CALI_MODE_MISC, - AW_CALI_MODE_MAX + AW_CALI_MODE_ALL, + AW_CALI_MODE_MAX, }; enum { diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c index 8142b833d3ca..eabb7764526f 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.c +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -94,6 +94,7 @@ static uint32_t afe_param_msg_id[MSG_PARAM_ID_MAX] = { extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, int16_t size, uint32_t *buf_len); #elif defined CONFIG_AW882XX_DSP +//extern int afe_get_topology(int port_id); extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id,uint32_t param_id, void *buf, int cmd_size, bool write); extern int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size); @@ -113,15 +114,24 @@ static int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size) { return 0; } +//static int afe_get_topology(int port_id) +//{ + //return 0; +//} #endif -#ifdef CONFIG_AW882XX_DSP -extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +/*#ifdef AW_QCOM_PLATFORM +extern void aw_set_port_id(int tx_port_id, int rx_port_id); #else +static void aw_set_port_id(int tx_port_id, int rx_port_id) { + return; +} +#endif*/ + +#if 0 static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) { -#if 0 /*for v3*/ int copp_idx = 0; uint32_t enable_param; @@ -158,7 +168,7 @@ static int aw_adm_param_enable(int port_id, int module_id, int param_id, int ena pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", __func__, module_id, INSTANCE_ID_0, enable, rc); return rc; -#endif + return 0; } #endif @@ -222,21 +232,46 @@ static int aw_check_dsp_ready(uint32_t param_id) static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int size) { int ret; + //int try = 0; mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + //if (aw_check_dsp_ready(param_id)) { ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, true); mutex_unlock(&g_aw_dsp_lock); return ret; + //} else { + // try++; + // usleep_range(AW_10000_US, AW_10000_US + 10); + // aw_pr_info("afe topo not ready try again"); + //} + //} + //mutex_unlock(&g_aw_dsp_lock); + + //return -EINVAL; } static int aw_qcom_read_data_from_dsp(uint32_t param_id, void *data, int size) { int ret; + //int try = 0; mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + // if (aw_check_dsp_ready(param_id)) { ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, false); mutex_unlock(&g_aw_dsp_lock); return ret; +/* } else { + try++; + usleep_range(AW_10000_US, AW_10000_US + 10); + aw_pr_info("afe topo not ready try again"); + } + } + mutex_unlock(&g_aw_dsp_lock); + + return -EINVAL; +*/ } static int aw_qcom_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) @@ -353,6 +388,7 @@ static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) } /************************* dsp communication function *****************************/ + int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) { int ret; @@ -422,6 +458,7 @@ int aw_dsp_set_algo_params_path(struct aw_device *aw_dev) aw_pr_info("set algo params path: %s", aw_dev->algo_path); return 0; } + int aw_dsp_set_afe_module_en(int type, int enable) { int ret; From 79a91e0d678b72467d0d4291027df98b588f06b5 Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 12 Nov 2021 16:30:53 +0800 Subject: [PATCH 34/79] audio_kernel:add two aw88261 dailink config Rhode5G+ use 6375+ two aw88261 smart pa. add two aw88261 dailink config. use macro CONFIG_AW882XX_STEREO_SMARTPA for dual pa. Change-Id: I8a414315e766396fc5be029c3a8569ce9f82a3db Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2114850 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/Kbuild | 4 ++++ asoc/codecs/aw882xxacf/Kbuild | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/asoc/Kbuild b/asoc/Kbuild index b80f81a35fed..8ce3ad3f30e5 100755 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -259,6 +259,10 @@ INCS += $(COMMON_INC) \ EXTRA_CFLAGS += $(INCS) +ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA), y) + EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA +endif + CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ -DANI_LITTLE_BIT_ENDIAN \ -DDOT11F_LITTLE_ENDIAN_HOST \ diff --git a/asoc/codecs/aw882xxacf/Kbuild b/asoc/codecs/aw882xxacf/Kbuild index 1472548a2bb7..3a14a6cb3068 100644 --- a/asoc/codecs/aw882xxacf/Kbuild +++ b/asoc/codecs/aw882xxacf/Kbuild @@ -5,6 +5,11 @@ EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ EXTRA_CFLAGS += -DCONFIG_AW882XX_CODEC -DCONFIG_AW882XX_DSP +ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA), y) + EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA +endif + + aw882xx_acf-objs += aw882xx.o aw882xx_acf-objs += aw_bin_parse.o aw882xx_acf-objs += aw_calib.o From fd8a4ec71334f6ae23555dc540b0f5c2a562db39 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 25 Nov 2021 14:32:37 +0800 Subject: [PATCH 35/79] Audio/aw882xx:update Aw882xx v1.8.1 update Aw882xx v1.8.1. aw find there lost some register init in driver. these register maybe will cause pop issue. aw update diver version. Change-Id: I12ee16ae2068f59df330fe77465f07203ec870fc Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2127266 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Hong Zhang Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 2 +- asoc/codecs/aw882xxacf/aw_init.c | 11 +++++++++++ asoc/codecs/aw882xxacf/aw_pid_2055_reg.h | 6 ++++++ asoc/codecs/aw882xxacf/aw_pid_2113_reg.h | 6 ++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 57da457fa5ec..74ee9c6e2492 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.8.0" +#define AW882XX_DRIVER_VERSION "v1.8.0.1" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ diff --git a/asoc/codecs/aw882xxacf/aw_init.c b/asoc/codecs/aw882xxacf/aw_init.c index 8acd57c63f76..15a067cef503 100644 --- a/asoc/codecs/aw882xxacf/aw_init.c +++ b/asoc/codecs/aw882xxacf/aw_init.c @@ -1063,6 +1063,11 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, + AW_PID_2055_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + ret = aw_device_probe(aw_pa); aw882xx->aw_pa = aw_pa; @@ -1624,6 +1629,12 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2113_INIT_CHECK_REG, + AW_PID_2113_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + aw_pid_2113_efver_check(aw_pa); ret = aw_device_probe(aw_pa); diff --git a/asoc/codecs/aw882xxacf/aw_pid_2055_reg.h b/asoc/codecs/aw882xxacf/aw_pid_2055_reg.h index 66b9401b1cb7..86e0448f89d2 100644 --- a/asoc/codecs/aw882xxacf/aw_pid_2055_reg.h +++ b/asoc/codecs/aw882xxacf/aw_pid_2055_reg.h @@ -2021,4 +2021,10 @@ const unsigned char aw_pid_2055_reg_access[AW_PID_2055_REG_MAX] = { *******************************************/ #define AW_PID_2055_VOL_STEP (6 * 8) +/******************************************** + * Init check + *******************************************/ +#define AW_PID_2055_INIT_CHECK_REG (0x73) +#define AW_PID_2055_INIT_CHECK_VALUE (0x1D40) + #endif /* #ifndef __AW_PID_2055_REG_H__ */ diff --git a/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h b/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h index 7c43888a0165..b2146b1b20d3 100644 --- a/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h +++ b/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h @@ -2303,4 +2303,10 @@ const unsigned char aw_pid_2113_reg_access[AW_PID_2113_REG_MAX] = { #define AW_PID_2113_TEML_MASK (0x83ff) #define AW_PID_2113_DEFAULT_CFG (0x0000) +/******************************************** + * Init check + *******************************************/ +#define AW_PID_2113_INIT_CHECK_REG (0x73) +#define AW_PID_2113_INIT_CHECK_VALUE (0x1D40) + #endif /* #ifndef __AW_PID_2113_REG_H__ */ From 5fb3f91a67e784b1a715bc412851ed9e9725a25d Mon Sep 17 00:00:00 2001 From: liaohj Date: Tue, 14 Dec 2021 17:02:35 +0800 Subject: [PATCH 36/79] Aw882xxacf: update driver to V1.9.0.2 1. fix milanf (use params in acdb) adev_open take too long time issue. driver don't add profile relate control for not CONFIG_AW882XX_ALGO_BIN_PARAMS macro. 2.add ramping controler 3.add priority for profile. Change-Id: Ib53941065cab8af48d7b9dde9438d41761ec33a8 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2143951 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/Kbuild | 3 + asoc/codecs/aw882xxacf/aw882xx.c | 712 +++++++++++++++++++++++++++-- asoc/codecs/aw882xxacf/aw882xx.h | 79 ++++ asoc/codecs/aw882xxacf/aw_device.c | 19 +- asoc/codecs/aw882xxacf/aw_device.h | 2 + asoc/codecs/aw882xxacf/aw_init.c | 5 + 6 files changed, 767 insertions(+), 53 deletions(-) diff --git a/asoc/codecs/aw882xxacf/Kbuild b/asoc/codecs/aw882xxacf/Kbuild index 3a14a6cb3068..09054499d34f 100644 --- a/asoc/codecs/aw882xxacf/Kbuild +++ b/asoc/codecs/aw882xxacf/Kbuild @@ -9,6 +9,9 @@ ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA), y) EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA endif +ifeq ($(CONFIG_AW882XX_PARAMS_FROM_BIN), y) + EXTRA_CFLAGS += -DCONFIG_AW882XX_ALGO_BIN_PARAMS +endif aw882xx_acf-objs += aw882xx.o aw882xx_acf-objs += aw_bin_parse.o diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 74ee9c6e2492..709edb7c8ebc 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.8.0.1" +#define AW882XX_DRIVER_VERSION "v1.9.0.2" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -56,7 +56,80 @@ static unsigned int g_runin_test; static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; + +#define AW882XX_MOTO_MAX_GAIN (127) + +#define AW882XX_SCENE_CALIBRATION_ID 0 +#define AW882XX_SCENE_BYPASS_ID 1 +#define AW882XX_SCENE_HANDSET_ID 2 +#define AW882XX_SCENE_VOICE_ID 3 +#define AW882XX_SCENE_VOIP_ID 4 +#define AW882XX_SCENE_DEEPBUFFER_ID 5 +#define AW882XX_SCENE_FASTTRACK_ID 6 + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { + { + AW882XX_SCENE_CALIBRATION_ID, + "Calibration", + 1, + 1, + 0, + 0, + }, + { + AW882XX_SCENE_BYPASS_ID, + "Bypass", + 2, + 2, + 0, + 0, + }, + { + AW882XX_SCENE_HANDSET_ID, + "Handset", + 3, + 3, + 0, + 0, + }, + { + AW882XX_SCENE_VOICE_ID, + "Voice", + 4, + 4, + 0, + 0, + }, + { + AW882XX_SCENE_VOIP_ID, + "Voip", + 5, + 5, + 0, + 0, + }, + { + AW882XX_SCENE_DEEPBUFFER_ID, + "deepbuffer", + 6, + 6, + 0, + 0, + }, + { + AW882XX_SCENE_FASTTRACK_ID, + "fasttrack", + 7, + 7, + 0, + 0, + }, +}; +#endif + static const char *const switch_status[] = { "Off", "On" }; +static const char *const aw882xx_ramp_status[] = { "Off", "On" }; static const char *const aw882xx_switch[] = {"Disable", "Enable"}; #ifdef AW_SPIN_ENABLE static const char *const aw882xx_spin[] = {"spin_0", "spin_90", @@ -621,12 +694,137 @@ static int aw882xx_switch_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_ramp_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = 2; + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + strlcpy(uinfo->value.enumerated.name, + aw882xx_ramp_status[uinfo->value.enumerated.item], + strlen(aw882xx_ramp_status[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw882xx_ramp_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int *ramp_status = &aw882xx->aw882xx_ramp_status; + + aw_dev_dbg(aw882xx->dev, "%s: aw_dev_ramp_status_switch=%d\n", + __func__, *ramp_status); + + ucontrol->value.integer.value[0] = *ramp_status; + + return 0; +} + +static int aw882xx_ramp_status_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int *ramp_status = &aw882xx->aw882xx_ramp_status; + + aw_dev_dbg(aw882xx->dev, "%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + *ramp_status = ucontrol->value.integer.value[0]; + + if (*ramp_status == AW882XX_RAMP_ON) + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_ON; + else if (*ramp_status == AW882XX_RAMP_OFF) + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; + + return 0; +} + + +int aw882xx_dev_gain_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = AW882XX_MOTO_MAX_GAIN; + return 0; +} + +/* aw882xx gain ctl */ +static int aw882xx_dev_gain_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + unsigned int volume = 0; + aw_dev->ops.aw_get_volume(aw_dev, &volume); + + ucontrol->value.integer.value[0] = aw_dev->cur_gain; + + aw_dev_info(aw882xx->dev, "cur gain = %d, volume = %d", + aw_dev->cur_gain, volume); + + return 0; +} + +static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + int value = 0; + int aw882xx_volume = 0; + + /* Note: The larger the volume value is, the smaller the actual volume */ + int useful_range = desc->mute_volume - desc->init_volume; + + aw_dev_info(aw882xx->dev, "desc->init_volume = %d mute_volume = %d", + desc->init_volume, desc->mute_volume ); + + if (ucontrol->value.integer.value[0] > AW882XX_MOTO_MAX_GAIN) { + aw_dev_err(aw882xx->dev, "set val %ld overflow %d", + ucontrol->value.integer.value[0], AW882XX_MOTO_MAX_GAIN); + return 0; + } + + aw_dev->cur_gain = ucontrol->value.integer.value[0]; + + /* hal gain map to aw882xx valume value */ + value = ucontrol->value.integer.value[0]; + aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + + desc->init_volume; + + aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); + aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); + + return 0; +} + static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) { struct snd_kcontrol_new *aw882xx_dev_control = NULL; char *kctl_name; - aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 2, GFP_KERNEL); + aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 4, GFP_KERNEL); if (aw882xx_dev_control == NULL) { aw_dev_err(aw882xx->codec->dev, "kcontrol malloc failed!"); return -ENOMEM; @@ -656,8 +854,35 @@ static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) aw882xx_dev_control[1].get = aw882xx_switch_get; aw882xx_dev_control[1].put = aw882xx_switch_set; + //ramp control + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_ramp_status_switch", aw882xx->index); + + aw882xx_dev_control[2].name = kctl_name; + aw882xx_dev_control[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[2].info = aw882xx_ramp_status_info; + aw882xx_dev_control[2].get = aw882xx_ramp_status_get; + aw882xx_dev_control[2].put = aw882xx_ramp_status_set; + //end ramp control + + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_gain_ctl", aw882xx->index); + + aw882xx_dev_control[3].name = kctl_name; + aw882xx_dev_control[3].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[3].info = aw882xx_dev_gain_ctl_info; + aw882xx_dev_control[3].get = aw882xx_dev_gain_ctl_get; + aw882xx_dev_control[3].put = aw882xx_dev_gain_ctl_set; + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, - aw882xx_dev_control, 2); + aw882xx_dev_control, 4); return 0; } @@ -1074,50 +1299,6 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, } #endif -static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct aw_device *aw_dev; - aw_snd_soc_codec_t *codec = - aw_componet_codec_ops.kcontrol_codec(kcontrol); - struct aw882xx *aw882xx = - aw_componet_codec_ops.codec_get_drvdata(codec); - int ctrl_value; - - aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", - ucontrol->value.integer.value[0]); - - aw_dev = aw882xx->aw_pa; - - ctrl_value = ucontrol->value.integer.value[0]; - - aw_dev_set_algo_prof(aw_dev, ctrl_value); - - return 0; -} - -static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct aw_device *aw_dev; - aw_snd_soc_codec_t *codec = - aw_componet_codec_ops.kcontrol_codec(kcontrol); - struct aw882xx *aw882xx = - aw_componet_codec_ops.codec_get_drvdata(codec); - int ctrl_value; - - aw_dev = aw882xx->aw_pa; - - aw_dev_get_algo_prof(aw_dev, &ctrl_value); - - ucontrol->value.integer.value[0] = ctrl_value; - - aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", - ucontrol->value.integer.value[0]); - - return 0; -} - static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1241,7 +1422,7 @@ static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, struct aw_device *aw_dev = aw882xx->aw_pa; struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; - pr_debug("%s: aw882xx_monitor_get %d\n", + pr_debug("%s: aw882xx_monitor_get %d", __func__, monitor_cfg->monitor_switch); ucontrol->value.integer.value[0] = monitor_cfg->monitor_switch; @@ -1274,6 +1455,421 @@ static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, return 0; } +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + aw_dev_set_algo_prof(aw_dev, ctrl_value); + + return 0; +} +static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, get algo prof failed"); + } + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) +{ + int index = -1; + int i = 0; + + /* Get the highest priority array subscript from all active scene */ + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + if (aw882xx_scene_state[i].is_active) { + if (index < 0) { + index = i; + } else { + if(aw882xx_scene_state[i].priority < + aw882xx_scene_state[index].priority) { + index = i; + } + } + } + } + + if (index >= 0) { + return aw882xx_scene_state[index].skt_profile_id; + } + + return AW_ALGO_PROFILE_ID_0; +} + +static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, + int scene_id, bool is_active) { + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[scene_id]; + + if (is_active) { + p_aw882xx_scene_st->active_cnt++; + p_aw882xx_scene_st->is_active = 1; + } else { + p_aw882xx_scene_st->active_cnt--; + if (p_aw882xx_scene_st->active_cnt < 0) { + p_aw882xx_scene_st->active_cnt = 0; + } + if (p_aw882xx_scene_st->active_cnt == 0) { + p_aw882xx_scene_st->is_active = 0; + } + } +} + + +static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) +{ + int ret = 0; + int new_skt_prof_id = 0; + int cur_skt_prof_id = aw882xx->cur_algo_prof_id; + struct aw_device *aw_dev = NULL; + + aw_dev_info(aw882xx->dev, "enter"); + + new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); + if (new_skt_prof_id > AW_ALGO_PROFILE_ID_MAX) { + /* no active scene */ + aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); + return 0; + } + + if (cur_skt_prof_id != new_skt_prof_id) { + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + new_skt_prof_id, cur_skt_prof_id); + aw882xx->cur_algo_prof_id = new_skt_prof_id; + + /* set new scene pramas to skt */ + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; + } + } else { + aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", + new_skt_prof_id); + } + + return 0; + +} + +static int aw882xx_algo_dump_scene_st(struct aw882xx *aw882xx) +{ + int i = 0; + + aw_dev_info(aw882xx->dev, "aw882xx cur algo prof id =%d", + aw882xx->cur_algo_prof_id); + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + aw_dev_info(aw882xx->dev, "scene[%s] %s active_cnt %d", + aw882xx_scene_state[i].name, + aw882xx_scene_state[i].is_active? "[active]" : "[disactive]", + aw882xx_scene_state[i].active_cnt); + } + return 0; +} + +static int aw882xx_get_algo_cali_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_CALIBRATION_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_set_algo_cali_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_CALIBRATION_ID, is_active); + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_bypass_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_BYPASS_ID]; + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + return 0; +} + +static int aw882xx_set_algo_bypass_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_BYPASS_ID, is_active); + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_handset_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_HANDSET_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_handset_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_HANDSET_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_voice_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_VOICE_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_voice_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_VOICE_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_voip_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_VOIP_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_voip_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_VOIP_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_DEEPBUFFER_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_DEEPBUFFER_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_FASTTRACK_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_FASTTRACK_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} +#endif + static const struct soc_enum aw882xx_snd_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), @@ -1303,8 +1899,26 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS SOC_SINGLE_EXT("aw882xx_algo_prof_switch", 0, 0, 100, 0, aw882xx_get_prof_id, aw882xx_set_prof_id), + + /* for skt scene param switch */ + SOC_ENUM_EXT("aw882xx_algo_cali_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_cali_switch, aw882xx_set_algo_cali_switch), + SOC_ENUM_EXT("aw882xx_algo_bypass_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_bypass_switch, aw882xx_set_algo_bypass_switch), + SOC_ENUM_EXT("aw882xx_algo_handset_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_handset_switch, aw882xx_set_algo_handset_switch), + SOC_ENUM_EXT("aw882xx_algo_voice_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_voice_switch, aw882xx_set_algo_voice_switch), + SOC_ENUM_EXT("aw882xx_algo_voip_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_voip_switch, aw882xx_set_algo_voip_switch), + SOC_ENUM_EXT("aw882xx_algo_music_deepbuffer_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_music_deepbuffer_switch, aw882xx_set_algo_music_deepbuffer_switch), + SOC_ENUM_EXT("aw882xx_algo_music_fasttrack_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_music_fasttrack_switch, aw882xx_set_algo_music_fasttrack_switch), +#endif }; static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) @@ -2372,6 +2986,8 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, aw882xx->i2c_packet.reg_data = NULL; aw882xx->index = g_aw882xx_dev_cnt; + + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; #ifdef AW882XX_RUNIN_TEST g_runin_test = 0; INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h index d50a66456c27..1360b864097c 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.h +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -49,6 +49,29 @@ enum aw882xx_int_type { INT_TYPE_OTHI = 0x8, }; +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +enum { + AW_ALGO_PROFILE_ID_0 = 0, + AW_ALGO_PROFILE_ID_1, + AW_ALGO_PROFILE_ID_2, + AW_ALGO_PROFILE_ID_3, + AW_ALGO_PROFILE_ID_4, + AW_ALGO_PROFILE_ID_5, + AW_ALGO_PROFILE_ID_6, + AW_ALGO_PROFILE_ID_MAX, +}; + +struct aw882xx_scene_info { + int id; + char name[20]; + int priority; + int skt_profile_id; + int is_active; + int active_cnt; +}; +#endif + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) #define AW_KERNEL_VER_OVER_4_19_1 #endif @@ -110,6 +133,49 @@ enum { AW_BOP_ENABLE, }; +enum { + AW882XX_RAMP_OFF = 0, + AW882XX_RAMP_ON, +}; + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +enum { + AW_ALGO_CALI_OFF = 0, + AW_ALGO_CALI_ON, +}; + +enum { + AW_ALGO_BYPASS_OFF = 0, + AW_ALGO_BYPASS_ON, +}; + +enum { + AW_ALGO_HANDSET_OFF = 0, + AW_ALGO_HANDSET_ON, +}; + +enum { + AW_ALGO_VOICE_OFF = 0, + AW_ALGO_VOICE_ON, +}; + +enum { + AW_ALGO_VOIP_OFF = 0, + AW_ALGO_VOIP_ON, +}; + +enum { + AW_ALGO_MUSIC_DEEPBUFFER_OFF = 0, + AW_ALGO_MUSIC_DEEPBUFFER_ON, +}; + +enum { + AW_ALGO_MUSIC_FASTTRACK_OFF = 0, + AW_ALGO_MUSIC_FASTTRACK_ON, +}; +#endif + + enum { AWRW_HDR_WR_FLAG = 0, AWRW_HDR_ADDR_BYTES, @@ -134,6 +200,19 @@ struct aw882xx { int rate; int pstream; int cstream; + int aw882xx_ramp_status; /* ramp status */ +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + int aw882xx_algo_cali; + int aw882xx_algo_bypass; + int aw882xx_algo_handset; + int aw882xx_algo_voice; + int aw882xx_algo_voip; + int aw882xx_algo_music_deepbuffer; + int aw882xx_algo_music_fasttrack; + int aw882xx_algo_prof_id; + int cur_algo_prof_id; +#endif + unsigned int fade_flag; unsigned char index; diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index 25c036e22cd8..bb2608e2d5d0 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -41,6 +41,7 @@ static char *profile_name[AW_PROFILE_MAX] = { static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ static unsigned int g_fade_in_time = AW_1000_US / 10; + static unsigned int g_fade_out_time = AW_1000_US >> 1; static LIST_HEAD(g_dev_list); static DEFINE_MUTEX(g_dev_lock); @@ -763,7 +764,7 @@ static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) static void aw_dev_mute(struct aw_device *aw_dev, bool mute) { struct aw_mute_desc *mute_desc = &aw_dev->mute_desc; - + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; aw_dev_dbg(aw_dev->dev, "enter"); if (mute) { @@ -772,10 +773,18 @@ static void aw_dev_mute(struct aw_device *aw_dev, bool mute) mute_desc->mask, mute_desc->enable); } else { - aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, - mute_desc->mask, - mute_desc->disable); - aw_dev_fade_in(aw_dev); + if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) { + g_fade_in_time = AW_1000_US / 10; + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } } aw_dev_info(aw_dev->dev, "done"); } diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 7b7315d24555..0629d9e71968 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -211,6 +211,8 @@ struct aw_device { unsigned int re_max; unsigned int re_min; + int cur_gain; + struct device *dev; struct i2c_client *i2c; char acf_name[AW_NAME_MAX]; diff --git a/asoc/codecs/aw882xxacf/aw_init.c b/asoc/codecs/aw882xxacf/aw_init.c index 15a067cef503..dfc112442d92 100644 --- a/asoc/codecs/aw882xxacf/aw_init.c +++ b/asoc/codecs/aw882xxacf/aw_init.c @@ -1068,6 +1068,11 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) AW_PID_2055_INIT_CHECK_VALUE); usleep_range(AW_3000_US, AW_3000_US +10); + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, + AW_PID_2055_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + ret = aw_device_probe(aw_pa); aw882xx->aw_pa = aw_pa; From 846f446d07947ce0d0c0a9f391f8840e681a4a4d Mon Sep 17 00:00:00 2001 From: liaohj Date: Mon, 27 Dec 2021 09:31:23 +0800 Subject: [PATCH 37/79] codec:aw882xxacd driver v1.9.04 1.fix null point issue. 2.add spin function code. Change-Id: I68cd07bcad3d41499aee8cc4348eea0fd413c171 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2154549 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 157 ++++++++++++++++++++++++++--- asoc/codecs/aw882xxacf/aw_device.c | 12 +++ asoc/codecs/aw882xxacf/aw_device.h | 3 + asoc/codecs/aw882xxacf/aw_dsp.c | 50 +++++++++ asoc/codecs/aw882xxacf/aw_dsp.h | 12 ++- 5 files changed, 217 insertions(+), 17 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 709edb7c8ebc..2c0f54902a5f 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.2" +#define AW882XX_DRIVER_VERSION "v1.9.0.4" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -48,6 +48,7 @@ static unsigned int g_algo_tx_en = false; static unsigned int g_algo_copp_en = false; #ifdef AW_SPIN_ENABLE static unsigned int g_spin_value = 0; +static uint32_t g_spin_en = 0; #endif #ifdef AW882XX_RUNIN_TEST static unsigned int g_runin_test; @@ -1297,6 +1298,126 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); return 0; } + +static int aw882xx_get_spin_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin_param(aw_dev, &ctrl_value, spin_param->relase_time); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin status failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + if ((aw882xx->pstream) && (g_algo_rx_en == true) ) { + ret = aw_dev_set_spin_param(aw_dev, ctrl_value, spin_param->relase_time); + if (ret) + aw_dev_err(aw882xx->dev, "set spin status error, ret=%d", ret); + + g_spin_en = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "stream no start only record"); + } + + return 0; +} + +static int aw882xx_get_spin_relase_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin_param(aw_dev, spin_param->enable, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin release time failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + if ((aw882xx->pstream) && (g_spin_en == 1) && (g_algo_rx_en == true)) { + ret = aw_dev_set_spin_param(aw_dev, g_spin_en , ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin release time error, ret=%d", ret); + } else { + aw_dev_info(aw882xx->dev, "set release time failed"); + } + + return 0; +} + #endif static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, @@ -1530,7 +1651,7 @@ static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) return aw882xx_scene_state[index].skt_profile_id; } - return AW_ALGO_PROFILE_ID_0; + return 0; } static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, @@ -1562,29 +1683,29 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev_info(aw882xx->dev, "enter"); + aw_dev = aw882xx->aw_pa; + new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); - if (new_skt_prof_id > AW_ALGO_PROFILE_ID_MAX) { + if (new_skt_prof_id < AW_ALGO_PROFILE_ID_1) { /* no active scene */ aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); return 0; } - if (cur_skt_prof_id != new_skt_prof_id) { - aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", - new_skt_prof_id, cur_skt_prof_id); - aw882xx->cur_algo_prof_id = new_skt_prof_id; + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + new_skt_prof_id, cur_skt_prof_id); + aw882xx->cur_algo_prof_id = new_skt_prof_id; - /* set new scene pramas to skt */ - ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); - if (ret < 0) { - aw_dev_err(aw882xx->dev, "set algo prof failed"); - return -1; - } - } else { - aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", - new_skt_prof_id); + /* set new scene pramas to skt */ + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; } + aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", + new_skt_prof_id); + return 0; } @@ -1888,6 +2009,10 @@ static struct snd_kcontrol_new aw882xx_controls[] = { #ifdef AW_SPIN_ENABLE SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[2], aw882xx_get_spin, aw882xx_set_spin), + SOC_ENUM_EXT("aw882xx_spin_status", aw882xx_snd_enum[1], + aw882xx_get_spin_status, aw882xx_set_spin_status), + SOC_ENUM_EXT("aw882xx_spin_relase_time", 0, 0, 1000000, 0, + aw882xx_get_spin_relase_time, aw882xx_set_spin_relase_time), #endif #ifdef AW882XX_RUNIN_TEST SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[1], diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index bb2608e2d5d0..0fa87fd45b0d 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -1494,6 +1494,18 @@ int aw_dev_set_algo_params_path(struct aw_device *aw_dev) return aw_dsp_set_algo_params_path(aw_dev); } +#ifdef AW_SPIN_ENABLE +int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time) +{ + return aw_dsp_set_spin_param(aw_dev, enable, relase_time); +} + +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time) +{ + return aw_dsp_get_spin_param(aw_dev, enable, relase_time); +} +#endif + static void aw_device_parse_sound_channel_dt(struct aw_device *aw_dev) { int ret; diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 0629d9e71968..80df939a1e81 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -289,6 +289,9 @@ int aw_dev_set_algo_params_path(struct aw_device *aw_dev); int aw_device_probe(struct aw_device *aw_dev); int aw_device_remove(struct aw_device *aw_dev); int aw_dev_get_list_head(struct list_head **head); +#ifdef AW_SPIN_ENABLE +int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time); +#endif #endif diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c index eabb7764526f..4b905c141a55 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.c +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -48,6 +48,7 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_REAL_DATA_L (0X10013D21) #define AW_MSG_ID_REAL_DATA_R (0X10013D22) #define AW_MSG_ID_SET_BIN_PARAMS (0X10013D30) +#define AW_MSG_ID_SPIN_ENABLE (0x10013D2F) #define AFE_MSG_ID_MSG_0 (0X10013D2A) #define AFE_MSG_ID_MSG_1 (0X10013D2B) @@ -555,6 +556,55 @@ int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te) return 0; } +#ifdef AW_SPIN_ENABLE +int aw_dsp_get_spin_param(struct aw_device *aw_dev, + uint32_t *enable, uint32_t *relase_time) +{ + int ret; + struct aw_spin_param spin_param; + + ret = aw_read_data_from_dsp(AW_MSG_ID_SPIN_ENABLE, + &spin_param, + sizeof(spin_param)); + if (ret) { + *spin_param.enable = 0; + *spin_param.relase_time = 0; + aw_pr_err("read spin params failed "); + return ret; + } + + *spin_param.enable = spin_param->enable; + *spin_param.relase_time = spin_param->relase_time; + + aw_pr_info("get spin params done"); + return 0; +} + +int aw_dsp_set_spin_param(struct aw_device *aw_dev, + uint32_t enable, uint32_t relase_time) +{ + int ret; + struct aw_spin_param spin_param; + + if (!enable) { + aw_pr_dbg("need to set the rotation parameters"); + return 0; + } + + spin_param.enable = enable; + spin_param.relase_time = relase_time; + + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN_ENABLE, + &spin_param, + sizeof(spin_param)); + if (ret) + return -EINVAL; + + aw_pr_info("set spin params failed"); + return 0; +} +#endif + int aw_dsp_read_spin(int *spin_mode) { int ret; diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h index aaf0089a0d92..b6cb47d46f8c 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.h +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -31,6 +31,13 @@ enum { #define AW_DSP_MSG_HDR_VER (1) typedef struct aw_msg_hdr aw_dsp_msg_t; +#ifdef AW_SPIN_ENABLE +struct aw_spin_param { + uint32_t enable; + uint32_t relase_time; +}; +#endif + int aw_dsp_write_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); int aw_dsp_read_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); @@ -61,6 +68,9 @@ int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); void aw_device_parse_port_id_dt(struct aw_device *aw_dev); - +#ifdef AW_SPIN_ENABLE +int aw_dsp_set_spin_param(struct aw_device *aw_dev, + uint32_t enable, uint32_t relase_time); +#endif #endif From 09d199978c5be80f8b36ac1b6ab3c29a607a80c8 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 27 Jan 2022 14:04:50 +0800 Subject: [PATCH 38/79] audio:aw882xx:add set dai name interface after reboot,sometimes i2c num change. so need to update dailink dai name. add set dai name interface. aw882xx ko depend on machine_dlkm ko. Change-Id: Ib00f61baea88d1d37dd699f98ac81287cebbfb58 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2180067 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/Android.mk | 3 +++ asoc/codecs/aw882xxacf/aw882xx.c | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw882xxacf/Android.mk b/asoc/codecs/aw882xxacf/Android.mk index b82a1f9a359a..309b2e8a5938 100644 --- a/asoc/codecs/aw882xxacf/Android.mk +++ b/asoc/codecs/aw882xxacf/Android.mk @@ -2,6 +2,9 @@ DLKM_DIR := $(TOP)/device/qcom/common/dlkm LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) + +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/machine_dlkm.ko + LOCAL_MODULE := aw882xx_acf.ko LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 2c0f54902a5f..f5584f63ab36 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -2054,14 +2054,14 @@ static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) &aw882xx_controls[0], ARRAY_SIZE(aw882xx_controls)); } -#ifdef AW_MTK_PLATFORM_WITH_DSP + static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name) { char buf[50]; int i2cbus = aw882xx->i2c->adapter->nr; int i2caddr = aw882xx->i2c->addr; - snprintf(buf, 50, "%s-%x-%x", *name, i2cbus, i2caddr); + snprintf(buf, 50, "%s.%x-00%x", *name, i2cbus, i2caddr); (*name) = aw882xx_devm_kstrdup(aw882xx->dev, buf); if (!(*name)) return -ENOMEM; @@ -2070,6 +2070,9 @@ static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name return 0; } + +#ifdef AW_MTK_PLATFORM_WITH_DSP + static const struct snd_soc_dapm_widget aw882xx_dapm_widgets[] = { /* playback */ SND_SOC_DAPM_AIF_IN("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0), @@ -2311,10 +2314,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aw882xx = { }; #endif +void awinic_set_dai_name(const char* drvdainame, const char*drvname); + int aw_componet_codec_register(struct aw882xx *aw882xx) { struct snd_soc_dai_driver *dai_drv; int ret; + char *aw882xxdrvname = AW882XX_I2C_NAME; + dai_drv = devm_kzalloc(aw882xx->dev, sizeof(aw882xx_dai), GFP_KERNEL); if (dai_drv == NULL) { aw_dev_err(aw882xx->dev, "dai_driver malloc failed"); @@ -2325,6 +2332,11 @@ int aw_componet_codec_register(struct aw882xx *aw882xx) #ifdef CONFIG_AW882XX_STEREO_SMARTPA aw882xx_dai_drv_append_suffix(aw882xx, dai_drv, ARRAY_SIZE(aw882xx_dai)); + //change dai name // + aw882xx_name_append_suffix(aw882xx, (const char**)&aw882xxdrvname); + awinic_set_dai_name(dai_drv->name, aw882xxdrvname); + aw_dev_err(aw882xx->dev, "dai name %s codec name %s", dai_drv->name,aw882xxdrvname); + //change dai name end// #endif ret = aw882xx->codec_ops->register_codec(aw882xx->dev, @@ -2334,6 +2346,7 @@ int aw_componet_codec_register(struct aw882xx *aw882xx) aw_dev_err(aw882xx->dev, "failed to register aw882xx: %d", ret); return -EINVAL; } + return 0; } /***************************************************** From 1a1ee3c1a5bb33ecaeafded864357e7e9e4c3259 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 27 Jan 2022 14:07:36 +0800 Subject: [PATCH 39/79] audio:update dailink name from aw882xx driver after reboot,sometimes i2c num changed. so need update dai name accored to actual sw i2c num. Change-Id: I0c1cea8b18fa2726db29819c7dec3c4f48eb608e Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2180071 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/holi.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/asoc/holi.c b/asoc/holi.c index 4297fe03cee8..1daa75d0473e 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -6182,6 +6182,42 @@ static const struct of_device_id holi_asoc_machine_of_match[] = { {}, }; +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +struct snd_soc_dai_link_component aw_codecs[4]; +static unsigned int aw_codecs_num; + +void awinic_set_dai_name(const char* drvdainame, const char*drvname) +{ + if (aw_codecs_num < ARRAY_SIZE(aw_codecs)) { + aw_codecs[aw_codecs_num].dai_name = drvdainame; + aw_codecs[aw_codecs_num].name = drvname; + aw_codecs[aw_codecs_num].of_node = NULL; + aw_codecs_num++; + } +} +EXPORT_SYMBOL(awinic_set_dai_name); + +static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) +{ + int i; + + if (aw_codecs_num != 0) { + for (i = 0; i < len1; i++) { + if((!strcmp(dailink[i].stream_name, "Secondary MI2S Playback")) || + (!strcmp(dailink[i].stream_name, "Secondary MI2S Capture"))){ + dailink[i].num_codecs = aw_codecs_num; + dailink[i].codecs = aw_codecs; + } + } + } else { + pr_err("aw_codecs is empty\n"); + return -EINVAL; + } + return 0; +} +#endif + + static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { struct snd_soc_card *card = NULL; @@ -6313,6 +6349,10 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) total_links = len_2; } +#ifdef CONFIG_AW882XX_STEREO_SMARTPA + aw_update_dai_link_name(dailink, total_links); +#endif + if (card) { card->dai_link = dailink; card->num_links = total_links; From 313d7159a613610a9e012741b69fd7ec70d7fcd7 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 27 Jan 2022 16:44:40 +0800 Subject: [PATCH 40/79] audio:aw882xxacf:fix single smart pa build fail aw882xx_devm_kstrdup is defined in CONFIG_AW882XX_STEREO_SMARTPA. aw882xx_name_append_suffix call aw882xx_devm_kstrdup. so aw882xx_devm_kstrdup should in CONFIG_AW882XX_STEREO_SMARTPA. Change-Id: I0b22e43316c6ede7599ad0393a99aa1b3a6fd40f Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2180292 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index f5584f63ab36..c8be339556ce 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -2054,7 +2054,7 @@ static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) &aw882xx_controls[0], ARRAY_SIZE(aw882xx_controls)); } - +#ifdef CONFIG_AW882XX_STEREO_SMARTPA static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name) { char buf[50]; @@ -2069,7 +2069,7 @@ static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name aw_dev_info(aw882xx->dev, "name is %s", (*name)); return 0; } - +#endif #ifdef AW_MTK_PLATFORM_WITH_DSP From 280b799bf1872d66d867f32c437dbed3356597e6 Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 28 Jan 2022 09:26:50 +0800 Subject: [PATCH 41/79] audio:aw882xx:fix single pa build fail. aw882xxdrvname & awinic_set_dai_name also defined in CONFIG_AW882XX_STEREO_SMARTPA to avoid single pa build fail. Change-Id: I9b17f9c8e1210195c03ebc5d3fdbbd69f19488d3 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2180925 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index c8be339556ce..0e36ded8a684 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -2314,13 +2314,17 @@ static struct snd_soc_codec_driver soc_codec_dev_aw882xx = { }; #endif +#ifdef CONFIG_AW882XX_STEREO_SMARTPA void awinic_set_dai_name(const char* drvdainame, const char*drvname); +#endif int aw_componet_codec_register(struct aw882xx *aw882xx) { struct snd_soc_dai_driver *dai_drv; int ret; +#ifdef CONFIG_AW882XX_STEREO_SMARTPA char *aw882xxdrvname = AW882XX_I2C_NAME; +#endif dai_drv = devm_kzalloc(aw882xx->dev, sizeof(aw882xx_dai), GFP_KERNEL); if (dai_drv == NULL) { From a1c7c897eecb83ccc3dc6f2a30ad4b08d233218c Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 24 Feb 2022 16:03:24 +0800 Subject: [PATCH 42/79] codec/aw882xx:fix build fail when spin enable 1.fix build fail when spin enable 2.enable spin function for AW882XX_STEREO_SMARTPA in driver Change-Id: If496d8c385e06cf82c02f3b56dfe0c2a506597cf Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2200224 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/Kbuild | 1 + asoc/codecs/aw882xxacf/aw882xx.c | 18 +++++++++--------- asoc/codecs/aw882xxacf/aw_device.c | 2 +- asoc/codecs/aw882xxacf/aw_device.h | 1 + asoc/codecs/aw882xxacf/aw_dsp.c | 15 +++++++-------- asoc/codecs/aw882xxacf/aw_dsp.h | 2 ++ 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/asoc/codecs/aw882xxacf/Kbuild b/asoc/codecs/aw882xxacf/Kbuild index 09054499d34f..067c9b3cd05e 100644 --- a/asoc/codecs/aw882xxacf/Kbuild +++ b/asoc/codecs/aw882xxacf/Kbuild @@ -7,6 +7,7 @@ EXTRA_CFLAGS += -DCONFIG_AW882XX_CODEC -DCONFIG_AW882XX_DSP ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA), y) EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA + EXTRA_CFLAGS += -DAW_SPIN_ENABLE endif ifeq ($(CONFIG_AW882XX_PARAMS_FROM_BIN), y) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 0e36ded8a684..874a25945591 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -1314,7 +1314,7 @@ static int aw882xx_get_spin_status(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; if (aw882xx->pstream) { - ret = aw_dev_get_spin_param(aw_dev, &ctrl_value, spin_param->relase_time); + ret = aw_dev_get_spin_param(aw_dev, &ctrl_value, &(spin_param.relase_time)); if (ret) { aw_dev_err(aw882xx->dev, "get spin status failed!, ret = %d", ret); ctrl_value = 0; @@ -1347,8 +1347,8 @@ static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, ctrl_value = ucontrol->value.integer.value[0]; - if ((aw882xx->pstream) && (g_algo_rx_en == true) ) { - ret = aw_dev_set_spin_param(aw_dev, ctrl_value, spin_param->relase_time); + if (g_algo_rx_en == true) { + ret = aw_dev_set_spin_param(aw_dev, ctrl_value, spin_param.relase_time); if (ret) aw_dev_err(aw882xx->dev, "set spin status error, ret=%d", ret); @@ -1375,7 +1375,7 @@ static int aw882xx_get_spin_relase_time(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; if (aw882xx->pstream) { - ret = aw_dev_get_spin_param(aw_dev, spin_param->enable, &ctrl_value); + ret = aw_dev_get_spin_param(aw_dev, &(spin_param.enable), &ctrl_value); if (ret) { aw_dev_err(aw882xx->dev, "get spin release time failed!, ret = %d", ret); ctrl_value = 0; @@ -1399,7 +1399,7 @@ static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); - struct aw_spin_param spin_param; + //struct aw_spin_param spin_param; aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); @@ -1407,7 +1407,8 @@ static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; - if ((aw882xx->pstream) && (g_spin_en == 1) && (g_algo_rx_en == true)) { + //if ((aw882xx->pstream) && (g_spin_en == 1) && (g_algo_rx_en == true)) { + if ( (g_spin_en == 1) && (g_algo_rx_en == true)) { ret = aw_dev_set_spin_param(aw_dev, g_spin_en , ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "set spin release time error, ret=%d", ret); @@ -1683,8 +1684,6 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev_info(aw882xx->dev, "enter"); - aw_dev = aw882xx->aw_pa; - new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); if (new_skt_prof_id < AW_ALGO_PROFILE_ID_1) { /* no active scene */ @@ -1695,6 +1694,7 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", new_skt_prof_id, cur_skt_prof_id); aw882xx->cur_algo_prof_id = new_skt_prof_id; + aw_dev = aw882xx->aw_pa; /* set new scene pramas to skt */ ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); @@ -2011,7 +2011,7 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_spin, aw882xx_set_spin), SOC_ENUM_EXT("aw882xx_spin_status", aw882xx_snd_enum[1], aw882xx_get_spin_status, aw882xx_set_spin_status), - SOC_ENUM_EXT("aw882xx_spin_relase_time", 0, 0, 1000000, 0, + SOC_SINGLE_EXT("aw882xx_spin_relase_time", 0, 0, 1000000, 0, aw882xx_get_spin_relase_time, aw882xx_set_spin_relase_time), #endif #ifdef AW882XX_RUNIN_TEST diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index 0fa87fd45b0d..a4758c3a2102 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -1500,7 +1500,7 @@ int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t re return aw_dsp_set_spin_param(aw_dev, enable, relase_time); } -int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time) +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t *enable, uint32_t *relase_time) { return aw_dsp_get_spin_param(aw_dev, enable, relase_time); } diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 80df939a1e81..2b181a59e030 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -291,6 +291,7 @@ int aw_device_remove(struct aw_device *aw_dev); int aw_dev_get_list_head(struct list_head **head); #ifdef AW_SPIN_ENABLE int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time); +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t* enable, uint32_t* relase_time); #endif #endif diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c index 4b905c141a55..ed2425d92dc6 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.c +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -567,16 +567,15 @@ int aw_dsp_get_spin_param(struct aw_device *aw_dev, &spin_param, sizeof(spin_param)); if (ret) { - *spin_param.enable = 0; - *spin_param.relase_time = 0; + spin_param.enable = 0; + spin_param.relase_time = 0; aw_pr_err("read spin params failed "); return ret; } - *spin_param.enable = spin_param->enable; - *spin_param.relase_time = spin_param->relase_time; + *enable = spin_param.enable; + *relase_time = spin_param.relase_time; - aw_pr_info("get spin params done"); return 0; } @@ -587,7 +586,6 @@ int aw_dsp_set_spin_param(struct aw_device *aw_dev, struct aw_spin_param spin_param; if (!enable) { - aw_pr_dbg("need to set the rotation parameters"); return 0; } @@ -597,10 +595,11 @@ int aw_dsp_set_spin_param(struct aw_device *aw_dev, ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN_ENABLE, &spin_param, sizeof(spin_param)); - if (ret) + if (ret){ + pr_err("set spin params failed\n"); return -EINVAL; + } - aw_pr_info("set spin params failed"); return 0; } #endif diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h index b6cb47d46f8c..b5c30838d4d6 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.h +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -71,6 +71,8 @@ void aw_device_parse_port_id_dt(struct aw_device *aw_dev); #ifdef AW_SPIN_ENABLE int aw_dsp_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time); +int aw_dsp_get_spin_param(struct aw_device *aw_dev, + uint32_t *enable, uint32_t *relase_time); #endif #endif From fd33eb2be8b921d8e46a7f4f441bc93257ff0f8d Mon Sep 17 00:00:00 2001 From: liaohj Date: Wed, 9 Mar 2022 14:30:44 +0800 Subject: [PATCH 43/79] audio:aw882xx:update driver to v1.9.0.5 update to driver v1.9.0.5 release from aw fae luofuhong optimize init code. Change-Id: I8c4ec722f526f2caecd98dfe76e36e382cab36e8 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2211100 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 2 +- asoc/codecs/aw882xxacf/aw_device.c | 232 +++++++++++++---------- asoc/codecs/aw882xxacf/aw_device.h | 16 ++ asoc/codecs/aw882xxacf/aw_init.c | 24 ++- asoc/codecs/aw882xxacf/aw_pid_2113_reg.h | 11 ++ 5 files changed, 176 insertions(+), 109 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 874a25945591..c55a78290c88 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.4" +#define AW882XX_DRIVER_VERSION "v1.9.0.5" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index a4758c3a2102..c2d768060f8c 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -570,6 +570,113 @@ static char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index) return 0; }*/ +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + uint16_t reg_icalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); + reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); + else + reg_icalk = (reg_icalk >> desc->icalk_shift) & (reg_icalkl >> desc->icalkl_shift); + } + + if (reg_icalk & (~desc->icalk_sign_mask)) + reg_icalk = reg_icalk | (~desc->icalk_neg_mask); + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + uint16_t reg_vcalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->vcalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); + reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); + else + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) & (reg_vcalkl >> desc->vcalkl_shift); + } + + if (reg_vcalk & (~desc->vcalk_sign_mask)) + reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { + aw_dev_info(aw_dev->dev, "REG None!"); + return 0; + } + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret < 0) + return ret; + + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + + icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; + if (!vcalk) { + aw_dev_err(aw_dev->dev, "vcalk is 0"); + return -EINVAL; + } + + vcalb = desc->vcal_factor * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); + + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + + + /*pwd enable update reg*/ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) { @@ -579,6 +686,7 @@ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) unsigned int read_val; int ret = -1; unsigned int init_volume = 0; + unsigned int efcheck_val = 0; struct aw_int_desc *int_desc = &aw_dev->int_desc; struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; @@ -655,6 +763,16 @@ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) reg_val |= read_val; } + if (reg_addr == aw_dev->efcheck_desc.reg) { + efcheck_val = reg_val & (~aw_dev->efcheck_desc.mask); + if (efcheck_val == aw_dev->efcheck_desc.or_val) + aw_dev->efuse_check = AW_EF_OR_CHECK; + else + aw_dev->efuse_check = AW_EF_AND_CHECK; + + aw_dev_info(aw_dev->dev, "efuse check: %d", aw_dev->efuse_check); + } + if (reg_addr == aw_dev->vcalb_desc.vcalb_reg) continue; @@ -667,6 +785,12 @@ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) break; } + ret = aw_dev_set_vcalb(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "can't set vcalb"); + return ret; + } + aw_dev->ops.aw_get_volume(aw_dev, &init_volume); aw_dev->volume_desc.init_volume = init_volume; @@ -789,104 +913,6 @@ static void aw_dev_mute(struct aw_device *aw_dev, bool mute) aw_dev_info(aw_dev->dev, "done"); } -static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) -{ - int ret = -1; - unsigned int reg_val = 0; - uint16_t reg_icalk = 0; - uint16_t reg_icalkl = 0; - struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; - - if (desc->icalkl_reg == AW_REG_NONE) { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); - reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); - } else { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); - reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); - reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); - reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); - } - - if (reg_icalk & (~desc->icalk_sign_mask)) - reg_icalk = reg_icalk | (~desc->icalk_neg_mask); - - *icalk = (int16_t)reg_icalk; - - return ret; -} - -static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) -{ - int ret = -1; - unsigned int reg_val = 0; - uint16_t reg_vcalk = 0; - uint16_t reg_vcalkl = 0; - struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; - - if (desc->vcalkl_reg == AW_REG_NONE) { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); - reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); - } else { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); - reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); - reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); - reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); - } - - if (reg_vcalk & (~desc->vcalk_sign_mask)) - reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); - - *vcalk = (int16_t)reg_vcalk; - - return ret; -} - -static int aw_dev_set_vcalb(struct aw_device *aw_dev) -{ - int ret = -1; - unsigned int reg_val; - int vcalb; - int icalk; - int vcalk; - int16_t icalk_val = 0; - int16_t vcalk_val = 0; - - struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; - - if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { - aw_dev_info(aw_dev->dev, "REG None!"); - return 0; - } - - ret = aw_dev_get_icalk(aw_dev, &icalk_val); - if (ret < 0) - return ret; - - ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); - if (ret < 0) - return ret; - - icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; - vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; - if (!vcalk) { - aw_dev_err(aw_dev->dev, "vcalk is 0"); - return -EINVAL; - } - - vcalb = desc->vcal_factor * icalk / vcalk; - - reg_val = (unsigned int)vcalb; - aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", - icalk, vcalk, vcalb, reg_val); - - ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); - - aw_dev_info(aw_dev->dev, "done"); - - return ret; -} int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status) { @@ -1186,8 +1212,6 @@ int aw_device_irq_reinit(struct aw_device *aw_dev) if (ret < 0) return ret; - /*update vcalb*/ - aw_dev_set_vcalb(aw_dev); return 0; } @@ -1217,10 +1241,10 @@ int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) if (ret < 0) return ret; - ret = aw_dev_set_vcalb(aw_dev); - if (ret < 0) { - aw_dev_err(aw_dev->dev, "can't set vcalb"); - return ret; + if (aw_dev->ops.aw_frcset_check) { + ret = aw_dev->ops.aw_frcset_check(aw_dev); + if (ret) + return ret; } aw_dev->status = AW_DEV_PW_ON; diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 2b181a59e030..4c894a4f1001 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -28,6 +28,12 @@ enum { AW_DEV_TYPE_NONE = 1, }; + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + enum { AW_DEV_CH_PRI_L = 0, AW_DEV_CH_PRI_R = 1, @@ -68,6 +74,7 @@ struct aw_device_ops { void (*aw_set_algo)(struct aw_device *aw_dev); unsigned int (*aw_get_irq_type)(struct aw_device *aw_dev, unsigned int value); void (*aw_reg_force_set)(struct aw_device *aw_dev); + int (*aw_frcset_check)(struct aw_device *aw_dev); }; struct aw_int_desc { @@ -195,12 +202,20 @@ struct aw_container { uint8_t data[]; }; +struct aw_efcheck_desc { + unsigned int reg; + unsigned int mask; + unsigned int and_val; + unsigned int or_val; +}; + struct aw_device { int index; int status; int bstcfg_enable; int frcset_en; int bop_en; + int efuse_check; unsigned int mute_st; unsigned int amppd_st; @@ -238,6 +253,7 @@ struct aw_device { struct aw_monitor_desc monitor_desc; struct aw_soft_rst soft_rst; struct aw_bop_desc bop_desc; + struct aw_efcheck_desc efcheck_desc; struct aw_device_ops ops; struct list_head list_node; }; diff --git a/asoc/codecs/aw882xxacf/aw_init.c b/asoc/codecs/aw882xxacf/aw_init.c index dfc112442d92..e42f983d2d6d 100644 --- a/asoc/codecs/aw882xxacf/aw_init.c +++ b/asoc/codecs/aw882xxacf/aw_init.c @@ -311,6 +311,7 @@ static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_1852_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_1852_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -595,6 +596,7 @@ static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_2013_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2013_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -851,6 +853,7 @@ static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_2032_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2032_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1059,6 +1062,7 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->cco_mux_desc.bypass_val = AW_PID_2055_CCO_MUX_BYPASS_VALUE; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1321,6 +1325,7 @@ static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_2071_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2071_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1462,11 +1467,12 @@ static unsigned int aw_pid_2113_get_irq_type(struct aw_device *aw_dev, return ret; } -static void aw_pid_2113_efver_check(struct aw_device *aw_dev) +static int aw_pid_2113_frcset_check(struct aw_device *aw_dev) { unsigned int reg_val = 0; uint16_t temh = 0; uint16_t teml = 0; + uint16_t tem = 0; aw_dev->ops.aw_i2c_read(aw_dev, AW_PID_2113_EFRH3_REG, ®_val); @@ -1476,13 +1482,19 @@ static void aw_pid_2113_efver_check(struct aw_device *aw_dev) AW_PID_2113_EFRL3_REG, ®_val); teml = ((uint16_t)reg_val & (~AW_PID_2113_TEML_MASK)); - if ((temh | teml) == AW_PID_2113_DEFAULT_CFG) + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + tem = (temh | teml); + else + tem = (temh & teml); + + if (tem == AW_PID_2113_DEFAULT_CFG) aw_dev->frcset_en = AW_FRCSET_ENABLE; else aw_dev->frcset_en = AW_FRCSET_DISABLE; aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcset_en is %d", - (temh | teml), aw_dev->frcset_en); + tem, aw_dev->frcset_en); + return 0; } static void aw_pid_2113_reg_force_set(struct aw_device *aw_dev) @@ -1556,6 +1568,7 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->ops.aw_get_reg_num = aw_pid_2113_get_reg_num; aw_pa->ops.aw_get_irq_type = aw_pid_2113_get_irq_type; aw_pa->ops.aw_reg_force_set = aw_pid_2113_reg_force_set; + aw_pa->ops.aw_frcset_check = aw_pid_2113_frcset_check; aw_pa->int_desc.mask_reg = AW_PID_2113_SYSINTM_REG; aw_pa->int_desc.mask_default = AW_PID_2113_SYSINTM_DEFAULT; @@ -1640,7 +1653,10 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) AW_PID_2113_INIT_CHECK_VALUE); usleep_range(AW_3000_US, AW_3000_US +10); - aw_pid_2113_efver_check(aw_pa); + aw_pa->efcheck_desc.reg = AW_PID_2113_DBGCTRL_REG; + aw_pa->efcheck_desc.mask = AW_PID_2113_EF_DBMD_MASK; + aw_pa->efcheck_desc.and_val = AW_PID_2113_AND_VALUE; + aw_pa->efcheck_desc.or_val = AW_PID_2113_OR_VALUE; ret = aw_device_probe(aw_pa); diff --git a/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h b/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h index b2146b1b20d3..d3c9a35af9dc 100644 --- a/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h +++ b/asoc/codecs/aw882xxacf/aw_pid_2113_reg.h @@ -2270,6 +2270,17 @@ const unsigned char aw_pid_2113_reg_access[AW_PID_2113_REG_MAX] = { /* detail information of registers end */ + + +/******************************************** + * Efdbmd + *******************************************/ +#define AW_PID_2113_EF_DBMD_MASK (0xfff7) +#define AW_PID_2113_AND_VALUE (0x0000) +#define AW_PID_2113_OR_VALUE (0x0008) + + + /******************************************** * Volume Coefficient *******************************************/ From 1074cf1c8109b84efb0669e0af047ce55c87fc9b Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 24 Mar 2022 10:42:13 +0800 Subject: [PATCH 44/79] codec:aw882xx:update driver to v1.9.0.6 update driver to v1.9.0.6 fix" when scene change. ramp change the gain incorrect issue." Change-Id: I65c27d3b0708d805c951b1422b42b15b2dad40ad Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2224702 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index c55a78290c88..1a5008f6f592 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.5" +#define AW882XX_DRIVER_VERSION "v1.9.0.6" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -814,8 +814,10 @@ static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + desc->init_volume; - aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); + mutex_lock(&aw882xx->lock); aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); + mutex_unlock(&aw882xx->lock); + aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); return 0; } From 065226986fb5f6f3706b140b0a1aea8d8da10e7c Mon Sep 17 00:00:00 2001 From: zhouwl Date: Mon, 9 May 2022 10:20:39 +0800 Subject: [PATCH 45/79] asoc: add earpiece dsense kcontrol use gpio to control earpiece dsense, change gpio output level when earpiece is using. Change-Id: I17ea0e6fa2ca4523495f011825c0e8891527bf58 Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/2263126 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/holi.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/asoc/holi.c b/asoc/holi.c index 1daa75d0473e..8c214e9c8d6c 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -576,6 +576,7 @@ static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", "KHZ_44P1", "KHZ_48", "KHZ_88P2", "KHZ_96"}; static const char *const afe_loopback_tx_ch_text[] = {"One", "Two"}; +static const char *const earpiece_dsense_text[] = {"On", "Off"}; static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); @@ -710,6 +711,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text); static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text); static SOC_ENUM_SINGLE_EXT_DECL(afe_loopback_tx_chs, afe_loopback_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(earpiece_dsense_en, earpiece_dsense_text); static bool is_initial_boot; static bool codec_reg_done; @@ -717,6 +719,8 @@ static struct snd_soc_card snd_soc_card_holi_msm; static int dmic_0_1_gpio_cnt; static int dmic_2_3_gpio_cnt; static int dmic_4_5_gpio_cnt; +static int earpiece_dsense_en_gpio; +static bool is_earpiece_dsense_disable; static void *def_wcd_mbhc_cal(void); @@ -3219,6 +3223,36 @@ static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, return 0; } +/* when gpio output is high, means earpiece dsense is off */ +static int earpiece_dsense_en_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = is_earpiece_dsense_disable; + pr_debug("get earpiece_dsense_pin state: %s\n", + is_earpiece_dsense_disable ? "high" : "low"); + return 0; +} + +static int earpiece_dsense_en_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + gpio_direction_output(earpiece_dsense_en_gpio, 0); + is_earpiece_dsense_disable = 0; + break; + case 1: + gpio_direction_output(earpiece_dsense_en_gpio, 1); + is_earpiece_dsense_disable = 1; + break; + default: + return -EINVAL; + } + pr_debug("set earpiece_dsense_pin: %s\n", + ucontrol->value.integer.value[0] ? "high" : "low"); + return 0; +} + static const struct snd_kcontrol_new msm_int_wcd937x_snd_controls[] = { SOC_ENUM_EXT("RX_CDC_DMA_RX_0 Format", rx_cdc_dma_rx_0_format, cdc_dma_rx_format_get, cdc_dma_rx_format_put), @@ -3593,6 +3627,8 @@ static const struct snd_kcontrol_new msm_common_snd_controls[] = { msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, TDM_MAX_SLOTS + MAX_PATH, NULL, tdm_slot_map_put), + SOC_ENUM_EXT("Earpiece Dsense Enable", earpiece_dsense_en, + earpiece_dsense_en_get, earpiece_dsense_en_put), }; static const struct snd_kcontrol_new msm_snd_controls[] = { @@ -6883,6 +6919,19 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) for (index = PRIM_MI2S; index < MI2S_MAX; index++) atomic_set(&(pdata->mi2s_gpio_ref_count[index]), 0); + earpiece_dsense_en_gpio = of_get_named_gpio(pdev->dev.of_node, + "earpiece-dsense-enable", 0); + if (earpiece_dsense_en_gpio < 0) { + pr_err("missing earpiece_dsense_en_gpio in dt node\n"); + } else { + if (!gpio_is_valid(earpiece_dsense_en_gpio)) { + pr_err("Invalid earpiece_dsense_en_gpio\n"); + } else { + gpio_direction_output(earpiece_dsense_en_gpio, 1); + is_earpiece_dsense_disable = 1; + } + } + /* Register LPASS audio hw vote */ lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote"); if (IS_ERR(lpass_audio_hw_vote)) { From 30e452fc7a62042685de782f1348750730fd3ea6 Mon Sep 17 00:00:00 2001 From: liaohj Date: Sat, 7 May 2022 14:20:40 +0800 Subject: [PATCH 46/79] techpack/audio:enlarge map size for aw882xx algo after aw882xx algo upate to support get params from ram. need enlarge mapsize to 16k. Change-Id: I2821093f74819af9a191c17c9b02cde7292f8521 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2262542 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- dsp/Kbuild | 4 ++++ dsp/q6afe.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/dsp/Kbuild b/dsp/Kbuild index dffcb6212a21..847df7faadd7 100755 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -19,6 +19,10 @@ ifeq ($(CONFIG_AW882XX_PARAMS_FROM_BIN), y) CDEFINES += -DCONFIG_AW882XX_PARAMS_STORED_IN_BIN endif +ifeq ($(CONFIG_AW882XX_MAPSIZE_16K), y) +CDEFINES += -DCONFIG_AW882XX_MAPSIZE_16K +endif + ifeq ($(CONFIG_SND_SOC_AUTO), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 55887af988e4..8d61544eccb9 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -4010,7 +4010,11 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, if (aw_cal->map_data.dma_buf == 0) { /*Minimal chunk size is 8K*/ +#ifdef CONFIG_AW882XX_MAPSIZE_16K + aw_cal->map_data.map_size = SZ_16K; +#else aw_cal->map_data.map_size = SZ_8K; +#endif result = msm_audio_ion_alloc(&(aw_cal->map_data.dma_buf), aw_cal->map_data.map_size, &(aw_cal->cal_data.paddr),&len, @@ -4036,8 +4040,11 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, pr_err("%s: Invalid AFE port = 0x%x\n", __func__, port_id); goto err; } - +#ifdef CONFIG_AW882XX_MAPSIZE_16K + if (cmd_size > (SZ_16K - sizeof(struct param_hdr_v3))) { +#else if (cmd_size > (SZ_8K - sizeof(struct param_hdr_v3))) { +#endif pr_err("%s: Invalid payload size = %d\n", __func__, cmd_size); result = -EINVAL; goto err; From 570d2ffa109e1ab83c348b173aed842d2a0ef2eb Mon Sep 17 00:00:00 2001 From: liaohj Date: Sat, 7 May 2022 13:45:30 +0800 Subject: [PATCH 47/79] techpack/audio:update aw882xx driver to v1.9.0.7 update aw882xx driver to v1.9.0.7 support get algo params from ram. skt-prof-mode = 1 to support by product. Change-Id: I068641aa12d079c8526397367324d3aeacf084e1 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2262501 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 130 ++++++++-- asoc/codecs/aw882xxacf/aw882xx.h | 12 +- asoc/codecs/aw882xxacf/aw_data_type.h | 26 ++ asoc/codecs/aw882xxacf/aw_device.c | 331 ++++++++++++++++++++++++++ asoc/codecs/aw882xxacf/aw_device.h | 13 +- asoc/codecs/aw882xxacf/aw_dsp.c | 28 +++ asoc/codecs/aw882xxacf/aw_dsp.h | 1 + 7 files changed, 520 insertions(+), 21 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 1a5008f6f592..b7dad55ddbd4 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.6" +#define AW882XX_DRIVER_VERSION "v1.9.0.7" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -56,6 +56,7 @@ static unsigned int g_runin_test; static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; +struct aw_container *g_awinic_skt_cfg = NULL; #define AW882XX_MOTO_MAX_GAIN (127) @@ -489,6 +490,7 @@ static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) if (mute) { aw882xx->pstream = false; + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; cancel_delayed_work_sync(&aw882xx->dc_work); cancel_delayed_work_sync(&aw882xx->start_work); mutex_lock(&aw882xx->lock); @@ -896,6 +898,8 @@ static void aw882xx_request_firmware(struct work_struct *work) container_of(work, struct aw882xx, fw_work.work); const struct firmware *cont = NULL; struct aw_container *aw_cfg = NULL; + const struct firmware *skt_cont = NULL; + struct aw_container *aw_skt_cfg = NULL; int ret = -1; aw882xx->fw_status = AW_DEV_FW_FAILED; @@ -946,6 +950,56 @@ static void aw882xx_request_firmware(struct work_struct *work) } mutex_unlock(&g_aw882xx_lock); + + /*load skt bin*/ + if (aw882xx->skt_prof_mode == AW_PARAMS_DATA_MODE) { + ret = request_firmware(&skt_cont, AW_ALGO_SKT_BIN, aw882xx->dev); + if ((ret) || (!skt_cont)) { + aw_dev_info(aw882xx->dev, "load [%s] failed!", AW_ALGO_SKT_BIN); + return; + } + + aw_dev_info(aw882xx->dev, "load [%s] , file size: [%zu]", + AW_ALGO_SKT_BIN, skt_cont ? skt_cont->size : 0); + + if (g_awinic_skt_cfg == NULL) { + aw_skt_cfg = vzalloc(skt_cont->size + sizeof(int)); + if (aw_skt_cfg == NULL) { + release_firmware(skt_cont); + aw_dev_err(aw882xx->dev, "malloc failed"); + return; + } + aw_skt_cfg->len = skt_cont->size; + memcpy(aw_skt_cfg->data, skt_cont->data, skt_cont->size); + release_firmware(skt_cont); + + ret = aw_dev_load_acf_check(aw_skt_cfg); + if (ret) { + aw_dev_err(aw882xx->dev, "Load [%s] failed ....!", AW_ALGO_SKT_BIN); + vfree(aw_skt_cfg); + aw_skt_cfg = NULL; + return; + } + g_awinic_skt_cfg = aw_skt_cfg; + /*set init algo prof*/ + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + + } else { + aw_skt_cfg = g_awinic_skt_cfg; + release_firmware(skt_cont); + aw_dev_info(aw882xx->dev, "[%s] already loaded...", AW_ALGO_SKT_BIN); + } + + /*parse skt bin*/ + if ((aw882xx->aw_pa->channel == AW_DEV_CH_PRI_L) || (aw882xx->aw_pa->channel == AW_DEV_CH_SEC_L)) { + ret = aw_dev_parse_skt_bin(aw882xx->aw_pa, aw_skt_cfg); + if (ret < 0) { + aw_dev_info(aw882xx->dev, "parse skt bin failed"); + return; + } + } + } + mutex_lock(&aw882xx->lock); /*aw device init*/ ret = aw_device_init(aw882xx->aw_pa, aw_cfg); @@ -1091,10 +1145,22 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, g_algo_rx_en = ctrl_value; aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); - if (ctrl_value) { - ret = aw_dev_set_algo_params_path(aw_dev); - if (ret < 0) - aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + if (ctrl_value) { + ret = aw_dev_set_algo_params_path(aw_dev); + if (ret < 0) + aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + } + } else { + if (aw882xx->aw_pa->pre_prof_id != AW_DEFAULT_PRO_ID) { + aw882xx->cur_algo_prof_id = aw882xx->aw_pa->pre_prof_id; + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + ret = aw_dev_skt_prof_mode(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -EINVAL; + } + } } return 0; } @@ -1589,6 +1655,7 @@ static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); int ctrl_value; + int ret = -1; aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); @@ -1597,7 +1664,15 @@ static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, ctrl_value = ucontrol->value.integer.value[0]; - aw_dev_set_algo_prof(aw_dev, ctrl_value); + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + aw_dev_set_algo_prof(aw_dev, ctrl_value); + } else { + ret = aw_dev_skt_prof_mode(aw_dev, ctrl_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set_prof failed!, ret = %d", ret); + } + aw882xx->cur_algo_prof_id = ctrl_value; + } return 0; } @@ -1615,10 +1690,14 @@ static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; if (aw882xx->pstream) { - ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); - if (ret) { - aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); - ctrl_value = 0; + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); + ctrl_value = 0; + } + } else { + ctrl_value = aw882xx->cur_algo_prof_id; } ucontrol->value.integer.value[0] = ctrl_value; } else { @@ -1679,7 +1758,7 @@ static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) { - int ret = 0; + int ret = -1; int new_skt_prof_id = 0; int cur_skt_prof_id = aw882xx->cur_algo_prof_id; struct aw_device *aw_dev = NULL; @@ -1699,15 +1778,15 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev = aw882xx->aw_pa; /* set new scene pramas to skt */ - ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); - if (ret < 0) { - aw_dev_err(aw882xx->dev, "set algo prof failed"); - return -1; + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + } else { + ret = aw_dev_skt_prof_mode(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; + } } - - aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", - new_skt_prof_id); - return 0; } @@ -2477,6 +2556,15 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, __func__, aw882xx->fade_flag); } + ret = of_property_read_u32(np, "skt-prof-mode", &aw882xx->skt_prof_mode); + if (ret) { + aw882xx->skt_prof_mode = AW_PARAMS_PATH_MODE; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + return 0; } @@ -3183,6 +3271,10 @@ static int aw882xx_i2c_remove(struct i2c_client *i2c) vfree(g_awinic_cfg); g_awinic_cfg = NULL; } + if (g_awinic_skt_cfg) { + vfree(g_awinic_skt_cfg); + g_awinic_skt_cfg = NULL; + } } mutex_unlock(&g_aw882xx_lock); diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h index 1360b864097c..e4299e40948b 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.h +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -15,6 +15,9 @@ #define AW882XX_LOAD_FW_DELAY_TIME (0) #define AW_START_RETRIES (5) #define AW882XX_RUNIN_TEST +#define AW_ALGO_SKT_BIN "aw_skt.bin" +#define AW_DEFAULT_PRO_ID (0xFFFFFFFF) + #define AW_I2C_RETRIES 5 /* 5 times */ #define AW_I2C_RETRY_DELAY 5 /* 5 ms */ @@ -69,6 +72,13 @@ struct aw882xx_scene_info { int is_active; int active_cnt; }; + +enum { + AW_PARAMS_PATH_MODE = 0, + AW_PARAMS_DATA_MODE, +}; + + #endif @@ -212,7 +222,7 @@ struct aw882xx { int aw882xx_algo_prof_id; int cur_algo_prof_id; #endif - + unsigned int skt_prof_mode; unsigned int fade_flag; unsigned char index; diff --git a/asoc/codecs/aw882xxacf/aw_data_type.h b/asoc/codecs/aw882xxacf/aw_data_type.h index 36aaf4f077ce..5184dfa01189 100644 --- a/asoc/codecs/aw882xxacf/aw_data_type.h +++ b/asoc/codecs/aw882xxacf/aw_data_type.h @@ -18,12 +18,14 @@ struct aw_msg_hdr { #define CUSTOMER_NAME_MAX (16) #define CFG_VERSION_MAX (4) #define DEV_NAME_MAX (16) +#define PROFILE_STR_MAX (32) #define ACF_FILE_ID (0xa15f908) enum aw_cfg_hdr_version { AW_CFG_HDR_VER_0_0_0_1 = 0x00000001, + AW_CFG_HDR_VER_1_0_0_0 = 0x01000000, }; enum aw_cfg_dde_type { @@ -53,6 +55,12 @@ enum profile_data_type { AW_PROFILE_DATA_TYPE_MAX, }; +enum algo_profile_data_type { + AW_PROFILE_DATA_TYPE_ALGO_DSP, + AW_PROFILE_DATA_TYPE_ALGO_MAX, +}; + + enum aw_prof_type { AW_PROFILE_MUSIC = 0, AW_PROFILE_VOICE, @@ -100,6 +108,22 @@ struct aw_cfg_dde { uint32_t reserve[5]; }; +struct aw_cfg_dde_v_1_0_0_0 { + uint32_t type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + uint16_t dev_index; /*dev id*/ + uint16_t dev_bus; /*dev bus id*/ + uint16_t dev_addr; /*dev addr id*/ + uint16_t dev_profile; /*dev profile id*/ + uint32_t data_type; /*data type id*/ + uint32_t data_size; + uint32_t data_offset; + uint32_t data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + uint32_t chip_id; + uint32_t reserve[4]; +}; + struct aw_sec_data_desc { uint32_t len; unsigned char *data; @@ -108,6 +132,7 @@ struct aw_sec_data_desc { struct aw_prof_desc { uint32_t id; uint32_t prof_st; + char *prf_str; struct aw_sec_data_desc sec_desc[AW_PROFILE_DATA_TYPE_MAX]; }; @@ -117,6 +142,7 @@ struct aw_all_prof_info { struct aw_prof_info { int count; + char **prof_name_list; struct aw_prof_desc *prof_desc; }; diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index c2d768060f8c..193d77598b42 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -150,6 +150,102 @@ static int aw_dev_check_cfg_by_hdr(struct aw_container *aw_cfg) return 0; } +static int aw_dev_parse_check_acf_by_hdr_v_1_0_0_0(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = NULL; + unsigned int end_data_offset = 0; + unsigned int act_data = 0; + unsigned int hdr_ddt_len = 0; + uint8_t act_crc8 = 0; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /*check file type id is awinic acf file*/ + if (cfg_hdr->a_id != ACF_FILE_ID) { + aw_pr_err("not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->a_hdr_offset + cfg_hdr->a_ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + aw_pr_err("hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->a_hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /*check data size*/ + cfg_dde = (struct aw_cfg_dde_v_1_0_0_0 *)((char *)aw_cfg->data + cfg_hdr->a_hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->a_ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + aw_pr_err("act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + aw_pr_err("a_ddt_num[%d] end_data_offset[%d] overflow file size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = aw_dev_crc8_check(aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size); + if (act_crc8 != cfg_dde[i].data_crc) { + aw_pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (uint32_t)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + aw_pr_info("project name [%s]", cfg_hdr->a_project); + aw_pr_info("custom name [%s]", cfg_hdr->a_custom); + aw_pr_info("version name [%d.%d.%d.%d]", cfg_hdr->a_version[3], cfg_hdr->a_version[2], + cfg_hdr->a_version[1], cfg_hdr->a_version[0]); + aw_pr_info("author id %d", cfg_hdr->a_author_id); + + return 0; +} + +static int aw_dev_parse_raw_dsp_skt(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP].len = data_len; + + return 0; +} + +static int aw_dev_get_chan_group(struct aw_device *aw_dev, int *chan_group) +{ + switch (aw_dev->channel) { + + case AW_DEV_CH_PRI_L: + case AW_DEV_CH_PRI_R: + *chan_group = AW_DEV_CH_GROUP_PRI; + break; + case AW_DEV_CH_SEC_L: + case AW_DEV_CH_SEC_R: + *chan_group = AW_DEV_CH_GROUP_SEC; + break; + default: + aw_pr_err("can not find chan_group, channel %d ", aw_dev->channel); + return -EINVAL; + } + + aw_pr_dbg("chan_group[%d] ", *chan_group); + return 0; +} + int aw_dev_load_acf_check(struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr = NULL; @@ -169,6 +265,8 @@ int aw_dev_load_acf_check(struct aw_container *aw_cfg) switch (cfg_hdr->a_hdr_version) { case AW_CFG_HDR_VER_0_0_0_1: return aw_dev_check_cfg_by_hdr(aw_cfg); + case AW_CFG_HDR_VER_1_0_0_0: + return aw_dev_parse_check_acf_by_hdr_v_1_0_0_0(aw_cfg); default: aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); return -EINVAL; @@ -452,6 +550,234 @@ static int aw_dev_cfg_get_vaild_prof(struct aw_device *aw_dev, return 0; } + +/******************ACF bin format V1.0.0.0************************/ +static int aw_dev_parse_get_scene_count_v1_0_0_0(struct aw_device *aw_dev, + struct aw_container *aw_cfg, int *count) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = + (struct aw_cfg_dde_v_1_0_0_0 *)(aw_cfg->data + cfg_hdr->a_hdr_offset); + int i = 0; + int chan_group = -1; + int ret = -1; + + ret = aw_dev_get_chan_group(aw_dev, &chan_group); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get chan_group failed "); + return ret; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) && + (cfg_dde[i].type == AW_SKT_TYPE_ID) && + (chan_group == cfg_dde[i].dev_index)) { + (*count)++; + } + } + + if ((*count) == 0) { + aw_dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "scene count is %d", (*count)); + return 0; +} + + +static int aw_dev_parse_create_prof_name_list_v_1_0_0_0(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_prof_desc *prof_desc= prof_info->prof_desc; + int i; + + if (prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc is NULL"); + return -EINVAL; + } + + prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + aw_dev_err(aw_dev->dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < prof_info->count; i++) { + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + aw_dev_info(aw_dev->dev, "prof name list[%d]:%s, id=%d", i, prof_info->prof_name_list[i], prof_desc[i].id); + } + + return 0; +} + +static int aw_dev_parse_skt_type_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + int i = 0; + int ret = -1; + int scene_count = 0; + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = + (struct aw_cfg_dde_v_1_0_0_0 *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + int chan_group = -1; + aw_dev_info(aw_dev->dev, "enter"); + + ret = aw_dev_get_chan_group(aw_dev, &chan_group); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get chan_group failed "); + return ret; + } + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((cfg_dde[i].type == AW_SKT_TYPE_ID) && + (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) && + (chan_group == cfg_dde[i].dev_index)) { + + ret = aw_dev_parse_raw_dsp_skt(aw_dev, + (uint8_t *)prof_hdr + cfg_dde[i].data_offset, + cfg_dde[i].data_size, &prof_info->prof_desc[scene_count]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dsp bin data failed"); + return ret; + } + + prof_info->prof_desc[scene_count].prf_str = cfg_dde[i].dev_profile_str; + prof_info->prof_desc[scene_count].id = cfg_dde[i].dev_profile; + + scene_count++; + } + } + aw_dev_dbg(aw_dev->dev, "scene_count = %d", scene_count); + return 0; +} + +static int aw_dev_parse_acf_by_hdr_v_1_0_0_0(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + int ret; + + ret = aw_dev_parse_get_scene_count_v1_0_0_0(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get scene count failed"); + return ret; + } + + prof_info->prof_desc = devm_kzalloc(aw_dev->dev, + prof_info->count * sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc devm_kzalloc failed"); + return -ENOMEM; + } + + ret = aw_dev_parse_skt_type_v_1_0_0_0(aw_dev, cfg_hdr); + if (ret < 0) { + aw_dev_err(aw_dev->dev, " failed"); + return ret; + } + + ret = aw_dev_parse_create_prof_name_list_v_1_0_0_0(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw_dev_parse_skt_bin(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + int ret = -1; + + if (aw_dev == NULL || aw_cfg == NULL) { + aw_dev_err(aw_dev->dev, "pointer is NULL"); + return -ENOMEM; + } + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + if (cfg_hdr->a_hdr_version == AW_CFG_HDR_VER_1_0_0_0) { + ret = aw_dev_parse_acf_by_hdr_v_1_0_0_0(aw_dev, aw_cfg); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + } else { + aw_dev_err(aw_dev->dev, "skt bin format [0x%x] unsupported", cfg_hdr->a_hdr_version); + } + + return 0; +} + +struct aw_sec_data_desc *aw_dev_get_algo_prof_data(struct aw_device *aw_dev, int prof_id) +{ + struct aw_sec_data_desc *prof_data = NULL; + + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + int i; + + aw_dev_dbg(aw_dev->dev, "prof num = %d", prof_info->count); + for (i = 0; i < prof_info->count; i++) { + if (prof_id == prof_info->prof_desc[i].id) { + prof_data = &prof_info->prof_desc[i].sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP]; + break; + } + } + + return prof_data; +} + +int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id) +{ + struct aw_sec_data_desc *prof_data = NULL; + int ret = -1; + struct list_head *dev_list = NULL; + struct list_head *pos = NULL; + struct aw_device *local_dev = NULL; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + aw_dev_info(aw_dev->dev, "set_cur_prof=%d,pre_prof=%d", + prof_id, aw_dev->pre_prof_id); + + if (prof_id != aw_dev->pre_prof_id) { + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + + if ((local_dev->channel == AW_DEV_CH_PRI_L) || (local_dev->channel == AW_DEV_CH_SEC_L)) { + prof_data = aw_dev_get_algo_prof_data(local_dev, prof_id); + if (prof_data) { + ret = aw_dev_set_algo_prof_data(local_dev, prof_data->data, prof_data->len); + if (ret < 0) { + aw_dev_err(local_dev->dev, "set algo prof [%d] failed", prof_id); + return -EINVAL; + } + } else { + aw_dev_err(local_dev->dev, "prof data is NULL"); + return -EINVAL; + } + } + } + + aw_dev->pre_prof_id = prof_id; + aw_dev_info(aw_dev->dev, "set algo prof [%d] ok", prof_id); + } + + return 0; +} + + static int aw_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr = NULL; @@ -1503,6 +1829,11 @@ int aw_dev_get_spin(int *spin_mode) return aw_dsp_read_spin(spin_mode); } +int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len) +{ + return aw_dsp_set_algo_prof_data(aw_dev, prof_data, prof_len); +} + int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id) { return aw_dsp_set_algo_prof(aw_dev, prof_id); diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 4c894a4f1001..69607cf93b11 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -58,6 +58,12 @@ enum AW_DEV_FW_STATUS { AW_DEV_FW_OK, }; +enum { + AW_DEV_CH_GROUP_PRI = 0, + AW_DEV_CH_GROUP_SEC = 1, + AW_DEV_CH_GROUP_MAX, +}; + struct aw_device_ops { int (*aw_i2c_write)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int reg_data); int (*aw_i2c_read)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int *reg_data); @@ -234,6 +240,7 @@ struct aw_device { char monitor_name[AW_NAME_MAX]; void *private_data; char algo_path[AW_ALGO_PATH_MAX]; + int pre_prof_id; struct aw_int_desc int_desc; struct aw_pwd_desc pwd_desc; @@ -249,6 +256,7 @@ struct aw_device { struct aw_ipeak_desc ipeak_desc; struct aw_volume_desc volume_desc; struct aw_prof_info prof_info; + struct aw_prof_info skt_prof_info; struct aw_cali_desc cali_desc; struct aw_monitor_desc monitor_desc; struct aw_soft_rst soft_rst; @@ -258,10 +266,11 @@ struct aw_device { struct list_head list_node; }; - +struct aw_sec_data_desc *aw_dev_get_algo_prof_data(struct aw_device *aw_dev, int prof_id); int aw_dev_load_acf_check(struct aw_container *aw_cfg); void aw_dev_deinit(struct aw_device *aw_dev); int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw_dev_parse_skt_bin(struct aw_device *aw_dev, struct aw_container *aw_cfg); int aw_device_start(struct aw_device *aw_dev); int aw_device_stop(struct aw_device *aw_dev); int aw_dev_reg_update(struct aw_device *aw_dev, bool force); @@ -298,6 +307,8 @@ int aw_dev_get_afe_module_en(int type, int *status); int aw_dev_set_copp_module_en(bool enable); int aw_dev_set_spin(int spin_mode); int aw_dev_get_spin(int *spin_mode); +int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id); +int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len); int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id); int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id); int aw_dev_set_algo_params_path(struct aw_device *aw_dev); diff --git a/asoc/codecs/aw882xxacf/aw_dsp.c b/asoc/codecs/aw882xxacf/aw_dsp.c index ed2425d92dc6..ba2baa236795 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.c +++ b/asoc/codecs/aw882xxacf/aw_dsp.c @@ -29,6 +29,8 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_ALGO_PARAMS_PATH (0x00000014) #define AW_MSG_ID_VERSION (0x00000008) #define AW_MSG_ID_VERSION_NEW (0x00000012) +#define AW_MSG_ID_SET_ALGO_PROF_PRI (0x10013D25) +#define AW_MSG_ID_SET_ALGO_PROF_SEC (0x10013D2C) /*dsp params id*/ @@ -389,6 +391,32 @@ static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) } /************************* dsp communication function *****************************/ +int aw_dsp_set_algo_prof_data(struct aw_device *aw_dev, void *data, unsigned int size) +{ + int ret = 0; + uint32_t params_id = 0; + + if (aw_dev->channel == AW_DEV_CH_PRI_L) { + params_id = AW_MSG_ID_SET_ALGO_PROF_PRI; + } else if (aw_dev->channel == AW_DEV_CH_SEC_L) { + params_id = AW_MSG_ID_SET_ALGO_PROF_SEC; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + aw_dev_dbg(aw_dev->dev, "params_id=%u,prof_data:%p, len=%u", params_id, data, size); + + ret = aw_qcom_write_data_to_dsp(params_id, data, size); + if (ret < 0) { + aw_pr_err("write prof_id failed "); + return ret; + } + + aw_pr_dbg("write prof_id done"); + return 0; + +} int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) { diff --git a/asoc/codecs/aw882xxacf/aw_dsp.h b/asoc/codecs/aw882xxacf/aw_dsp.h index b5c30838d4d6..8b99b8092f58 100644 --- a/asoc/codecs/aw882xxacf/aw_dsp.h +++ b/asoc/codecs/aw882xxacf/aw_dsp.h @@ -63,6 +63,7 @@ int aw_dsp_set_copp_module_en(bool enable); int aw_dsp_write_spin(int spin_mode); int aw_dsp_read_spin(int *spin_mode); int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf); +int aw_dsp_set_algo_prof_data(struct aw_device *aw_dev, void *data, unsigned int size); int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id); int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); From dfb4cee98759c1b739459e2fa4c6b0445cd033de Mon Sep 17 00:00:00 2001 From: liaohj Date: Tue, 24 May 2022 13:41:56 +0800 Subject: [PATCH 48/79] aw882xxacf: add CONFIG_AW882XX_ALGO_BIN_PARAM to fix build fail Aw v1.9.0.7 driver build fail for not defined CONFIG_AW882XX_ALGO_BIN_PARAM product. add CONFIG_AW882XX_ALGO_BIN_PARAM control for new bin ram code. Change-Id: I630729f0e1dd105f24bc03a00f497a595a49972c Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2278006 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 10 +++++++++- asoc/codecs/aw882xxacf/aw_device.c | 2 ++ asoc/codecs/aw882xxacf/aw_device.h | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index b7dad55ddbd4..e7b07999558f 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -898,8 +898,10 @@ static void aw882xx_request_firmware(struct work_struct *work) container_of(work, struct aw882xx, fw_work.work); const struct firmware *cont = NULL; struct aw_container *aw_cfg = NULL; +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS const struct firmware *skt_cont = NULL; struct aw_container *aw_skt_cfg = NULL; +#endif int ret = -1; aw882xx->fw_status = AW_DEV_FW_FAILED; @@ -950,7 +952,7 @@ static void aw882xx_request_firmware(struct work_struct *work) } mutex_unlock(&g_aw882xx_lock); - +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS /*load skt bin*/ if (aw882xx->skt_prof_mode == AW_PARAMS_DATA_MODE) { ret = request_firmware(&skt_cont, AW_ALGO_SKT_BIN, aw882xx->dev); @@ -999,6 +1001,7 @@ static void aw882xx_request_firmware(struct work_struct *work) } } } +#endif mutex_lock(&aw882xx->lock); /*aw device init*/ @@ -1145,6 +1148,7 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, g_algo_rx_en = ctrl_value; aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { if (ctrl_value) { ret = aw_dev_set_algo_params_path(aw_dev); @@ -1162,6 +1166,8 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, } } } +#endif + return 0; } @@ -2556,6 +2562,7 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, __func__, aw882xx->fade_flag); } +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS ret = of_property_read_u32(np, "skt-prof-mode", &aw882xx->skt_prof_mode); if (ret) { aw882xx->skt_prof_mode = AW_PARAMS_PATH_MODE; @@ -2564,6 +2571,7 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, dev_info(dev, "%s: fade_flag = %d\n", __func__, aw882xx->fade_flag); } +#endif return 0; } diff --git a/asoc/codecs/aw882xxacf/aw_device.c b/asoc/codecs/aw882xxacf/aw_device.c index 193d77598b42..705a66659a5e 100644 --- a/asoc/codecs/aw882xxacf/aw_device.c +++ b/asoc/codecs/aw882xxacf/aw_device.c @@ -1844,10 +1844,12 @@ int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id) return aw_dsp_get_algo_prof(aw_dev, prof_id); } +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS int aw_dev_set_algo_params_path(struct aw_device *aw_dev) { return aw_dsp_set_algo_params_path(aw_dev); } +#endif #ifdef AW_SPIN_ENABLE int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time) diff --git a/asoc/codecs/aw882xxacf/aw_device.h b/asoc/codecs/aw882xxacf/aw_device.h index 69607cf93b11..71c231097e9a 100644 --- a/asoc/codecs/aw882xxacf/aw_device.h +++ b/asoc/codecs/aw882xxacf/aw_device.h @@ -311,7 +311,10 @@ int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id); int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len); int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id); int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id); + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS int aw_dev_set_algo_params_path(struct aw_device *aw_dev); +#endif int aw_device_probe(struct aw_device *aw_dev); int aw_device_remove(struct aw_device *aw_dev); From 937a15bec39a220f454fe937b46fb708e262f886 Mon Sep 17 00:00:00 2001 From: zhouwl Date: Thu, 30 Jun 2022 11:19:40 +0800 Subject: [PATCH 49/79] q6afe: update for aw882xx update afe_callback to handle RESET_EVENTS for aw882xx; call aw_cal_unmap_memory to unmap memory. Change-Id: Iadc69189a18e1f24d9c5fb888d6f91a35222d06a Signed-off-by: zhouwl Reviewed-on: https://gerrit.mot.com/2318311 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- dsp/q6afe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 8d61544eccb9..d78ce8f79086 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -30,6 +30,8 @@ #define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) #define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) #define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) + +void aw_cal_unmap_memory(void); #endif /* #ifdef CONFIG_SND_SOC_AWINIC_AW882XX */ #define WAKELOCK_TIMEOUT 5000 #define AFE_CLK_TOKEN 1024 @@ -1053,6 +1055,10 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } if (data->opcode == RESET_EVENTS) { + #ifdef CONFIG_SND_SOC_AWINIC_AW882XX + aw_cal_unmap_memory(); + #endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ + pr_debug("%s: reset event = %d %d apr[%pK]\n", __func__, data->reset_event, data->reset_proc, this_afe.apr); From 687bd6b788738a4702f252614e8b68307fba114b Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 28 Jun 2022 14:49:27 +0800 Subject: [PATCH 50/79] asoc:add aw87xxx codec driver add awinic smart k audio amplifier; penang will use aw87xxx. Change-Id: Icc68ffee6d29db8a22cd2c805b90a682cad5ce18 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2321859 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/Kbuild | 4 + asoc/codecs/Kbuild | 1 + asoc/codecs/aw87xxx/Kbuild | 16 + asoc/codecs/aw87xxx/aw87xxx.c | 1499 +++++ asoc/codecs/aw87xxx/aw87xxx.h | 121 + asoc/codecs/aw87xxx/aw87xxx_acf_bin.c | 1558 ++++++ asoc/codecs/aw87xxx/aw87xxx_acf_bin.h | 191 + asoc/codecs/aw87xxx/aw87xxx_bin_parse.c | 515 ++ asoc/codecs/aw87xxx/aw87xxx_bin_parse.h | 73 + asoc/codecs/aw87xxx/aw87xxx_device.c | 976 ++++ asoc/codecs/aw87xxx/aw87xxx_device.h | 149 + asoc/codecs/aw87xxx/aw87xxx_dsp.c | 355 ++ asoc/codecs/aw87xxx/aw87xxx_dsp.h | 65 + asoc/codecs/aw87xxx/aw87xxx_log.h | 33 + asoc/codecs/aw87xxx/aw87xxx_monitor.c | 1208 ++++ asoc/codecs/aw87xxx/aw87xxx_monitor.h | 96 + asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h | 2315 ++++++++ asoc/codecs/aw87xxx/aw87xxx_pid_39_reg.h | 67 + asoc/codecs/aw87xxx/aw87xxx_pid_59_3x9_reg.h | 93 + asoc/codecs/aw87xxx/aw87xxx_pid_59_5x9_reg.h | 94 + asoc/codecs/aw87xxx/aw87xxx_pid_5a_reg.h | 4124 ++++++++++++++ asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h | 5246 ++++++++++++++++++ asoc/codecs/aw87xxx/aw87xxx_pid_76_reg.h | 1205 ++++ asoc/codecs/aw87xxx/aw87xxx_pid_9b_reg.h | 81 + asoc/codecs/wcd937x/wcd937x.c | 84 + asoc/holi.c | 13 + 26 files changed, 20182 insertions(+) create mode 100644 asoc/codecs/aw87xxx/Kbuild create mode 100644 asoc/codecs/aw87xxx/aw87xxx.c create mode 100644 asoc/codecs/aw87xxx/aw87xxx.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_acf_bin.c create mode 100644 asoc/codecs/aw87xxx/aw87xxx_acf_bin.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_bin_parse.c create mode 100644 asoc/codecs/aw87xxx/aw87xxx_bin_parse.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_device.c create mode 100644 asoc/codecs/aw87xxx/aw87xxx_device.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_dsp.c create mode 100644 asoc/codecs/aw87xxx/aw87xxx_dsp.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_log.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_monitor.c create mode 100644 asoc/codecs/aw87xxx/aw87xxx_monitor.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_39_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_59_3x9_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_59_5x9_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_5a_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_76_reg.h create mode 100644 asoc/codecs/aw87xxx/aw87xxx_pid_9b_reg.h diff --git a/asoc/Kbuild b/asoc/Kbuild index 8ce3ad3f30e5..327bd8b52434 100755 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -273,6 +273,10 @@ CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ -Werror\ -D__linux__ +ifdef CONFIG_SND_SOC_AW87XXX +CDEFINES += -DCONFIG_SND_SOC_AW87XXX +endif + KBUILD_CPPFLAGS += $(CDEFINES) # Currently, for versions of gcc which support it, the kernel Makefile diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 505adf9807cb..0ce9191e3df5 100755 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -276,6 +276,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += rouleur/ obj-y += aw882xx/ obj-y += aw882xxacf/ + obj-y += aw87xxx/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/asoc/codecs/aw87xxx/Kbuild b/asoc/codecs/aw87xxx/Kbuild new file mode 100644 index 000000000000..d7ab1e501a9b --- /dev/null +++ b/asoc/codecs/aw87xxx/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ + +ifdef CONFIG_SND_SOC_AW87XXX +AW87XXX_OBJS += aw87xxx.o +AW87XXX_OBJS += aw87xxx_device.o +AW87XXX_OBJS += aw87xxx_monitor.o +AW87XXX_OBJS += aw87xxx_bin_parse.o +AW87XXX_OBJS += aw87xxx_dsp.o +AW87XXX_OBJS += aw87xxx_acf_bin.o +endif + +obj-$(CONFIG_SND_SOC_AW87XXX) += aw87xxx_dlkm.o +aw87xxx_dlkm-y := $(AW87XXX_OBJS) diff --git a/asoc/codecs/aw87xxx/aw87xxx.c b/asoc/codecs/aw87xxx/aw87xxx.c new file mode 100644 index 000000000000..28c7d422718f --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx.c @@ -0,0 +1,1499 @@ +/* + * aw87xxx.c aw87xxx pa module + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_device.h" +#include "aw87xxx_log.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_acf_bin.h" +#include "aw87xxx_bin_parse.h" +#include "aw87xxx_dsp.h" + +/***************************************************************** +* aw87xxx marco +******************************************************************/ +#define AW87XXX_I2C_NAME "aw87xxx_pa" +#define AW87XXX_DRIVER_VERSION "v2.6.0" +#define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" + +/************************************************************************* + * aw87xxx variable + ************************************************************************/ +static LIST_HEAD(g_aw87xxx_list); +static DEFINE_MUTEX(g_aw87xxx_mutex_lock); +unsigned int g_aw87xxx_dev_cnt = 0; + +static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; +static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", + "spin_180", "spin_270"}; +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .add_codec_controls = snd_soc_add_component_controls, + .unregister_codec = snd_soc_unregister_component, +}; +#else +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .add_codec_controls = snd_soc_add_codec_controls, + .unregister_codec = snd_soc_unregister_codec, +}; +#endif + + +/************************************************************************ + * + * aw87xxx device update profile + * + ************************************************************************/ +static int aw87xxx_power_down(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = 0; + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + struct aw_data_container *data_container = NULL; + struct aw_device *aw_dev = &aw87xxx->aw_dev; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + if (!prof_info->status) { + AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); + return -EINVAL; + } + + prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); + if (prof_desc == NULL) + goto no_bin_pwr_off; + + if (!prof_desc->prof_st) + goto no_bin_pwr_off; + + + data_container = &prof_desc->data_container; + AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", + profile, data_container->len); + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); + } else { + if (aw_dev->ops.pwr_off_func) { + ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); + goto pwr_off_failed; + } + } else { + ret = aw87xxx_dev_default_pwr_off(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); + goto pwr_off_failed; + } + } + } + + aw87xxx->current_profile = prof_desc->prof_name; + return 0; + +pwr_off_failed: +no_bin_pwr_off: + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); + aw87xxx->current_profile = aw87xxx->prof_off_name; + return ret; +} + +static int aw87xxx_power_on(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = -EINVAL; + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + struct aw_data_container *data_container = NULL; + struct aw_device *aw_dev = &aw87xxx->aw_dev; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + if (!prof_info->status) { + AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); + return -EINVAL; + } + + if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) + return aw87xxx_power_down(aw87xxx, profile); + + prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); + if (prof_desc == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); + return -EINVAL; + } + + if (!prof_desc->prof_st) { + AW_DEV_LOGE(aw87xxx->dev, "not found data container"); + return -EINVAL; + } + + data_container = &prof_desc->data_container; + AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", + profile, data_container->len); + + if (aw_dev->ops.pwr_on_func) { + ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", + profile); + return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); + } + } else { + ret = aw87xxx_dev_default_pwr_on(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", + profile); + return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); + } + } + + aw87xxx->current_profile = prof_desc->prof_name; + AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] succeed", profile); + + return 0; +} + + + +int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = -1; + + AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] enter", profile); + mutex_lock(&aw87xxx->reg_lock); + aw87xxx_monitor_stop(&aw87xxx->monitor); + if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) { + ret = aw87xxx_power_down(aw87xxx, profile); + } else { + ret = aw87xxx_power_on(aw87xxx, profile); + if (!ret) + aw87xxx_monitor_start(&aw87xxx->monitor); + } + mutex_unlock(&aw87xxx->reg_lock); + + return ret; +} + +int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = -1; + + if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) + ret = aw87xxx_power_down(aw87xxx, profile); + else + ret = aw87xxx_power_on(aw87xxx, profile); + + return ret; +} + +char *aw87xxx_show_current_profile(int dev_index) +{ + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx->dev_index == dev_index) { + AW_DEV_LOGI(aw87xxx->dev, "current profile is [%s]", + aw87xxx->current_profile); + return aw87xxx->current_profile; + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return NULL; +} +EXPORT_SYMBOL(aw87xxx_show_current_profile); + +int aw87xxx_set_profile(int dev_index, char *profile) +{ + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (profile && aw87xxx->dev_index == dev_index) { + AW_DEV_LOGD(aw87xxx->dev, "set dev_index = %d, profile = %s", + dev_index, profile); + return aw87xxx_update_profile(aw87xxx, profile); + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return -EINVAL; +} +EXPORT_SYMBOL(aw87xxx_set_profile); + +int aw87xxx_set_profile_by_id(int dev_index, int profile_id) +{ + char *profile = NULL; + + profile = aw87xxx_ctos_get_prof_name(profile_id); + if (profile == NULL) { + AW_LOGE("aw87xxx, dev_index[%d] profile[%d] not support!", + dev_index, profile_id); + return -EINVAL; + } + + AW_LOGI("aw87xxx, dev_index[%d] set profile[%s] by id[%d]", + dev_index, profile, profile_id); + return aw87xxx_set_profile(dev_index, profile); +} +EXPORT_SYMBOL(aw87xxx_set_profile_by_id); + +/**************************************************************************** + * + * aw87xxx Kcontrols + * + ****************************************************************************/ +static int aw87xxx_profile_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count = 0; + char *name = NULL; + char *profile_name = NULL; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + /*make sure have prof */ + count = aw87xxx_acf_get_profile_count(aw87xxx->dev, &aw87xxx->acf_info); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + AW_DEV_LOGE(aw87xxx->dev, "get count[%d] failed", count); + return 0; + } + + uinfo->value.enumerated.items = count; + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, + &aw87xxx->acf_info, count); + if (profile_name == NULL) { + strlcpy(uinfo->value.enumerated.name, "NULL", + strlen("NULL") + 1); + return 0; + } + + strlcpy(name, profile_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw87xxx_profile_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -1; + char *profile_name = NULL; + int index = ucontrol->value.integer.value[0]; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + struct acf_bin_info *acf_info = NULL; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + acf_info = &aw87xxx->acf_info; + + profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, acf_info, index); + if (!profile_name) { + AW_DEV_LOGE(aw87xxx->dev, "not found profile name,index=[%d]", + index); + return -EINVAL; + } + + AW_DEV_LOGI(aw87xxx->dev, "set profile [%s]", profile_name); + + ret = aw87xxx_update_profile(aw87xxx, profile_name); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "set dev_index[%d] profile failed, profile = %s", + aw87xxx->dev_index, profile_name); + return ret; + } + + return 0; +} + +static int aw87xxx_profile_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int index = 0; + char *profile; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + if (!aw87xxx->current_profile) { + AW_DEV_LOGE(aw87xxx->dev, "profile not init"); + return -EINVAL; + } + + profile = aw87xxx->current_profile; + AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", + aw87xxx->current_profile); + + + index = aw87xxx_acf_get_prof_index_form_name(aw87xxx->dev, + &aw87xxx->acf_info, aw87xxx->current_profile); + if (index < 0) { + AW_DEV_LOGE(aw87xxx->dev, "get profile index failed"); + return index; + } + + ucontrol->value.integer.value[0] = index; + + return 0; +} + +static int aw87xxx_vmax_get_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = INT_MIN; + uinfo->value.integer.max = AW_VMAX_MAX; + + return 0; +} + +static int aw87xxx_vmax_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -1; + int vmax_val = 0; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + ret = aw87xxx_monitor_no_dsp_get_vmax(&aw87xxx->monitor, &vmax_val); + if (ret < 0) + return ret; + + ucontrol->value.integer.value[0] = vmax_val; + AW_DEV_LOGI(aw87xxx->dev, "get vmax = [0x%x]", vmax_val); + + return 0; +} + +static int aw87xxx_monitor_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_monitor_switch); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_monitor_switch[uinfo->value.enumerated.item], + strlen(aw87xxx_monitor_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw87xxx_monitor_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = ucontrol->value.integer.value[0]; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + struct aw_monitor *aw_monitor = &aw87xxx->monitor; + int ret = -1; + + ret = aw87xxx_dev_monitor_switch_set(aw_monitor, ctrl_value); + if (ret) + return ret; + + return 0; +} + +static int aw87xxx_monitor_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + struct aw_monitor *aw_monitor = &aw87xxx->monitor; + + ucontrol->value.integer.value[0] = aw_monitor->monitor_hdr.monitor_switch; + + AW_DEV_LOGI(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw87xxx_spin_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_spin_switch); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_spin_switch[uinfo->value.enumerated.item], + strlen(aw87xxx_spin_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw87xxx_spin_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = 0; + int ret = 0; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw87xxx_dsp_set_spin(ctrl_value); + if (ret) { + AW_DEV_LOGE(aw87xxx->dev, "write spin failed"); + return ret; + } + AW_DEV_LOGD(aw87xxx->dev, "write spin done ctrl_value=%d", ctrl_value); + return 0; +} + +static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = aw87xxx_dsp_get_spin(); + AW_DEV_LOGD(aw87xxx->dev, "current spin is %ld", ucontrol->value.integer.value[0]); + + return 0; +} + + +static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, + void *codec) +{ + struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; + aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; + char *kctl_name[AW87XXX_PRIVATE_KCONTROL_NUM]; + int kcontrol_num = AW87XXX_PRIVATE_KCONTROL_NUM; + int ret = -1; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + aw87xxx->codec = soc_codec; + + aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, + sizeof(struct snd_kcontrol_new) * kcontrol_num, + GFP_KERNEL); + if (aw87xxx_kcontrol == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); + return -ENOMEM; + } + + kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[0] == NULL) + return -ENOMEM; + + snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_profile_switch_%d", + aw87xxx->dev_index); + + aw87xxx_kcontrol[0].name = kctl_name[0]; + aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[0].info = aw87xxx_profile_switch_info; + aw87xxx_kcontrol[0].get = aw87xxx_profile_switch_get; + aw87xxx_kcontrol[0].put = aw87xxx_profile_switch_put; + aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; + + kctl_name[1] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[1] == NULL) + return -ENOMEM; + + snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_vmax_get_%d", + aw87xxx->dev_index); + + aw87xxx_kcontrol[1].name = kctl_name[1]; + aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[1].access = SNDRV_CTL_ELEM_ACCESS_READ; + aw87xxx_kcontrol[1].info = aw87xxx_vmax_get_info; + aw87xxx_kcontrol[1].get = aw87xxx_vmax_get; + aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; + + kctl_name[2] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[2] == NULL) + return -ENOMEM; + + snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_monitor_switch_%d", + aw87xxx->dev_index); + + aw87xxx_kcontrol[2].name = kctl_name[2]; + aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[2].info = aw87xxx_monitor_switch_info; + aw87xxx_kcontrol[2].get = aw87xxx_monitor_switch_get; + aw87xxx_kcontrol[2].put = aw87xxx_monitor_switch_put; + aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; + + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, + aw87xxx_kcontrol, kcontrol_num); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", + ret); + return ret; + } + + AW_DEV_LOGI(aw87xxx->dev, "add codec controls[%s,%s,%s]", + aw87xxx_kcontrol[0].name, + aw87xxx_kcontrol[1].name, + aw87xxx_kcontrol[2].name); + + return 0; +} + +static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, + void *codec) +{ + struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; + aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; + char *kctl_name[AW87XXX_PUBLIC_KCONTROL_NUM]; + int kcontrol_num = AW87XXX_PUBLIC_KCONTROL_NUM; + int ret = -1; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + aw87xxx->codec = soc_codec; + + aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, + sizeof(struct snd_kcontrol_new) * kcontrol_num, + GFP_KERNEL); + if (aw87xxx_kcontrol == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); + return -ENOMEM; + } + + kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[0] == NULL) + return -ENOMEM; + + snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_spin_switch"); + + aw87xxx_kcontrol[0].name = kctl_name[0]; + aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[0].info = aw87xxx_spin_switch_info; + aw87xxx_kcontrol[0].get = aw87xxx_spin_switch_get; + aw87xxx_kcontrol[0].put = aw87xxx_spin_switch_put; + aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; + + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, + aw87xxx_kcontrol, kcontrol_num); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", + ret); + return ret; + } + + AW_DEV_LOGI(aw87xxx->dev, "add public codec controls[%s]", + aw87xxx_kcontrol[0].name); + + return 0; +} + +/**************************************************************************** + * + *aw87xxx kcontrol create + * + ****************************************************************************/ +int aw87xxx_add_codec_controls(void *codec) +{ + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + int ret = -1; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, codec); + if (ret < 0) + return ret; + + if (aw87xxx->dev_index == 0) { + ret = aw87xxx_public_kcontrol_create(aw87xxx, codec); + if (ret < 0) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL(aw87xxx_add_codec_controls); + + +/**************************************************************************** + * + * aw87xxx firmware cfg load + * + ***************************************************************************/ +static void aw87xxx_fw_cfg_free(struct aw87xxx *aw87xxx) +{ + AW_DEV_LOGD(aw87xxx->dev, "enter"); + aw87xxx_acf_profile_free(aw87xxx->dev, &aw87xxx->acf_info); + aw87xxx_monitor_cfg_free(&aw87xxx->monitor); +} + +static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) +{ + char *profile = NULL; + + profile = aw87xxx_acf_get_prof_off_name(aw87xxx->dev, &aw87xxx->acf_info); + if (profile == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "get profile off name failed"); + return -EINVAL; + } + + snprintf(aw87xxx->prof_off_name, AW_PROFILE_STR_MAX, "%s", profile); + aw87xxx->current_profile = profile; + AW_DEV_LOGI(aw87xxx->dev, "init profile name [%s]", + aw87xxx->current_profile); + + return 0; +} + +static void aw87xxx_fw_load_retry(struct aw87xxx *aw87xxx) +{ + struct acf_bin_info *acf_info = &aw87xxx->acf_info; + int ram_timer_val = 2000; + + AW_DEV_LOGD(aw87xxx->dev, "failed to read [%s]", + aw87xxx->fw_name); + + if (acf_info->load_count < AW_LOAD_FW_RETRIES) { + AW_DEV_LOGD(aw87xxx->dev, + "restart hrtimer to load firmware"); + schedule_delayed_work(&aw87xxx->fw_load_work, + msecs_to_jiffies(ram_timer_val)); + } else { + acf_info->load_count = 0; + AW_DEV_LOGE(aw87xxx->dev, + "can not load firmware,please check name or file exists"); + return; + } + acf_info->load_count++; +} + +static void aw87xxx_fw_load(const struct firmware *fw, void *context) +{ + int ret = -1; + struct aw87xxx *aw87xxx = context; + struct acf_bin_info *acf_info = &aw87xxx->acf_info; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + if (!fw) { + aw87xxx_fw_load_retry(aw87xxx); + return; + } + + AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %ld", + aw87xxx->fw_name, (u_long)(fw ? fw->size : 0)); + + mutex_lock(&aw87xxx->reg_lock); + acf_info->fw_data = vmalloc(fw->size); + if (!acf_info->fw_data) { + AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); + goto exit_vmalloc_failed; + } + memset(acf_info->fw_data, 0, fw->size); + memcpy(acf_info->fw_data, fw->data, fw->size); + acf_info->fw_size = fw->size; + + ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed"); + goto exit_acf_parse_failed; + } + + ret = aw87xxx_init_default_prof(aw87xxx); + if (ret < 0) { + aw87xxx_fw_cfg_free(aw87xxx); + goto exit_acf_parse_failed; + } + + AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); + mutex_unlock(&aw87xxx->reg_lock); + release_firmware(fw); + return; + +exit_acf_parse_failed: +exit_vmalloc_failed: + release_firmware(fw); + mutex_unlock(&aw87xxx->reg_lock); +} + +static void aw87xxx_fw_load_work_routine(struct work_struct *work) +{ + struct aw87xxx *aw87xxx = container_of(work, + struct aw87xxx, fw_load_work.work); + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + if (prof_info->status == AW_ACF_WAIT) { + request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, + aw87xxx->fw_name, + aw87xxx->dev, + GFP_KERNEL, aw87xxx, + aw87xxx_fw_load); + } +} + +static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) +{ +#ifdef AW_CFG_UPDATE_DELAY + int cfg_timer_val = AW_CFG_UPDATE_DELAY_TIMER; +#else + int cfg_timer_val = 0; +#endif + AW_DEV_LOGI(aw87xxx->dev, "enter"); + snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); + aw87xxx_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); + + INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work_routine); + schedule_delayed_work(&aw87xxx->fw_load_work, + msecs_to_jiffies(cfg_timer_val)); +} + +/**************************************************************************** + * + *aw87xxx attribute node + * + ****************************************************************************/ +static ssize_t aw87xxx_attr_get_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_device *aw_dev = &aw87xxx->aw_dev; + + mutex_lock(&aw87xxx->reg_lock); + for (i = 0; i < aw_dev->reg_max_addr; i++) { + if (!(aw_dev->reg_access[i] & AW_DEV_REG_RD_ACCESS)) + continue; + ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, i, ®_val); + if (ret < 0) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read reg [0x%x] failed\n", i); + AW_DEV_LOGE(aw87xxx->dev, "read reg [0x%x] failed", i); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_val); + AW_DEV_LOGD(aw87xxx->dev, "reg:0x%02X=0x%02X", + i, reg_val); + } + } + mutex_unlock(&aw87xxx->reg_lock); + + return len; +} + +static ssize_t aw87xxx_attr_set_reg(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + unsigned int databuf[2] = { 0 }; + int ret = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + + mutex_lock(&aw87xxx->reg_lock); + if (sscanf(buf, "0x%x 0x%x", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] >= aw87xxx->aw_dev.reg_max_addr) { + AW_DEV_LOGE(aw87xxx->dev, "set reg[0x%x] error,is out of reg_addr_max[0x%x]", + databuf[0], aw87xxx->aw_dev.reg_max_addr); + mutex_unlock(&aw87xxx->reg_lock); + return -EINVAL; + } + + ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, + databuf[0], databuf[1]); + if (ret < 0) + AW_DEV_LOGE(aw87xxx->dev, "set [0x%x]=0x%x failed", + databuf[0], databuf[1]); + else + AW_DEV_LOGD(aw87xxx->dev, "set [0x%x]=0x%x succeed", + databuf[0], databuf[1]); + } else { + AW_DEV_LOGE(aw87xxx->dev, "i2c write cmd input error"); + } + mutex_unlock(&aw87xxx->reg_lock); + + return len; +} + +static ssize_t aw87xxx_attr_get_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + unsigned int i = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + + if (!prof_info->status) { + len += snprintf(buf + len, PAGE_SIZE - len, + "profile_cfg not load\n"); + return len; + } + + AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); + + for (i = 0; i < prof_info->count; i++) { + if (!strncmp(aw87xxx->current_profile, prof_info->prof_name_list[i], + AW_PROFILE_STR_MAX)) + len += snprintf(buf + len, PAGE_SIZE - len, + ">%s\n", prof_info->prof_name_list[i]); + else + len += snprintf(buf + len, PAGE_SIZE - len, + " %s\n", prof_info->prof_name_list[i]); + } + + return len; +} + +static ssize_t aw87xxx_attr_set_profile(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + char profile[AW_PROFILE_STR_MAX] = {0}; + int ret = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + + if (strlen(buf) > AW_PROFILE_STR_MAX) { + AW_DEV_LOGE(aw87xxx->dev, "input profile_str_len is out of max[%d]", + AW_PROFILE_STR_MAX); + return -EINVAL; + } + + if (sscanf(buf, "%s", profile) == 1) { + AW_DEV_LOGD(aw87xxx->dev, "set profile [%s]", profile); + ret = aw87xxx_update_profile(aw87xxx, profile); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "set profile[%s] failed", + profile); + return ret; + } + } + + return len; +} + +static ssize_t aw87xxx_attr_get_hwen(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + int hwen = aw87xxx->aw_dev.hwen_status; + + if (hwen >= AW_DEV_HWEN_INVALID) + len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: invalid\n"); + else if (hwen == AW_DEV_HWEN_ON) + len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: on\n"); + else if (hwen == AW_DEV_HWEN_OFF) + len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: off\n"); + + return len; +} + +static ssize_t aw87xxx_attr_set_hwen(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + int ret = -1; + unsigned int state; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + + ret = kstrtouint(buf, 0, &state); + if (ret) { + AW_DEV_LOGE(aw87xxx->dev, "fail to channelge str to int"); + return ret; + } + + mutex_lock(&aw87xxx->reg_lock); + if (state == AW_DEV_HWEN_OFF) + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /*OFF*/ + else if (state == AW_DEV_HWEN_ON) + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /*ON*/ + else + AW_DEV_LOGE(aw87xxx->dev, "input [%d] error, hwen_on=[%d],hwen_off=[%d]", + state, AW_DEV_HWEN_ON, AW_DEV_HWEN_OFF); + mutex_unlock(&aw87xxx->reg_lock); + return len; +} + +int aw87xxx_awrw_write(struct aw87xxx *aw87xxx, + const char *buf, size_t count) +{ + int i = 0, ret = -1; + char *data_buf = NULL; + int buf_len = 0; + int temp_data = 0; + int data_str_size = 0; + char *reg_data; + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + /* one addr or one data string Composition of Contains two bytes of symbol(0X)*/ + /* and two byte of hexadecimal data*/ + data_str_size = 2 + 2 * AWRW_DATA_BYTES; + + /* The buf includes the first address of the register to be written and all data */ + buf_len = AWRW_ADDR_BYTES + packet->reg_num * AWRW_DATA_BYTES; + AW_DEV_LOGI(aw87xxx->dev, "buf_len = %d,reg_num = %d", buf_len, packet->reg_num); + data_buf = vmalloc(buf_len); + if (data_buf == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "alloc memory failed"); + return -ENOMEM; + } + memset(data_buf, 0, buf_len); + + data_buf[0] = packet->reg_addr; + reg_data = data_buf + 1; + + AW_DEV_LOGD(aw87xxx->dev, "reg_addr: 0x%02x", data_buf[0]); + + /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ + for (i = 0; i < packet->reg_num; i++) { + ret = sscanf(buf + AWRW_HDR_LEN + 1 + i * (data_str_size + 1), + "0x%x", &temp_data); + if (ret != 1) { + AW_DEV_LOGE(aw87xxx->dev, "sscanf failed,ret=%d", ret); + vfree(data_buf); + data_buf = NULL; + return ret; + } + reg_data[i] = temp_data; + AW_DEV_LOGD(aw87xxx->dev, "[%d] : 0x%02x", i, reg_data[i]); + } + + mutex_lock(&aw87xxx->reg_lock); + ret = i2c_master_send(aw87xxx->aw_dev.i2c, data_buf, buf_len); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "write failed"); + vfree(data_buf); + data_buf = NULL; + return -EFAULT; + } + mutex_unlock(&aw87xxx->reg_lock); + + vfree(data_buf); + data_buf = NULL; + + AW_DEV_LOGD(aw87xxx->dev, "down"); + return 0; +} + +static int aw87xxx_awrw_data_check(struct aw87xxx *aw87xxx, + int *data, size_t count) +{ + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int req_data_len = 0; + int act_data_len = 0; + int data_str_size = 0; + + if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || + (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { + AW_DEV_LOGE(aw87xxx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", + data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); + return -EINVAL; + } + + /* one data string Composition of Contains two bytes of symbol(0x)*/ + /* and two byte of hexadecimal data*/ + data_str_size = 2 + 2 * AWRW_DATA_BYTES; + act_data_len = count - AWRW_HDR_LEN - 1; + + /* There is a comma(,) or space between each piece of data */ + if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { + /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ + req_data_len = (data_str_size + 1) * packet->reg_num; + if (req_data_len > act_data_len) { + AW_DEV_LOGE(aw87xxx->dev, "data_len checkfailed,requeset data_len [%d],actaul data_len [%d]", + req_data_len, act_data_len); + return -EINVAL; + } + } + + return 0; +} + +/* flag addr_bytes data_bytes reg_num reg_addr*/ +static int aw87xxx_awrw_parse_buf(struct aw87xxx *aw87xxx, + const char *buf, size_t count, int *wr_status) +{ + int data[AWRW_HDR_MAX] = {0}; + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int ret = -1; + + if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + &data[AWRW_HDR_WR_FLAG], &data[AWRW_HDR_ADDR_BYTES], + &data[AWRW_HDR_DATA_BYTES], &data[AWRW_HDR_REG_NUM], + &data[AWRW_HDR_REG_ADDR]) == 5) { + + packet->reg_addr = data[AWRW_HDR_REG_ADDR]; + packet->reg_num = data[AWRW_HDR_REG_NUM]; + *wr_status = data[AWRW_HDR_WR_FLAG]; + ret = aw87xxx_awrw_data_check(aw87xxx, data, count); + if (ret < 0) + return ret; + + return 0; + } + + return -EINVAL; +} + +static ssize_t aw87xxx_attr_awrw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int wr_status = 0; + int ret = -1; + + if (count < AWRW_HDR_LEN) { + AW_DEV_LOGE(aw87xxx->dev, "data count too smaller, please check write format"); + AW_DEV_LOGE(aw87xxx->dev, "string %s,count=%ld", + buf, (u_long)count); + return -EINVAL; + } + + AW_DEV_LOGI(aw87xxx->dev, "string:[%s],count=%ld", buf, (u_long)count); + ret = aw87xxx_awrw_parse_buf(aw87xxx, buf, count, &wr_status); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "can not parse string"); + return ret; + } + + if (wr_status == AWRW_FLAG_WRITE) { + ret = aw87xxx_awrw_write(aw87xxx, buf, count); + if (ret < 0) + return ret; + } else if (wr_status == AWRW_FLAG_READ) { + packet->status = AWRW_I2C_ST_READ; + AW_DEV_LOGI(aw87xxx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + } else { + AW_DEV_LOGE(aw87xxx->dev, "please check str format, unsupport read_write_status: %d", + wr_status); + return -EINVAL; + } + + return count; +} + +static ssize_t aw87xxx_attr_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int data_len = 0; + size_t len = 0; + int ret = -1, i = 0; + char *reg_data = NULL; + + if (packet->status != AWRW_I2C_ST_READ) { + AW_DEV_LOGE(aw87xxx->dev, "please write read cmd first"); + return -EINVAL; + } + + data_len = AWRW_DATA_BYTES * packet->reg_num; + reg_data = (char *)vmalloc(data_len); + if (reg_data == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "memory alloc failed"); + ret = -EINVAL; + goto exit; + } + + mutex_lock(&aw87xxx->reg_lock); + ret = aw87xxx_dev_i2c_read_msg(&aw87xxx->aw_dev, packet->reg_addr, + (char *)reg_data, data_len); + if (ret < 0) { + ret = -EFAULT; + mutex_unlock(&aw87xxx->reg_lock); + goto exit; + } + mutex_unlock(&aw87xxx->reg_lock); + + AW_DEV_LOGI(aw87xxx->dev, "reg_addr 0x%02x, reg_num %d", + packet->reg_addr, packet->reg_num); + + for (i = 0; i < data_len; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + AW_DEV_LOGI(aw87xxx->dev, "0x%02x", reg_data[i]); + } + + ret = len; + +exit: + if (reg_data) { + vfree(reg_data); + reg_data = NULL; + } + packet->status = AWRW_I2C_ST_NONE; + return ret; +} + +static ssize_t aw87xxx_drv_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "driver_ver: %s \n", AW87XXX_DRIVER_VERSION); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw87xxx_attr_get_reg, aw87xxx_attr_set_reg); +static DEVICE_ATTR(profile, S_IWUSR | S_IRUGO, + aw87xxx_attr_get_profile, aw87xxx_attr_set_profile); +static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, + aw87xxx_attr_get_hwen, aw87xxx_attr_set_hwen); +static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, + aw87xxx_attr_awrw_show, aw87xxx_attr_awrw_store); +static DEVICE_ATTR(drv_ver, S_IRUGO, aw87xxx_drv_ver_show, NULL); + +static struct attribute *aw87xxx_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_profile.attr, + &dev_attr_hwen.attr, + &dev_attr_awrw.attr, + &dev_attr_drv_ver.attr, + NULL +}; + +static struct attribute_group aw87xxx_attribute_group = { + .attrs = aw87xxx_attributes +}; + +/**************************************************************************** + * + *aw87xxx device probe + * + ****************************************************************************/ + +int aw87xxx_dtsi_dev_index_check(struct aw87xxx *cur_aw87xxx) +{ + struct list_head *pos = NULL; + struct aw87xxx *list_aw87xxx = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + list_aw87xxx = list_entry(pos, struct aw87xxx, list); + if (list_aw87xxx->dev_index == cur_aw87xxx->dev_index) { + AW_DEV_LOGE(cur_aw87xxx->dev, "dev_index has already existing,check failed"); + return -EINVAL; + } + } + + return 0; +} + +static int aw87xxx_dtsi_parse(struct aw87xxx *aw87xxx, + struct device_node *dev_node) +{ + int ret = -1; + int32_t dev_index = -EINVAL; + + ret = of_property_read_u32(dev_node, "dev_index", &dev_index); + if (ret < 0) { + AW_DEV_LOGI(aw87xxx->dev, "dev_index parse failed, user default[%d], ret=%d", + g_aw87xxx_dev_cnt, ret); + aw87xxx->dev_index = g_aw87xxx_dev_cnt; + } else { + aw87xxx->dev_index = dev_index; + AW_DEV_LOGI(aw87xxx->dev, "parse dev_index=[%d]", + aw87xxx->dev_index); + } + + ret = aw87xxx_dtsi_dev_index_check(aw87xxx); + if (ret < 0) + return ret; + + ret = of_get_named_gpio(dev_node, "reset-gpio", 0); + if (ret < 0) { + AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); + aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; + } else { + aw87xxx->aw_dev.rst_gpio = ret; + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; + AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%d] parse succeed", ret); + if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { + ret = devm_gpio_request_one(aw87xxx->dev, + aw87xxx->aw_dev.rst_gpio, + GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "reset request failed"); + return ret; + } + } + } + + aw87xxx_device_parse_port_id_dt(&aw87xxx->aw_dev); + aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); + + return 0; +} + +static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) +{ + struct aw87xxx *aw87xxx = NULL; + + aw87xxx = devm_kzalloc(&client->dev, sizeof(struct aw87xxx), + GFP_KERNEL); + if (aw87xxx == NULL) { + AW_DEV_LOGE(&client->dev, "failed to devm_kzalloc aw87xxx"); + return NULL; + } + memset(aw87xxx, 0, sizeof(struct aw87xxx)); + + aw87xxx->dev = &client->dev; + aw87xxx->aw_dev.dev = &client->dev; + aw87xxx->aw_dev.i2c_bus = client->adapter->nr; + aw87xxx->aw_dev.i2c_addr = client->addr; + aw87xxx->aw_dev.i2c = client; + aw87xxx->aw_dev.hwen_status = false; + aw87xxx->aw_dev.reg_access = NULL; + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; + aw87xxx->off_bin_status = AW87XXX_NO_OFF_BIN; + aw87xxx->codec = NULL; + aw87xxx->current_profile = aw87xxx->prof_off_name; + + mutex_init(&aw87xxx->reg_lock); + + AW_DEV_LOGI(&client->dev, "struct aw87xxx devm_kzalloc and init down"); + return aw87xxx; +} + +static int aw87xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *dev_node = client->dev.of_node; + struct aw87xxx *aw87xxx = NULL; + int ret = -1; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + AW_DEV_LOGE(&client->dev, "check_functionality failed"); + ret = -ENODEV; + goto exit_check_functionality_failed; + } + + /* aw87xxx i2c_dev struct init */ + aw87xxx = aw87xxx_malloc_init(client); + if (aw87xxx == NULL) + goto exit_malloc_init_failed; + + i2c_set_clientdata(client, aw87xxx); + + /* aw87xxx dev_node parse */ + ret = aw87xxx_dtsi_parse(aw87xxx, dev_node); + if (ret < 0) + goto exit_device_init_failed; + + /*hw power on PA*/ + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); + + /* aw87xxx devices private attributes init */ + ret = aw87xxx_dev_init(&aw87xxx->aw_dev); + if (ret < 0) + goto exit_device_init_failed; + + /*product register reset */ + aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); + + /*hw power off */ + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); + + /* create debug attrbute nodes */ + ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); + if (ret < 0) + AW_DEV_LOGE(aw87xxx->dev, "failed to create sysfs nodes, will not allowed to use"); + + /* cfg_load init */ + aw87xxx_fw_load_init(aw87xxx); + + /*monitor init*/ + aw87xxx_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); + + /*add device to total list */ + mutex_lock(&g_aw87xxx_mutex_lock); + g_aw87xxx_dev_cnt++; + list_add(&aw87xxx->list, &g_aw87xxx_list); + mutex_unlock(&g_aw87xxx_mutex_lock); + + AW_DEV_LOGI(aw87xxx->dev, "succeed, dev_index=[%d], g_aw87xxx_dev_cnt= [%d]", + aw87xxx->dev_index, g_aw87xxx_dev_cnt); + + return 0; + +exit_device_init_failed: + AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); + if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) + devm_gpio_free(&client->dev, aw87xxx->aw_dev.rst_gpio); + devm_kfree(&client->dev, aw87xxx); + aw87xxx = NULL; +exit_malloc_init_failed: +exit_check_functionality_failed: + return ret; +} + +static int aw87xxx_i2c_remove(struct i2c_client *client) +{ + struct aw87xxx *aw87xxx = i2c_get_clientdata(client); + + if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) + devm_gpio_free(&client->dev, aw87xxx->aw_dev.rst_gpio); + + aw87xxx_monitor_exit(&aw87xxx->monitor); + + /*rm attr node*/ + sysfs_remove_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); + + aw87xxx_fw_cfg_free(aw87xxx); + + mutex_lock(&g_aw87xxx_mutex_lock); + g_aw87xxx_dev_cnt--; + list_del(&aw87xxx->list); + mutex_unlock(&g_aw87xxx_mutex_lock); + + devm_kfree(&client->dev, aw87xxx); + aw87xxx = NULL; + + return 0; +} + +static void aw87xxx_i2c_shutdown(struct i2c_client *client) +{ + struct aw87xxx *aw87xxx = i2c_get_clientdata(client); + + AW_DEV_LOGI(&client->dev, "enter"); + + /*soft and hw power off*/ + aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); +} + + +static const struct i2c_device_id aw87xxx_i2c_id[] = { + {AW87XXX_I2C_NAME, 0}, + {}, +}; + +static const struct of_device_id extpa_of_match[] = { + {.compatible = "awinic,aw87xxx_pa"}, + {}, +}; + +static struct i2c_driver aw87xxx_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = AW87XXX_I2C_NAME, + .of_match_table = extpa_of_match, + }, + .probe = aw87xxx_i2c_probe, + .remove = aw87xxx_i2c_remove, + .shutdown = aw87xxx_i2c_shutdown, + .id_table = aw87xxx_i2c_id, +}; + +static int __init aw87xxx_pa_init(void) +{ + int ret; + + AW_LOGI("driver version: %s", AW87XXX_DRIVER_VERSION); + + ret = i2c_add_driver(&aw87xxx_i2c_driver); + if (ret < 0) { + AW_LOGE("Unable to register driver, ret= %d", ret); + return ret; + } + return 0; +} + +static void __exit aw87xxx_pa_exit(void) +{ + AW_LOGI("enter"); + i2c_del_driver(&aw87xxx_i2c_driver); +} + +module_init(aw87xxx_pa_init); +module_exit(aw87xxx_pa_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("awinic aw87xxx pa driver"); +MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/aw87xxx/aw87xxx.h b/asoc/codecs/aw87xxx/aw87xxx.h new file mode 100644 index 000000000000..45d3cea7779a --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx.h @@ -0,0 +1,121 @@ +#ifndef __AW87XXX_H__ +#define __AW87XXX_H__ +#include +#include +#include +#include + +#include "aw87xxx_device.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_acf_bin.h" + +#define AW_CFG_UPDATE_DELAY +#define AW_CFG_UPDATE_DELAY_TIMER (3000) + +#define AW87XXX_NO_OFF_BIN (0) +#define AW87XXX_OFF_BIN_OK (1) + +#define AW87XXX_PRIVATE_KCONTROL_NUM (3) +#define AW87XXX_PUBLIC_KCONTROL_NUM (1) + +#define AW_I2C_RETRIES (5) +#define AW_I2C_RETRY_DELAY (2) +#define AW_I2C_READ_MSG_NUM (2) + +#define AW87XXX_FW_NAME_MAX (64) +#define AW_NAME_BUF_MAX (64) +#define AW_LOAD_FW_RETRIES (3) + +#define AW_DEV_REG_RD_ACCESS (1 << 0) +#define AW_DEV_REG_WR_ACCESS (1 << 1) + +#define AWRW_ADDR_BYTES (1) +#define AWRW_DATA_BYTES (1) +#define AWRW_HDR_LEN (24) + +/*********************************************************** + * + * aw87xxx codec control compatible with kernel 4.19 + * + ***********************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +#endif + +struct aw_componet_codec_ops { + int (*add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*unregister_codec)(struct device *dev); +}; + + +/******************************************** + * + * aw87xxx devices attributes + * + *******************************************/ +enum { + AWRW_FLAG_WRITE = 0, + AWRW_FLAG_READ, +}; + +enum { + AWRW_I2C_ST_NONE = 0, + AWRW_I2C_ST_READ, + AWRW_I2C_ST_WRITE, +}; + +enum { + AWRW_HDR_WR_FLAG = 0, + AWRW_HDR_ADDR_BYTES, + AWRW_HDR_DATA_BYTES, + AWRW_HDR_REG_NUM, + AWRW_HDR_REG_ADDR, + AWRW_HDR_MAX, +}; + +struct aw_i2c_packet { + char status; + unsigned int reg_num; + unsigned int reg_addr; + char *reg_data; +}; + + +/******************************************** + * + * aw87xxx device struct + * + *******************************************/ +struct aw87xxx { + char fw_name[AW87XXX_FW_NAME_MAX]; + int32_t dev_index; + char *current_profile; + char prof_off_name[AW_PROFILE_STR_MAX]; + uint32_t off_bin_status; + struct device *dev; + + struct mutex reg_lock; + struct aw_device aw_dev; + struct aw_i2c_packet i2c_packet; + + struct delayed_work fw_load_work; + struct acf_bin_info acf_info; + + aw_snd_soc_codec_t *codec; + + struct list_head list; + + struct aw_monitor monitor; +}; + +int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile); +int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile); + +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_acf_bin.c b/asoc/codecs/aw87xxx/aw87xxx_acf_bin.c new file mode 100644 index 000000000000..00c7aedb7c11 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_acf_bin.c @@ -0,0 +1,1558 @@ +/* + * aw87xxx_acf_bin.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_acf_bin.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_log.h" +#include "aw87xxx_bin_parse.h" + +/************************************************************************* + * + *Table corresponding to customized profile ids to profile names + * + *************************************************************************/ +enum aw_customers_profile_id { + AW_CTOS_PROFILE_OFF = 0, + AW_CTOS_PROFILE_MUSIC, + AW_CTOS_PROFILE_VOICE, + AW_CTOS_PROFILE_VOIP, + AW_CTOS_PROFILE_RINGTONE, + AW_CTOS_PROFILE_RINGTONE_HS, + AW_CTOS_PROFILE_LOWPOWER, + AW_CTOS_PROFILE_BYPASS, + AW_CTOS_PROFILE_MMI, + AW_CTOS_PROFILE_FM, + AW_CTOS_PROFILE_NOTIFICATION, + AW_CTOS_PROFILE_RECEIVER, + AW_CTOS_PROFILE_MAX, +}; + +static char *g_ctos_profile_name[AW_PROFILE_MAX] = { + [AW_CTOS_PROFILE_OFF] = "Off", + [AW_CTOS_PROFILE_MUSIC] = "Music", + [AW_CTOS_PROFILE_VOICE] = "Voice", + [AW_CTOS_PROFILE_VOIP] = "Voip", + [AW_CTOS_PROFILE_RINGTONE] = "Ringtone", + [AW_CTOS_PROFILE_RINGTONE_HS] = "Ringtone_hs", + [AW_CTOS_PROFILE_LOWPOWER] = "Lowpower", + [AW_CTOS_PROFILE_BYPASS] = "Bypass", + [AW_CTOS_PROFILE_MMI] = "Mmi", + [AW_CTOS_PROFILE_FM] = "Fm", + [AW_CTOS_PROFILE_NOTIFICATION] = "Notification", + [AW_CTOS_PROFILE_RECEIVER] = "Receiver", +}; + + +char *aw87xxx_ctos_get_prof_name(int profile_id) +{ + if (profile_id < 0 || profile_id >= AW_CTOS_PROFILE_MAX) + return NULL; + else + return g_ctos_profile_name[profile_id]; +} + + +static char *g_profile_name[] = {"Music", "Voice", "Voip", + "Ringtone", "Ringtone_hs", "Lowpower", "Bypass", "Mmi", + "Fm", "Notification", "Receiver", "Off"}; + +static char *g_power_off_name[] = {"Off", "OFF", "off", "oFF", "power_down"}; + +static char *aw_get_prof_name(int profile) +{ + if (profile < 0 || profile >= AW_PROFILE_MAX) + return "NULL"; + else + return g_profile_name[profile]; +} + +/************************************************************************* + * + *acf check + * + *************************************************************************/ +static int aw_crc8_check(const unsigned char *data, unsigned int data_size) + +{ + unsigned char crc_value = 0x00; + unsigned char *pdata; + int i; + unsigned char pdatabuf = 0; + + pdata = (unsigned char *)data; + + while (data_size--) { + pdatabuf = *pdata++; + for (i = 0; i < 8; i++) { + if ((crc_value ^ (pdatabuf)) & 0x01) { + crc_value ^= 0x18; + crc_value >>= 1; + crc_value |= 0x80; + } else { + crc_value >>= 1; + } + pdatabuf >>= 1; + } + } + + return (int)crc_value; +} + +static int aw_check_file_id(struct device *dev, + char *fw_data, int32_t file_id) +{ + int32_t *acf_file_id = NULL; + + acf_file_id = (int32_t *)fw_data; + if (*acf_file_id != file_id) { + AW_DEV_LOGE(dev, "file id [%x] check failed", *acf_file_id); + return -ENFILE; + } + + return 0; +} + +static int aw_check_header_size(struct device *dev, + char *fw_data, size_t fw_size) +{ + if (fw_size < sizeof(struct aw_acf_hdr)) { + AW_DEV_LOGE(dev, "acf size check failed,size less-than aw_acf_hdr"); + return -ENOEXEC; + } + + return 0; +} + +/*************************************************************************** + * V0.0.0.1 version acf check + **************************************************************************/ +static int aw_check_ddt_size_v_0_0_0_1(struct device *dev, char *fw_data) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data; + struct aw_acf_dde *acf_dde = NULL; + + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + /* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */ + if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde)) { + AW_DEV_LOGE(dev, "acf ddt size check failed"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_size_v_0_0_0_1(struct device *dev, + char *fw_data, size_t fw_size) +{ + int i = 0; + size_t data_size = 0; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (acf_dde[i].data_size % 2) { + AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed", + i, acf_dde[i].data_size, acf_dde[i].dev_name, + acf_dde[i].data_type); + return -EINVAL; + } + data_size += acf_dde[i].data_size; + } + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) { + AW_DEV_LOGE(dev, "acf size check failed"); + AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld", + (u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) + + acf_hdr->ddt_size)); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_crc_v_0_0_0_1(struct device *dev, char *fw_data) +{ + int i = 0; + size_t crc_val = 0; + char *data = NULL; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + data = fw_data + acf_dde[i].data_offset; + crc_val = aw_crc8_check(data, acf_dde[i].data_size); + if (crc_val != acf_dde[i].data_crc) { + AW_DEV_LOGE(dev, "acf dde_crc check failed"); + return -EINVAL; + } + } + + return 0; +} + +static int aw_check_profile_id_v_0_0_0_1(struct device *dev, char *fw_data) +{ + int i = 0; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (acf_dde[i].data_type == AW_MONITOR) + continue; + if (acf_dde[i].dev_profile > AW_PROFILE_MAX) { + AW_DEV_LOGE(dev, "parse profile_id[%d] failed", acf_dde[i].dev_profile); + return -EINVAL; + } + } + + return 0; +} +static int aw_check_data_v_0_0_0_1(struct device *dev, + char *fw_data, size_t size) +{ + int ret = -1; + + /* check file type id is awinic acf file */ + ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID); + if (ret < 0) + return ret; + + /* check ddt_size in header is equal to all ddt aize */ + ret = aw_check_ddt_size_v_0_0_0_1(dev, fw_data); + if (ret < 0) + return ret; + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + ret = aw_check_data_size_v_0_0_0_1(dev, fw_data, size); + if (ret < 0) + return ret; + + /* check crc in is equal to dde data crc */ + ret = aw_check_data_crc_v_0_0_0_1(dev, fw_data); + if (ret < 0) + return ret; + + /* check profile id is in profile_id_max */ + ret = aw_check_profile_id_v_0_0_0_1(dev, fw_data); + if (ret < 0) + return ret; + + AW_DEV_LOGI(dev, "acf fimware check succeed"); + + return 0; +} + +/*************************************************************************** + * V1.0.0.0 version acf chack + **************************************************************************/ +static int aw_check_ddt_size_v_1_0_0_0(struct device *dev, char *fw_data) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; + + acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); + + /* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */ + if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde_v_1_0_0_0)) { + AW_DEV_LOGE(dev, "acf ddt size check failed"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_size_v_1_0_0_0(struct device *dev, + char *fw_data, size_t fw_size) +{ + int i = 0; + size_t data_size = 0; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (acf_dde[i].data_size % 2) { + AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed", + i, acf_dde[i].data_size, acf_dde[i].dev_name, + acf_dde[i].data_type); + return -EINVAL; + } + data_size += acf_dde[i].data_size; + } + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) { + AW_DEV_LOGE(dev, "acf size check failed"); + AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld", + (u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) + + acf_hdr->ddt_size)); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_crc_v_1_0_0_0(struct device *dev, char *fw_data) +{ + int i = 0; + size_t crc_val = 0; + char *data = NULL; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + data = fw_data + acf_dde[i].data_offset; + crc_val = aw_crc8_check(data, acf_dde[i].data_size); + if (crc_val != acf_dde[i].data_crc) { + AW_DEV_LOGE(dev, "acf dde_crc check failed"); + return -EINVAL; + } + } + + return 0; +} + +static int aw_check_data_v_1_0_0_0(struct device *dev, + char *fw_data, size_t size) +{ + int ret = -1; + + /* check file type id is awinic acf file */ + ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID); + if (ret < 0) + return ret; + + /* check ddt_size in header is equal to all ddt aize */ + ret = aw_check_ddt_size_v_1_0_0_0(dev, fw_data); + if (ret < 0) + return ret; + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + ret = aw_check_data_size_v_1_0_0_0(dev, fw_data, size); + if (ret < 0) + return ret; + + /* check crc in is equal to dde data crc */ + ret = aw_check_data_crc_v_1_0_0_0(dev, fw_data); + if (ret < 0) + return ret; + + AW_DEV_LOGI(dev, "acf fimware check succeed"); + + return 0; +} + +/*************************************************************************** + * acf chack API + **************************************************************************/ +static int aw_check_acf_firmware(struct device *dev, + char *fw_data, size_t size) +{ + int ret = -1; + struct aw_acf_hdr *acf_hdr = NULL; + + if (fw_data == NULL) { + AW_DEV_LOGE(dev, "fw_data is NULL,fw_data check failed"); + return -ENODATA; + } + + /* check file size is less-than header size */ + ret = aw_check_header_size(dev, fw_data, size); + if (ret < 0) + return ret; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + AW_DEV_LOGI(dev, "project name: [%s]", acf_hdr->project); + AW_DEV_LOGI(dev, "custom name: [%s]", acf_hdr->custom); + AW_DEV_LOGI(dev, "version name: [%s]", acf_hdr->version); + AW_DEV_LOGI(dev, "author_id: [%d]", acf_hdr->author_id); + + switch (acf_hdr->hdr_version) { + case AW_ACF_HDR_VER_0_0_0_1: + return aw_check_data_v_0_0_0_1(dev, fw_data, size); + case AW_ACF_HDR_VER_1_0_0_0: + return aw_check_data_v_1_0_0_0(dev, fw_data, size); + default: + AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]", + acf_hdr->hdr_version); + return -EINVAL; + } + + return ret; +} + + + +/************************************************************************* + * + *acf parse + * + *************************************************************************/ +static int aw_parse_raw_reg(struct device *dev, uint8_t *data, + uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + AW_DEV_LOGD(dev, "data_size:%d enter", data_len); + + prof_desc->data_container.data = data; + prof_desc->data_container.len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static int aw_parse_reg_with_hdr(struct device *dev, uint8_t *data, + uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin = NULL; + int ret = -1; + + AW_DEV_LOGD(dev, "data_size:%d enter", data_len); + + aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (aw_bin == NULL) { + AW_DEV_LOGE(dev, "devm_kzalloc aw_bin failed"); + return -ENOMEM; + } + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw87xxx_parsing_bin_file(aw_bin); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + AW_DEV_LOGE(dev, "bin num or type error"); + goto parse_bin_failed; + } + + prof_desc->data_container.data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->data_container.len = aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW_PROFILE_OK; + + kfree(aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + kfree(aw_bin); + aw_bin = NULL; + return ret; +} + +static int aw_parse_monitor_config(struct device *dev, + char *monitor_data, uint32_t data_len) +{ + int ret = -1; + + if (monitor_data == NULL || data_len == 0) { + AW_DEV_LOGE(dev, "no data to parse"); + return -EBFONT; + } + + ret = aw87xxx_monitor_bin_parse(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "monitor_config parse failed"); + return ret; + } + + AW_DEV_LOGI(dev, "monitor_bin parse succeed"); + + return 0; +} + +static int aw_check_prof_str_is_off(char *profile_name) +{ + int i = 0; + + for (i = 0; i < AW_POWER_OFF_NAME_SUPPORT_COUNT; i++) { + if (strnstr(profile_name, g_power_off_name[i], + strlen(profile_name) + 1)) + return 0; + } + + return -EINVAL; +} + +/*************************************************************************** + * V0.0.0.1 version acf paese + **************************************************************************/ +static int aw_check_product_name_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde *prof_hdr) +{ + int i = 0; + + for (i = 0; i < acf_info->product_cnt; i++) { + if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) { + AW_DEV_LOGD(dev, "bin_dev_name:%s", + prof_hdr->dev_name); + return 0; + } + } + + return -ENXIO; +} + +static int aw_check_data_type_is_monitor_v_0_0_0_1(struct device *dev, + struct aw_acf_dde *prof_hdr) +{ + if (prof_hdr->data_type == AW_MONITOR) { + AW_DEV_LOGD(dev, "bin data is monitor"); + return 0; + } + + return -ENXIO; +} + +static int aw_parse_data_by_sec_type_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde *prof_hdr, + struct aw_prof_desc *profile_prof_desc) +{ + int ret = -1; + char *cfg_data = acf_info->fw_data + prof_hdr->data_offset; + + switch (prof_hdr->data_type) { + case AW_BIN_TYPE_REG: + snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile); + AW_DEV_LOGD(dev, "parse reg type data enter,profile=%s", + aw_get_prof_name(prof_hdr->dev_profile)); + ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size, + profile_prof_desc); + break; + case AW_BIN_TYPE_HDR_REG: + snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile); + AW_DEV_LOGD(dev, "parse hdr_reg type data enter,profile=%s", + aw_get_prof_name(prof_hdr->dev_profile)); + ret = aw_parse_reg_with_hdr(dev, cfg_data, + prof_hdr->data_size, + profile_prof_desc); + break; + } + + return ret; +} + +static int aw_parse_dev_type_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret = -1; + int sec_num = 0; + char *cfg_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + struct aw_acf_dde *acf_dde = + (struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && + (acf_dde[i].type == AW_DDE_DEV_TYPE_ID)) { + + ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]); + if (ret == 0) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + ret = aw_parse_monitor_config(dev, cfg_data, acf_dde[i].data_size); + if (ret < 0) + return ret; + continue; + } + + prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile]; + ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i], + prof_desc); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse dev type data failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + AW_DEV_LOGD(dev, "get dev type num is %d, please use default", + sec_num); + return AW_DEV_TYPE_NONE; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_parse_default_type_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret = -1; + int sec_num = 0; + char *cfg_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + struct aw_acf_dde *acf_dde = + (struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_info->dev_index == acf_dde[i].dev_index) && + (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID)) { + + ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]); + if (ret == 0) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + ret = aw_parse_monitor_config(dev, cfg_data, acf_dde[i].data_size); + if (ret < 0) + return ret; + continue; + } + + prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile]; + ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i], + prof_desc); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse default type data failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + AW_DEV_LOGE(dev, "get dev default type failed, get num[%d]", + sec_num); + return -EINVAL; + } + + return 0; +} + +static int aw_get_prof_count_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int prof_count = 0; + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) { + prof_count++; + } else if (i == AW_PROFILE_OFF) { + prof_count++; + AW_DEV_LOGI(dev, "not found profile [Off], set default"); + } + } + + AW_DEV_LOGI(dev, "get profile count=[%d]", prof_count); + return prof_count; +} + +static int aw_set_prof_off_info_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_all_prof_info *all_prof_info, + int index) +{ + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (index >= prof_info->count) { + AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]", + index, prof_info->count); + return -EINVAL; + } + + if (prof_desc[AW_PROFILE_OFF].prof_st == AW_PROFILE_OK) { + prof_info->prof_desc[index] = prof_desc[AW_PROFILE_OFF]; + AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]", + prof_info->prof_desc[index].dev_name, + aw_get_prof_name(AW_PROFILE_OFF)); + } else { + memset(&prof_info->prof_desc[index].data_container, 0, + sizeof(struct aw_data_container)); + prof_info->prof_desc[index].prof_st = AW_PROFILE_WAIT; + prof_info->prof_desc[index].prof_name = aw_get_prof_name(AW_PROFILE_OFF); + AW_DEV_LOGI(dev, "set default power_off with no data to profile"); + } + + return 0; +} + + +static int aw_get_vaild_prof_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret = 0; + int index = 0; + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->count = 0; + ret = aw_get_prof_count_v_0_0_0_1(dev, acf_info, all_prof_info); + if (ret < 0) + return ret; + prof_info->count = ret; + prof_info->prof_desc = devm_kzalloc(dev, + prof_info->count * sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + AW_DEV_LOGE(dev, "prof_desc kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (i != AW_PROFILE_OFF && prof_desc[i].prof_st == AW_PROFILE_OK) { + if (index >= prof_info->count) { + AW_DEV_LOGE(dev, "get profile index[%d] overflow count[%d]", + index, prof_info->count); + return -ENOMEM; + } + prof_info->prof_desc[index] = prof_desc[i]; + AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]", + prof_info->prof_desc[index].dev_name, + aw_get_prof_name(i)); + index++; + } + } + + ret = aw_set_prof_off_info_v_0_0_0_1(dev, acf_info, all_prof_info, index); + if (ret < 0) + return ret; + + AW_DEV_LOGD(dev, "get vaild profile succeed"); + return 0; +} + +static int aw_set_prof_name_list_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int count = acf_info->prof_info.count; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev, + count * (AW_PROFILE_STR_MAX), GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < count; ++i) { + snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s", + prof_info->prof_desc[i].prof_name); + AW_DEV_LOGI(dev, "index=[%d], profile_name=[%s]", + i, prof_info->prof_name_list[i]); + } + + return 0; +} + +static int aw_parse_acf_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info) + +{ + int ret = 0; + struct aw_all_prof_info all_prof_info; + + AW_DEV_LOGD(dev, "enter"); + acf_info->prof_info.status = AW_ACF_WAIT; + + memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info)); + + ret = aw_parse_dev_type_v_0_0_0_1(dev, acf_info, &all_prof_info); + if (ret < 0) { + return ret; + } else if (ret == AW_DEV_TYPE_NONE) { + AW_DEV_LOGD(dev, "get dev type num is 0, parse default dev type"); + ret = aw_parse_default_type_v_0_0_0_1(dev, acf_info, &all_prof_info); + if (ret < 0) + return ret; + } + + ret = aw_get_vaild_prof_v_0_0_0_1(dev, acf_info, &all_prof_info); + if (ret < 0) { + aw87xxx_acf_profile_free(dev, acf_info); + AW_DEV_LOGE(dev, "hdr_cersion[0x%x] parse failed", + acf_info->acf_hdr.hdr_version); + return ret; + } + + ret = aw_set_prof_name_list_v_0_0_0_1(dev, acf_info); + if (ret < 0) { + aw87xxx_acf_profile_free(dev, acf_info); + AW_DEV_LOGE(dev, "creat prof_id_and_name_list failed"); + return ret; + } + + acf_info->prof_info.status = AW_ACF_UPDATE; + AW_DEV_LOGI(dev, "acf parse success"); + return 0; +} + +/*************************************************************************** + * V1.0.0.0 version acf paese + **************************************************************************/ +static int aw_check_product_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde_v_1_0_0_0 *prof_hdr) +{ + int i = 0; + + for (i = 0; i < acf_info->product_cnt; i++) { + if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) { + AW_DEV_LOGI(dev, "bin_dev_name:%s", prof_hdr->dev_name); + return 0; + } + } + + return -ENXIO; +} + +static int aw_get_dde_type_info_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i; + int dev_num = 0; + int default_num = 0; + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_prof_info *prof_info = &acf_info->prof_info; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + + prof_info->prof_type = AW_DEV_NONE_TYPE_ID; + for (i = 0; i < acf_hdr->dde_num; i++) { + if (acf_dde[i].type == AW_DDE_DEV_TYPE_ID) + dev_num++; + if (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (!(dev_num || default_num)) { + AW_DEV_LOGE(dev, "can't find scene"); + return -EINVAL; + } + + if (dev_num != 0) + prof_info->prof_type = AW_DDE_DEV_TYPE_ID; + else if (default_num != 0) + prof_info->prof_type = AW_DDE_DEV_DEFAULT_TYPE_ID; + + return 0; +} + + +static int aw_parse_get_dev_type_prof_count_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + int i = 0; + int ret = 0; + int found_off_prof_flag = 0; + int count = acf_info->prof_info.count; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr)) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str); + if (ret == 0) { + found_off_prof_flag = AW_PROFILE_OK; + } + count++; + } + } + + if (count == 0) { + AW_DEV_LOGE(dev, "can't find profile"); + return -EINVAL; + } + + if (!found_off_prof_flag) { + count++; + AW_DEV_LOGD(dev, "set no config power off profile in count"); + } + + acf_info->prof_info.count = count; + AW_DEV_LOGI(dev, "profile dev_type profile count is %d", acf_info->prof_info.count); + return 0; +} + +static int aw_parse_get_default_type_prof_count_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + int i = 0; + int ret = 0; + int found_off_prof_flag = 0; + int count = acf_info->prof_info.count; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + (acf_info->dev_index == acf_dde[i].dev_index) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str); + if (ret == 0) { + found_off_prof_flag = AW_PROFILE_OK; + } + count++; + } + } + + if (count == 0) { + AW_DEV_LOGE(dev, "can't find profile"); + return -EINVAL; + } + + if (!found_off_prof_flag) { + count++; + AW_DEV_LOGD(dev, "set no config power off profile in count"); + } + + acf_info->prof_info.count = count; + AW_DEV_LOGI(dev, "profile default_type profile count is %d", acf_info->prof_info.count); + return 0; +} + +static int aw_parse_get_profile_count_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int ret = 0; + + ret = aw_get_dde_type_info_v_1_0_0_0(dev, acf_info); + if (ret < 0) + return ret; + + if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { + ret = aw_parse_get_dev_type_prof_count_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse dev_type profile count failed"); + return ret; + } + } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { + ret = aw_parse_get_default_type_prof_count_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse default_type profile count failed"); + return ret; + } + } else { + AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]", + acf_info->prof_info.prof_type); + return -EINVAL; + } + + AW_DEV_LOGI(dev, "profile count is %d", acf_info->prof_info.count); + return 0; +} + +static int aw_parse_dev_type_prof_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + struct aw_prof_info *prof_info = &acf_info->prof_info; + int i, ret, list_index = 0; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + (acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + if (list_index > prof_info->count) { + AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n", + __func__, list_index, + acf_dde[i].dev_profile_str); + return -EINVAL; + } + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s", + acf_dde[i].dev_profile_str); + AW_DEV_LOGI(dev, "profile_name=[%s]", + prof_info->prof_name_list[list_index]); + list_index++; + } + } + + return 0; +} + +static int aw_parse_default_type_prof_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + struct aw_prof_info *prof_info = &acf_info->prof_info; + int i, ret, list_index = 0; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + (acf_info->dev_index == acf_dde[i].dev_index) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + if (list_index > prof_info->count) { + AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n", + __func__, list_index, + acf_dde[i].dev_profile_str); + return -EINVAL; + } + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s", + acf_dde[i].dev_profile_str); + AW_DEV_LOGI(dev, "profile_name=[%s]", + prof_info->prof_name_list[list_index]); + list_index++; + } + } + + return 0; +} + +static int aw_parse_prof_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int ret = 0; + int count = acf_info->prof_info.count; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev, + count * (AW_PROFILE_STR_MAX), GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { + ret = aw_parse_dev_type_prof_name_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse dev_type profile count failed"); + return ret; + } + } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { + ret = aw_parse_default_type_prof_name_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse default_type profile count failed"); + return ret; + } + } else { + AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]", + acf_info->prof_info.prof_type); + return -EINVAL; + } + + AW_DEV_LOGI(dev, "profile name parse succeed"); + return 0; +} + + +static int aw_search_prof_index_from_list_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_prof_desc **prof_desc, + struct aw_acf_dde_v_1_0_0_0 *prof_hdr) +{ + int i = 0; + int count = acf_info->prof_info.count; + char (*prof_name_list)[AW_PROFILE_STR_MAX] = acf_info->prof_info.prof_name_list; + + for (i = 0; i < count; i++) { + if (!strncmp(prof_name_list[i], prof_hdr->dev_profile_str, AW_PROFILE_STR_MAX)) { + *prof_desc = &(acf_info->prof_info.prof_desc[i]); + return 0; + } + } + + if (i == count) + AW_DEV_LOGE(dev, "not find prof_id and prof_name in list"); + + return -EINVAL; +} + +static int aw_parse_data_by_sec_type_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde_v_1_0_0_0 *prof_hdr) +{ + int ret = -1; + char *cfg_data = acf_info->fw_data + prof_hdr->data_offset; + struct aw_prof_desc *prof_desc = NULL; + + ret = aw_search_prof_index_from_list_v_1_0_0_0(dev, acf_info, &prof_desc, prof_hdr); + if (ret < 0) + return ret; + + switch (prof_hdr->data_type) { + case AW_BIN_TYPE_REG: + snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + AW_DEV_LOGI(dev, "parse reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]", + prof_hdr->dev_name, prof_hdr->dev_profile, + prof_hdr->dev_profile_str); + prof_desc->prof_name = prof_hdr->dev_profile_str; + ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size, + prof_desc); + break; + case AW_BIN_TYPE_HDR_REG: + snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + AW_DEV_LOGI(dev, "parse hdr_reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]", + prof_hdr->dev_name, prof_hdr->dev_profile, + prof_hdr->dev_profile_str); + prof_desc->prof_name = prof_hdr->dev_profile_str; + ret = aw_parse_reg_with_hdr(dev, cfg_data, + prof_hdr->data_size, prof_desc); + break; + } + + return ret; +} + +static int aw_parse_dev_type_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int ret; + int parse_prof_count = 0; + char *cfg_data = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_dde[i].type == AW_DDE_DEV_TYPE_ID) && + (acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + if (acf_dde[i].data_type == AW_MONITOR) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + AW_DEV_LOGD(dev, "parse monitor type data enter"); + ret = aw_parse_monitor_config(dev, cfg_data, + acf_dde[i].data_size); + } else { + ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info, + &acf_dde[i]); + if (ret < 0) + AW_DEV_LOGE(dev, "parse dev type data failed"); + else + parse_prof_count++; + } + } + } + + if (parse_prof_count == 0) { + AW_DEV_LOGE(dev, "get dev type num is %d, parse failed", parse_prof_count); + return -EINVAL; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_parse_default_type_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int ret; + int parse_prof_count = 0; + char *cfg_data = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) && + (acf_info->dev_index == acf_dde[i].dev_index) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + if (acf_dde[i].data_type == AW_MONITOR) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + AW_DEV_LOGD(dev, "parse monitor type data enter"); + ret = aw_parse_monitor_config(dev, cfg_data, + acf_dde[i].data_size); + } else { + ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info, + &acf_dde[i]); + if (ret < 0) + AW_DEV_LOGE(dev, "parse default type data failed"); + else + parse_prof_count++; + } + } + } + + if (parse_prof_count == 0) { + AW_DEV_LOGE(dev, "get default type num is %d,parse failed", parse_prof_count); + return -EINVAL; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_parse_by_hdr_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int ret; + + if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { + ret = aw_parse_dev_type_v_1_0_0_0(dev, acf_info); + if (ret < 0) + return ret; + } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { + ret = aw_parse_default_type_v_1_0_0_0(dev, acf_info); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_set_prof_off_info_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + int i = 0; + int ret = 0; + + for (i = 0; i < prof_info->count; ++i) { + if (!(prof_info->prof_desc[i].prof_st)) { + snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s", + g_power_off_name[0]); + prof_info->prof_desc[i].prof_name = prof_info->prof_name_list[i]; + prof_info->prof_desc[i].prof_st = AW_PROFILE_WAIT; + memset(&prof_info->prof_desc[i].data_container, 0, + sizeof(struct aw_data_container)); + return 0; + } + + ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]); + if (ret == 0) { + AW_DEV_LOGD(dev, "found profile off,data_len=[%d]", + prof_info->prof_desc[i].data_container.len); + return 0; + } + } + + AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]", + i, prof_info->count); + return -EINVAL; +} + +static int aw_parse_acf_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) + +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + int ret; + + ret = aw_parse_get_profile_count_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "get profile count failed"); + return ret; + } + + ret = aw_parse_prof_name_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "get profile count failed"); + return ret; + } + + acf_info->prof_info.prof_desc = devm_kzalloc(dev, + prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL); + if (acf_info->prof_info.prof_desc == NULL) { + AW_DEV_LOGE(dev, "prof_desc devm_kzalloc failed"); + return -ENOMEM; + } + + ret = aw_parse_by_hdr_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse data failed"); + return ret; + } + + ret = aw_set_prof_off_info_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "set profile off info failed"); + return ret; + } + + prof_info->status = AW_ACF_UPDATE; + AW_DEV_LOGI(dev, "acf paese succeed"); + return 0; +} + + +/************************************************************************* + * + *acf parse API + * + *************************************************************************/ +void aw87xxx_acf_profile_free(struct device *dev, struct acf_bin_info *acf_info) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->count = 0; + prof_info->status = AW_ACF_WAIT; + memset(&acf_info->acf_hdr, 0, sizeof(struct aw_acf_hdr)); + + if (prof_info->prof_desc) { + devm_kfree(dev, prof_info->prof_desc); + prof_info->prof_desc = NULL; + } + + if (prof_info->prof_name_list) { + devm_kfree(dev, prof_info->prof_name_list); + prof_info->prof_name_list = NULL; + } + + if (acf_info->fw_data) { + vfree(acf_info->fw_data); + acf_info->fw_data = NULL; + } +} + +int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info) +{ + int ret = 0; + + AW_DEV_LOGD(dev, "enter"); + acf_info->prof_info.status = AW_ACF_WAIT; + ret = aw_check_acf_firmware(dev, acf_info->fw_data, + acf_info->fw_size); + if (ret < 0) { + AW_DEV_LOGE(dev, "load firmware check failed"); + return -EINVAL; + } + + memcpy(&acf_info->acf_hdr, acf_info->fw_data, + sizeof(struct aw_acf_hdr)); + + switch (acf_info->acf_hdr.hdr_version) { + case AW_ACF_HDR_VER_0_0_0_1: + return aw_parse_acf_v_0_0_0_1(dev, acf_info); + case AW_ACF_HDR_VER_1_0_0_0: + return aw_parse_acf_v_1_0_0_0(dev, acf_info); + default: + AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]", + acf_info->acf_hdr.hdr_version); + return -EINVAL; + } + + return ret; +} + +struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name) +{ + int i = 0; + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + AW_DEV_LOGD(dev, "enter"); + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return NULL; + } + + for (i = 0; i < prof_info->count; i++) { + if (!strncmp(profile_name, prof_info->prof_desc[i].prof_name, + AW_PROFILE_STR_MAX)) { + prof_desc = &prof_info->prof_desc[i]; + break; + } + } + + if (i == prof_info->count) { + AW_DEV_LOGE(dev, "profile not found"); + return NULL; + } + + AW_DEV_LOGI(dev, "get prof desc down"); + return prof_desc; +} + +int aw87xxx_acf_get_prof_index_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name) +{ + int i = 0; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return -EINVAL; + } + + for (i = 0; i < prof_info->count; i++) { + if (!strncmp(profile_name, prof_info->prof_name_list[i], + AW_PROFILE_STR_MAX)) { + return i; + } + } + + AW_DEV_LOGE(dev, "profile_index not found"); + return -EINVAL; +} + +char *aw87xxx_acf_get_prof_name_form_index(struct device *dev, + struct acf_bin_info *acf_info, int index) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return NULL; + } + + if (index >= prof_info->count || index < 0) { + AW_DEV_LOGE(dev, "profile_index out of table"); + return NULL; + } + + return prof_info->prof_desc[index].prof_name; +} + + +int aw87xxx_acf_get_profile_count(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return -EINVAL; + } + + if (prof_info->count > 0) { + return prof_info->count; + } + + return -EINVAL; +} + +char *aw87xxx_acf_get_prof_off_name(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int ret = 0; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return NULL; + } + + for (i = 0; i < prof_info->count; i++) { + ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]); + if (ret == 0) + return prof_info->prof_name_list[i]; + } + + return NULL; +} + +void aw87xxx_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index) +{ + + acf_info->load_count = 0; + acf_info->prof_info.status = AW_ACF_WAIT; + acf_info->dev_index = index; + acf_info->aw_dev = aw_dev; + acf_info->product_cnt = aw_dev->product_cnt; + acf_info->product_tab = aw_dev->product_tab; + acf_info->prof_info.prof_desc = NULL; + acf_info->fw_data = NULL; + acf_info->fw_size = 0; +} + diff --git a/asoc/codecs/aw87xxx/aw87xxx_acf_bin.h b/asoc/codecs/aw87xxx/aw87xxx_acf_bin.h new file mode 100644 index 000000000000..ebe0c77f5674 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_acf_bin.h @@ -0,0 +1,191 @@ +#ifndef __AW87XXX_ACF_BIN_H__ +#define __AW87XXX_ACF_BIN_H__ + +#include "aw87xxx_device.h" + +#define AW_PROJECT_NAME_MAX (24) +#define AW_CUSTOMER_NAME_MAX (16) +#define AW_CFG_VERSION_MAX (4) +#define AW_TBL_VERSION_MAX (4) +#define AW_DDE_DEVICE_TYPE (0) +#define AW_DDE_SKT_TYPE (1) +#define AW_DDE_DEFAULT_TYPE (2) + +#define AW_REG_ADDR_BYTE (1) +#define AW_REG_DATA_BYTE (1) + +#define AW_ACF_FILE_ID (0xa15f908) +#define AW_PROFILE_STR_MAX (32) +#define AW_POWER_OFF_NAME_SUPPORT_COUNT (5) + +enum aw_cfg_hdr_version { + AW_ACF_HDR_VER_0_0_0_1 = 0x00000001, + AW_ACF_HDR_VER_1_0_0_0 = 0x01000000, +}; + +enum aw_acf_dde_type_id { + AW_DEV_NONE_TYPE_ID = 0xFFFFFFFF, + AW_DDE_DEV_TYPE_ID = 0x00000000, + AW_DDE_SKT_TYPE_ID = 0x00000001, + AW_DDE_DEV_DEFAULT_TYPE_ID = 0x00000002, + AW_DDE_TYPE_MAX, +}; + +enum aw_raw_data_type_id { + AW_BIN_TYPE_REG = 0x00000000, + AW_BIN_TYPE_DSP, + AW_BIN_TYPE_DSP_CFG, + AW_BIN_TYPE_DSP_FW, + AW_BIN_TYPE_HDR_REG, + AW_BIN_TYPE_HDR_DSP_CFG, + AW_BIN_TYPE_HDR_DSP_FW, + AW_BIN_TYPE_MUTLBIN, + AW_SKT_UI_PROJECT, + AW_DSP_CFG, + AW_MONITOR, + AW_BIN_TYPE_MAX, +}; + +enum { + AW_DEV_TYPE_OK = 0, + AW_DEV_TYPE_NONE = 1, +}; + +enum aw_profile_status { + AW_PROFILE_WAIT = 0, + AW_PROFILE_OK, +}; + +enum aw_acf_load_status { + AW_ACF_WAIT = 0, + AW_ACF_UPDATE, +}; + +enum aw_bin_dev_profile_id { + AW_PROFILE_MUSIC = 0x0000, + AW_PROFILE_VOICE, + AW_PROFILE_VOIP, + AW_PROFILE_RINGTONE, + AW_PROFILE_RINGTONE_HS, + AW_PROFILE_LOWPOWER, + AW_PROFILE_BYPASS, + AW_PROFILE_MMI, + AW_PROFILE_FM, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_RECEIVER, + AW_PROFILE_OFF, + AW_PROFILE_MAX, +}; + +struct aw_acf_hdr { + int32_t a_id; /* acf file ID 0xa15f908 */ + char project[AW_PROJECT_NAME_MAX]; /* project name */ + char custom[AW_CUSTOMER_NAME_MAX]; /* custom name :huawei xiaomi vivo oppo */ + uint8_t version[AW_CFG_VERSION_MAX]; /* author update version */ + int32_t author_id; /* author id */ + int32_t ddt_size; /* sub section table entry size */ + int32_t dde_num; /* sub section table entry num */ + int32_t ddt_offset; /* sub section table offset in file */ + int32_t hdr_version; /* sub section table version */ + int32_t reserve[3]; /* Reserved Bits */ +}; + +struct aw_acf_dde { + int32_t type; /* dde type id */ + char dev_name[AW_CUSTOMER_NAME_MAX]; /* customer dev name */ + int16_t dev_index; /* dev id */ + int16_t dev_bus; /* dev bus id */ + int16_t dev_addr; /* dev addr id */ + int16_t dev_profile; /* dev profile id */ + int32_t data_type; /* data type id */ + int32_t data_size; /* dde data size in block */ + int32_t data_offset; /* dde data offset in block */ + int32_t data_crc; /* dde data crc checkout */ + int32_t reserve[5]; /* Reserved Bits */ +}; + +struct aw_acf_dde_v_1_0_0_0 { + uint32_t type; /* DDE type id */ + char dev_name[AW_CUSTOMER_NAME_MAX]; /* customer dev name */ + uint16_t dev_index; /* dev id */ + uint16_t dev_bus; /* dev bus id */ + uint16_t dev_addr; /* dev addr id */ + uint16_t dev_profile; /* dev profile id*/ + uint32_t data_type; /* data type id */ + uint32_t data_size; /* dde data size in block */ + uint32_t data_offset; /* dde data offset in block */ + uint32_t data_crc; /* dde data crc checkout */ + char dev_profile_str[AW_PROFILE_STR_MAX]; /* dde custom profile name */ + uint32_t chip_id; /* dde custom product chip id */ + uint32_t reserve[4]; +}; + +struct aw_data_with_header { + uint32_t check_sum; + uint32_t header_ver; + uint32_t bin_data_type; + uint32_t bin_data_ver; + uint32_t bin_data_size; + uint32_t ui_ver; + char product[8]; + uint32_t addr_byte_len; + uint32_t data_byte_len; + uint32_t device_addr; + uint32_t reserve[4]; +}; + +struct aw_data_container { + uint32_t len; + uint8_t *data; +}; + +struct aw_prof_desc { + uint32_t prof_st; + char *prof_name; + char dev_name[AW_CUSTOMER_NAME_MAX]; + struct aw_data_container data_container; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + int status; + int prof_type; + char (*prof_name_list)[AW_PROFILE_STR_MAX]; + struct aw_prof_desc *prof_desc; +}; + +struct acf_bin_info { + int load_count; + int fw_size; + int16_t dev_index; + char *fw_data; + int product_cnt; + const char **product_tab; + struct aw_device *aw_dev; + + struct aw_acf_hdr acf_hdr; + struct aw_prof_info prof_info; +}; + +char *aw87xxx_ctos_get_prof_name(int profile_id); +void aw87xxx_acf_profile_free(struct device *dev, + struct acf_bin_info *acf_info); +int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info); +struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name); +int aw87xxx_acf_get_prof_index_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name); +char *aw87xxx_acf_get_prof_name_form_index(struct device *dev, + struct acf_bin_info *acf_info, int index); +int aw87xxx_acf_get_profile_count(struct device *dev, + struct acf_bin_info *acf_info); +char *aw87xxx_acf_get_prof_off_name(struct device *dev, + struct acf_bin_info *acf_info); +void aw87xxx_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index); + + +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_bin_parse.c b/asoc/codecs/aw87xxx/aw87xxx_bin_parse.c new file mode 100644 index 000000000000..7eab9efde147 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_bin_parse.c @@ -0,0 +1,515 @@ +/* +* aw87xxx_bin_parse.c +* +* Copyright (c) 2020 AWINIC Technology CO., LTD +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx_bin_parse.h" + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + + +static int aw_parse_bin_header_1_0_0(struct aw_bin *bin); + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +static int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +static int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +static int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +static int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +static int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +static void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +static int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +static int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +static int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +int aw87xxx_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} diff --git a/asoc/codecs/aw87xxx/aw87xxx_bin_parse.h b/asoc/codecs/aw87xxx/aw87xxx_bin_parse.h new file mode 100644 index 000000000000..c6e6eaa8a7f7 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_bin_parse.h @@ -0,0 +1,73 @@ +#ifndef __AW87XXX_BIN_PARSE_H__ +#define __AW87XXX_BIN_PARSE_H__ + +#define NULL ((void *)0) +#define GET_32_DATA(w, x, y, z) ((unsigned int)(((w) << 24) | ((x) << 16) | ((y) << 8) | (z))) +#define BIN_NUM_MAX 100 +#define HEADER_LEN 60 +/********************************************************* + * + * header information + * + ********************************************************/ +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, + DATA_TYPE_MONITOR_ANALOG = 0x00020000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[8]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +/************************************************************ +* +* function define +* +************************************************************/ +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + unsigned char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +extern int aw87xxx_parsing_bin_file(struct aw_bin *bin); +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_device.c b/asoc/codecs/aw87xxx/aw87xxx_device.c new file mode 100644 index 000000000000..f30411cf7f31 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_device.c @@ -0,0 +1,976 @@ +/* + * aw87xxx_device.c aw87xxx pa module + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_device.h" +#include "aw87xxx_log.h" +#include "aw87xxx_pid_9b_reg.h" +#include "aw87xxx_pid_18_reg.h" +#include "aw87xxx_pid_39_reg.h" +#include "aw87xxx_pid_59_3x9_reg.h" +#include "aw87xxx_pid_59_5x9_reg.h" +#include "aw87xxx_pid_5a_reg.h" +#include "aw87xxx_pid_76_reg.h" +#include "aw87xxx_pid_60_reg.h" + +/************************************************************************* + * aw87xxx variable + ************************************************************************/ +const char *g_aw_pid_9b_product[] = { + "aw87319", +}; +const char *g_aw_pid_18_product[] = { + "aw87418", +}; + +const char *g_aw_pid_39_product[] = { + "aw87329", + "aw87339", + "aw87349", +}; + +const char *g_aw_pid_59_3x9_product[] = { + "aw87359", + "aw87389", +}; + +const char *g_aw_pid_59_5x9_product[] = { + "aw87509", + "aw87519", + "aw87529", + "aw87539", +}; + +const char *g_aw_pid_5a_product[] = { + "aw87549", + "aw87559", + "aw87569", + "aw87579", + "aw81509", +}; + +const char *g_aw_pid_76_product[] = { + "aw87390", + "aw87320", + "aw87401", + "aw87360", +}; + +const char *g_aw_pid_60_product[] = { + "aw87560", + "aw87561", + "aw87562", + "aw87501", +}; + +static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev); + +/*************************************************************************** + * + * reading and writing of I2C bus + * + ***************************************************************************/ +int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_write_byte_data(aw_dev->i2c, reg_addr, reg_data); + if (ret < 0) + AW_DEV_LOGE(aw_dev->dev, "i2c_write cnt=%d error=%d", + cnt, ret); + else + break; + + cnt++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw_dev->i2c, reg_addr); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "i2c_read cnt=%d error=%d", + cnt, ret); + } else { + *reg_data = ret; + break; + } + cnt++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +int aw87xxx_dev_i2c_read_msg(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *data_buf, uint32_t data_len) +{ + int ret = -1; + + struct i2c_msg msg[] = { + [0] = { + .addr = aw_dev->i2c_addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw_dev->i2c_addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw_dev->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "transfer failed"); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + AW_DEV_LOGE(aw_dev->dev, "transfer failed(size error)"); + return -ENXIO; + } + + return 0; +} + +int aw87xxx_dev_i2c_write_bits(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t mask, uint8_t reg_data) +{ + int ret = -1; + unsigned char reg_val = 0; + + ret = aw87xxx_dev_i2c_read_byte(aw_dev, reg_addr, ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= (reg_data & (~mask)); + ret = aw87xxx_dev_i2c_write_byte(aw_dev, reg_addr, reg_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "i2c write error, ret=%d", ret); + return ret; + } + + return 0; +} + +/************************************************************************ + * + * aw87xxx device update profile data to registers + * + ************************************************************************/ +static int aw87xxx_dev_reg_update(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int i = 0; + int ret = -1; + + if (profile_data == NULL) + return -EINVAL; + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "dev is pwr_off,can not update reg"); + return -EINVAL; + } + + for (i = 0; i < profile_data->len; i = i + 2) { + AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + profile_data->data[i], profile_data->data[i + 1]); + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], + profile_data->data[i + 1]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void aw87xxx_dev_reg_mute_bits_set(struct aw_device *aw_dev, + uint8_t *reg_val, bool enable) +{ + if (enable) { + *reg_val &= aw_dev->mute_desc.mask; + *reg_val |= aw_dev->mute_desc.enable; + } else { + *reg_val &= aw_dev->mute_desc.mask; + *reg_val |= aw_dev->mute_desc.disable; + } +} + +static int aw87xxx_dev_reg_update_mute(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int i = 0; + int ret = -1; + uint8_t reg_val = 0; + + if (profile_data == NULL) + return -EINVAL; + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "hwen is off,can not update reg"); + return -EINVAL; + } + + if (aw_dev->mute_desc.mask == AW_DEV_REG_INVALID_MASK) { + AW_DEV_LOGE(aw_dev->dev, "mute ctrl mask invalid"); + return -EINVAL; + } + + for (i = 0; i < profile_data->len; i = i + 2) { + AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + profile_data->data[i], profile_data->data[i + 1]); + + reg_val = profile_data->data[i + 1]; + if (profile_data->data[i] == aw_dev->mute_desc.addr) { + aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); + AW_DEV_LOGD(aw_dev->dev, "change mute_mask, val = 0x%02x", + reg_val); + } + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], reg_val); + if (ret < 0) + return ret; + } + + return 0; +} + +/************************************************************************ + * + * aw87xxx device hadware and soft contols + * + ************************************************************************/ +static bool aw87xxx_dev_gpio_is_valid(struct aw_device *aw_dev) +{ + if (gpio_is_valid(aw_dev->rst_gpio)) + return true; + else + return false; +} + +void aw87xxx_dev_hw_pwr_ctrl(struct aw_device *aw_dev, bool enable) +{ + if (aw_dev->hwen_status == AW_DEV_HWEN_INVALID) { + AW_DEV_LOGD(aw_dev->dev, "product not have reset-pin,hardware pwd control invalid"); + return; + } + if (enable) { + if (aw87xxx_dev_gpio_is_valid(aw_dev)) { + gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_LOW_LEVEL); + mdelay(2); + gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_HIGHT_LEVEL); + mdelay(2); + aw_dev->hwen_status = AW_DEV_HWEN_ON; + AW_DEV_LOGI(aw_dev->dev, "hw power on"); + } else { + AW_DEV_LOGI(aw_dev->dev, "hw already power on"); + } + } else { + if (aw87xxx_dev_gpio_is_valid(aw_dev)) { + gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_LOW_LEVEL); + mdelay(2); + aw_dev->hwen_status = AW_DEV_HWEN_OFF; + AW_DEV_LOGI(aw_dev->dev, "hw power off"); + } else { + AW_DEV_LOGI(aw_dev->dev, "hw already power off"); + } + } +} + +static int aw87xxx_dev_mute_ctrl(struct aw_device *aw_dev, bool enable) +{ + int ret = 0; + + if (enable) { + ret = aw87xxx_dev_i2c_write_bits(aw_dev, aw_dev->mute_desc.addr, + aw_dev->mute_desc.mask, aw_dev->mute_desc.enable); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw_dev->dev, "set mute down"); + } else { + ret = aw87xxx_dev_i2c_write_bits(aw_dev, aw_dev->mute_desc.addr, + aw_dev->mute_desc.mask, aw_dev->mute_desc.disable); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw_dev->dev, "close mute down"); + } + + return 0; +} + +void aw87xxx_dev_soft_reset(struct aw_device *aw_dev) +{ + int i = 0; + int ret = -1; + struct aw_soft_rst_desc *soft_rst = &aw_dev->soft_rst_desc; + + AW_DEV_LOGD(aw_dev->dev, "enter"); + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "hw is off,can not softrst"); + return; + } + + if (aw_dev->soft_rst_enable == AW_DEV_SOFT_RST_DISENABLE) { + AW_DEV_LOGD(aw_dev->dev, "softrst is disenable"); + return; + } + + if (soft_rst->access == NULL || soft_rst->len == 0) { + AW_DEV_LOGE(aw_dev->dev, "softrst_info not init"); + return; + } + + if (soft_rst->len % 2) { + AW_DEV_LOGE(aw_dev->dev, "softrst data_len[%d] is odd number,data not available", + aw_dev->soft_rst_desc.len); + return; + } + + for (i = 0; i < soft_rst->len; i += 2) { + AW_DEV_LOGD(aw_dev->dev, "softrst_reg=0x%02x, val = 0x%02x", + soft_rst->access[i], soft_rst->access[i + 1]); + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, soft_rst->access[i], + soft_rst->access[i + 1]); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "write failed,ret = %d,cnt=%d", + ret, i); + return; + } + } + AW_DEV_LOGD(aw_dev->dev, "down"); +} + + +int aw87xxx_dev_default_pwr_off(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int ret = 0; + + AW_DEV_LOGD(aw_dev->dev, "enter"); + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "hwen is already off"); + return 0; + } + + if (aw_dev->soft_off_enable && profile_data) { + ret = aw87xxx_dev_reg_update(aw_dev, profile_data); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "update profile[Off] fw config failed"); + goto reg_off_update_failed; + } + } + + aw87xxx_dev_hw_pwr_ctrl(aw_dev, false); + AW_DEV_LOGD(aw_dev->dev, "down"); + return 0; + +reg_off_update_failed: + aw87xxx_dev_hw_pwr_ctrl(aw_dev, false); + return ret; +} + + +/************************************************************************ + * + * aw87xxx device power on process function + * + ************************************************************************/ + +int aw87xxx_dev_default_pwr_on(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int ret = 0; + + /*hw power on*/ + aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); + + ret = aw87xxx_dev_reg_update(aw_dev, profile_data); + if (ret < 0) + return ret; + + return 0; +} + +/**************************************************************************** + * + * aw87xxx chip esd status check + * + ****************************************************************************/ +int aw87xxx_dev_esd_reg_status_check(struct aw_device *aw_dev) +{ + int ret; + unsigned char reg_val = 0; + struct aw_esd_check_desc *esd_desc = &aw_dev->esd_desc; + + AW_DEV_LOGD(aw_dev->dev, "enter"); + + if (!esd_desc->first_update_reg_addr) { + AW_DEV_LOGE(aw_dev->dev, "esd check info if not init,please check"); + return -EINVAL; + } + + ret = aw87xxx_dev_i2c_read_byte(aw_dev, esd_desc->first_update_reg_addr, + ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read reg 0x%02x failed", + esd_desc->first_update_reg_addr); + return ret; + } + + AW_DEV_LOGD(aw_dev->dev, "0x%02x:default val=0x%02x real val=0x%02x", + esd_desc->first_update_reg_addr, + esd_desc->first_update_reg_val, reg_val); + + if (reg_val == esd_desc->first_update_reg_val) { + AW_DEV_LOGE(aw_dev->dev, "reg status check failed"); + return -EINVAL; + } + return 0; +} + +int aw87xxx_dev_check_reg_is_rec_mode(struct aw_device *aw_dev) +{ + int ret; + unsigned char reg_val = 0; + struct aw_rec_mode_desc *rec_desc = &aw_dev->rec_desc; + + if (!rec_desc->addr) { + AW_DEV_LOGE(aw_dev->dev, "rec check info if not init,please check"); + return -EINVAL; + } + + ret = aw87xxx_dev_i2c_read_byte(aw_dev, rec_desc->addr, ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read reg 0x%02x failed", + rec_desc->addr); + return ret; + } + + if (rec_desc->enable) { + if (reg_val & ~(rec_desc->mask)) { + AW_DEV_LOGI(aw_dev->dev, "reg status is receiver mode"); + aw_dev->is_rec_mode = AW_IS_REC_MODE; + } else { + aw_dev->is_rec_mode = AW_NOT_REC_MODE; + } + } else { + if (!(reg_val & ~(rec_desc->mask))) { + AW_DEV_LOGI(aw_dev->dev, "reg status is receiver mode"); + aw_dev->is_rec_mode = AW_IS_REC_MODE; + } else { + aw_dev->is_rec_mode = AW_NOT_REC_MODE; + } + } + return 0; +} + + +/**************************************************************************** + * + * aw87xxx product attributes init info + * + ****************************************************************************/ + +/********************** aw87xxx_pid_9A attributes ***************************/ + +static int aw_dev_pid_9b_reg_update(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int i = 0; + int ret = -1; + uint8_t reg_val = 0; + + if (profile_data == NULL) + return -EINVAL; + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "dev is pwr_off,can not update reg"); + return -EINVAL; + } + + if (profile_data->len != AW_PID_9B_BIN_REG_CFG_COUNT) { + AW_DEV_LOGE(aw_dev->dev, "reg_config count of bin is error,can not update reg"); + return -EINVAL; + } + ret = aw87xxx_dev_i2c_write_byte(aw_dev, AW87XXX_PID_9B_ENCRYPTION_REG, + AW87XXX_PID_9B_ENCRYPTION_BOOST_OUTPUT_SET); + if (ret < 0) + return ret; + + for (i = 1; i < AW_PID_9B_BIN_REG_CFG_COUNT; i++) { + AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + i, profile_data->data[i]); + reg_val = profile_data->data[i]; + if (i == AW87XXX_PID_9B_SYSCTRL_REG) { + aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); + AW_DEV_LOGD(aw_dev->dev, "change mute_mask, val = 0x%02x", + reg_val); + } + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, i, reg_val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_dev_pid_9b_pwr_on(struct aw_device *aw_dev, struct aw_data_container *data) +{ + int ret = 0; + + /*hw power on*/ + aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); + + /* open the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, true); + if (ret < 0) + return ret; + + /* Update scene parameters in mute mode */ + ret = aw_dev_pid_9b_reg_update(aw_dev, data); + if (ret < 0) + return ret; + + /* close the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, false); + if (ret < 0) + return ret; + + return 0; +} + +static void aw_dev_pid_9b_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_9B_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_9b_reg_access; + + aw_dev->mute_desc.addr = AW87XXX_PID_9B_SYSCTRL_REG; + aw_dev->mute_desc.mask = AW87XXX_PID_9B_REG_EN_SW_MASK; + aw_dev->mute_desc.enable = AW87XXX_PID_9B_REG_EN_SW_DISABLE_VALUE; + aw_dev->mute_desc.disable = AW87XXX_PID_9B_REG_EN_SW_ENABLE_VALUE; + aw_dev->ops.pwr_on_func = aw_dev_pid_9b_pwr_on; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_9b_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_9b_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_DISENABLE; + + aw_dev->product_tab = g_aw_pid_9b_product; + aw_dev->product_cnt = AW87XXX_PID_9B_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_9B_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_9B_SPK_MODE_ENABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_9B_SPK_MODE_DISABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_9B_SPK_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_9B_SYSCTRL_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_9B_SYSCTRL_DEFAULT; +} + +static int aw_dev_pid_9a_init(struct aw_device *aw_dev) +{ + int ret = 0; + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, AW87XXX_PID_9B_ENCRYPTION_REG, + AW87XXX_PID_9B_ENCRYPTION_BOOST_OUTPUT_SET); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "write 0x64=0x2C error"); + return -EINVAL; + } + + ret = aw87xxx_dev_get_chipid(aw_dev); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read chipid is failed,ret=%d", ret); + return ret; + } + + if (aw_dev->chipid == AW_DEV_CHIPID_9B) { + AW_DEV_LOGI(aw_dev->dev, "product is pid_9B class"); + aw_dev_pid_9b_init(aw_dev); + } else { + AW_DEV_LOGE(aw_dev->dev, "product is not pid_9B class,not support"); + return -EINVAL; + } + + return 0; +} + +/********************** aw87xxx_pid_9b attributes end ***********************/ + +/********************** aw87xxx_pid_18 attributes ***************************/ +static int aw_dev_pid_18_pwr_on(struct aw_device *aw_dev, struct aw_data_container *data) +{ + int ret = 0; + + /*hw power on*/ + aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); + + /* open the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, true); + if (ret < 0) + return ret; + + /* Update scene parameters in mute mode */ + ret = aw87xxx_dev_reg_update_mute(aw_dev, data); + if (ret < 0) + return ret; + + /* close the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, false); + if (ret < 0) + return ret; + + return 0; +} + +static void aw_dev_chipid_18_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_18_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_18_reg_access; + + aw_dev->mute_desc.addr = AW87XXX_PID_18_SYSCTRL_REG; + aw_dev->mute_desc.mask = AW87XXX_PID_18_REG_EN_SW_MASK; + aw_dev->mute_desc.enable = AW87XXX_PID_18_REG_EN_SW_DISABLE_VALUE; + aw_dev->mute_desc.disable = AW87XXX_PID_18_REG_EN_SW_ENABLE_VALUE; + aw_dev->ops.pwr_on_func = aw_dev_pid_18_pwr_on; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_18_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_18_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_18_product; + aw_dev->product_cnt = AW87XXX_PID_18_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_18_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_18_REG_REC_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_18_REG_REC_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_18_REG_REC_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_18_CLASSD_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_18_CLASSD_DEFAULT; +} +/********************** aw87xxx_pid_18 attributes end ***********************/ + +/********************** aw87xxx_pid_39 attributes ***************************/ +static void aw_dev_chipid_39_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_39_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_39_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_39_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_39_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_39_product; + aw_dev->product_cnt = AW87XXX_PID_39_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_39_REG_MODECTRL; + aw_dev->rec_desc.disable = AW87XXX_PID_39_REC_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_39_REC_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_39_REC_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_39_REG_MODECTRL; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_39_MODECTRL_DEFAULT; +} +/********************* aw87xxx_pid_39 attributes end *************************/ + + +/********************* aw87xxx_pid_59_5x9 attributes *************************/ +static void aw_dev_chipid_59_5x9_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_59_5X9_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_59_5x9_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_59_5x9_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_59_5x9_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_59_5x9_product; + aw_dev->product_cnt = AW87XXX_PID_59_5X9_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_59_5X9_REG_SYSCTRL; + aw_dev->rec_desc.disable = AW87XXX_PID_59_5X9_REC_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_59_5X9_REC_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_59_5X9_REC_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_5X9_REG_ENCR; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_5X9_ENCRY_DEFAULT; +} +/******************* aw87xxx_pid_59_5x9 attributes end ***********************/ + +/********************* aw87xxx_pid_59_3x9 attributes *************************/ +static void aw_dev_chipid_59_3x9_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_59_3X9_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_59_3x9_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_59_3x9_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_59_3x9_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_59_3x9_product; + aw_dev->product_cnt = AW87XXX_PID_59_3X9_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_59_3X9_REG_MDCRTL; + aw_dev->rec_desc.disable = AW87XXX_PID_59_3X9_SPK_MODE_ENABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_59_3X9_SPK_MODE_DISABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_59_3X9_SPK_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_3X9_REG_ENCR; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_3X9_ENCR_DEFAULT; +} +/******************* aw87xxx_pid_59_3x9 attributes end ***********************/ + +/********************** aw87xxx_pid_5a attributes ****************************/ +static void aw_dev_chipid_5a_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_5A_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_5a_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_5a_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_5a_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_5a_product; + aw_dev->product_cnt = AW87XXX_PID_5A_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_5A_REG_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_5A_REG_RCV_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_5A_REG_RCV_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_5A_REG_RCV_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_5A_REG_DFT3R_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_5A_DFT3R_DEFAULT; +} +/********************** aw87xxx_pid_5a attributes end ************************/ + +/********************** aw87xxx_pid_76 attributes ****************************/ +static void aw_dev_chipid_76_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_76_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_76_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_76_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_76_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* software power off control info */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_76_product; + aw_dev->product_cnt = AW87XXX_PID_76_PROFUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_76_MDCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_76_EN_SPK_ENABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_76_EN_SPK_DISABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_76_EN_SPK_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_76_DFT_ADP1_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_76_DFT_ADP1_CHECK; +} +/********************** aw87xxx_pid_76 attributes end ************************/ + +/********************** aw87xxx_pid_60 attributes ****************************/ +static void aw_dev_chipid_60_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_60_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_60_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_60_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_60_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* software power off control info */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_60_product; + aw_dev->product_cnt = AW87XXX_PID_60_PROFUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_60_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_60_RCV_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_60_RCV_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_60_RCV_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_60_NG3_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_60_ESD_REG_VAL; +} +/********************** aw87xxx_pid_60 attributes end ************************/ + +static int aw_dev_chip_init(struct aw_device *aw_dev) +{ + int ret = 0; + + /*get info by chipid*/ + switch (aw_dev->chipid) { + case AW_DEV_CHIPID_9A: + ret = aw_dev_pid_9a_init(aw_dev); + if (ret < 0) + AW_DEV_LOGE(aw_dev->dev, "product is pid_9B init failed"); + break; + case AW_DEV_CHIPID_9B: + aw_dev_pid_9b_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_9B class"); + break; + case AW_DEV_CHIPID_18: + aw_dev_chipid_18_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_18 class"); + break; + case AW_DEV_CHIPID_39: + aw_dev_chipid_39_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_39 class"); + break; + case AW_DEV_CHIPID_59: + if (aw87xxx_dev_gpio_is_valid(aw_dev)) { + aw_dev_chipid_59_5x9_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_59_5x9 class"); + } else { + aw_dev_chipid_59_3x9_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_59_3x9 class"); + } + break; + case AW_DEV_CHIPID_5A: + aw_dev_chipid_5a_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_5A class"); + break; + case AW_DEV_CHIPID_76: + aw_dev_chipid_76_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_76 class"); + break; + case AW_DEV_CHIPID_60: + aw_dev_chipid_60_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_60 class"); + break; + default: + AW_DEV_LOGE(aw_dev->dev, "unsupported device revision [0x%x]", + aw_dev->chipid); + return -EINVAL; + } + + return 0; +} + +static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned char reg_val = 0; + + for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { + ret = aw87xxx_dev_i2c_read_byte(aw_dev, AW_DEV_REG_CHIPID, ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "[%d] read chip is failed, ret=%d", + cnt, ret); + continue; + } + break; + } + + + if (cnt == AW_READ_CHIPID_RETRIES) { + AW_DEV_LOGE(aw_dev->dev, "read chip is failed,cnt=%d", cnt); + return -EINVAL; + } + + AW_DEV_LOGI(aw_dev->dev, "read chipid[0x%x] succeed", reg_val); + aw_dev->chipid = reg_val; + + return 0; +} + +int aw87xxx_dev_init(struct aw_device *aw_dev) +{ + int ret = -1; + + ret = aw87xxx_dev_get_chipid(aw_dev); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read chipid is failed,ret=%d", ret); + return ret; + } + + ret = aw_dev_chip_init(aw_dev); + + return ret; +} + + diff --git a/asoc/codecs/aw87xxx/aw87xxx_device.h b/asoc/codecs/aw87xxx/aw87xxx_device.h new file mode 100644 index 000000000000..8981a2bdf355 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_device.h @@ -0,0 +1,149 @@ +#ifndef __AW87XXX_DEVICE_H__ +#define __AW87XXX_DEVICE_H__ +#include +#include +#include +#include +#include "aw87xxx_acf_bin.h" + +#define AW87XXX_PID_9B_PRODUCT_MAX (1) +#define AW87XXX_PID_18_PRODUCT_MAX (1) +#define AW87XXX_PID_39_PRODUCT_MAX (3) +#define AW87XXX_PID_59_3X9_PRODUCT_MAX (2) +#define AW87XXX_PID_59_5X9_PRODUCT_MAX (4) +#define AW87XXX_PID_5A_PRODUCT_MAX (5) +#define AW87XXX_PID_76_PROFUCT_MAX (4) +#define AW87XXX_PID_60_PROFUCT_MAX (4) +#define AW_PRODUCT_NAME_LEN (8) + +#define AW_GPIO_HIGHT_LEVEL (1) +#define AW_GPIO_LOW_LEVEL (0) + +#define AW_I2C_RETRIES (5) +#define AW_I2C_RETRY_DELAY (2) +#define AW_I2C_READ_MSG_NUM (2) + +#define AW_READ_CHIPID_RETRIES (5) +#define AW_READ_CHIPID_RETRY_DELAY (2) +#define AW_DEV_REG_CHIPID (0x00) + +#define AW_DEV_REG_INVALID_MASK (0xff) + +#define AW_NO_RESET_GPIO (-1) + +#define AW_PID_9B_BIN_REG_CFG_COUNT (10) + +/******************************************** + * + * aw87xxx devices attributes + * + *******************************************/ +struct aw_device; + +struct aw_device_ops { + int (*pwr_on_func)(struct aw_device *aw_dev, struct aw_data_container *data); + int (*pwr_off_func)(struct aw_device *aw_dev, struct aw_data_container *data); +}; + +enum aw_dev_chipid { + AW_DEV_CHIPID_18 = 0x18, + AW_DEV_CHIPID_39 = 0x39, + AW_DEV_CHIPID_59 = 0x59, + AW_DEV_CHIPID_69 = 0x69, + AW_DEV_CHIPID_5A = 0x5A, + AW_DEV_CHIPID_9A = 0x9A, + AW_DEV_CHIPID_9B = 0x9B, + AW_DEV_CHIPID_76 = 0x76, + AW_DEV_CHIPID_60 = 0x60, +}; + +enum aw_dev_hw_status { + AW_DEV_HWEN_OFF = 0, + AW_DEV_HWEN_ON, + AW_DEV_HWEN_INVALID, + AW_DEV_HWEN_STATUS_MAX, +}; + +enum aw_dev_soft_off_enable { + AW_DEV_SOFT_OFF_DISENABLE = 0, + AW_DEV_SOFT_OFF_ENABLE = 1, +}; + +enum aw_dev_soft_rst_enable { + AW_DEV_SOFT_RST_DISENABLE = 0, + AW_DEV_SOFT_RST_ENABLE = 1, +}; + +enum aw_reg_receiver_mode { + AW_NOT_REC_MODE = 0, + AW_IS_REC_MODE = 1, +}; + +struct aw_mute_desc { + uint8_t addr; + uint8_t enable; + uint8_t disable; + uint16_t mask; +}; + +struct aw_soft_rst_desc { + int len; + unsigned char *access; +}; + +struct aw_esd_check_desc { + uint8_t first_update_reg_addr; + uint8_t first_update_reg_val; +}; + +struct aw_rec_mode_desc { + uint8_t addr; + uint8_t enable; + uint8_t disable; + uint8_t mask; +}; + +struct aw_device { + uint8_t i2c_addr; + uint8_t chipid; + uint8_t soft_rst_enable; + uint8_t soft_off_enable; + uint8_t is_rec_mode; + int hwen_status; + int i2c_bus; + int rst_gpio; + int reg_max_addr; + int product_cnt; + const char **product_tab; + const unsigned char *reg_access; + + struct device *dev; + struct i2c_client *i2c; + struct aw_mute_desc mute_desc; + struct aw_soft_rst_desc soft_rst_desc; + struct aw_esd_check_desc esd_desc; + struct aw_rec_mode_desc rec_desc; + + struct aw_device_ops ops; +}; + + +int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t reg_data); +int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *reg_data); +int aw87xxx_dev_i2c_read_msg(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *data_buf, uint32_t data_len); +int aw87xxx_dev_i2c_write_bits(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t mask, uint8_t reg_data); +void aw87xxx_dev_soft_reset(struct aw_device *aw_dev); +void aw87xxx_dev_hw_pwr_ctrl(struct aw_device *aw_dev, bool enable); +int aw87xxx_dev_default_pwr_on(struct aw_device *aw_dev, + struct aw_data_container *profile_data); +int aw87xxx_dev_default_pwr_off(struct aw_device *aw_dev, + struct aw_data_container *profile_data); +int aw87xxx_dev_esd_reg_status_check(struct aw_device *aw_dev); +int aw87xxx_dev_check_reg_is_rec_mode(struct aw_device *aw_dev); +int aw87xxx_dev_init(struct aw_device *aw_dev); + +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_dsp.c b/asoc/codecs/aw87xxx/aw87xxx_dsp.c new file mode 100644 index 000000000000..bd9896cd172b --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_dsp.c @@ -0,0 +1,355 @@ +/* + * aw87xxx_dsp.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx_log.h" +#include "aw87xxx_dsp.h" + +static DEFINE_MUTEX(g_dsp_lock); +static unsigned int g_spin_value = 0; + +static int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; +static int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + +#ifdef AW_MTK_OPEN_DSP_PLATFORM +extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, + uint32_t data_size); +extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, + int16_t size, uint32_t *buf_len); +/* +static int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, + uint32_t data_size) +{ + AW_LOGI("enter"); + return 0; +} + +static int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, + int16_t size, uint32_t *buf_len) +{ + AW_LOGI("enter"); + return 0; +} +*/ +#elif defined AW_QCOM_OPEN_DSP_PLATFORM +extern int afe_get_topology(int port_id); +extern int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write); +/* +static int afe_get_topology(int port_id) +{ + return -EPERM; +} + +static int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + AW_LOGI("enter, no define AWINIC_ADSP_ENABLE", __func__); + return 0; +} +*/ +#endif + +#ifdef AW_QCOM_OPEN_DSP_PLATFORM +extern void aw_set_port_id(int rx_port_id); +#else +static void aw_set_port_id(int rx_port_id) +{ + return; +} +#endif + +uint8_t aw87xxx_dsp_isEnable(void) +{ +#if (defined AW_QCOM_OPEN_DSP_PLATFORM) || (defined AW_MTK_OPEN_DSP_PLATFORM) + return true; +#else + return false; +#endif +} + +/*****************mtk dsp communication function start**********************/ +#ifdef AW_MTK_OPEN_DSP_PLATFORM +static int aw_mtk_write_data_to_dsp(int32_t param_id, + void *data, int size) +{ + int32_t *dsp_data = NULL; + mtk_dsp_hdr_t *hdr = NULL; + int ret; + + dsp_data = kzalloc(sizeof(mtk_dsp_hdr_t) + size, GFP_KERNEL); + if (!dsp_data) { + AW_LOGE("kzalloc dsp_msg error"); + return -ENOMEM; + } + + hdr = (mtk_dsp_hdr_t *)dsp_data; + hdr->type = DSP_MSG_TYPE_DATA; + hdr->opcode_id = param_id; + hdr->version = AW_DSP_MSG_HDR_VER; + + memcpy(((char *)dsp_data) + sizeof(mtk_dsp_hdr_t), + data, size); + + ret = mtk_spk_send_ipi_buf_to_dsp(dsp_data, + sizeof(mtk_dsp_hdr_t) + size); + if (ret < 0) { + AW_LOGE("write data failed"); + kfree(dsp_data); + dsp_data = NULL; + return ret; + } + + kfree(dsp_data); + dsp_data = NULL; + return 0; +} + +static int aw_mtk_read_data_from_dsp(int32_t param_id, void *data, + int data_size) +{ + int ret; + mtk_dsp_hdr_t hdr; + + mutex_lock(&g_dsp_lock); + hdr.type = DSP_MSG_TYPE_CMD; + hdr.opcode_id = param_id; + hdr.version = AW_DSP_MSG_HDR_VER; + + ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, sizeof(mtk_dsp_hdr_t)); + if (ret < 0) + goto failed; + + ret = mtk_spk_recv_ipi_buf_from_dsp(data, data_size, &data_size); + if (ret < 0) + goto failed; + + mutex_unlock(&g_dsp_lock); + return 0; + +failed: + mutex_unlock(&g_dsp_lock); + return ret; +} + +#endif +/********************mtk dsp communication function end***********************/ + +/******************qcom dsp communication function start**********************/ +#ifdef AW_QCOM_OPEN_DSP_PLATFORM +static void aw_check_dsp_ready(void) +{ + int ret; + + ret = afe_get_topology(g_rx_port_id); + AW_LOGD("topo_id 0x%x", ret); + + if (ret != g_rx_topo_id) + AW_LOGE("topo id 0x%x", ret); + +} + +static int aw_qcom_write_data_to_dsp(int32_t param_id, + void *data, int data_size) +{ + int ret = 0; + + AW_LOGI("enter"); + mutex_lock(&g_dsp_lock); + aw_check_dsp_ready(); + ret = aw_send_afe_cal_apr(param_id, data, + data_size, true); + mutex_unlock(&g_dsp_lock); + return ret; +} + +static int aw_qcom_read_data_from_dsp(int32_t param_id, + void *data, int data_size) +{ + int ret = 0; + + AW_LOGI("enter"); + mutex_lock(&g_dsp_lock); + aw_check_dsp_ready(); + ret = aw_send_afe_cal_apr(param_id, data, + data_size, false); + mutex_unlock(&g_dsp_lock); + return ret; +} + +#endif +/*****************qcom dsp communication function end*********************/ + +/*****************read/write msg communication function*********************/ +static int aw_write_data_to_dsp(int32_t param_id, void *data, int data_size) +{ +#if defined AW_QCOM_OPEN_DSP_PLATFORM + return aw_qcom_write_data_to_dsp(param_id, data, data_size); +#elif defined AW_MTK_OPEN_DSP_PLATFORM + return aw_mtk_write_data_to_dsp(param_id, data, data_size); +#else + return -EINVAL; +#endif +} + +static int aw_read_data_from_dsp(int32_t param_id, void *data, int data_size) +{ +#if defined AW_QCOM_OPEN_DSP_PLATFORM + return aw_qcom_read_data_from_dsp(param_id, data, data_size); +#elif defined AW_MTK_OPEN_DSP_PLATFORM + return aw_mtk_read_data_from_dsp(param_id, data, data_size); +#else + return -EINVAL; +#endif +} + +/***************read/write msg communication function end*******************/ + +int aw87xxx_dsp_get_rx_module_enable(int *enable) +{ + if (!enable) { + AW_LOGE("enable is NULL"); + return -EINVAL; + } + + return aw_read_data_from_dsp(AWDSP_RX_SET_ENABLE, + (void *)enable, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_rx_module_enable(int enable) +{ + switch (enable) { + case AW_RX_MODULE_DISENABLE: + AW_LOGD("set enable=%d", enable); + break; + case AW_RX_MODULE_ENABLE: + AW_LOGD("set enable=%d", enable); + break; + default: + AW_LOGE("unsupport enable=%d", enable); + return -EINVAL; + } + + return aw_write_data_to_dsp(AWDSP_RX_SET_ENABLE, + &enable, sizeof(uint32_t)); +} + + +int aw87xxx_dsp_get_vmax(uint32_t *vmax, int dev_index) +{ + int32_t param_id = 0; + + switch (dev_index % AW_DSP_CHANNEL_MAX) { + case AW_DSP_CHANNEL_0: + param_id = AWDSP_RX_VMAX_0; + break; + case AW_DSP_CHANNEL_1: + param_id = AWDSP_RX_VMAX_1; + break; + default: + AW_LOGE("algo only support double PA channel:%d unsupport", + dev_index); + return -EINVAL; + } + + return aw_read_data_from_dsp(param_id, + (void *)vmax, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_vmax(uint32_t vmax, int dev_index) +{ + int32_t param_id = 0; + + switch (dev_index % AW_DSP_CHANNEL_MAX) { + case AW_DSP_CHANNEL_0: + param_id = AWDSP_RX_VMAX_0; + break; + case AW_DSP_CHANNEL_1: + param_id = AWDSP_RX_VMAX_1; + break; + default: + AW_LOGE("algo only support double PA channel:%d unsupport", + dev_index); + return -EINVAL; + } + + return aw_write_data_to_dsp(param_id, &vmax, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_spin(uint32_t ctrl_value) +{ + int ret = 0; + + if (ctrl_value >= AW_SPIN_MAX) { + AW_LOGE("spin [%d] unsupported ", ctrl_value); + return -EINVAL; + } + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN, &ctrl_value, + sizeof(uint32_t)); + if (ret) { + AW_LOGE("spin [%d] set failed ", ctrl_value); + return ret; + } + + g_spin_value = ctrl_value; + return 0; +} + +int aw87xxx_dsp_get_spin(void) +{ + return g_spin_value; +} + +int aw87xxx_spin_set_record_val(void) +{ + AW_LOGD("record write spin enter"); + + return aw87xxx_dsp_set_spin(g_spin_value); +} +EXPORT_SYMBOL(aw87xxx_spin_set_record_val); + +void aw87xxx_device_parse_topo_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-topo-id", &g_rx_topo_id); + if (ret < 0) { + g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; + AW_DEV_LOGI(aw_dev->dev, "read aw-rx-topo-id failed,use default"); + } + + AW_DEV_LOGI(aw_dev->dev, "rx-topo-id: 0x%x", g_rx_topo_id); +} + +void aw87xxx_device_parse_port_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-port-id", &g_rx_port_id); + if (ret < 0) { + g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + AW_DEV_LOGI(aw_dev->dev, "read aw-rx-port-id failed,use default"); + } + + aw_set_port_id(g_rx_port_id); + AW_DEV_LOGI(aw_dev->dev, "rx-port-id: 0x%x", g_rx_port_id); +} + diff --git a/asoc/codecs/aw87xxx/aw87xxx_dsp.h b/asoc/codecs/aw87xxx/aw87xxx_dsp.h new file mode 100644 index 000000000000..2668170f152d --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_dsp.h @@ -0,0 +1,65 @@ +#ifndef __AW87XXX_DSP_H__ +#define __AW87XXX_DSP_H__ + +#include "aw87xxx_device.h" + +/*#define AW_MTK_OPEN_DSP_PLATFORM*/ +/*#define AW_QCOM_OPEN_DSP_PLATFORM*/ + +/*Note: The pord_ID is configured according to different platforms*/ +#define AW_DSP_SLEEP_TIME (10) + +#define AW_DSP_MSG_HDR_VER (1) + +#define AW_RX_DEFAULT_TOPO_ID (0x1000FF01) +#define AW_RX_DEFAULT_PORT_ID (0x4000) + +#define AWDSP_RX_SET_ENABLE (0x10013D11) +#define AWDSP_RX_PARAMS (0x10013D12) +#define AWDSP_RX_VMAX_0 (0X10013D17) +#define AWDSP_RX_VMAX_1 (0X10013D18) +#define AW_MSG_ID_SPIN (0x10013D2E) + +enum { + AW_SPIN_0 = 0, + AW_SPIN_90, + AW_SPIN_180, + AW_SPIN_270, + AW_SPIN_MAX, +}; + +typedef struct mtk_dsp_msg_header { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reserver[3]; +} mtk_dsp_hdr_t; + +enum aw_rx_module_enable { + AW_RX_MODULE_DISENABLE = 0, + AW_RX_MODULE_ENABLE, +}; + +enum aw_dsp_msg_type { + DSP_MSG_TYPE_DATA = 0, + DSP_MSG_TYPE_CMD = 1, +}; + +enum aw_dsp_channel { + AW_DSP_CHANNEL_0 = 0, + AW_DSP_CHANNEL_1, + AW_DSP_CHANNEL_MAX, +}; + +uint8_t aw87xxx_dsp_isEnable(void); +int aw87xxx_dsp_get_rx_module_enable(int *enable); +int aw87xxx_dsp_set_rx_module_enable(int enable); +int aw87xxx_dsp_get_vmax(uint32_t *vmax, int channel); +int aw87xxx_dsp_set_vmax(uint32_t vmax, int channel); +int aw87xxx_dsp_set_spin(uint32_t ctrl_value); +int aw87xxx_dsp_get_spin(void); +int aw87xxx_spin_set_record_val(void); +void aw87xxx_device_parse_port_id_dt(struct aw_device *aw_dev); +void aw87xxx_device_parse_topo_id_dt(struct aw_device *aw_dev); + +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_log.h b/asoc/codecs/aw87xxx/aw87xxx_log.h new file mode 100644 index 000000000000..67263484d6d9 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_log.h @@ -0,0 +1,33 @@ +#ifndef __AW87XXX_LOG_H__ +#define __AW87XXX_LOG_H__ + +#include + + +/******************************************** + * + * print information control + * + *******************************************/ +#define AW_LOGI(fmt, ...)\ + pr_info("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) + +#define AW_LOGD(fmt, ...)\ + pr_debug("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) + +#define AW_LOGE(fmt, ...)\ + pr_err("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) + + +#define AW_DEV_LOGI(dev, fmt, ...)\ + pr_info("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) + +#define AW_DEV_LOGD(dev, fmt, ...)\ + pr_debug("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) + +#define AW_DEV_LOGE(dev, fmt, ...)\ + pr_err("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) + + + +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_monitor.c b/asoc/codecs/aw87xxx/aw87xxx_monitor.c new file mode 100644 index 000000000000..86958aae69a9 --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_monitor.c @@ -0,0 +1,1208 @@ +/* + * aw87xxx_monitor.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_log.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_dsp.h" +#include "aw87xxx_bin_parse.h" +#include "aw87xxx_device.h" + +#define AW_MONITOT_BIN_PARSE_VERSION "V0.1.0" + +#define AW_GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | \ + (((uint8_t)y) << 8) | ((uint8_t)z))) + +/**************************************************************************** + * + * aw87xxx monitor bin check + * + ****************************************************************************/ +static int aw_monitor_check_header_v_1_0_0(struct device *dev, + char *data, uint32_t data_len) +{ + int i = 0; + struct aw_bin_header *header = (struct aw_bin_header *)data; + + if (header->bin_data_type != DATA_TYPE_MONITOR_ANALOG) { + AW_DEV_LOGE(dev, "monitor data_type check error!"); + return -EINVAL; + } + + if (header->bin_data_size != AW_MONITOR_HDR_DATA_SIZE) { + AW_DEV_LOGE(dev, "monitor data_size error!"); + return -EINVAL; + } + + if (header->data_byte_len != AW_MONITOR_HDR_DATA_BYTE_LEN) { + AW_DEV_LOGE(dev, "monitor data_byte_len error!"); + return -EINVAL; + } + + for (i = 0; i < AW_MONITOR_DATA_VER_MAX; i++) { + if (header->bin_data_ver == i) { + AW_LOGD("monitor bin_data_ver[0x%x]", i); + break; + } + } + if (i == AW_MONITOR_DATA_VER_MAX) + return -EINVAL; + + return 0; +} + +static int aw_monitor_check_data_v1_size(struct device *dev, + char *data, int32_t data_len) +{ + int32_t bin_header_len = sizeof(struct aw_bin_header); + int32_t monitor_header_len = sizeof(struct aw_monitor_header); + int32_t monitor_data_len = sizeof(struct vmax_step_config); + int32_t len = 0; + struct aw_monitor_header *monitor_header = NULL; + + AW_DEV_LOGD(dev, "enter"); + + if (data_len < bin_header_len + monitor_header_len) { + AW_DEV_LOGE(dev, "bin len is less than aw_bin_header and monitoor_header,check failed"); + return -EINVAL; + } + + monitor_header = (struct aw_monitor_header *)(data + bin_header_len); + len = data_len - bin_header_len - monitor_header_len; + if (len < monitor_header->step_count * monitor_data_len) { + AW_DEV_LOGE(dev, "bin data len is not enough,check failed"); + return -EINVAL; + } + + AW_DEV_LOGD(dev, "succeed"); + + return 0; +} + +static int aw_monitor_check_data_size(struct device *dev, + char *data, int32_t data_len) +{ + int ret = -1; + struct aw_bin_header *header = (struct aw_bin_header *)data; + + switch (header->bin_data_ver) { + case AW_MONITOR_DATA_VER: + ret = aw_monitor_check_data_v1_size(dev, data, data_len); + if (ret < 0) + return ret; + break; + default: + AW_DEV_LOGE(dev, "bin data_ver[0x%x] non support", + header->bin_data_ver); + return -EINVAL; + } + + return 0; +} + + +static int aw_monitor_check_bin_header(struct device *dev, + char *data, int32_t data_len) +{ + int ret = -1; + struct aw_bin_header *header = NULL; + + if (data_len < sizeof(struct aw_bin_header)) { + AW_DEV_LOGE(dev, "bin len is less than aw_bin_header,check failed"); + return -EINVAL; + } + header = (struct aw_bin_header *)data; + + switch (header->header_ver) { + case HEADER_VERSION_1_0_0: + ret = aw_monitor_check_header_v_1_0_0(dev, data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "monitor bin haeder info check error!"); + return ret; + } + break; + default: + AW_DEV_LOGE(dev, "bin version[0x%x] non support", + header->header_ver); + return -EINVAL; + } + + return 0; +} + +static int aw_monitor_bin_check_sum(struct device *dev, + char *data, int32_t data_len) +{ + int i, data_sum = 0; + uint32_t *check_sum = (uint32_t *)data; + + for (i = 4; i < data_len; i++) + data_sum += data[i]; + + if (*check_sum != data_sum) { + AW_DEV_LOGE(dev, "check_sum[%d] is not equal to data_sum[%d]", + *check_sum, data_sum); + return -ENOMEM; + } + + AW_DEV_LOGD(dev, "succeed"); + + return 0; +} + +static int aw_monitor_bin_check(struct device *dev, + char *monitor_data, uint32_t data_len) +{ + int ret = -1; + + if (monitor_data == NULL || data_len == 0) { + AW_DEV_LOGE(dev, "none data to parse"); + return -EINVAL; + } + + ret = aw_monitor_bin_check_sum(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "bin data check sum failed"); + return ret; + } + + ret = aw_monitor_check_bin_header(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "bin data len check failed"); + return ret; + } + + ret = aw_monitor_check_data_size(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "bin header info check failed"); + return ret; + } + + return 0; +} + +/***************************************************************************** + * + * aw87xxx monitor header bin parse + * + *****************************************************************************/ +static void aw_monitor_write_to_table_v1(struct device *dev, + struct vmax_step_config *vmax_step, + char *vmax_data, uint32_t step_count) +{ + int i = 0; + int index = 0; + int vmax_step_size = (int)sizeof(struct vmax_step_config); + + for (i = 0; i < step_count; i++) { + index = vmax_step_size * i; + vmax_step[i].vbat_min = + AW_GET_32_DATA(vmax_data[index + 3], + vmax_data[index + 2], + vmax_data[index + 1], + vmax_data[index + 0]); + vmax_step[i].vbat_max = + AW_GET_32_DATA(vmax_data[index + 7], + vmax_data[index + 6], + vmax_data[index + 5], + vmax_data[index + 4]); + vmax_step[i].vmax_vol = + AW_GET_32_DATA(vmax_data[index + 11], + vmax_data[index + 10], + vmax_data[index + 9], + vmax_data[index + 8]); + } + + for (i = 0; i < step_count; i++) + AW_DEV_LOGI(dev, "vbat_min:%d, vbat_max%d, vmax_vol:0x%x", + vmax_step[i].vbat_min, + vmax_step[i].vbat_max, + vmax_step[i].vmax_vol); +} + +static int aw_monitor_parse_vol_data_v1(struct device *dev, + struct aw_monitor *monitor, char *monitor_data) +{ + uint32_t step_count = 0; + char *vmax_data = NULL; + struct vmax_step_config *vmax_step = NULL; + + AW_DEV_LOGD(dev, "enter"); + + step_count = monitor->monitor_hdr.step_count; + if (step_count) { + vmax_step = devm_kzalloc(dev, sizeof(struct vmax_step_config) * step_count, + GFP_KERNEL); + if (vmax_step == NULL) { + AW_DEV_LOGE(dev, "vmax_cfg vmalloc failed"); + return -ENOMEM; + } + memset(vmax_step, 0, + sizeof(struct vmax_step_config) * step_count); + } + + vmax_data = monitor_data + sizeof(struct aw_bin_header) + + sizeof(struct aw_monitor_header); + aw_monitor_write_to_table_v1(dev, vmax_step, vmax_data, step_count); + monitor->vmax_cfg = vmax_step; + + AW_DEV_LOGI(dev, "vmax_data parse succeed"); + + return 0; +} + +static int aw_monitor_parse_data_v1(struct device *dev, + struct aw_monitor *monitor, char *monitor_data) +{ + int ret = -1; + int header_len = 0; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + header_len = sizeof(struct aw_bin_header); + memcpy(monitor_hdr, monitor_data + header_len, + sizeof(struct aw_monitor_header)); + + AW_DEV_LOGI(dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d, step_count:%d", + monitor_hdr->monitor_switch, monitor_hdr->monitor_time, + monitor_hdr->monitor_count, monitor_hdr->step_count); + + ret = aw_monitor_parse_vol_data_v1(dev, monitor, monitor_data); + if (ret < 0) { + AW_DEV_LOGE(dev, "vmax_data parse failed"); + return ret; + } + + monitor->bin_status = AW_MONITOR_CFG_OK; + + return 0; +} + + +static int aw_monitor_parse_v_1_0_0(struct device *dev, + struct aw_monitor *monitor, char *monitor_data) +{ + int ret = -1; + struct aw_bin_header *header = (struct aw_bin_header *)monitor_data; + + switch (header->bin_data_ver) { + case AW_MONITOR_DATA_VER: + ret = aw_monitor_parse_data_v1(dev, monitor, monitor_data); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +void aw87xxx_monitor_cfg_free(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + + monitor->bin_status = AW_MONITOR_CFG_WAIT; + memset(&monitor->monitor_hdr, 0, + sizeof(struct aw_monitor_header)); + if (monitor->vmax_cfg) { + devm_kfree(aw87xxx->dev, monitor->vmax_cfg); + monitor->vmax_cfg = NULL; + } +} + +int aw87xxx_monitor_bin_parse(struct device *dev, + char *monitor_data, uint32_t data_len) +{ + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = NULL; + struct aw_bin_header *bin_header = NULL; + + if (aw87xxx == NULL) { + AW_DEV_LOGE(dev, "get struct aw87xxx failed"); + return -EINVAL; + } + + monitor = &aw87xxx->monitor; + monitor->bin_status = AW_MONITOR_CFG_WAIT; + + AW_DEV_LOGI(dev, "monitor bin parse version: %s", + AW_MONITOT_BIN_PARSE_VERSION); + + ret = aw_monitor_bin_check(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "monitor bin check failed"); + return ret; + } + + bin_header = (struct aw_bin_header *)monitor_data; + switch (bin_header->bin_data_ver) { + case DATA_VERSION_V1: + ret = aw_monitor_parse_v_1_0_0(dev, monitor, + monitor_data); + if (ret < 0) { + aw87xxx_monitor_cfg_free(monitor); + return ret; + } + break; + default: + AW_DEV_LOGE(dev, "Unrecognized this bin data version[0x%x]", + bin_header->bin_data_ver); + } + + return 0; +} + +/*************************************************************************** + * + * aw87xxx monitor get adjustment vmax of power + * + ***************************************************************************/ +static int aw_monitor_get_battery_capacity(struct device *dev, + struct aw_monitor *monitor, int *vbat_capacity) +{ + char name[] = "battery"; + int ret = -1; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + psy = power_supply_get_by_name(name); + if (psy == NULL) { + AW_DEV_LOGE(dev, "no struct power supply name:%s", name); + return -EINVAL; + } + + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &prop); + if (ret < 0) { + AW_DEV_LOGE(dev, "get vbat capacity failed"); + return -EINVAL; + } + *vbat_capacity = prop.intval; + AW_DEV_LOGI(dev, "The percentage is %d", + *vbat_capacity); + + return 0; +} + +static int aw_search_vmax_from_table(struct device *dev, + struct aw_monitor *monitor, + const int vbat_vol, int *vmax_vol) +{ + int i = 0; + int vmax_set = 0; + uint32_t vmax_flag = 0; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + struct vmax_step_config *vmax_cfg = monitor->vmax_cfg; + + if (monitor->bin_status == AW_MONITOR_CFG_WAIT) { + AW_DEV_LOGE(dev, "vmax_cfg not loaded or parse failed"); + return -ENODATA; + } + + for (i = 0; i < monitor_hdr->step_count; i++) { + if (vbat_vol == AW_VBAT_MAX) { + vmax_set = AW_VMAX_MAX; + vmax_flag = 1; + AW_DEV_LOGD(dev, "vbat=%d, setting vmax=0x%x", + vbat_vol, vmax_set); + break; + } + + if (vbat_vol >= vmax_cfg[i].vbat_min && + vbat_vol < vmax_cfg[i].vbat_max) { + vmax_set = vmax_cfg[i].vmax_vol; + vmax_flag = 1; + AW_DEV_LOGD(dev, "read setting vmax=0x%x, step[%d]: vbat_min=%d,vbat_max=%d", + vmax_set, i, + vmax_cfg[i].vbat_min, + vmax_cfg[i].vbat_max); + break; + } + } + + if (!vmax_flag) { + AW_DEV_LOGE(dev, "vmax_cfg not found"); + return -ENODATA; + } + + *vmax_vol = vmax_set; + return 0; +} + + +/*************************************************************************** + * + *monitor_esd_func + * + ***************************************************************************/ +static int aw_chip_status_recover(struct aw87xxx *aw87xxx) +{ + int ret = -1; + struct aw_monitor *monitor = &aw87xxx->monitor; + char *profile = aw87xxx->current_profile; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + ret = aw87xxx_update_profile_esd(aw87xxx, profile); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", + profile); + return ret; + } + + AW_DEV_LOGI(aw87xxx->dev, "current prof[%s], dev_index[%d] ", + profile, aw87xxx->dev_index); + + monitor->pre_vmax = AW_VMAX_INIT_VAL; + monitor->first_entry = AW_FIRST_ENTRY; + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + + return 0; +} + +static int aw_monitor_chip_esd_check_work(struct aw87xxx *aw87xxx) +{ + int ret = 0; + int i = 0; + + for (i = 0; i < REG_STATUS_CHECK_MAX; i++) { + AW_DEV_LOGD(aw87xxx->dev, "reg_status_check[%d]", i); + + ret = aw87xxx_dev_esd_reg_status_check(&aw87xxx->aw_dev); + if (ret < 0) { + aw_chip_status_recover(aw87xxx); + } else { + AW_DEV_LOGD(aw87xxx->dev, "chip status check succeed"); + break; + } + msleep(AW_ESD_CHECK_DELAY); + } + + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "chip status recover failed,chip off"); + aw87xxx_update_profile_esd(aw87xxx, aw87xxx->prof_off_name); + return ret; + } + + return 0; +} + + +/*************************************************************************** + * + * aw87xxx monitor work with dsp + * + ***************************************************************************/ +static int aw_monitor_update_vmax_to_dsp(struct device *dev, + struct aw_monitor *monitor, int vmax_set) +{ + int ret = -1; + uint32_t enable = 0; + + if (monitor->pre_vmax != vmax_set) { + ret = aw87xxx_dsp_get_rx_module_enable(&enable); + if (!enable || ret < 0) { + AW_DEV_LOGE(dev, "get rx failed or rx disable, ret=%d, enable=%d", + ret, enable); + return -EPERM; + } + + ret = aw87xxx_dsp_set_vmax(vmax_set, monitor->dev_index); + if (ret) { + AW_DEV_LOGE(dev, "set dsp msg fail, ret=%d", ret); + return ret; + } + + AW_DEV_LOGI(dev, "set dsp vmax=0x%x sucess", vmax_set); + monitor->pre_vmax = vmax_set; + } else { + AW_DEV_LOGI(dev, "vmax=0x%x no change", vmax_set); + } + + return 0; +} + +static void aw_monitor_with_dsp_vmax_work(struct device *dev, + struct aw_monitor *monitor) +{ + int ret = -1; + int vmax_set = 0; + int vbat_capacity = 0; + int ave_capacity = 0; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + AW_DEV_LOGD(dev, "enter with dsp monitor"); + + ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); + if (ret < 0) + return; + + if (monitor->timer_cnt < monitor_hdr->monitor_count) { + monitor->timer_cnt++; + monitor->vbat_sum += vbat_capacity; + AW_DEV_LOGI(dev, "timer_cnt = %d", + monitor->timer_cnt); + } + if ((monitor->timer_cnt >= monitor_hdr->monitor_count) || + (monitor->first_entry == AW_FIRST_ENTRY)) { + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + ave_capacity = monitor->vbat_sum / monitor->timer_cnt; + + if (monitor->custom_capacity) + ave_capacity = monitor->custom_capacity; + + AW_DEV_LOGI(dev, "get average capacity = %d", ave_capacity); + + ret = aw_search_vmax_from_table(dev, monitor, + ave_capacity, &vmax_set); + if (ret < 0) + AW_DEV_LOGE(dev, "not find vmax_vol"); + else + aw_monitor_update_vmax_to_dsp(dev, monitor, vmax_set); + + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + } +} + +static void aw_monitor_work_func(struct work_struct *work) +{ + int ret = 0; + struct aw87xxx *aw87xxx = container_of(work, + struct aw87xxx, monitor.with_dsp_work.work); + struct device *dev = aw87xxx->dev; + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + AW_DEV_LOGD(dev, "enter"); + + if (monitor->esd_enable) { + ret = aw_monitor_chip_esd_check_work(aw87xxx); + if (ret < 0) + return; + } + + if (monitor_hdr->monitor_switch && !(aw87xxx->aw_dev.is_rec_mode) && + monitor->open_dsp_en && monitor->bin_status == AW_ACF_UPDATE) { + AW_DEV_LOGD(dev, "start low power protection"); + aw_monitor_with_dsp_vmax_work(dev, monitor); + } + + if (monitor->esd_enable || (monitor_hdr->monitor_switch && + !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en && + monitor->bin_status == AW_ACF_UPDATE)) { + schedule_delayed_work(&monitor->with_dsp_work, + msecs_to_jiffies(monitor_hdr->monitor_time)); + } +} + +void aw87xxx_monitor_stop(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + cancel_delayed_work_sync(&monitor->with_dsp_work); +} + +void aw87xxx_monitor_start(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + int ret = 0; + + ret = aw87xxx_dev_check_reg_is_rec_mode(&aw87xxx->aw_dev); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "get reg current mode failed"); + return; + } + + if (monitor->esd_enable || (monitor->monitor_hdr.monitor_switch && + !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en + && monitor->bin_status == AW_ACF_UPDATE)) { + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + monitor->pre_vmax = AW_VMAX_INIT_VAL; + monitor->first_entry = AW_FIRST_ENTRY; + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + + schedule_delayed_work(&monitor->with_dsp_work, + msecs_to_jiffies(monitor->monitor_hdr.monitor_time)); + } +} +/*************************************************************************** + * + * aw87xxx no dsp monitor func + * + ***************************************************************************/ +int aw87xxx_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, int32_t *vmax) +{ + int vbat_capacity = 0; + int ret = -1; + int vmax_vol = 0; + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + struct device *dev = aw87xxx->dev; + + ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); + if (ret < 0) + return ret; + + if (monitor->custom_capacity) + vbat_capacity = monitor->custom_capacity; + AW_DEV_LOGI(dev, "get_battery_capacity is[%d]", vbat_capacity); + + ret = aw_search_vmax_from_table(dev, monitor, + vbat_capacity, &vmax_vol); + if (ret < 0) { + AW_DEV_LOGE(dev, "not find vmax_vol"); + return ret; + } + + *vmax = vmax_vol; + return 0; +} + + +/*************************************************************************** + * + * aw87xxx monitor sysfs nodes + * + ***************************************************************************/ +static ssize_t aw_attr_get_esd_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (monitor->esd_enable) { + AW_DEV_LOGI(aw87xxx->dev, "esd-enable=true"); + len += snprintf(buf + len, PAGE_SIZE - len, + "esd-enable=true\n"); + } else { + AW_DEV_LOGI(aw87xxx->dev, "esd-enable=false"); + len += snprintf(buf + len, PAGE_SIZE - len, + "esd-enable=false\n"); + } + + return len; +} + +static ssize_t aw_attr_set_esd_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char esd_enable[AW_ESD_ENABLE_STRLEN] = {0}; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (strlen(buf) > AW_ESD_ENABLE_STRLEN) { + AW_DEV_LOGE(aw87xxx->dev, "input esd_enable_str_len is out of max[%d]", + AW_ESD_ENABLE_STRLEN); + return -EINVAL; + } + + if (sscanf(buf, "%s", esd_enable) == 1) { + AW_DEV_LOGD(aw87xxx->dev, "input esd-enable=[%s]", esd_enable); + if (!strcmp(esd_enable, "true")) + monitor->esd_enable = AW_ESD_ENABLE; + else + monitor->esd_enable = AW_ESD_DISABLE; + AW_DEV_LOGI(dev, "set esd-enable=[%s]", + monitor->esd_enable ? "true" : "false"); + } else { + AW_DEV_LOGE(aw87xxx->dev, "input esd-enable error"); + return -EINVAL; + } + + return len; +} + +static ssize_t aw_attr_get_vbat(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = -1; + int vbat_capacity = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (monitor->custom_capacity == 0) { + ret = aw_monitor_get_battery_capacity(dev, monitor, + &vbat_capacity); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "get battery_capacity failed"); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "vbat capacity=%d\n", vbat_capacity); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "vbat capacity=%d\n", + monitor->custom_capacity); + } + + return len; +} + +static ssize_t aw_attr_set_vbat(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret = -1; + int capacity = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + ret = kstrtouint(buf, 0, &capacity); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw87xxx->dev, "set capacity = %d", capacity); + if (capacity >= AW_VBAT_CAPACITY_MIN && + capacity <= AW_VBAT_CAPACITY_MAX){ + monitor->custom_capacity = capacity; + } else { + AW_DEV_LOGE(aw87xxx->dev, "vbat_set=invalid,please input value [%d-%d]", + AW_VBAT_CAPACITY_MIN, AW_VBAT_CAPACITY_MAX); + return -EINVAL; + } + + return len; +} + +static ssize_t aw_attr_get_vmax(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = -1; + int vbat_capacity = 0; + int vmax_get = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (monitor->open_dsp_en) { + ret = aw87xxx_dsp_get_vmax(&vmax_get, aw87xxx->dev_index); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, + "get dsp vmax fail, ret=%d", ret); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "get_vmax=0x%x\n", vmax_get); + } else { + ret = aw_monitor_get_battery_capacity(dev, monitor, + &vbat_capacity); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw87xxx->dev, "get_battery_capacity is [%d]", + vbat_capacity); + + if (monitor->custom_capacity) { + vbat_capacity = monitor->custom_capacity; + AW_DEV_LOGI(aw87xxx->dev, "get custom_capacity is [%d]", + vbat_capacity); + } + + ret = aw_search_vmax_from_table(aw87xxx->dev, monitor, + vbat_capacity, &vmax_get); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "not find vmax_vol"); + len += snprintf(buf + len, PAGE_SIZE - len, + "not_find_vmax_vol\n"); + return len; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%x\n", vmax_get); + AW_DEV_LOGI(aw87xxx->dev, "0x%x", vmax_get); + } + + return len; +} + +static ssize_t aw_attr_set_vmax(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t vmax_set = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + ret = kstrtouint(buf, 0, &vmax_set); + if (ret < 0) + return ret; + + AW_DEV_LOGI(aw87xxx->dev, "vmax_set=0x%x", vmax_set); + + if (monitor->open_dsp_en) { + ret = aw87xxx_dsp_set_vmax(vmax_set, aw87xxx->dev_index); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "send dsp_msg error, ret = %d", + ret); + return ret; + } + msleep(2); + } else { + AW_DEV_LOGE(aw87xxx->dev, "no_dsp system,vmax_set invalid"); + return -EINVAL; + } + + return count; +} + +static ssize_t aw_attr_get_monitor_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw87xxx monitor switch: %d\n", + monitor_hdr->monitor_switch); + return len; +} + + +int aw87xxx_dev_monitor_switch_set(struct aw_monitor *monitor, uint32_t enable) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + AW_DEV_LOGI(aw87xxx->dev, "monitor switch set =%d", enable); + + if (!monitor->bin_status) { + AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); + return -EINVAL; + } + + if (monitor_hdr->monitor_switch == enable) + return 0; + + if (enable > 0) { + monitor_hdr->monitor_switch = 1; + if (monitor->open_dsp_en) { + monitor->pre_vmax = AW_VMAX_INIT_VAL; + monitor->first_entry = AW_FIRST_ENTRY; + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + } + } else { + monitor_hdr->monitor_switch = 0; + } + + return 0; +} + +static ssize_t aw_attr_set_monitor_switch(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t enable = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + ret = aw87xxx_dev_monitor_switch_set(monitor, enable); + if (ret) + return ret; + + return count; +} + +static ssize_t aw_attr_get_monitor_time(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw_monitor_timer = %d(ms)\n", + monitor_hdr->monitor_time); + return len; +} + +static ssize_t aw_attr_set_monitor_time(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int timer_val = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + ret = kstrtouint(buf, 0, &timer_val); + if (ret < 0) + return ret; + + AW_DEV_LOGI(aw87xxx->dev, "input monitor timer=%d(ms)", timer_val); + + if (!monitor->bin_status) { + AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); + return -EINVAL; + } + + if (timer_val != monitor_hdr->monitor_time) + monitor_hdr->monitor_time = timer_val; + else + AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_time"); + + return count; +} + +static ssize_t aw_attr_get_monitor_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw_monitor_count = %d\n", + monitor_hdr->monitor_count); + return len; +} + +static ssize_t aw_attr_set_monitor_count(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int monitor_count = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + ret = kstrtouint(buf, 0, &monitor_count); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw87xxx->dev, "input monitor count=%d", monitor_count); + + if (!monitor->bin_status) { + AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); + return -EINVAL; + } + + if (monitor_count != monitor_hdr->monitor_count) + monitor_hdr->monitor_count = monitor_count; + else + AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_count"); + + return count; +} + + +static ssize_t aw_attr_get_rx(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + ssize_t len = 0; + int ret = -1; + uint32_t enable = 0; + + if (monitor->open_dsp_en) { + ret = aw87xxx_dsp_get_rx_module_enable(&enable); + if (ret) { + AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", ret); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "aw87xxx rx: %d\n", enable); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "command is invalid\n"); + } + + return len; +} + +static ssize_t aw_attr_set_rx(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + int ret = -1; + uint32_t enable; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + if (monitor->open_dsp_en) { + AW_DEV_LOGI(aw87xxx->dev, "set rx enable=%d", enable); + + ret = aw87xxx_dsp_set_rx_module_enable(enable); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", + ret); + return ret; + } + } else { + AW_DEV_LOGE(aw87xxx->dev, "command is invalid"); + return -EINVAL; + } + + return count; +} + + +static DEVICE_ATTR(esd_enable, S_IWUSR | S_IRUGO, + aw_attr_get_esd_enable, aw_attr_set_esd_enable); +static DEVICE_ATTR(vbat, S_IWUSR | S_IRUGO, + aw_attr_get_vbat, aw_attr_set_vbat); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, + aw_attr_get_vmax, aw_attr_set_vmax); + +static DEVICE_ATTR(monitor_switch, S_IWUSR | S_IRUGO, + aw_attr_get_monitor_switch, aw_attr_set_monitor_switch); +static DEVICE_ATTR(monitor_time, S_IWUSR | S_IRUGO, + aw_attr_get_monitor_time, aw_attr_set_monitor_time); +static DEVICE_ATTR(monitor_count, S_IWUSR | S_IRUGO, + aw_attr_get_monitor_count, aw_attr_set_monitor_count); +static DEVICE_ATTR(rx, S_IWUSR | S_IRUGO, + aw_attr_get_rx, aw_attr_set_rx); + +static struct attribute *aw_monitor_vol_adjust[] = { + &dev_attr_esd_enable.attr, + &dev_attr_vbat.attr, + &dev_attr_vmax.attr, + NULL +}; + +static struct attribute_group aw_monitor_vol_adjust_group = { + .attrs = aw_monitor_vol_adjust, +}; + +static struct attribute *aw_monitor_control[] = { + &dev_attr_monitor_switch.attr, + &dev_attr_monitor_time.attr, + &dev_attr_monitor_count.attr, + &dev_attr_rx.attr, + NULL +}; + +static struct attribute_group aw_monitor_control_group = { + .attrs = aw_monitor_control, +}; + +/*************************************************************************** + * + * aw87xxx monitor init + * + ***************************************************************************/ +static void aw_monitor_dtsi_parse(struct device *dev, + struct aw_monitor *monitor, + struct device_node *dev_node) +{ + int ret = -1; + const char *esd_enable; + + ret = of_property_read_string(dev_node, "esd-enable", &esd_enable); + if (ret < 0) { + AW_DEV_LOGI(dev, "esd_enable parse failed, user default[disable]"); + monitor->esd_enable = AW_ESD_DISABLE; + } else { + if (!strcmp(esd_enable, "true")) + monitor->esd_enable = AW_ESD_ENABLE; + else + monitor->esd_enable = AW_ESD_DISABLE; + + AW_DEV_LOGI(dev, "parse esd-enable=[%s]", + monitor->esd_enable ? "true" : "false"); + } +} + +void aw87xxx_monitor_init(struct device *dev, struct aw_monitor *monitor, + struct device_node *dev_node) +{ + int ret = -1; + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + + monitor->dev_index = aw87xxx->dev_index; + monitor->monitor_hdr.monitor_time = AW_DEFAULT_MONITOR_TIME; + + aw_monitor_dtsi_parse(dev, monitor, dev_node); + + /* get platform open dsp type */ + monitor->open_dsp_en = aw87xxx_dsp_isEnable(); + + ret = sysfs_create_group(&dev->kobj, &aw_monitor_vol_adjust_group); + if (ret < 0) + AW_DEV_LOGE(dev, "failed to create monitor vol_adjust sysfs nodes"); + + INIT_DELAYED_WORK(&monitor->with_dsp_work, aw_monitor_work_func); + + if (monitor->open_dsp_en) { + ret = sysfs_create_group(&dev->kobj, &aw_monitor_control_group); + if (ret < 0) + AW_DEV_LOGE(dev, "failed to create monitor dsp control sysfs nodes"); + } + + if (!ret) + AW_DEV_LOGI(dev, "monitor init succeed"); +} + +void aw87xxx_monitor_exit(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + /*rm attr node*/ + sysfs_remove_group(&aw87xxx->dev->kobj, + &aw_monitor_vol_adjust_group); + + aw87xxx_monitor_stop(monitor); + + if (monitor->open_dsp_en) { + sysfs_remove_group(&aw87xxx->dev->kobj, + &aw_monitor_control_group); + } +} + diff --git a/asoc/codecs/aw87xxx/aw87xxx_monitor.h b/asoc/codecs/aw87xxx/aw87xxx_monitor.h new file mode 100644 index 000000000000..4b0282b4148c --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_monitor.h @@ -0,0 +1,96 @@ +#ifndef __AW87XXX_MONITOR_H__ +#define __AW87XXX_MONITOR_H__ + +#define AW_WAIT_DSP_OPEN_TIME (3000) +#define AW_VBAT_CAPACITY_MIN (0) +#define AW_VBAT_CAPACITY_MAX (100) +#define AW_VMAX_INIT_VAL (0xFFFFFFFF) +#define AW_VBAT_MAX (100) +#define AW_VMAX_MAX (0) +#define AW_DEFAULT_MONITOR_TIME (3000) +#define AW_WAIT_TIME (3000) +#define REG_STATUS_CHECK_MAX (10) +#define AW_ESD_CHECK_DELAY (1) + +#define AW_ESD_ENABLE (true) +#define AW_ESD_DISABLE (false) +#define AW_ESD_ENABLE_STRLEN (16) + +enum aw_monitor_init { + AW_MONITOR_CFG_WAIT = 0, + AW_MONITOR_CFG_OK = 1, +}; + +enum aw_monitor_hdr_info { + AW_MONITOR_HDR_DATA_SIZE = 0x00000004, + AW_MONITOR_HDR_DATA_BYTE_LEN = 0x00000004, +}; + +enum aw_monitor_data_ver { + AW_MONITOR_DATA_VER = 0x00000001, + AW_MONITOR_DATA_VER_MAX, +}; + +enum aw_monitor_first_enter { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +struct aw_bin_header { + uint32_t check_sum; + uint32_t header_ver; + uint32_t bin_data_type; + uint32_t bin_data_ver; + uint32_t bin_data_size; + uint32_t ui_ver; + char product[8]; + uint32_t addr_byte_len; + uint32_t data_byte_len; + uint32_t device_addr; + uint32_t reserve[4]; +}; + +struct aw_monitor_header { + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t step_count; + uint32_t reserve[4]; +}; + +struct vmax_step_config { + uint32_t vbat_min; + uint32_t vbat_max; + int vmax_vol; +}; + +struct aw_monitor { + bool open_dsp_en; + bool esd_enable; + int32_t dev_index; + uint8_t first_entry; + uint8_t timer_cnt; + uint32_t vbat_sum; + int32_t custom_capacity; + uint32_t pre_vmax; + + int bin_status; + struct aw_monitor_header monitor_hdr; + struct vmax_step_config *vmax_cfg; + + struct delayed_work with_dsp_work; +}; + +void aw87xxx_monitor_cfg_free(struct aw_monitor *monitor); +int aw87xxx_monitor_bin_parse(struct device *dev, + char *monitor_data, uint32_t data_len); +void aw87xxx_monitor_stop(struct aw_monitor *monitor); +void aw87xxx_monitor_start(struct aw_monitor *monitor); +int aw87xxx_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, + int32_t *vmax); +void aw87xxx_monitor_init(struct device *dev, struct aw_monitor *monitor, + struct device_node *dev_node); +void aw87xxx_monitor_exit(struct aw_monitor *monitor); +int aw87xxx_dev_monitor_switch_set(struct aw_monitor *monitor, uint32_t enable); + +#endif diff --git a/asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h b/asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h new file mode 100644 index 000000000000..7b95d2252e6c --- /dev/null +++ b/asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h @@ -0,0 +1,2315 @@ +#ifndef __AW87XXX_PID_18_REG_H__ +#define __AW87XXX_PID_18_REG_H__ + +/* registers list */ +#define AW87XXX_PID_18_CHIPID_REG (0x00) +#define AW87XXX_PID_18_SYSST_REG (0x01) +#define AW87XXX_PID_18_SYSINT_REG (0x02) +#define AW87XXX_PID_18_SYSCTRL_REG (0x03) +#define AW87XXX_PID_18_CPOC_REG (0x04) +#define AW87XXX_PID_18_CLASSD_REG (0x05) +#define AW87XXX_PID_18_MADPVTH_REG (0x06) +#define AW87XXX_PID_18_A3PARAM_REG (0x07) +#define AW87XXX_PID_18_A3A2PO_REG (0x08) +#define AW87XXX_PID_18_A2PARAM_REG (0x09) +#define AW87XXX_PID_18_A1PARAM_REG (0x0A) +#define AW87XXX_PID_18_POPCLK_REG (0x0B) +#define AW87XXX_PID_18_GTDRCPSS_REG (0x0C) +#define AW87XXX_PID_18_MULTI_REG (0x0D) +#define AW87XXX_PID_18_DFT1_REG (0x61) +#define AW87XXX_PID_18_DFT2_REG (0x62) +#define AW87XXX_PID_18_DFT3_REG (0x63) +#define AW87XXX_PID_18_DFT4_REG (0x64) +#define AW87XXX_PID_18_DFT5_REG (0x65) +#define AW87XXX_PID_18_DFT6_REG (0x66) + +#define AW87XXX_PID_18_CLASSD_DEFAULT (0x10) + +/******************************************** + * soft control info + * If you need to update this file, add this information manually + *******************************************/ +unsigned char aw87xxx_pid_18_softrst_access[2] = {0x00, 0xaa}; + +/******************************************** + * Register Access + *******************************************/ +#define AW87XXX_PID_18_REG_MAX (0x67) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw87xxx_pid_18_reg_access[AW87XXX_PID_18_REG_MAX] = { + [AW87XXX_PID_18_CHIPID_REG] = (REG_RD_ACCESS), + [AW87XXX_PID_18_SYSST_REG] = (REG_RD_ACCESS), + [AW87XXX_PID_18_SYSINT_REG] = (REG_RD_ACCESS), + [AW87XXX_PID_18_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_CPOC_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_CLASSD_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_MADPVTH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A3PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A3A2PO_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A2PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A1PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_POPCLK_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_GTDRCPSS_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_MULTI_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT6_REG] = (REG_RD_ACCESS), +}; + +/* detail information of registers begin */ +/* CHIPID (0x00) detail */ +/* IDCODE bit 7:0 (CHIPID 0x00) */ +#define AW87XXX_PID_18_IDCODE_START_BIT (0) +#define AW87XXX_PID_18_IDCODE_BITS_LEN (8) +#define AW87XXX_PID_18_IDCODE_MASK \ + (~(((1< Date: Mon, 18 Jul 2022 11:20:51 +0800 Subject: [PATCH 51/79] asoc:revert aw87xxx code in wcd937x driver remove aw87xxx code; use mixer_paths.xml to control aw87xxx profile. Change-Id: I0234538aa3af690e0e9521d286c8ceac1b58109b Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2337390 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wang Wang Submit-Approved: Jira Key --- asoc/codecs/wcd937x/wcd937x.c | 84 ----------------------------------- 1 file changed, 84 deletions(-) diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 45667e5790a1..7bec6b399a87 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/asoc/codecs/wcd937x/wcd937x.c @@ -50,15 +50,6 @@ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) -#ifdef CONFIG_SND_SOC_AW87XXX -extern int aw87xxx_set_profile(int dev_index, char *profile); -static char *aw_profile[] = {"Music", "Off"}; -enum aw87xxx_dev_index { - AW_DEV_0 = 0, - AW_DEV_1 = 0, -}; -#endif - enum { CODEC_TX = 0, CODEC_RX, @@ -758,66 +749,6 @@ static int wcd937x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, } -#ifdef CONFIG_SND_SOC_AW87XXX -static int aw87xxx_pa_dev_0_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - int ret = 0; - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = aw87xxx_set_profile(AW_DEV_0, aw_profile[0]); - if (ret < 0) { - pr_err("[Awinic] %s: set profile[%s] failed", - __func__, aw_profile[0]); - return ret; - } - break; - case SND_SOC_DAPM_POST_PMD: - ret = aw87xxx_set_profile(AW_DEV_0, aw_profile[1]); - if (ret < 0) { - pr_err("[Awinic] %s: set profile[%s] failed", - __func__, aw_profile[1]); - return ret; - } - break; - default: - pr_err("%s: invalid DAPM event %d\n", __func__, event); - return -EINVAL; - } - return 0; -} - -static int aw87xxx_pa_dev_1_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - int ret = 0; - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = aw87xxx_set_profile(AW_DEV_1, aw_profile[0]); - if (ret < 0) { - pr_err("[Awinic] %s: set profile[%s] failed", - __func__, aw_profile[0]); - return ret; - } - break; - case SND_SOC_DAPM_POST_PMD: - ret = aw87xxx_set_profile(AW_DEV_1, aw_profile[1]); - if (ret < 0) { - pr_err("[Awinic] %s: set profile[%s] failed", - __func__, aw_profile[1]); - return ret; - } - break; - default: - pr_err("%s: invalid DAPM event %d\n", __func__, event); - return -EINVAL; - } - return 0; -} -#endif - static int wcd937x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -2494,15 +2425,6 @@ static const struct snd_soc_dapm_widget wcd937x_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), -#ifdef CONFIG_SND_SOC_AW87XXX - SND_SOC_DAPM_DAC_E("AW87XXX", NULL, SND_SOC_NOPM, 0, 0, - aw87xxx_pa_dev_0_event, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_DAC_E("AW87XXX", NULL, SND_SOC_NOPM, 0, 0, - aw87xxx_pa_dev_1_event, SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMD), -#endif - SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, @@ -2666,13 +2588,7 @@ static const struct snd_soc_dapm_route wcd937x_audio_map[] = { {"RDAC4", NULL, "RX3"}, {"AUX_RDAC", "Switch", "RDAC4"}, {"AUX PGA", NULL, "AUX_RDAC"}, - -#ifdef CONFIG_SND_SOC_AW87XXX - {"AW87XXX", NULL, "AUX PGA"}, - {"AUX", NULL, "AW87XXX"}, -#else {"AUX", NULL, "AUX PGA"}, -#endif {"RDAC3_MUX", "RX3", "RX3"}, {"RDAC3_MUX", "RX1", "RX1"}, From 9e53975a24b321f350eea348381935ef376c15ba Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Thu, 21 Jul 2022 16:19:03 +0800 Subject: [PATCH 52/79] asoc:integrate aw87xxx algo support add aw87xxx adsp algo support; for penang bringup. Change-Id: Icce5f9708c734160b50c152f7b08f97f3bd764b9 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2340718 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Wanlong Zhou Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw87xxx/aw87xxx.c | 108 ++++++++++++++++++++++++++++++ asoc/codecs/aw87xxx/aw87xxx.h | 7 +- asoc/codecs/aw87xxx/aw87xxx_dsp.c | 72 ++++++++++++++++++++ asoc/codecs/aw87xxx/aw87xxx_dsp.h | 3 +- dsp/Kbuild | 4 ++ dsp/q6afe.c | 14 +++- 6 files changed, 205 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/aw87xxx/aw87xxx.c b/asoc/codecs/aw87xxx/aw87xxx.c index 28c7d422718f..1d6e10ee42b6 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.c +++ b/asoc/codecs/aw87xxx/aw87xxx.c @@ -66,9 +66,16 @@ static LIST_HEAD(g_aw87xxx_list); static DEFINE_MUTEX(g_aw87xxx_mutex_lock); unsigned int g_aw87xxx_dev_cnt = 0; +#ifdef AW87XXX_RUNIN_TEST +static unsigned int g_runin_test; +#endif + static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", "spin_180", "spin_270"}; + +static const char *const aw87xxx_runin_test_switch[] = {"Off", "On"}; + #ifdef AW_KERNEL_VER_OVER_4_19_1 static struct aw_componet_codec_ops aw_componet_codec_ops = { .add_codec_controls = snd_soc_add_component_controls, @@ -212,6 +219,9 @@ int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) ret = aw87xxx_power_on(aw87xxx, profile); if (!ret) aw87xxx_monitor_start(&aw87xxx->monitor); +#ifdef AW87XXX_RUNIN_TEST + schedule_delayed_work(&aw87xxx->adsp_status, msecs_to_jiffies(500)); +#endif } mutex_unlock(&aw87xxx->reg_lock); @@ -533,6 +543,85 @@ static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, } +static int aw87xxx_runin_test_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = g_runin_test; + AW_DEV_LOGI(aw87xxx->dev, "aw87xxx_runin test get val %d", g_runin_test); + + return 0; +} + +static int aw87xxx_runin_test_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = 0; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + ctrl_value = ucontrol->value.integer.value[0]; + + if (ctrl_value > 1 ) { + ctrl_value = 0; + } + + g_runin_test = ctrl_value; + + AW_DEV_LOGI(aw87xxx->dev, "aw87xxx_runin test set val %d", ctrl_value); + + return 0; +} + +static int aw87xxx_runin_test_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_runin_test_switch); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_runin_test_switch[uinfo->value.enumerated.item], + strlen(aw87xxx_runin_test_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static void aw87xxx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + uint32_t ctrl_value = 0; + AW_LOGI("g_runin_test = %d", g_runin_test); + + /*no test reg set default value*/ + if (g_runin_test == 0) { + return; + } + + /*set afe rx module disable*/ + ret = aw87xxx_dsp_set_rx_module_enable(ctrl_value); + if (ret) { + AW_LOGE("disable afe rx module %d falied , ret=%d", ctrl_value, ret); + return; + } + AW_LOGI("disable afe module success"); + + /*set skt module disable*/ + ret = aw87xxx_dsp_set_copp_module_enable(ctrl_value); + if (ret) { + AW_LOGE("disable skt module %d falied , ret=%d\n", ctrl_value, ret); + return; + } + AW_LOGI("disable skt module success"); + +} + static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, void *codec) { @@ -648,6 +737,20 @@ static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, aw87xxx_kcontrol[0].put = aw87xxx_spin_switch_put; aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; + kctl_name[1] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[1] == NULL) + return -ENOMEM; + + snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_runin_test_switch"); + + aw87xxx_kcontrol[1].name = kctl_name[1]; + aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[1].info = aw87xxx_runin_test_switch_info; + aw87xxx_kcontrol[1].get = aw87xxx_runin_test_switch_get; + aw87xxx_kcontrol[1].put = aw87xxx_runin_test_switch_put; + aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, aw87xxx_kcontrol, kcontrol_num); if (ret < 0) { @@ -1391,6 +1494,11 @@ static int aw87xxx_i2c_probe(struct i2c_client *client, /*monitor init*/ aw87xxx_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); +#ifdef AW87XXX_RUNIN_TEST + g_runin_test = 0; + INIT_DELAYED_WORK(&aw87xxx->adsp_status, aw87xxx_set_adsp_module_status); +#endif + /*add device to total list */ mutex_lock(&g_aw87xxx_mutex_lock); g_aw87xxx_dev_cnt++; diff --git a/asoc/codecs/aw87xxx/aw87xxx.h b/asoc/codecs/aw87xxx/aw87xxx.h index 45d3cea7779a..0a6ef9d441ad 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.h +++ b/asoc/codecs/aw87xxx/aw87xxx.h @@ -16,7 +16,7 @@ #define AW87XXX_OFF_BIN_OK (1) #define AW87XXX_PRIVATE_KCONTROL_NUM (3) -#define AW87XXX_PUBLIC_KCONTROL_NUM (1) +#define AW87XXX_PUBLIC_KCONTROL_NUM (2) #define AW_I2C_RETRIES (5) #define AW_I2C_RETRY_DELAY (2) @@ -33,6 +33,8 @@ #define AWRW_DATA_BYTES (1) #define AWRW_HDR_LEN (24) +#define AW87XXX_RUNIN_TEST 1 + /*********************************************************** * * aw87xxx codec control compatible with kernel 4.19 @@ -113,6 +115,9 @@ struct aw87xxx { struct list_head list; struct aw_monitor monitor; +#ifdef AW87XXX_RUNIN_TEST + struct delayed_work adsp_status; +#endif }; int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile); diff --git a/asoc/codecs/aw87xxx/aw87xxx_dsp.c b/asoc/codecs/aw87xxx/aw87xxx_dsp.c index bd9896cd172b..f09ac010b47c 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_dsp.c +++ b/asoc/codecs/aw87xxx/aw87xxx_dsp.c @@ -20,15 +20,35 @@ #include #include #include +#include #include "aw87xxx_log.h" #include "aw87xxx_dsp.h" +/***************dsp communicate**************/ +#ifdef AW_QCOM_OPEN_DSP_PLATFORM +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 1)) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#endif + static DEFINE_MUTEX(g_dsp_lock); static unsigned int g_spin_value = 0; static int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; static int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; +#define AW_COPP_MODULE_ID (0X10013D02) /*SKT module id*/ +#define AW_COPP_PARAMS_ID_AWDSP_ENABLE (0X10013D14) /*SKT enable param id*/ + #ifdef AW_MTK_OPEN_DSP_PLATFORM extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); @@ -77,6 +97,46 @@ static void aw_set_port_id(int rx_port_id) } #endif +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + /*for v3*/ + int copp_idx = 0; + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_info("%s Awinic port_id 0x%x, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + + copp_idx = adm_get_default_copp_idx(port_id); + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_info("%s Awinic port_id 0x%x, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + if (rc) + pr_err("%s: Awinic Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; +} + uint8_t aw87xxx_dsp_isEnable(void) { #if (defined AW_QCOM_OPEN_DSP_PLATFORM) || (defined AW_MTK_OPEN_DSP_PLATFORM) @@ -252,6 +312,18 @@ int aw87xxx_dsp_set_rx_module_enable(int enable) &enable, sizeof(uint32_t)); } +int aw87xxx_dsp_set_copp_module_enable(bool enable) +{ + int ret; + + ret = aw_adm_param_enable(g_rx_port_id, AW_COPP_MODULE_ID, + AW_COPP_PARAMS_ID_AWDSP_ENABLE, enable); + if (ret) + return -EINVAL; + + AW_LOGI("set skt %s", enable == 1 ? "enable" : "disable"); + return 0; +} int aw87xxx_dsp_get_vmax(uint32_t *vmax, int dev_index) { diff --git a/asoc/codecs/aw87xxx/aw87xxx_dsp.h b/asoc/codecs/aw87xxx/aw87xxx_dsp.h index 2668170f152d..b79635f777cf 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_dsp.h +++ b/asoc/codecs/aw87xxx/aw87xxx_dsp.h @@ -4,7 +4,7 @@ #include "aw87xxx_device.h" /*#define AW_MTK_OPEN_DSP_PLATFORM*/ -/*#define AW_QCOM_OPEN_DSP_PLATFORM*/ +#define AW_QCOM_OPEN_DSP_PLATFORM /*Note: The pord_ID is configured according to different platforms*/ #define AW_DSP_SLEEP_TIME (10) @@ -54,6 +54,7 @@ enum aw_dsp_channel { uint8_t aw87xxx_dsp_isEnable(void); int aw87xxx_dsp_get_rx_module_enable(int *enable); int aw87xxx_dsp_set_rx_module_enable(int enable); +int aw87xxx_dsp_set_copp_module_enable(bool enable); int aw87xxx_dsp_get_vmax(uint32_t *vmax, int channel); int aw87xxx_dsp_set_vmax(uint32_t vmax, int channel); int aw87xxx_dsp_set_spin(uint32_t ctrl_value); diff --git a/dsp/Kbuild b/dsp/Kbuild index 847df7faadd7..a3468fb4220d 100755 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -23,6 +23,10 @@ ifeq ($(CONFIG_AW882XX_MAPSIZE_16K), y) CDEFINES += -DCONFIG_AW882XX_MAPSIZE_16K endif +ifdef CONFIG_SND_SOC_AW87XXX +CDEFINES += -DCONFIG_SND_SOC_AW87XXX +endif + ifeq ($(CONFIG_SND_SOC_AUTO), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf diff --git a/dsp/q6afe.c b/dsp/q6afe.c index d78ce8f79086..cec141f315e9 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -33,6 +33,17 @@ void aw_cal_unmap_memory(void); #endif /* #ifdef CONFIG_SND_SOC_AWINIC_AW882XX */ + +#ifdef CONFIG_SND_SOC_AW87XXX +static int g_rx_port_id = 0; + +void aw_set_port_id(int rx_port_id) +{ + g_rx_port_id = rx_port_id; +} +EXPORT_SYMBOL(aw_set_port_id); +#endif /* #ifdef CONFIG_SND_SOC_AW87XXX */ + #define WAKELOCK_TIMEOUT 5000 #define AFE_CLK_TOKEN 1024 #define AFE_NOWAIT_TOKEN 2048 @@ -609,6 +620,7 @@ int afe_get_topology(int port_id) done: return topology; } +EXPORT_SYMBOL(afe_get_topology); /** * afe_set_aanc_info - @@ -12058,7 +12070,7 @@ static int afe_unmap_cal_data(int32_t cal_type, ret = afe_cmd_memory_unmap( cal_block->map_data.q6map_handle); atomic_set(&this_afe.mem_map_cal_index, -1); -#ifdef CONFIG_AW882XX_PARAMS_STORED_IN_BIN +#if defined(CONFIG_AW882XX_PARAMS_STORED_IN_BIN) || defined(CONFIG_SND_SOC_AW87XXX) atomic_set(&this_afe.mem_map_cal_handles[cal_index],0); #endif if (ret < 0) { From f5abde2d5be06125ee5db012ee0f13c48ee8db13 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Fri, 29 Jul 2022 14:51:00 +0800 Subject: [PATCH 53/79] aw87xxx:fix bypass failure udpate q6afe.c; fix aw87xxx bypass failure. Change-Id: I80b5708c326e420d98e2af4ed9894af9866369cb Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2348916 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Wang Wang Submit-Approved: Jira Key --- dsp/q6afe.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index cec141f315e9..f71bc241f6a2 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -4006,11 +4006,19 @@ int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) } EXPORT_SYMBOL(aw_send_afe_tx_module_enable); +#ifdef CONFIG_SND_SOC_AW87XXX +int aw_send_afe_cal_apr(uint32_t param_id, void *buf, int cmd_size, bool write) /*aw87xxx*/ +#else int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, - uint32_t param_id, void *buf, int cmd_size, bool write) + uint32_t param_id, void *buf, int cmd_size, bool write) /*aw882xx*/ +#endif { int32_t result = 0; - uint32_t port_id = rx_port_id; +#ifdef CONFIG_SND_SOC_AW87XXX + uint32_t port_id = g_rx_port_id; +#else + uint32_t port_id = rx_port_id; /*aw882xx*/ +#endif int32_t module_id = AFE_MODULE_ID_AWDSP_RX; uint32_t port_index = 0; uint32_t payload_size = 0; @@ -4021,10 +4029,12 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, pr_debug("%s: enter\n", __func__); +#ifndef CONFIG_SND_SOC_AW87XXX if (param_id == AFE_PARAM_ID_AWDSP_TX_SET_ENABLE) { port_id = tx_port_id; module_id = AFE_MODULE_ID_AWDSP_TX; } +#endif if (aw_cal->map_data.dma_buf == 0) { /*Minimal chunk size is 8K*/ From 4a1717eb25e317afa4ca941ed59e93a878e91f0f Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 30 Aug 2022 16:24:11 +0800 Subject: [PATCH 54/79] wcd937x:add interface to control aux path mode for penang5g, one speaker is connected to aux path; add interface to control aux path mode; this change is from qcom. Change-Id: Ifdd56353dea943fc4bb1aa70e0710e31cc7dea68 Reviewed-on: https://gerrit.mot.com/2374619 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/wcd937x/wcd937x.c | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c index 7bec6b399a87..cd2375636de8 100644 --- a/asoc/codecs/wcd937x/wcd937x.c +++ b/asoc/codecs/wcd937x/wcd937x.c @@ -1934,6 +1934,57 @@ static int wcd937x_tx_ch_pwr_level_put(struct snd_kcontrol *kcontrol, return 0; } +static int wcd937x_aux_path_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int aux_mode; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + aux_mode = ((snd_soc_component_read32(component, + WCD937X_DIGITAL_CDC_PATH_MODE) & 0x40)>>6); + + ucontrol->value.integer.value[0] = aux_mode; + + dev_dbg(component->dev, "%s: aux_mode = 0x%x ", __func__, + aux_mode); + + return 0; +} + +static int wcd937x_aux_path_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int aux_mode; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + dev_dbg(component->dev, "%s: codec version %u, ucontrol->value.integer.value[0] = %ld ", + __func__, wcd937x->version, ucontrol->value.integer.value[0]); + + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld", + __func__, ucontrol->value.integer.value[0]); + + aux_mode = ucontrol->value.integer.value[0]; + + if (aux_mode) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_PATH_MODE, + 0x40, 0x40); + snd_soc_component_update_bits(component, + WCD937X_AUX_AUXPA, + 0x10, 0x10); + } else { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_PATH_MODE, + 0x40, 0x00); + snd_soc_component_update_bits(component, + WCD937X_AUX_AUXPA, + 0x10, 0x00); + } + return 0; +} + static int wcd937x_ear_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2067,6 +2118,11 @@ static int wcd937x_codec_enable_vdd_buck(struct snd_soc_dapm_widget *w, return 0; } + +static const char * const wcd937x_aux_path_mode_text[] = { + "HP_MODE", "NORMAL_MODE", +}; + static const char * const rx_hph_mode_mux_text[] = { "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_AB_HIFI", @@ -2196,6 +2252,11 @@ static const struct soc_enum rx_hph_mode_mux_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), rx_hph_mode_mux_text); +static const struct soc_enum wcd937x_aux_path_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wcd937x_aux_path_mode_text), + wcd937x_aux_path_mode_text); + + static SOC_ENUM_SINGLE_EXT_DECL(wcd937x_ear_pa_gain_enum, wcd937x_ear_pa_gain_text); @@ -2244,6 +2305,9 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = { wcd937x_tx_ch_pwr_level_get, wcd937x_tx_ch_pwr_level_put), SOC_ENUM_EXT("TX CH3 PWR", wcd937x_tx_ch_pwr_level_enum, wcd937x_tx_ch_pwr_level_get, wcd937x_tx_ch_pwr_level_put), + SOC_ENUM_EXT("AUX PATH Mode", wcd937x_aux_path_mode_enum, + wcd937x_aux_path_mode_get, wcd937x_aux_path_mode_put), + }; static const struct snd_kcontrol_new adc1_switch[] = { From 0fe2cbe283e4389ca2acc513b305543fa69d8a04 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Fri, 2 Sep 2022 10:04:47 +0800 Subject: [PATCH 55/79] aw87xxx:add delay work to update spin update aw87xxx driver; add delay work to update spin after power on. Change-Id: I78b37caeb3873253acbcdd8ea0e6b0a835fb3e2b Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2376772 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw87xxx/aw87xxx.c | 97 +++++++++++++++++++++++++++++++++++ asoc/codecs/aw87xxx/aw87xxx.h | 3 +- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/aw87xxx/aw87xxx.c b/asoc/codecs/aw87xxx/aw87xxx.c index 1d6e10ee42b6..677bb923b477 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.c +++ b/asoc/codecs/aw87xxx/aw87xxx.c @@ -70,9 +70,13 @@ unsigned int g_aw87xxx_dev_cnt = 0; static unsigned int g_runin_test; #endif +static unsigned int g_saved_spin_value; +static unsigned int g_spin_enable; + static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", "spin_180", "spin_270"}; +static const char *const aw87xxx_spin_status[] = {"Off", "On"}; static const char *const aw87xxx_runin_test_switch[] = {"Off", "On"}; @@ -222,6 +226,7 @@ int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) #ifdef AW87XXX_RUNIN_TEST schedule_delayed_work(&aw87xxx->adsp_status, msecs_to_jiffies(500)); #endif + schedule_delayed_work(&aw87xxx->spin_status, msecs_to_jiffies(100)); } mutex_unlock(&aw87xxx->reg_lock); @@ -522,6 +527,12 @@ static int aw87xxx_spin_switch_put(struct snd_kcontrol *kcontrol, struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; ctrl_value = ucontrol->value.integer.value[0]; + g_saved_spin_value = ctrl_value; + + //don't send status to adsp until it's enabled when speaker on + if (!g_spin_enable) + return 0; + ret = aw87xxx_dsp_set_spin(ctrl_value); if (ret) { AW_DEV_LOGE(aw87xxx->dev, "write spin failed"); @@ -536,6 +547,11 @@ static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, { struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + if (!g_spin_enable){ + ucontrol->value.integer.value[0] = g_saved_spin_value; + return 0; + } + ucontrol->value.integer.value[0] = aw87xxx_dsp_get_spin(); AW_DEV_LOGD(aw87xxx->dev, "current spin is %ld", ucontrol->value.integer.value[0]); @@ -543,6 +559,69 @@ static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, } +static int aw87xxx_spin_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_spin_status); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_spin_status[uinfo->value.enumerated.item], + strlen(aw87xxx_spin_status[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw87xxx_spin_status_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = 0; + //int ret = 0; + //struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + ctrl_value = ucontrol->value.integer.value[0]; + + g_spin_enable = ctrl_value; + + return 0; +} + +static int aw87xxx_spin_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = g_spin_enable; + AW_DEV_LOGD(aw87xxx->dev, "current spin status enable is %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +// delayed work to update spin value +static void aw87xxx_set_spin_status(struct work_struct *work) +{ + int ret = 0; + AW_LOGI("g_saved_spin_value = %d, g_spin_enable = %d", g_saved_spin_value, g_spin_enable); + + if (!g_spin_enable) + return; + + ret = aw87xxx_dsp_set_spin(g_saved_spin_value); + if (ret) { + AW_LOGE("write spin failed"); + } + AW_LOGD("write spin done g_saved_spin_value=%d", g_saved_spin_value); + + return; +} + static int aw87xxx_runin_test_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -751,6 +830,20 @@ static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, aw87xxx_kcontrol[1].put = aw87xxx_runin_test_switch_put; aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; + kctl_name[2] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[2] == NULL) + return -ENOMEM; + + snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_spin_status"); + + aw87xxx_kcontrol[2].name = kctl_name[2]; + aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[2].info = aw87xxx_spin_status_info; + aw87xxx_kcontrol[2].get = aw87xxx_spin_status_get; + aw87xxx_kcontrol[2].put = aw87xxx_spin_status_put; + aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, aw87xxx_kcontrol, kcontrol_num); if (ret < 0) { @@ -1498,6 +1591,10 @@ static int aw87xxx_i2c_probe(struct i2c_client *client, g_runin_test = 0; INIT_DELAYED_WORK(&aw87xxx->adsp_status, aw87xxx_set_adsp_module_status); #endif + g_saved_spin_value = AW_SPIN_0; + INIT_DELAYED_WORK(&aw87xxx->spin_status, aw87xxx_set_spin_status); + + g_spin_enable = 0; /*add device to total list */ mutex_lock(&g_aw87xxx_mutex_lock); diff --git a/asoc/codecs/aw87xxx/aw87xxx.h b/asoc/codecs/aw87xxx/aw87xxx.h index 0a6ef9d441ad..e29c1ed0fa15 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.h +++ b/asoc/codecs/aw87xxx/aw87xxx.h @@ -16,7 +16,7 @@ #define AW87XXX_OFF_BIN_OK (1) #define AW87XXX_PRIVATE_KCONTROL_NUM (3) -#define AW87XXX_PUBLIC_KCONTROL_NUM (2) +#define AW87XXX_PUBLIC_KCONTROL_NUM (3) #define AW_I2C_RETRIES (5) #define AW_I2C_RETRY_DELAY (2) @@ -118,6 +118,7 @@ struct aw87xxx { #ifdef AW87XXX_RUNIN_TEST struct delayed_work adsp_status; #endif + struct delayed_work spin_status; }; int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile); From 40f17b3f9fe18fffd7cd8c07ca3c6211f65a58d0 Mon Sep 17 00:00:00 2001 From: Yunxian He Date: Fri, 28 Oct 2022 09:58:24 +0800 Subject: [PATCH 56/79] [Penang5g] [Audio] Turn on PA ESD protection mechanism Modify point: 1, ESD will cause PA status abnormal, can't recover automatically, turn on PA protection mechanism. Detect abnormal, auto reset Change-Id: Ie00c24f02c53f5bc33fd11d406b16ca70f911651 Signed-off-by: Yunxian He Reviewed-on: https://gerrit.mot.com/2415687 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw87xxx/aw87xxx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw87xxx/aw87xxx.c b/asoc/codecs/aw87xxx/aw87xxx.c index 677bb923b477..ff7961d4d04e 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.c +++ b/asoc/codecs/aw87xxx/aw87xxx.c @@ -239,9 +239,11 @@ int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile) if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) ret = aw87xxx_power_down(aw87xxx, profile); - else + else { + aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); + msleep(2); ret = aw87xxx_power_on(aw87xxx, profile); - + } return ret; } From f729cabe4597303cedcaa93ee7fce124eecb14a2 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Mon, 31 Oct 2022 11:37:12 +0800 Subject: [PATCH 57/79] asoc: update aw88261 driver to 1.9.0.9 changes: 1. fix ramp volume issue 2. add default spin relase time value 3. correct some logs Change-Id: I6a81865ae9c657dbf9de90dff61a5b45870cbe5a Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2416860 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index e7b07999558f..987ffd52e137 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.7" +#define AW882XX_DRIVER_VERSION "v1.9.0.9" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -49,6 +49,7 @@ static unsigned int g_algo_copp_en = false; #ifdef AW_SPIN_ENABLE static unsigned int g_spin_value = 0; static uint32_t g_spin_en = 0; +static uint32_t g_spin_relase_time = 100; #endif #ifdef AW882XX_RUNIN_TEST static unsigned int g_runin_test; @@ -796,9 +797,11 @@ static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, int value = 0; int aw882xx_volume = 0; + int useful_range = 0; + mutex_lock(&aw882xx->lock); /* Note: The larger the volume value is, the smaller the actual volume */ - int useful_range = desc->mute_volume - desc->init_volume; + useful_range = desc->mute_volume - desc->init_volume; aw_dev_info(aw882xx->dev, "desc->init_volume = %d mute_volume = %d", desc->init_volume, desc->mute_volume ); @@ -816,7 +819,7 @@ static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + desc->init_volume; - mutex_lock(&aw882xx->lock); + aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); mutex_unlock(&aw882xx->lock); aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); @@ -1412,7 +1415,7 @@ static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); - struct aw_spin_param spin_param; + //struct aw_spin_param spin_param; aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); @@ -1422,7 +1425,7 @@ static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, ctrl_value = ucontrol->value.integer.value[0]; if (g_algo_rx_en == true) { - ret = aw_dev_set_spin_param(aw_dev, ctrl_value, spin_param.relase_time); + ret = aw_dev_set_spin_param(aw_dev, ctrl_value, g_spin_relase_time); if (ret) aw_dev_err(aw882xx->dev, "set spin status error, ret=%d", ret); @@ -1455,9 +1458,10 @@ static int aw882xx_get_spin_relase_time(struct snd_kcontrol *kcontrol, ctrl_value = 0; } ucontrol->value.integer.value[0] = ctrl_value; + g_spin_relase_time = ctrl_value; } else { aw_dev_info(aw882xx->dev, "no stream, use record value"); - + ucontrol->value.integer.value[0] = g_spin_relase_time; } aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); return 0; @@ -1487,9 +1491,10 @@ static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, if (ret) aw_dev_err(aw882xx->dev, "set spin release time error, ret=%d", ret); } else { - aw_dev_info(aw882xx->dev, "set release time failed"); + aw_dev_info(aw882xx->dev, "set release time failed, g_spin_en=%d, g_algo_rx_en=%d\n", + g_spin_en, g_algo_rx_en); } - + g_spin_relase_time = ucontrol->value.integer.value[0]; return 0; } @@ -2566,10 +2571,10 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, ret = of_property_read_u32(np, "skt-prof-mode", &aw882xx->skt_prof_mode); if (ret) { aw882xx->skt_prof_mode = AW_PARAMS_PATH_MODE; - dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + dev_err(dev, "%s: skt-prof-mode get failed,use default value!\n", __func__); } else { - dev_info(dev, "%s: fade_flag = %d\n", - __func__, aw882xx->fade_flag); + dev_info(dev, "%s: skt-prof-mode = %d\n", + __func__, aw882xx->skt_prof_mode); } #endif From ec29bec10b20a4a454af2d0688d3785b1914b0ef Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Fri, 3 Mar 2023 14:56:04 +0800 Subject: [PATCH 58/79] aw87xxx:update driver update aw87xxx driver; version 2.6 -> 2.10. Change-Id: I518bd555621aed8fc031cc12a02d9c7a97f9050c Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2527519 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw87xxx/aw87xxx.c | 288 ++++++++++++++++++----- asoc/codecs/aw87xxx/aw87xxx.h | 2 +- asoc/codecs/aw87xxx/aw87xxx_device.c | 44 +++- asoc/codecs/aw87xxx/aw87xxx_device.h | 24 +- asoc/codecs/aw87xxx/aw87xxx_dsp.c | 2 +- asoc/codecs/aw87xxx/aw87xxx_monitor.c | 29 ++- asoc/codecs/aw87xxx/aw87xxx_monitor.h | 5 + asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h | 66 +++--- 8 files changed, 347 insertions(+), 113 deletions(-) diff --git a/asoc/codecs/aw87xxx/aw87xxx.c b/asoc/codecs/aw87xxx/aw87xxx.c index ff7961d4d04e..137bd2e02b9f 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.c +++ b/asoc/codecs/aw87xxx/aw87xxx.c @@ -56,7 +56,7 @@ * aw87xxx marco ******************************************************************/ #define AW87XXX_I2C_NAME "aw87xxx_pa" -#define AW87XXX_DRIVER_VERSION "v2.6.0" +#define AW87XXX_DRIVER_VERSION "v2.10.0" #define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" /************************************************************************* @@ -64,7 +64,7 @@ ************************************************************************/ static LIST_HEAD(g_aw87xxx_list); static DEFINE_MUTEX(g_aw87xxx_mutex_lock); -unsigned int g_aw87xxx_dev_cnt = 0; +unsigned int g_aw87xxx_dev_cnt; #ifdef AW87XXX_RUNIN_TEST static unsigned int g_runin_test; @@ -98,6 +98,26 @@ static struct aw_componet_codec_ops aw_componet_codec_ops = { * aw87xxx device update profile * ************************************************************************/ +static void aw87xxx_update_voltage_max(struct aw87xxx *aw87xxx, + struct aw_data_container *data_container) +{ + int i = 0; + struct aw_voltage_desc *vol_desc = &aw87xxx->aw_dev.vol_desc; + + if (data_container == NULL || data_container->len <= 2 + || vol_desc->addr == AW_REG_NONE) + return; + + for (i = 0; i < data_container->len; i = i + 2) { + if (data_container->data[i] == vol_desc->addr) { + vol_desc->vol_max = data_container->data[i + 1]; + AW_DEV_LOGD(aw87xxx->dev, "get voltage max = 0x%02x", + data_container->data[i + 1]); + return; + } + } +} + static int aw87xxx_power_down(struct aw87xxx *aw87xxx, char *profile) { int ret = 0; @@ -185,7 +205,7 @@ static int aw87xxx_power_on(struct aw87xxx *aw87xxx, char *profile) data_container = &prof_desc->data_container; AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", profile, data_container->len); - + aw87xxx_update_voltage_max(aw87xxx, data_container); if (aw_dev->ops.pwr_on_func) { ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); if (ret < 0) { @@ -220,6 +240,13 @@ int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) { ret = aw87xxx_power_down(aw87xxx, profile); } else { + ret = aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed!", aw87xxx->prof_off_name); + mutex_unlock(&aw87xxx->reg_lock); + return ret; + } + ret = aw87xxx_power_on(aw87xxx, profile); if (!ret) aw87xxx_monitor_start(&aw87xxx->monitor); @@ -271,9 +298,14 @@ int aw87xxx_set_profile(int dev_index, char *profile) struct list_head *pos = NULL; struct aw87xxx *aw87xxx = NULL; + if (!profile) { + AW_LOGE("profile is NULL"); + return -EINVAL; + } + list_for_each(pos, &g_aw87xxx_list) { aw87xxx = list_entry(pos, struct aw87xxx, list); - if (profile && aw87xxx->dev_index == dev_index) { + if (aw87xxx->dev_index == dev_index) { AW_DEV_LOGD(aw87xxx->dev, "set dev_index = %d, profile = %s", dev_index, profile); return aw87xxx_update_profile(aw87xxx, profile); @@ -302,6 +334,112 @@ int aw87xxx_set_profile_by_id(int dev_index, int profile_id) } EXPORT_SYMBOL(aw87xxx_set_profile_by_id); +int aw87xxx_set_boost_voltage(int dev_index, int status) +{ + int ret = 0; + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + struct aw_voltage_desc *vol_desc = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx == NULL) { + AW_LOGE("struct aw87xxx not ready"); + return -EINVAL; + } + + if (aw87xxx->dev_index == dev_index) { + AW_DEV_LOGD(aw87xxx->dev, "enter, set dev_index = %d, status = %d", dev_index, status); + if (0 == strncmp(aw87xxx->current_profile, aw87xxx->prof_off_name, + AW_PROFILE_STR_MAX)) { + AW_DEV_LOGE(aw87xxx->dev, "PA is power down, can not set voltage!"); + return -EINVAL; + } + + vol_desc = &aw87xxx->aw_dev.vol_desc; + if (vol_desc->addr == AW_REG_NONE) { + AW_DEV_LOGI(aw87xxx->dev, "PA unsupport set boost voltage!"); + return -EINVAL; + } + if (status == AW_VOLTAGE_HIGH) { + AW_DEV_LOGI(aw87xxx->dev, "set reg voltage max: 0x%02x = 0x%02x", + vol_desc->addr, vol_desc->vol_max); + ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, + vol_desc->addr, vol_desc->vol_max); + if (ret < 0) + return ret; + } else if (status == AW_VOLTAGE_LOW) { + AW_DEV_LOGI(aw87xxx->dev, "set reg voltage min: 0x%02x = 0x%02x", + vol_desc->addr, vol_desc->vol_min); + ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, + vol_desc->addr, vol_desc->vol_min); + if (ret < 0) + return ret; + } else { + AW_DEV_LOGE(aw87xxx->dev, "unsupport status: %d", status); + return -EINVAL; + } + + return 0; + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return -EINVAL; +} +EXPORT_SYMBOL(aw87xxx_set_boost_voltage); + +int aw87xxx_get_boost_voltage(int dev_index, int *status) +{ + int ret = 0; + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + uint8_t reg_voltage_val = 0; + struct aw_voltage_desc *vol_desc = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx == NULL) { + AW_LOGE("struct aw87xxx not ready"); + return -EINVAL; + } + + if (aw87xxx->dev_index == dev_index) { + if (0 == strncmp(aw87xxx->current_profile, aw87xxx->prof_off_name, + AW_PROFILE_STR_MAX)) { + AW_DEV_LOGE(aw87xxx->dev, "PA is power down, can not get voltage!"); + return -EINVAL; + } + + vol_desc = &aw87xxx->aw_dev.vol_desc; + if (vol_desc->addr == AW_REG_NONE) { + AW_DEV_LOGI(aw87xxx->dev, "PA unsupport get boost voltage!"); + return -EINVAL; + } + ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, + vol_desc->addr, ®_voltage_val); + if (ret < 0) + return ret; + + if (reg_voltage_val == vol_desc->vol_max) { + *status = AW_VOLTAGE_HIGH; + } else if (reg_voltage_val == vol_desc->vol_min) { + *status = AW_VOLTAGE_LOW; + } else { + AW_DEV_LOGE(aw87xxx->dev, "undefined reg val 0x%02x = 0x%02x", + vol_desc->addr, reg_voltage_val); + return -EINVAL; + } + + return 0; + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return -EINVAL; +} +EXPORT_SYMBOL(aw87xxx_get_boost_voltage); + /**************************************************************************** * * aw87xxx Kcontrols @@ -496,7 +634,7 @@ static int aw87xxx_monitor_switch_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = aw_monitor->monitor_hdr.monitor_switch; - AW_DEV_LOGI(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); + AW_DEV_LOGD(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); return 0; } @@ -560,6 +698,34 @@ static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, return 0; } +static int aw87xxx_hal_monitor_time_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = AW_MONITOR_TIME_MIN; + uinfo->value.integer.max = AW_MONITOR_TIME_MAX; + + return 0; +} + +static int aw87xxx_hal_monitor_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + aw87xxx->monitor.monitor_hdr.monitor_time; + + AW_LOGI("get monitor time %ld", ucontrol->value.integer.value[0]); + + return 0; +} static int aw87xxx_spin_status_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -846,6 +1012,20 @@ static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, aw87xxx_kcontrol[2].put = aw87xxx_spin_status_put; aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; + kctl_name[3] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[3] == NULL) + return -ENOMEM; + + snprintf(kctl_name[3], AW_NAME_BUF_MAX, "aw87xxx_hal_monitor_time"); + + aw87xxx_kcontrol[3].name = kctl_name[3]; + aw87xxx_kcontrol[3].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[3].access = SNDRV_CTL_ELEM_ACCESS_READ; + aw87xxx_kcontrol[3].info = aw87xxx_hal_monitor_time_info; + aw87xxx_kcontrol[3].get = aw87xxx_hal_monitor_time_get; + aw87xxx_kcontrol[3].private_value = (unsigned long)aw87xxx; + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, aw87xxx_kcontrol, kcontrol_num); if (ret < 0) { @@ -919,53 +1099,44 @@ static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) return 0; } -static void aw87xxx_fw_load_retry(struct aw87xxx *aw87xxx) -{ - struct acf_bin_info *acf_info = &aw87xxx->acf_info; - int ram_timer_val = 2000; - - AW_DEV_LOGD(aw87xxx->dev, "failed to read [%s]", - aw87xxx->fw_name); - - if (acf_info->load_count < AW_LOAD_FW_RETRIES) { - AW_DEV_LOGD(aw87xxx->dev, - "restart hrtimer to load firmware"); - schedule_delayed_work(&aw87xxx->fw_load_work, - msecs_to_jiffies(ram_timer_val)); - } else { - acf_info->load_count = 0; - AW_DEV_LOGE(aw87xxx->dev, - "can not load firmware,please check name or file exists"); - return; - } - acf_info->load_count++; -} - -static void aw87xxx_fw_load(const struct firmware *fw, void *context) +static void aw87xxx_fw_load_work(struct work_struct *work) { int ret = -1; - struct aw87xxx *aw87xxx = context; + struct aw87xxx *aw87xxx = container_of(work, + struct aw87xxx, fw_load_work.work); struct acf_bin_info *acf_info = &aw87xxx->acf_info; + const struct firmware *cont = NULL; AW_DEV_LOGD(aw87xxx->dev, "enter"); - if (!fw) { - aw87xxx_fw_load_retry(aw87xxx); + ret = request_firmware(&cont, aw87xxx->fw_name, aw87xxx->dev); + if ((ret) || (!cont)) { + AW_DEV_LOGD(aw87xxx->dev, "load [%s] failed!", aw87xxx->fw_name); + if (acf_info->load_count == AW_READ_CHIPID_RETRIES) { + acf_info->load_count = 0; + } else { + acf_info->load_count++; + /* sleep 1s */ + msleep(1000); + AW_DEV_LOGD(aw87xxx->dev, "load [%s] try [%d]!", + aw87xxx->fw_name, acf_info->load_count); + aw87xxx_fw_load_work(work); + } return; } - AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %ld", - aw87xxx->fw_name, (u_long)(fw ? fw->size : 0)); + AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %zu", + aw87xxx->fw_name, (cont ? cont->size : 0)); mutex_lock(&aw87xxx->reg_lock); - acf_info->fw_data = vmalloc(fw->size); + acf_info->fw_data = vmalloc(cont->size); if (!acf_info->fw_data) { AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); goto exit_vmalloc_failed; } - memset(acf_info->fw_data, 0, fw->size); - memcpy(acf_info->fw_data, fw->data, fw->size); - acf_info->fw_size = fw->size; + memset(acf_info->fw_data, 0, cont->size); + memcpy(acf_info->fw_data, cont->data, cont->size); + acf_info->fw_size = cont->size; ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); if (ret < 0) { @@ -978,34 +1149,12 @@ static void aw87xxx_fw_load(const struct firmware *fw, void *context) aw87xxx_fw_cfg_free(aw87xxx); goto exit_acf_parse_failed; } - AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); - mutex_unlock(&aw87xxx->reg_lock); - release_firmware(fw); - return; exit_acf_parse_failed: exit_vmalloc_failed: - release_firmware(fw); mutex_unlock(&aw87xxx->reg_lock); -} - -static void aw87xxx_fw_load_work_routine(struct work_struct *work) -{ - struct aw87xxx *aw87xxx = container_of(work, - struct aw87xxx, fw_load_work.work); - struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; - - AW_DEV_LOGD(aw87xxx->dev, "enter"); - - if (prof_info->status == AW_ACF_WAIT) { - request_firmware_nowait(THIS_MODULE, - FW_ACTION_HOTPLUG, - aw87xxx->fw_name, - aw87xxx->dev, - GFP_KERNEL, aw87xxx, - aw87xxx_fw_load); - } + release_firmware(cont); } static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) @@ -1019,7 +1168,7 @@ static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); aw87xxx_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); - INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work_routine); + INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work); schedule_delayed_work(&aw87xxx->fw_load_work, msecs_to_jiffies(cfg_timer_val)); } @@ -1467,6 +1616,8 @@ static int aw87xxx_dtsi_parse(struct aw87xxx *aw87xxx, { int ret = -1; int32_t dev_index = -EINVAL; + int32_t voltage_min = -EINVAL; + struct aw_voltage_desc *vol_desc = &aw87xxx->aw_dev.vol_desc; ret = of_property_read_u32(dev_node, "dev_index", &dev_index); if (ret < 0) { @@ -1503,6 +1654,17 @@ static int aw87xxx_dtsi_parse(struct aw87xxx *aw87xxx, } } + ret = of_property_read_u32(dev_node, "aw-voltage-min", &voltage_min); + if (ret < 0) { + AW_DEV_LOGI(aw87xxx->dev, "voltage_min parse failed, user default[0x%02x]", + AW_BOOST_VOLTAGE_MIN); + vol_desc->vol_min = AW_BOOST_VOLTAGE_MIN; + } else { + vol_desc->vol_min = voltage_min; + AW_DEV_LOGI(aw87xxx->dev, "parse aw_voltage_min=[0x%02x]", + vol_desc->vol_min); + } + aw87xxx_device_parse_port_id_dt(&aw87xxx->aw_dev); aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); @@ -1562,7 +1724,7 @@ static int aw87xxx_i2c_probe(struct i2c_client *client, /* aw87xxx dev_node parse */ ret = aw87xxx_dtsi_parse(aw87xxx, dev_node); if (ret < 0) - goto exit_device_init_failed; + goto exit_dtsi_parse_failed; /*hw power on PA*/ aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); @@ -1610,6 +1772,8 @@ static int aw87xxx_i2c_probe(struct i2c_client *client, return 0; exit_device_init_failed: + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); +exit_dtsi_parse_failed: AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) devm_gpio_free(&client->dev, aw87xxx->aw_dev.rst_gpio); diff --git a/asoc/codecs/aw87xxx/aw87xxx.h b/asoc/codecs/aw87xxx/aw87xxx.h index e29c1ed0fa15..a2cfcd98725a 100644 --- a/asoc/codecs/aw87xxx/aw87xxx.h +++ b/asoc/codecs/aw87xxx/aw87xxx.h @@ -16,7 +16,7 @@ #define AW87XXX_OFF_BIN_OK (1) #define AW87XXX_PRIVATE_KCONTROL_NUM (3) -#define AW87XXX_PUBLIC_KCONTROL_NUM (3) +#define AW87XXX_PUBLIC_KCONTROL_NUM (4) #define AW_I2C_RETRIES (5) #define AW_I2C_RETRY_DELAY (2) diff --git a/asoc/codecs/aw87xxx/aw87xxx_device.c b/asoc/codecs/aw87xxx/aw87xxx_device.c index f30411cf7f31..a7c6bbfadd89 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_device.c +++ b/asoc/codecs/aw87xxx/aw87xxx_device.c @@ -42,7 +42,7 @@ const char *g_aw_pid_9b_product[] = { "aw87319", }; const char *g_aw_pid_18_product[] = { - "aw87418", + "aw87358", }; const char *g_aw_pid_39_product[] = { @@ -69,6 +69,7 @@ const char *g_aw_pid_5a_product[] = { "aw87569", "aw87579", "aw81509", + "aw87579G", }; const char *g_aw_pid_76_product[] = { @@ -76,6 +77,7 @@ const char *g_aw_pid_76_product[] = { "aw87320", "aw87401", "aw87360", + "aw87390G", }; const char *g_aw_pid_60_product[] = { @@ -83,6 +85,7 @@ const char *g_aw_pid_60_product[] = { "aw87561", "aw87562", "aw87501", + "aw87550", }; static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev); @@ -212,6 +215,14 @@ static int aw87xxx_dev_reg_update(struct aw_device *aw_dev, AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", profile_data->data[i], profile_data->data[i + 1]); + //delay ms + if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { + AW_DEV_LOGI(aw_dev->dev, "delay %d ms", profile_data->data[i + 1]); + usleep_range(profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME, \ + profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME + 10); + continue; + } + ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], profile_data->data[i + 1]); if (ret < 0) @@ -256,6 +267,13 @@ static int aw87xxx_dev_reg_update_mute(struct aw_device *aw_dev, for (i = 0; i < profile_data->len; i = i + 2) { AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", profile_data->data[i], profile_data->data[i + 1]); + //delay ms + if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { + AW_DEV_LOGI(aw_dev->dev, "delay %d ms", profile_data->data[i + 1]); + usleep_range(profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME, \ + profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME + 10); + continue; + } reg_val = profile_data->data[i + 1]; if (profile_data->data[i] == aw_dev->mute_desc.addr) { @@ -539,6 +557,14 @@ static int aw_dev_pid_9b_reg_update(struct aw_device *aw_dev, for (i = 1; i < AW_PID_9B_BIN_REG_CFG_COUNT; i++) { AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", i, profile_data->data[i]); + //delay ms + if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { + AW_DEV_LOGI(aw_dev->dev, "delay %d ms", profile_data->data[i + 1]); + usleep_range(profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME, \ + profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME + 10); + continue; + } + reg_val = profile_data->data[i]; if (i == AW87XXX_PID_9B_SYSCTRL_REG) { aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); @@ -610,6 +636,8 @@ static void aw_dev_pid_9b_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_9B_SYSCTRL_REG; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_9B_SYSCTRL_DEFAULT; + + aw_dev->vol_desc.addr = AW_REG_NONE; } static int aw_dev_pid_9a_init(struct aw_device *aw_dev) @@ -699,6 +727,8 @@ static void aw_dev_chipid_18_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_18_CLASSD_REG; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_18_CLASSD_DEFAULT; + + aw_dev->vol_desc.addr = AW87XXX_PID_18_CPOC_REG; } /********************** aw87xxx_pid_18 attributes end ***********************/ @@ -728,6 +758,8 @@ static void aw_dev_chipid_39_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_39_REG_MODECTRL; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_39_MODECTRL_DEFAULT; + + aw_dev->vol_desc.addr = AW87XXX_PID_39_REG_CPOVP; } /********************* aw87xxx_pid_39 attributes end *************************/ @@ -758,6 +790,8 @@ static void aw_dev_chipid_59_5x9_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_5X9_REG_ENCR; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_5X9_ENCRY_DEFAULT; + + aw_dev->vol_desc.addr = AW_REG_NONE; } /******************* aw87xxx_pid_59_5x9 attributes end ***********************/ @@ -787,6 +821,8 @@ static void aw_dev_chipid_59_3x9_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_3X9_REG_ENCR; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_3X9_ENCR_DEFAULT; + + aw_dev->vol_desc.addr = AW87XXX_PID_59_3X9_REG_CPOVP; } /******************* aw87xxx_pid_59_3x9 attributes end ***********************/ @@ -816,6 +852,8 @@ static void aw_dev_chipid_5a_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_5A_REG_DFT3R_REG; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_5A_DFT3R_DEFAULT; + + aw_dev->vol_desc.addr = AW_REG_NONE; } /********************** aw87xxx_pid_5a attributes end ************************/ @@ -845,6 +883,8 @@ static void aw_dev_chipid_76_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_76_DFT_ADP1_REG; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_76_DFT_ADP1_CHECK; + + aw_dev->vol_desc.addr = AW87XXX_PID_76_CPOVP_REG; } /********************** aw87xxx_pid_76 attributes end ************************/ @@ -874,6 +914,8 @@ static void aw_dev_chipid_60_init(struct aw_device *aw_dev) /* esd reg info */ aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_60_NG3_REG; aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_60_ESD_REG_VAL; + + aw_dev->vol_desc.addr = AW_REG_NONE; } /********************** aw87xxx_pid_60 attributes end ************************/ diff --git a/asoc/codecs/aw87xxx/aw87xxx_device.h b/asoc/codecs/aw87xxx/aw87xxx_device.h index 8981a2bdf355..55a50f763c3f 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_device.h +++ b/asoc/codecs/aw87xxx/aw87xxx_device.h @@ -11,9 +11,9 @@ #define AW87XXX_PID_39_PRODUCT_MAX (3) #define AW87XXX_PID_59_3X9_PRODUCT_MAX (2) #define AW87XXX_PID_59_5X9_PRODUCT_MAX (4) -#define AW87XXX_PID_5A_PRODUCT_MAX (5) -#define AW87XXX_PID_76_PROFUCT_MAX (4) -#define AW87XXX_PID_60_PROFUCT_MAX (4) +#define AW87XXX_PID_5A_PRODUCT_MAX (6) +#define AW87XXX_PID_76_PROFUCT_MAX (5) +#define AW87XXX_PID_60_PROFUCT_MAX (5) #define AW_PRODUCT_NAME_LEN (8) #define AW_GPIO_HIGHT_LEVEL (1) @@ -33,6 +33,12 @@ #define AW_PID_9B_BIN_REG_CFG_COUNT (10) +#define AW87XXX_DELAY_REG_ADDR (0xFE) +#define AW87XXX_REG_DELAY_TIME (1000) + +#define AW_BOOST_VOLTAGE_MIN (0x00) + +#define AW_REG_NONE (0xFF) /******************************************** * * aw87xxx devices attributes @@ -79,6 +85,11 @@ enum aw_reg_receiver_mode { AW_IS_REC_MODE = 1, }; +enum aw_reg_voltage_status { + AW_VOLTAGE_LOW = 0, + AW_VOLTAGE_HIGH, +}; + struct aw_mute_desc { uint8_t addr; uint8_t enable; @@ -103,6 +114,12 @@ struct aw_rec_mode_desc { uint8_t mask; }; +struct aw_voltage_desc { + uint8_t addr; + uint8_t vol_max; + uint8_t vol_min; +}; + struct aw_device { uint8_t i2c_addr; uint8_t chipid; @@ -123,6 +140,7 @@ struct aw_device { struct aw_soft_rst_desc soft_rst_desc; struct aw_esd_check_desc esd_desc; struct aw_rec_mode_desc rec_desc; + struct aw_voltage_desc vol_desc; struct aw_device_ops ops; }; diff --git a/asoc/codecs/aw87xxx/aw87xxx_dsp.c b/asoc/codecs/aw87xxx/aw87xxx_dsp.c index f09ac010b47c..a699033f0b9a 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_dsp.c +++ b/asoc/codecs/aw87xxx/aw87xxx_dsp.c @@ -41,7 +41,7 @@ #endif static DEFINE_MUTEX(g_dsp_lock); -static unsigned int g_spin_value = 0; +static unsigned int g_spin_value; static int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; static int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; diff --git a/asoc/codecs/aw87xxx/aw87xxx_monitor.c b/asoc/codecs/aw87xxx/aw87xxx_monitor.c index 86958aae69a9..aecc808ae606 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_monitor.c +++ b/asoc/codecs/aw87xxx/aw87xxx_monitor.c @@ -657,6 +657,11 @@ void aw87xxx_monitor_start(struct aw_monitor *monitor) return; } + AW_DEV_LOGI(aw87xxx->dev, + "esd_enable:%d, monitor_switch:%d, monitor_open_dsp_en:%d, is_rec_mode:%d", + monitor->esd_enable, monitor->monitor_hdr.monitor_switch, + monitor->open_dsp_en, aw87xxx->aw_dev.is_rec_mode); + if (monitor->esd_enable || (monitor->monitor_hdr.monitor_switch && !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en && monitor->bin_status == AW_ACF_UPDATE)) { @@ -828,7 +833,7 @@ static ssize_t aw_attr_get_vmax(struct device *dev, return ret; } len += snprintf(buf + len, PAGE_SIZE - len, - "get_vmax=0x%x\n", vmax_get); + "get_vmax=%d\n", vmax_get); } else { ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); @@ -898,7 +903,7 @@ static ssize_t aw_attr_get_monitor_switch(struct device *dev, struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; len += snprintf(buf + len, PAGE_SIZE - len, - "aw87xxx monitor switch: %d\n", + "aw87xxx monitor switch: %u\n", monitor_hdr->monitor_switch); return len; } @@ -963,7 +968,7 @@ static ssize_t aw_attr_get_monitor_time(struct device *dev, struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; len += snprintf(buf + len, PAGE_SIZE - len, - "aw_monitor_timer = %d(ms)\n", + "aw_monitor_timer = %u(ms)\n", monitor_hdr->monitor_time); return len; } @@ -1005,7 +1010,7 @@ static ssize_t aw_attr_get_monitor_count(struct device *dev, struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; len += snprintf(buf + len, PAGE_SIZE - len, - "aw_monitor_count = %d\n", + "aw_monitor_count = %u\n", monitor_hdr->monitor_count); return len; } @@ -1054,7 +1059,7 @@ static ssize_t aw_attr_get_rx(struct device *dev, return ret; } len += snprintf(buf + len, PAGE_SIZE - len, - "aw87xxx rx: %d\n", enable); + "aw87xxx rx: %u\n", enable); } else { len += snprintf(buf + len, PAGE_SIZE - len, "command is invalid\n"); @@ -1100,7 +1105,7 @@ static DEVICE_ATTR(vbat, S_IWUSR | S_IRUGO, static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, aw_attr_get_vmax, aw_attr_set_vmax); -static DEVICE_ATTR(monitor_switch, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, aw_attr_get_monitor_switch, aw_attr_set_monitor_switch); static DEVICE_ATTR(monitor_time, S_IWUSR | S_IRUGO, aw_attr_get_monitor_time, aw_attr_set_monitor_time); @@ -1121,7 +1126,7 @@ static struct attribute_group aw_monitor_vol_adjust_group = { }; static struct attribute *aw_monitor_control[] = { - &dev_attr_monitor_switch.attr, + &dev_attr_monitor.attr, &dev_attr_monitor_time.attr, &dev_attr_monitor_count.attr, &dev_attr_rx.attr, @@ -1146,13 +1151,13 @@ static void aw_monitor_dtsi_parse(struct device *dev, ret = of_property_read_string(dev_node, "esd-enable", &esd_enable); if (ret < 0) { - AW_DEV_LOGI(dev, "esd_enable parse failed, user default[disable]"); - monitor->esd_enable = AW_ESD_DISABLE; + AW_DEV_LOGI(dev, "esd_enable parse failed, user default[enable]"); + monitor->esd_enable = AW_ESD_ENABLE; } else { - if (!strcmp(esd_enable, "true")) - monitor->esd_enable = AW_ESD_ENABLE; - else + if (!strcmp(esd_enable, "false")) monitor->esd_enable = AW_ESD_DISABLE; + else + monitor->esd_enable = AW_ESD_ENABLE; AW_DEV_LOGI(dev, "parse esd-enable=[%s]", monitor->esd_enable ? "true" : "false"); diff --git a/asoc/codecs/aw87xxx/aw87xxx_monitor.h b/asoc/codecs/aw87xxx/aw87xxx_monitor.h index 4b0282b4148c..83f6fec2599e 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_monitor.h +++ b/asoc/codecs/aw87xxx/aw87xxx_monitor.h @@ -12,6 +12,11 @@ #define REG_STATUS_CHECK_MAX (10) #define AW_ESD_CHECK_DELAY (1) + +#define AW_MONITOR_TIME_MIN (0) +#define AW_MONITOR_TIME_MAX (50000) + + #define AW_ESD_ENABLE (true) #define AW_ESD_DISABLE (false) #define AW_ESD_ENABLE_STRLEN (16) diff --git a/asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h b/asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h index b878aad8c647..324cc5acb9eb 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h +++ b/asoc/codecs/aw87xxx/aw87xxx_pid_60_reg.h @@ -2204,23 +2204,23 @@ maybe need to fix manually /* #define AW87XXX_PID_60_BST_DE_SOFT_DEFAULT (0x09) */ /* BST_BURST_KICK (0x65) detail */ -/* EN_TRANS_ERROR bit 0 (BST_BURST_KICK 0x65) */ -#define AW87XXX_PID_60_EN_TRANS_ERROR_START_BIT (0) -#define AW87XXX_PID_60_EN_TRANS_ERROR_BITS_LEN (1) -#define AW87XXX_PID_60_EN_TRANS_ERROR_MASK \ - (~(((1< Date: Thu, 13 Apr 2023 17:43:33 +0800 Subject: [PATCH 59/79] kernel:remove aw87xxx dump reg log 1.remove spare log 2.remove aw87xxx dump reg log Change-Id: Iedd603b6bc649e3ba139b7f37a85f155d4f936ca Reviewed-on: https://gerrit.mot.com/2575229 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw87xxx/aw87xxx_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw87xxx/aw87xxx_device.c b/asoc/codecs/aw87xxx/aw87xxx_device.c index a7c6bbfadd89..12b9941133ba 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_device.c +++ b/asoc/codecs/aw87xxx/aw87xxx_device.c @@ -212,8 +212,8 @@ static int aw87xxx_dev_reg_update(struct aw_device *aw_dev, } for (i = 0; i < profile_data->len; i = i + 2) { - AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", - profile_data->data[i], profile_data->data[i + 1]); + //AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + // profile_data->data[i], profile_data->data[i + 1]); //delay ms if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { From 9e1d037ed957c0eca1da53772c9d6c6b74142087 Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 9 Jun 2023 17:31:42 +0800 Subject: [PATCH 60/79] codecs:add fs1815 PA driver fogo use wcd9370+fs1815 4375 platform. add fs1815 driver. add CONFIG_SND_SOC_FS1815 to support. Change-Id: Ida5f39f95625b94e78f3313d15c6e15290fe11b0 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2639578 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Liyun Zhang Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/Kbuild | 4 + asoc/codecs/Kbuild | 1 + asoc/codecs/fs1815/Kbuild | 15 + asoc/codecs/fs1815/ReleaseNote.txt | 12 + asoc/codecs/fs1815/fs1815.c | 171 +++ asoc/codecs/fs1815/fsm-dev.h | 831 +++++++++++++ asoc/codecs/fs1815/fsm_core.c | 1731 ++++++++++++++++++++++++++++ asoc/codecs/fs1815/fsm_firmware.c | 136 +++ asoc/codecs/fs1815/fsm_i2c.c | 673 +++++++++++ asoc/codecs/fs1815/fsm_misc.c | 470 ++++++++ asoc/codecs/fs1815/fsm_public.h | 241 ++++ asoc/holi.c | 13 + 12 files changed, 4298 insertions(+) create mode 100644 asoc/codecs/fs1815/Kbuild create mode 100644 asoc/codecs/fs1815/ReleaseNote.txt create mode 100644 asoc/codecs/fs1815/fs1815.c create mode 100644 asoc/codecs/fs1815/fsm-dev.h create mode 100644 asoc/codecs/fs1815/fsm_core.c create mode 100644 asoc/codecs/fs1815/fsm_firmware.c create mode 100644 asoc/codecs/fs1815/fsm_i2c.c create mode 100644 asoc/codecs/fs1815/fsm_misc.c create mode 100644 asoc/codecs/fs1815/fsm_public.h diff --git a/asoc/Kbuild b/asoc/Kbuild index 327bd8b52434..e3b7ea95b2e3 100755 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -277,6 +277,10 @@ ifdef CONFIG_SND_SOC_AW87XXX CDEFINES += -DCONFIG_SND_SOC_AW87XXX endif +ifdef CONFIG_SND_SOC_FS1815 +CDEFINES += -DCONFIG_SND_SOC_FS1815 +endif + KBUILD_CPPFLAGS += $(CDEFINES) # Currently, for versions of gcc which support it, the kernel Makefile diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 0ce9191e3df5..5d6455d37920 100755 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -277,6 +277,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += aw882xx/ obj-y += aw882xxacf/ obj-y += aw87xxx/ + obj-y += fs1815/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/asoc/codecs/fs1815/Kbuild b/asoc/codecs/fs1815/Kbuild new file mode 100644 index 000000000000..fa6f13682dbd --- /dev/null +++ b/asoc/codecs/fs1815/Kbuild @@ -0,0 +1,15 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ + +ifdef CONFIG_SND_SOC_FS1815 +FS1815_OBJS += fs1815.o +FS1815_OBJS += fsm_core.o +FS1815_OBJS += fsm_firmware.o +FS1815_OBJS += fsm_i2c.o +FS1815_OBJS += fsm_misc.o +endif + +obj-$(CONFIG_SND_SOC_FS1815) += fs1815_dlkm.o +fs1815_dlkm-y := $(FS1815_OBJS) diff --git a/asoc/codecs/fs1815/ReleaseNote.txt b/asoc/codecs/fs1815/ReleaseNote.txt new file mode 100644 index 000000000000..91dfd84a8b7f --- /dev/null +++ b/asoc/codecs/fs1815/ReleaseNote.txt @@ -0,0 +1,12 @@ +v4.0.8 +2022-12-08 c7d54d6 FSDRV(fs1815): 4*fs1815 with i2c*2 application +2022-12-08 93ab5cc FSDRV(fs1815): SDZ pin 与友商兼容逻辑更新 +2022-11-18 c27e168 Merge "FSDRV(fs181599): Update i2c shutdown same as FS1815&FS1599" into spc-driver-v1 +2022-11-15 33ab193 FSDRV(fs181599): Update i2c shutdown same as FS1815&FS1599 +2022-11-15 7822aa7 FSDRV(fs1815): Update i2c shutdown to make sure device power down +2022-07-12 2aa9943 Merge "fix(fs1815): Add soft reset and shutdown function" into spc-driver-v1 +2022-06-09 5006522 feat(fs181599): Add fs1815&fs1599 compat driver +2022-06-16 4d6b5c2 fix(fs1815): Add soft reset and shutdown function +2022-05-26 eb95fce fix(fs1815): fix typo error of function symbol +2022-06-06 8fd4ca1 Merge "feat(fs1815): Add fs1815 i2c driver" into spc-driver-v1 +2022-04-07 f06858f feat(fs1815): Add fs1815 i2c driver diff --git a/asoc/codecs/fs1815/fs1815.c b/asoc/codecs/fs1815/fs1815.c new file mode 100644 index 000000000000..a1d191ed6e57 --- /dev/null +++ b/asoc/codecs/fs1815/fs1815.c @@ -0,0 +1,171 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2019-01-29 File created. + */ + +#include "fsm_public.h" +#if defined(CONFIG_FSM_FS1815) + +#define FS1815_STATUS 0xF000 +#define FS1815_BOVDS 0x0000 +#define FS1815_OTPDS 0x0200 +#define FS1815_OVDS 0x0300 +#define FS1815_UVDS 0x0400 +#define FS1815_OCDS 0x0500 +#define FS1815_OTWDS 0x0800 +#define FS1815_BOPS 0x0900 +#define FS1815_SPKS 0x0A00 +#define FS1815_SPKT 0x0B00 + +#define FS1815_DEVID 0xF001 +#define FS1815_REVID 0xF002 + +#define FS1815_ANASTAT 0xF003 +#define FS1815_DIGSTAT 0xF004 + +#define FS1815_CHIPINI 0xF00E + +#define FS1815_PWRCTRL 0xF010 +#define FS1815_PWDN 0x0010 +#define FS1815_I2CR 0x0110 + +#define FS1815_SYSCTRL 0xF011 +#define FS1815_OSCEN 0x0011 +#define FS1815_VBGEN 0x0111 +#define FS1815_ZMEN 0x0211 +#define FS1815_ADCEN 0x0311 +#define FS1815_BSTPD 0x0611 +#define FS1815_AMPEN 0x0711 + +#define FS1815_SPKCOEF 0xF014 + +#define FS1815_GAINCTRL 0xF018 + +#define FS1815_ACCKEY 0xF01F + +#define FS1815_BSTCTRL 0xF020 +#define FS1815_SSEND 0x0F20 +#define FS1815_VOUTSEL 0x4820 +#define FS1815_ILIMSEL 0x2420 +#define FS1815_BSTMODE 0x1020 + +#define FS1815_DACCTRL 0xF030 + +#define FS1815_LNMCTRL 0xF03F + +#define FS1815_TSCTRL 0xF04C +#define FS1815_OFFSTA 0x0E4C +#define FS1815_OFF_AUTOEN 0x0D4C +#define FS1815_TSEN 0x034C + +#define FS1815_AGC1 0x3059 +#define FS1815_AGC1RT 0x3059 +#define FS1815_AGC2 0x305A +#define FS1815_AGC3 0x305B +#define FS1815_AGC4 0x305C + +#define FS1815_BSGCTRL 0xF0BC + +#define FS1815_FW_NAME "fs1815.fsm" + + +static int fs1815_i2c_reset(fsm_dev_t *fsm_dev) +{ + uint16_t val; + int ret; + int i; + + if (!fsm_dev) { + return -EINVAL; + } + + for (i = 0; i < FSM_I2C_RETRY; i++) { + fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0002); // reset nack + fsm_reg_read(fsm_dev, REG(FS1815_PWRCTRL), NULL); // dummy read + fsm_delay_ms(15); // 15ms + ret = fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0001); + ret |= fsm_reg_read(fsm_dev, REG(FS1815_CHIPINI), &val); + if ((val == 0x0003) || (val == 0x0300)) { // init finished + break; + } + } + if (i == FSM_I2C_RETRY) { + pr_addr(err, "retry timeout"); + ret = -ETIMEDOUT; + } + + return ret; +} + +int fs1815_reg_init(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret; + + if (fsm_dev == NULL || cfg == NULL) { + return -EINVAL; + } + + ret = fs1815_i2c_reset(fsm_dev); + ret |= fsm_reg_write(fsm_dev, REG(FS1815_SPKCOEF), (fsm_dev->tcoef << 1)); + ret |= fsm_write_reg_tbl(fsm_dev, FSM_SCENE_COMMON); + ret |= fsm_write_reg_tbl(fsm_dev, cfg->next_scene); + if (ret) { + pr_addr(err, "init failed:%d", ret); + fsm_dev->state.dev_inited = false; + } + + return ret; +} + +int fs1815_start_up(fsm_dev_t *fsm_dev) +{ + int ret; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->revid == 0x0011) { + ret = fsm_reg_write(fsm_dev, REG(FS1815_BSGCTRL), 0x9433); + } + // 0x1100EF; all enable + ret = fsm_reg_write(fsm_dev, REG(FS1815_SYSCTRL), 0x008F); + // 0x100000; power up + ret |= fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0000); + ret |= fsm_config_vol(fsm_dev); + fsm_delay_ms(10); + fsm_dev->errcode = ret; + + return ret; +} + +int fs1815_shut_down(fsm_dev_t *fsm_dev) +{ + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + ret = fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0001); + ret |= fsm_reg_write(fsm_dev, REG(FS1815_SYSCTRL), 0x0000); + fsm_delay_ms(10); + + return ret; +} + +void fs1815_ops(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!fsm_dev || !cfg) { + return; + } + + fsm_set_fw_name(FS1815_FW_NAME); + fsm_dev->dev_ops.reg_init = fs1815_reg_init; + fsm_dev->dev_ops.start_up = fs1815_start_up; + fsm_dev->dev_ops.shut_down = fs1815_shut_down; + cfg->nondsp_mode = true; + cfg->store_otp = false; +} +#endif diff --git a/asoc/codecs/fs1815/fsm-dev.h b/asoc/codecs/fs1815/fsm-dev.h new file mode 100644 index 000000000000..bc10fca1d12b --- /dev/null +++ b/asoc/codecs/fs1815/fsm-dev.h @@ -0,0 +1,831 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-16 File created. + */ + +#ifndef __FSM_DEV_H__ +#define __FSM_DEV_H__ + +/* ------- MACRO FEATURE ------- */ + +/* debug print macro, default closed */ +//#define DEBUG +//#define FSM_DEBUG +//#define FSM_DEBUG_I2C + +#if defined(__KERNEL__) +/* kernel module: */ +#define CONFIG_FSM_MISC +//#define CONFIG_FSM_CODEC +#define CONFIG_FSM_FIRMWARE +#define CONFIG_FSM_I2C +//#define CONFIG_FSM_Q6AFE +#endif + +/* device chip select: */ +//#define CONFIG_FSM_NONDSP +//#define CONFIG_FSM_FS1958 +#define CONFIG_FSM_FS1815 + +/* VERSION INFORMATION */ +#define FSM_CODE_VERSION "v4.0.8" +#define FSM_CODE_DATE "20230612" +#define FSM_GIT_BRANCH "spc-driver-v1" +#define FSM_GIT_COMMIT "23749327" + +#define FSM_DRV_NAME "fs16xx" +#define FSM_FW_NAME "fs16xx_dft.fsm" + +// #define FSM_CALIB_SAVE_PATH "/data" +// TODO +#define FSM_CALIB_SAVE_PATH "/mnt/vendor/persist/audio" +//#ifndef BIT +//#define BIT(nr) (1UL << (nr)) +//#endif + +/* scenes defination */ +#define FSM_SCENE_UNKNOW (0) +#define FSM_SCENE_MUSIC BIT(0) +#define FSM_SCENE_VOICE BIT(1) +#define FSM_SCENE_VOIP BIT(2) +#define FSM_SCENE_RING BIT(3) +#define FSM_SCENE_LOW_PWR BIT(4) +#define FSM_SCENE_MMI_ALL BIT(5) +#define FSM_SCENE_MMI_ALL_BYPASS BIT(6) +#define FSM_SCENE_TOP_LEFT BIT(7) +#define FSM_SCENE_TOP_LEFT_BYPASS BIT(8) +#define FSM_SCENE_TOP_RIGHT BIT(9) +#define FSM_SCENE_TOP_RIGHT_BYPASS BIT(10) +#define FSM_SCENE_BOT_LEFT BIT(11) +#define FSM_SCENE_BOT_LEFT_BYPASS BIT(12) +#define FSM_SCENE_BOT_RIGHT BIT(13) +#define FSM_SCENE_BOT_RIGHT_BYPASS BIT(14) +#define FSM_SCENE_RCV BIT(15) +#define FSM_SCENE_MAX (16) // TODO +#define FSM_SCENE_COMMON (0xFFFF) +#define FSM_VOLUME_MAX (0xFF) + +/* ------- MACRO FEATURE ------- */ + +#ifndef pr_fmt +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ +#endif +#if defined(__KERNEL__) +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_REGULATOR) +#include +#include +#include +#endif +#define logprint(fmt, args...) printk(KERN_CONT fmt, ##args) +#elif defined(FSM_HAL_SUPPORT) +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsm_list.h" + +/* debug info */ +#ifdef FSM_DEBUG +#undef NDEBUG +#define LOG_NDEBUG 0 // open LOGV +#endif + +#if defined(LOG_TAG) +#undef LOG_TAG +#endif +#define LOG_TAG "fsm_hal" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#if defined(FSM_APP) +#if defined(FSM_DEBUG) +#define pr_debug(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#else +#define pr_debug(fmt, args...) +#endif +#define pr_info(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#define pr_err(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#define pr_warning(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#define logprint(fmt, args...) printf(fmt, ##args) +#elif defined(BUILD_FSTOOL) +#ifdef DEBUG +#undef DEBUG +#endif +#ifdef FSM_DEBUG +#undef FSM_DEBUG +#endif +#ifndef FSM_DEBUG_I2C +#define FSM_DEBUG_I2C +#endif +#define pr_debug(fmt, args...) printf(fmt "\n", ##args) +#define pr_info(fmt, args...) printf(fmt "\n", ##args) +#define pr_err(fmt, args...) printf(fmt "\n", ##args) +#define pr_warning(fmt, args...) printf(fmt "\n", ##args) +#define logprint(fmt, args...) printf(fmt, ##args) +#elif defined(__NDK_BUILD__) +#include +#define pr_debug(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_warning(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define logprint(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, fmt, ##args) +#elif defined(__ANDROID__) +#include +#define pr_debug(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_warning(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define logprint(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, fmt, ##args) +#else +#define pr_debug(fmt, args...) +#define pr_info(fmt, args...) +#define pr_err(fmt, args...) +#define pr_warning(fmt, args...) +#define logprint(fmt, args...) +#endif // FSM_APP +#endif // FSM_HAL_SUPPORT +#define pr_addr(type, fmt, args...) \ + pr_##type("%02X: " fmt, fsm_dev->addr, ##args) + +#define FSM_FUNC_EXIT(ret) \ + do { \ + if (ret) \ + pr_err("err: %d", ret); \ + } while(0) + +#define FSM_ADDR_EXIT(ret) \ + do { \ + if (ret) \ + pr_addr(err, "err: %d", ret); \ + } while(0) + +#define UNUSED(expr) do { (void)(expr); } while(0) + +enum dev_id_index { + FS1601S_DEV_ID = 0x3, + FS1603_DEV_ID = 0x5, + FS1818_DEV_ID = 0x6, + FS1860_DEV_ID = 0x7, + FS1896_DEV_ID = 0xB, + FS1815_DEV_ID = 0x12, + FS1820_DEV_ID = FS1815_DEV_ID, + FS1958_DEV_ID = 0x17, + FSM_DEV_ID_MAX, +}; +#define FS1603S_V1 0X05B1 +#define FS1603S_SERIES 0X05B0 +#define IS_FS1603S(id) (((id) & 0xFFF0) == FS1603S_SERIES) + +#define CUST_NAME_SIZE (32) +#define STRING_LEN_MAX (255) +#define LOG_BUF_SIZE (8 * 8 + 1) +#define FSM_AFE_HEADER_LEN (5) +#define FSM_DFT_AMB_TEMPR (25) +#define FSM_DFT_AMB_VBAT (3678) + +#define STEREO_COEF_LEN (10) +#define FSM_MAGNIF_FACTOR (10) // magnification factor: 2^(x) +#define FSM_PAYLOAD_SIZE (64) +#define FSM_WAIT_STABLE_RETRY (30) +#define FSM_CALIB_RE25_RETRY (50) +#define FSM_I2C_RETRY (5) +#define FSM_ZMDELTA_MAX 0x150 +#define FSM_RS_TRIM_DEFAULT 0x8F +#define FSM_MAGNIF_TEMPR_COEF 0xFFFF +#define FSM_OTP_ACC_KEY2 0xCA91 +#define FSM_DATA_TYPE_RE25 0 +#define FSM_DATA_TYPE_ZMDATA 1 +#define MODULE_INITED (13579) +#define FSM_VBATH_COUNT (10) +#define FSM_VBAT_HIGH (4000) // mV +#define FSM_VBAT_LOW (3600) // mV +#define FSM_VBAT_STEP ((FSM_VBAT_HIGH - FSM_VBAT_LOW) / 8) // mV + +#define FSM_DEV_MAX (4) +#define FSM_ADDR_BASE (0x34) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define HIGH8(val) ((val >> 8) & 0xFF) +#define LOW8(val) (val & 0xFF) +#define WORD(addr) ((*(addr) << 8) & *((addr) + 1)) +#define REG(bf) (bf & 0xFF) +#define FSM_REG_SIZE sizeof(uint16_t) +#define FSM_ADDR_SIZE sizeof(uint8_t) + +/* f0 test */ +#define COUNT(start, end, step) (((end) - (start)) / (step) + 1) +#define ACS_COEF_COUNT 12 +#define COEF_LEN 5 +#define FREQ_START 200 // 100*N +#define FREQ_END 1500 // bigger than FREQ_START && 100*N +#define F0_FREQ_STEP 100 // 50*N, N =1,2,3... +#define FREQ_COUNT COUNT(FREQ_START, FREQ_END, F0_FREQ_STEP) +#define FREQ_EXT_COUNT 300 +#define RE25_TEST_COUNT 10 +#define RE25_1ST_WAIT_MS 2500 +#define RE25_DELAY_MS 100 +#define F0_1ST_DELAY_MS 800 +#define F0_TEST_DELAY_MS 500 + +#define FSM_POS_MONO (0) +#define FSM_POS_LTOP BIT(3) +#define FSM_POS_LBTM BIT(2) +#define FSM_POS_RTOP BIT(1) +#define FSM_POS_RBTM BIT(0) + +//#undef NULL +//#define NULL ((void *)0) +// +//enum { +// false = 0, +// true = 1 +//}; + +enum fsm_error { + FSM_ERROR_OK = 0, + FSM_ERROR_BAD_PARMS, + FSM_ERROR_I2C_FATAL, + FSM_ERROR_NO_SCENE_MATCH, + FSM_ERROR_RE25_INVAILD, + FSM_ERROR_MAX, +}; + +enum fsm_channel { + FSM_CHN_UNKNOW = 0, + FSM_CHN_LEFT = 1, + FSM_CHN_RIGHT = 2, + FSM_CHN_MONO = 3, +}; + +enum fsm_format { + FSM_FMT_DSP = 1, + FSM_FMT_MSB = 2, + FSM_FMT_I2S = 3, + FSM_FMT_LSB_16 = 4, + FSM_FMT_LSB_20 = 6, + FSM_FMT_LSB_24 = 7, +}; + +enum fsadsp_tx_fmt { + FSADSP_TX_FMT_NONE = 0, + FSADSP_TX_FMT_I2S_S16, + FSADSP_TX_FMT_I2S_S32, + FSADSP_TX_FMT_TDM_MONO_VBAT, // SLOT:2 LEN: 16BIT IV + 16BIT VBAT + FSADSP_TX_FMT_TDM_STEREO, // SLOT:4 LEN: 16BIT IV on LEFT / RIGHT +}; + +enum fsm_bstmode { + FSM_BSTMODE_FLW = 0, + FSM_BSTMODE_BST = 1, + FSM_BSTMODE_ADP = 2, +}; + +enum fsm_dsp_state { + FSM_DSP_OFF = 0, + FSM_DSP_ON = 1, +}; + +enum fsm_wait_type { + FSM_WAIT_AMP_ON = 1, + FSM_WAIT_AMP_ADC_ON, + FSM_WAIT_AMP_OFF, + FSM_WAIT_AMP_ADC_OFF, + FSM_WAIT_AMP_ADC_PLL_OFF, + FSM_WAIT_TSIGNAL_ON, + FSM_WAIT_TSIGNAL_OFF, + FSM_WAIT_OTP_READY, + FSM_WAIT_BOOST_SSEND, + FSM_WAIT_STATUS_ON, +}; + +enum fsm_eq_ram_type { + FSM_EQ_RAM0 = 0, + FSM_EQ_RAM1, + FSM_EQ_RAM_MAX, +}; + +struct fsm_version { + char git_branch[50]; + char git_commit[41]; + char code_date[18]; + char code_version[10]; +}; +typedef struct fsm_version fsm_version_t; + +struct fsm_pll_config { + uint32_t bclk; + uint16_t c1; + uint16_t c2; + uint16_t c3; +}; +typedef struct fsm_pll_config fsm_pll_config_t; + +#define PRESET_VER_FS1601S 0x8301 // FS1601S +#define PRESET_VER_FS1603 0x8501 // FS1603 series +#define PRESET_VER_FS1801 0x8A01 // FS1801 series +#define PRESET_VER_FS1860 0x8701 // FS1860 +#define PRESET_VER3 (0x93) +#define IS_PRESET_V3(ver) (HIGH8(ver) >= PRESET_VER3) + +enum fsm_dsc_type { + FSM_DSC_DEV_INFO = 0, + FSM_DSC_SPK_INFO, + FSM_DSC_REG_COMMON, + FSM_DSC_REG_SCENES, + FSM_DSC_PRESET_EQ, + FSM_DSC_STEREO_COEF, + FSM_DSC_EXCER_RAM, + FSM_DSC_MAX, +}; + +enum fsm_info_type { + FSM_INFO_SPK_TMAX = 0, + FSM_INFO_SPK_TEMPR_COEF, + FSM_INFO_SPK_TEMPR_SEL, + FSM_INFO_SPK_RES, + FSM_INFO_SPK_RAPP, + FSM_INFO_SPK_TRE, + FSM_INFO_SPK_TM6, + FSM_INFO_SPK_TM24, + FSM_INFO_SPK_TERR, + FSM_INFO_SPK_TM01, + FSM_INFO_SPK_TM02, + FSM_INFO_SPK_TM03, + FSM_INFO_SPK_TM04, + FSM_INFO_SPK_TM05, + FSM_INFO_SPK_TM06, + FSM_INFO_RSRL_RATIO, + FSM_INFO_SPK_POSITION, + FSM_INFO_SPK_MAX, +}; + +enum fsm_test_type { + TEST_NONE = 0, + TEST_RE25, + TEST_F0, +}; + +#pragma pack(push, 1) +struct fsm_date { + uint32_t min : 6; + uint32_t hour : 5; + uint32_t day : 5; + uint32_t month : 4; + uint32_t year : 12; +}; +typedef struct fsm_date fsm_date_t; + +struct data32_list { + uint16_t len; + uint32_t data[]; +}; +typedef struct data32_list preset_data_t; +typedef struct data32_list ram_data_t; + +struct uint24 { + uint8_t DL; + uint8_t DM; + uint8_t DH; +}; +typedef struct uint24 uint24_t; + +struct data24_list { + uint16_t len; + uint24_t data[]; +}; + +struct data16_list { + uint16_t len; + uint16_t data[]; +}; +typedef struct data16_list info_list_t; +typedef struct data16_list stereo_coef_t; + +struct fsm_index { + uint16_t offset; + uint16_t type; +}; +typedef struct fsm_index fsm_index_t; + +struct dev_list { + uint16_t preset_ver; + char project[8]; + char customer[8]; + fsm_date_t date; + uint16_t data_len; + uint16_t crc16; + uint16_t len; + uint16_t bus; + uint16_t addr; + uint16_t dev_type; + uint16_t npreset; + uint16_t reg_scenes; + uint16_t eq_scenes; + fsm_index_t index[]; +}; +typedef struct dev_list dev_list_t; + +struct preset_list { + uint16_t len; + uint16_t scene; + uint32_t data[]; +}; +typedef struct preset_list preset_list_t; + +struct reg_unit { + uint16_t addr : 8; + uint16_t pos : 4; + uint16_t len : 4; + uint16_t value; +}; +typedef struct reg_unit reg_unit_t; + +struct reg_wait { + uint16_t val : 8; + uint16_t pos : 4; + uint16_t len : 4; +}; +typedef struct reg_wait reg_wait_t; + +struct reg_comm { + uint16_t len; + reg_unit_t reg[]; +}; +typedef struct reg_comm reg_comm_t; + +struct regs_unit { + uint16_t scene; + reg_unit_t reg; +}; +typedef struct regs_unit regs_unit_t; + +struct reg_scene { + uint16_t len; + regs_unit_t regs[]; +}; +typedef struct reg_scene reg_scene_t; + +struct reg_temp { + uint16_t new_val; + uint16_t old_val; +}; +typedef struct reg_temp reg_temp_t; + +struct preset_header { + uint16_t version; + char customer[8]; + char project[8]; + fsm_date_t date; + uint16_t size; + uint16_t crc16; + uint16_t ndev; +}; + +struct preset_file { + struct preset_header hdr; + fsm_index_t index[]; +}; + +struct calib_index { + uint8_t chn; + int re25; +}; + +struct fsm_calib_bin { + int size; + uint8_t ndev; + struct calib_index index[]; +}; + +#pragma pack(pop) + +struct fsm_srate { + uint32_t srate : 28; + uint32_t bf_val : 4; +}; + +struct fsm_dev; + +struct fsm_ops { + int (*reg_init)(struct fsm_dev *); + int (*dev_init)(struct fsm_dev *); + int (*pll_config)(struct fsm_dev *, bool on); + int (*i2s_config)(struct fsm_dev *); + int (*switch_preset)(struct fsm_dev *); + int (*check_stable)(struct fsm_dev *, int type); + int (*start_up)(struct fsm_dev *); + int (*set_unmute)(struct fsm_dev *); + int (*set_tsignal)(struct fsm_dev *, bool enable); + int (*set_mute)(struct fsm_dev *, bool mute); + int (*shut_down)(struct fsm_dev *); + int (*deinit)(struct fsm_dev *); + int (*dev_recover)(struct fsm_dev *); + int (*cal_zmdata)(struct fsm_dev *); + int (*pre_calib)(struct fsm_dev *); + int (*store_otp)(struct fsm_dev *, uint8_t valOTP); + int (*post_calib)(struct fsm_dev *); + int (*pre_f0_test)(struct fsm_dev *); + int (*f0_test)(struct fsm_dev *); + int (*post_f0_test)(struct fsm_dev *); +}; +typedef struct fsm_ops fsm_ops_t; + +struct fsm_config { + uint8_t dev_count; + uint16_t cur_angle; + uint16_t next_angle; + uint16_t next_scene; + uint16_t test_type; + uint16_t test_freq; + uint16_t freq_start; + uint16_t freq_end; + uint16_t freq_step; + uint16_t freq_count; + uint16_t volume; + uint16_t amb_tempr; + uint16_t state : 8; + uint16_t wait_type : 8; + uint16_t tx_fmt : 8; + uint16_t rsvd : 8; + unsigned int i2s_bclk; + unsigned int i2s_srate; + + // flags + uint32_t codec_inited : 3; + uint32_t force_fw : 1; + uint32_t force_init : 1; + uint32_t force_scene : 1; + uint32_t force_calib : 1; + uint32_t store_otp : 1; + uint32_t force_mute : 1; + uint32_t vddd_on : 1; + uint32_t reset_chip : 1; + uint32_t stop_test : 1; + uint32_t use_monitor : 1; + uint32_t skip_monitor : 1; + uint32_t stream_muted : 1; + uint32_t i2c_debug : 1; + uint32_t nondsp_mode : 1; + uint32_t speaker_on : 1; + //uint32_t bypass_dsp : 1; + + char *codec_name[FSM_DEV_MAX]; + char *codec_dai_name[FSM_DEV_MAX]; + char *fw_name; + void *preset; +}; +typedef struct fsm_config fsm_config_t; + +struct fsm_vbat_state { + uint16_t last_batv; + uint16_t cur_batv; + uint16_t batvh_count; +}; + +struct fsm_calib { + uint16_t pos : 8; + uint16_t addr : 8; + uint16_t cal_zm; + uint8_t count; + int errcode; + int re25; + int f0; +}; + +struct fsm_f0_data { + uint16_t freq; + uint16_t zmdata; + uint16_t min_zm; +}; + +struct fsm_cal_result { + uint16_t ndev; + uint16_t freq_start; + uint16_t freq_end; + uint16_t freq_step; + uint16_t freq_count; + struct fsm_calib info[FSM_DEV_MAX]; + uint16_t f0_zmdata[]; +}; + +struct re25_data { + uint16_t count; + uint16_t pre_val; + uint16_t min_val; + uint16_t zmdata; +}; + +struct f0_data { + uint16_t count : 8; + uint16_t min_idx : 8; + uint16_t min_zm; + uint16_t zmdata[]; +}; + +struct fsm_test_data { + int test_re25; + int test_f0; + int sum_re25; + int count_re25; + int sum_f0; + int count_f0; + struct re25_data re25; + struct f0_data f0; +}; + +struct fsm_livedata { + int Re25; + int R0; + int F0; + int Q; + int T; + int Vbat; + int Excer; +}; + +struct fsm_cal_data { + uint32_t channel; + uint32_t cal_re; + uint32_t cal_tempr; + uint32_t re_min : 16; + uint32_t re_max : 16; + bool calib_pass; // true: pass, false: fail +}; + +struct fsm_calib_v2 { + uint32_t dev_count; + struct fsm_cal_data cal_data[FSM_DEV_MAX]; +}; + +#if !defined(CONFIG_I2C) +struct i2c_client { + int addr; +}; +#endif + +struct fsm_msg { + uint32_t size; // size of *buf + void *buf; +}; +typedef struct fsm_msg fsm_msg_t; + +struct fsm_bpcoef { + int freq; + uint32_t coef[COEF_LEN]; +}; + +struct fsm_state { + uint16_t dev_inited : 1; + uint16_t amp_mute : 1; + uint16_t bypass_dsp : 1; + uint16_t calibrated : 1; + uint16_t otp_stored : 1; + uint16_t re25_runin : 1; + uint16_t f0_runing : 1; +}; + +struct fsm_compat { + uint16_t preset_unit_len; + uint16_t excer_ram_len; + uint16_t addr_excer_ram; + uint16_t otp_max_count; + uint16_t ACSEQA : 8; + uint16_t ACSEQWL : 8; + uint16_t DACEQA : 8; + uint16_t DACEQWL : 8; + int RS2RL_RATIO; +}; + +struct fsm_monitor_data { + uint16_t status; + uint16_t anastat; + uint16_t digstat; + uint16_t tsctrl; + uint16_t zmdata; + uint16_t batv; // mV + int ic_tempr; + int spk_tempr; + int spk_imp; +}; + +#if defined(CONFIG_FSM_MISC) || defined(FSM_HAL_SUPPORT) +#include +/* ioctl magic number and cmd. */ +#define FSM_IOC_MAGIC (0x7C) +#define FSM_IOC_GET_DEVICE _IOR(FSM_IOC_MAGIC, 1, int) +#define FSM_IOC_SET_SRATE _IOW(FSM_IOC_MAGIC, 2, int) +#define FSM_IOC_SET_SCENE _IOW(FSM_IOC_MAGIC, 3, int) +#define FSM_IOC_INIT _IOW(FSM_IOC_MAGIC, 4, int) +#define FSM_IOC_SPEAKER_ON _IOW(FSM_IOC_MAGIC, 5, int) +#define FSM_IOC_SPEAKER_OFF _IOW(FSM_IOC_MAGIC, 6, int) +#define FSM_IOC_CALIBRATE _IOW(FSM_IOC_MAGIC, 7, int) +#define FSM_IOC_F0_TEST _IOW(FSM_IOC_MAGIC, 8, int) +#define FSM_IOC_GET_RESULT _IOR(FSM_IOC_MAGIC, 9, void *) +#define FSM_IOC_SET_ADDR _IOW(FSM_IOC_MAGIC, 10, int) +#define FSM_IOC_SEND_APR _IOWR(FSM_IOC_MAGIC, 11, void *) +#define FSM_IOC_SET_SLAVE (0x0706) +#define FSM_IOC_MAXNR (12) +#ifdef CONFIG_COMPAT +#define FSM_IOC_GET_RESULT32 _IOR(FSM_IOC_MAGIC, 9, compat_uptr_t) +#define FSM_IOC_SEND_APR32 _IOWR(FSM_IOC_MAGIC, 11, compat_uptr_t) +#endif + +struct fsm_misc_args { + uint16_t scene; + int srate; + int bclk; + // init + bool force_init; + // re25 + bool force_calib; + bool store_otp; + // f0 + uint16_t freq_start; + uint16_t freq_end; + uint16_t freq_step; + uint16_t freq_count; +}; +#endif + +struct fsm_dev { + struct i2c_client *i2c; +#if defined(CONFIG_FSM_REGMAP) + struct regmap *regmap; +#endif + struct list_head list; +#if defined(__KERNEL__) + struct workqueue_struct *fsm_wq; + struct delayed_work init_work; + struct delayed_work monitor_work; + struct delayed_work interrupt_work; + struct mutex i2c_lock; + int irq_id; +#endif + struct fsm_ops dev_ops; + struct preset_file *preset; + struct dev_list *dev_list; + struct fsm_state state; + struct fsm_compat compat; + struct fsm_test_data *tdata; + uint16_t bus : 8; + uint16_t addr : 8; + uint16_t version; + uint16_t revid; + uint16_t ram_scene[FSM_EQ_RAM_MAX]; + uint16_t own_scene; + uint16_t cur_scene; + uint16_t cal_count; + uint16_t tmax; + uint16_t tcoef; + uint16_t tsel; + uint16_t rapp; + uint16_t rstrim; + uint16_t pos_mask : 8; // position + uint16_t acc_count : 8; // acc-key count + uint16_t rec_count; + + uint16_t load_fw : 1; + uint16_t use_irq : 1; + uint16_t is1603s : 1; + uint16_t is1801 : 1; + uint16_t is1820 : 1; + uint16_t is1958 : 1; + + int rst_gpio; + int irq_gpio; + int id; + int spkr; + int re25; + int re25_dft; + int f0; + int errcode; + char const *dev_name; +}; +typedef struct fsm_dev fsm_dev_t; + +#endif diff --git a/asoc/codecs/fs1815/fsm_core.c b/asoc/codecs/fs1815/fsm_core.c new file mode 100644 index 000000000000..0ad45c1f2c83 --- /dev/null +++ b/asoc/codecs/fs1815/fsm_core.c @@ -0,0 +1,1731 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-17 File created. + */ + +#include "fsm_public.h" +#include + +#define CRC16_TABLE_SIZE 256 +#define CRC16_POLY_NOMIAL 0xA001 + +static int fsm_try_init(void); +static int g_dev_inited = 0; + +static atomic_t fsm_amp_switch; + +static LIST_HEAD(fsm_dev_list); +#define fsm_list_init(fsm_dev) \ + do { \ + INIT_LIST_HEAD(&fsm_dev->list); \ + list_add(&fsm_dev->list, &fsm_dev_list); \ + } while (0) + +#define fsm_list_entry(fsm_dev, ops) \ + do { \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev) && ops) { \ + ops(fsm_dev); \ + } \ + } \ + } while(0) + +#define fsm_list_func(fsm_dev, func) \ + do { \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + func(fsm_dev); \ + } \ + } \ + } while(0) + +#define fsm_list_check(fsm_dev, type, ret) \ + do { \ + ret = 0; \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + ret |= fsm_stub_check_stable(fsm_dev, type); \ + if (ret) { \ + break; \ + } \ + } \ + } \ + } while(0) + +#define fsm_list_return(fsm_dev, func, ret) \ + do { \ + ret = 0; \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + ret |= func(fsm_dev); \ + } \ + } \ + } while(0) + +#define fsm_list_func_arg(fsm_dev, func, argv) \ + do { \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + func(fsm_dev, argv); \ + } \ + } \ + } while(0) + + +static uint16_t g_crc16table[CRC16_TABLE_SIZE] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, +}; + +static struct fsm_config g_fsm_config = { + .dev_count = 0, + .volume = FSM_VOLUME_MAX, + .next_scene = FSM_SCENE_MUSIC, + .i2s_bclk = 1536000, + .i2s_srate = 48000, + .test_type = TEST_NONE, + .freq_start = FREQ_START, + .freq_end = FREQ_END, + .freq_step = F0_FREQ_STEP, + .freq_count = FREQ_COUNT, + .amb_tempr = FSM_DFT_AMB_TEMPR, + .cur_angle = 0, + .next_angle = 0, + + // flags + .vddd_on = 0, + .codec_inited = 0, + .force_fw = 0, + .force_init = 0, + .force_scene = 0, + .force_calib = 0, + .store_otp = 0, + .nondsp_mode = 1, +#ifdef FSM_DEBUG + .i2c_debug = 1, +#endif + .force_mute = 0, + .stop_test = 0, + .use_monitor = 0, + .skip_monitor = 0, + .stream_muted = 1, + .fw_name = FSM_FW_NAME, + .preset = NULL, +}; + +static const struct fsm_srate g_srate_tbl[] = { + { 8000, 0 }, + { 16000, 3 }, + { 32000, 8 }, + { 44100, 7 }, + { 48000, 8 }, + // fs1860 support below srate: + { 88200, 9 }, + { 96000, 10 }, +}; + +int fsm_skip_device(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!fsm_dev || !cfg) { + return 1; + } + if (!fsm_dev->state.dev_inited) { + return 1; + } + if ((fsm_dev->own_scene & cfg->next_scene) == 0) { + return 1; + } + + return 0; +} + +fsm_config_t *fsm_get_config(void) +{ + return &g_fsm_config; +} +#ifdef __KERNEL__ +EXPORT_SYMBOL(fsm_get_config); +#endif + +void fsm_get_version(fsm_version_t *version) +{ + sprintf(version->git_branch, "%s", FSM_GIT_BRANCH); + sprintf(version->git_commit, "%s", FSM_GIT_COMMIT); + sprintf(version->code_date, "%s", FSM_CODE_DATE); + sprintf(version->code_version, "%s", FSM_CODE_VERSION); + pr_info("version %s", version->code_version); +} + +void *fsm_alloc_mem(int size) +{ +#if defined(__KERNEL__) + return kzalloc(size, GFP_KERNEL); +#elif defined(FSM_HAL_SUPPORT) + return malloc(size); +#else + return NULL; +#endif +} + +void fsm_free_mem(void **buf) +{ + if (*buf == NULL) { + return; + } + +#if defined(__KERNEL__) + kfree(*buf); +#elif defined(FSM_HAL_SUPPORT) + free(*buf); +#endif + *buf = NULL; +} + +void fsm_delay_ms(uint32_t delay_ms) +{ + if (delay_ms == 0) { + return; + } +#if defined(__KERNEL__) + usleep_range(delay_ms * 1000, delay_ms * 1000 + 1); +#elif defined(FSM_HAL_SUPPORT) + usleep(delay_ms * 1000); +#endif +} + +uint16_t set_bf_val(uint16_t *pval, const uint16_t bf, const uint16_t bf_val) +{ + uint8_t len = (bf >> 12) & 0x0F; + uint8_t pos = (bf >> 8) & 0x0F; + uint16_t new_val; + uint16_t old_val; + uint16_t msk; + + if (!pval) { + return 0xFFFF; + } + old_val = new_val = *pval; + msk = ((1 << (len + 1)) - 1) << pos; + new_val &= ~msk; + new_val |= bf_val << pos; + *pval = new_val; + + return old_val; +} + +uint16_t get_bf_val(const uint16_t bf, const uint16_t val) +{ + uint8_t len = (bf >> 12) & 0x0F; + uint8_t pos = (bf >> 8) & 0x0F; + uint16_t msk, value; + + msk = ((1 << (len + 1)) - 1) << pos; + value = (val & msk) >> pos; + + return value; +} + +int fsm_set_bf(fsm_dev_t *fsm_dev, const uint16_t bf, const uint16_t val) +{ + reg_unit_t reg; + uint16_t oldval; + uint16_t msk; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + reg.len = (bf >> 12) & 0x0F; + reg.pos = (bf >> 8) & 0x0F; + reg.addr = bf & 0xFF; + if (reg.len == 15) { + return fsm_reg_write(fsm_dev, reg.addr, val); + } + ret = fsm_reg_read(fsm_dev, reg.addr, &oldval); + if (ret) { + pr_err("get bf:%04X failed", bf); + return ret; + } + + msk = ((1 << (reg.len + 1)) - 1) << reg.pos; + reg.value = oldval & (~msk); + reg.value |= val << reg.pos; + + if(oldval == reg.value) { + return 0; + } + ret = fsm_reg_write(fsm_dev, reg.addr, reg.value); + if (ret) { + pr_err("set bf:%04X failed", bf); + return ret; + } + + return ret; +} + +int fsm_get_bf(fsm_dev_t *fsm_dev, const uint16_t bf, uint16_t *pval) +{ + reg_unit_t reg; + uint16_t msk; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + reg.len = (bf >> 12) & 0x0F; + reg.pos = (bf >> 8) & 0x0F; + reg.addr = bf & 0xFF; + ret = fsm_reg_multiread(fsm_dev, reg.addr, ®.value); + if (ret) { + pr_err("get bf:%04X failed", bf); + return ret; + } + msk = ((1 << (reg.len + 1)) - 1) << reg.pos; + reg.value &= msk; + if(pval) { + *pval = reg.value >> reg.pos; + } + + return ret; +} + +struct fsm_dev *fsm_get_fsm_dev(uint8_t addr) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + + if (!cfg || cfg->dev_count <= 0) { + return NULL; + } + + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { + if (fsm_dev && addr == fsm_dev->addr) { + return fsm_dev; + } + } + + return NULL; +} + +struct fsm_dev *fsm_get_fsm_dev_by_id(int id) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + + if (!cfg || cfg->dev_count <= 0) { + return NULL; + } + + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { + if (fsm_dev && id == fsm_dev->id) { + return fsm_dev; + } + } + + return NULL; +} + +int fsm_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) +{ + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } +#if defined(FSM_DEBUG_I2C) + if (g_fsm_config.i2c_debug) { + pr_addr(info, "%02X<-%04X", reg, val); + } +#endif +#if defined(CONFIG_FSM_REGMAP) + ret = fsm_regmap_write(fsm_dev, reg, val); +#elif defined(CONFIG_FSM_I2C) + ret = fsm_i2c_reg_write(fsm_dev, reg, val); +#elif defined(FSM_HAL_SUPPORT) + ret = fsm_hal_reg_write(fsm_dev, reg, val); +#else + ret = -EINVAL; +#endif + if (ret) { + pr_addr(err, "%02X<-%04X fail:%d", reg, val, ret); + } + + return ret; +} + +int fsm_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval) +{ + uint16_t value; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } +#if defined(CONFIG_FSM_REGMAP) + ret = fsm_regmap_read(fsm_dev, reg, &value); +#elif defined(CONFIG_FSM_I2C) + ret = fsm_i2c_reg_read(fsm_dev, reg, &value); +#elif defined(FSM_HAL_SUPPORT) + ret = fsm_hal_reg_read(fsm_dev, reg, &value); +#else + ret = -EINVAL; +#endif +#if defined(FSM_DEBUG_I2C) + if (g_fsm_config.i2c_debug) { + pr_addr(info, " %02X->%04X", reg, value); + } +#endif + if (ret) { + value = 0; +#if !defined(BUILD_FSTOOL) + pr_addr(err, " %02X->%04X fail:%d", reg, value, ret); +#endif + } + if (pval) { + *pval = value; + } + + return ret; +} + +int fsm_burst_write(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *data, int len) +{ + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } +#if defined(FSM_DEBUG_I2C) + if (len >= 4) { + if (g_fsm_config.i2c_debug) { + pr_addr(info, "%02X<-%02X %02X %02X %02X", reg, + data[0], data[1], data[2], data[3]); + } + } + // logprint("%s: %02X: %02X<-", __func__, fsm_dev->addr, reg); + // for (ret = 0; ret < len; ret++) { + // logprint("%02X ", data[ret]); + // } + // logprint("\n"); +#endif + +#if defined(CONFIG_FSM_REGMAP) + ret = fsm_regmap_bulkwrite(fsm_dev, reg, data, len); +#elif defined(CONFIG_FSM_I2C) + ret = fsm_i2c_bulkwrite(fsm_dev, reg, data, len); +#elif defined(FSM_HAL_SUPPORT) + ret = fsm_hal_bulkwrite(fsm_dev, reg, data, len); +#else + ret = -EINVAL; +#endif + if (ret) { + pr_addr(err, "BW %02X fail:%d", reg, ret); + } + + return ret; +} + +int fsm_reg_case(fsm_dev_t *fsm_dev, reg_unit_t *reg) +{ + reg_wait_t *reg_wait; + uint16_t temp; + uint16_t mask; + int count; + int ret = 0; + + if (!fsm_dev || !reg) { + return -EINVAL; + } + switch (reg->len) { + case 1: // delay ms + fsm_delay_ms(reg->value); + break; + case 2: // wait bits, 8bits max + reg_wait = (reg_wait_t *)®->value; + if (reg_wait->len >= 8) { + pr_addr(err, "invalid len:%d", reg_wait->len); + return -EINVAL; + } + mask = ((1 << (reg_wait->len + 1)) - 1); + for (count = 0; count < 35; count++) { + fsm_delay_ms(1); + ret = fsm_reg_multiread(fsm_dev, reg->addr, &temp); + temp = (temp >> reg_wait->pos) & (~mask); + if (temp == reg_wait->val) + break; + } + break; + default: + break; + } + return ret; +} + +static int fs1815_regs_compat_v1(fsm_dev_t *fsm_dev, reg_unit_t *reg) +{ + uint16_t temp; + uint16_t mask; + uint16_t val; + uint8_t addr; + int ret; + + addr = reg->addr; + val = reg->value; + if (addr == 0x20) { + if (reg->pos <= 8 && (reg->pos + reg->len) >= 12) { + mask = ((8 - reg->pos) << 8) | 0x4000 | addr; // bit[12..8] + val = get_bf_val(mask, reg->value); + if (val <= 0x07) val = val + 0; + else if (val <= 0x0F) val = val + 2; + else if (val <= 0x17) val = val + 4; + else if (val == 0x18) val = 0X1E; + else val = 0x1F; + temp = reg->value; + set_bf_val(&temp, mask, val); + val = temp; + } + } + else if (addr >= 0x59 && addr <= 0x5C) { + if (reg->pos == 0 && reg->len >= 3) { + mask = 0x3000 | addr; // bit[3..0] + val = get_bf_val(mask, reg->value); + if (val <= 0x2) val = val * 2 + 1; + else if (val <= 0x4) val = 0x6; + else if (val <= 0x9) val = val + 2; + else if (val <= 0xB) val = 0xC; + else val = 0xE; + temp = reg->value; + set_bf_val(&temp, mask, val); + val = temp; + } + } + mask = ((1 << (reg->len + 1)) - 1) << reg->pos; + val = (val << reg->pos); + ret = fsm_reg_multiread(fsm_dev, addr, &temp); + val = ((~mask & temp) | (val & mask)); + ret |= fsm_reg_write(fsm_dev, addr, val); + + return ret; +} + +int fsm_reg_update_bits(fsm_dev_t *fsm_dev, reg_unit_t *reg) +{ + uint16_t temp; + uint16_t mask; + uint16_t val; + uint8_t addr; + int ret; + + if (!fsm_dev || !reg) { + return -EINVAL; + } + if (reg->pos == 0xF && reg->len > 0) { + return fsm_reg_case(fsm_dev, reg); + } + addr = reg->addr; + if (fsm_dev->is1820 && fsm_dev->revid == 0x0011) { + if (addr == 0x20 || (addr >= 0x59 && addr <= 0x5C)) { + return fs1815_regs_compat_v1(fsm_dev, reg); + } + } + if (reg->pos == 0 && reg->len == 15) { + return fsm_reg_write(fsm_dev, addr, reg->value); + } + mask = ((1 << (reg->len + 1)) - 1) << reg->pos; + val = (reg->value << reg->pos); + ret = fsm_reg_multiread(fsm_dev, addr, &temp); + temp = ((~mask & temp) | (val & mask)); + ret |= fsm_reg_write(fsm_dev, addr, temp); + + return ret; +} + +int fsm_reg_update(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) +{ + uint16_t temp; + int ret; + + ret = fsm_reg_multiread(fsm_dev, reg, &temp); + if (!ret && temp != val) { + ret = fsm_reg_write(fsm_dev, reg, val); + } + if (ret) { + pr_addr(err, "update reg:%02X failed:%d", reg, ret); + } + + return ret; +} + +int fsm_reg_multiread(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval) +{ + uint16_t value; + uint16_t old; + int count = 0; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + while (count++ < FSM_I2C_RETRY) { + ret = fsm_reg_read(fsm_dev, reg, &value); + if (ret) + continue; + if (count > 1 && old == value) { + break; + } + old = value; + } + if (ret) { + value = 0; +#if !defined(BUILD_FSTOOL) + pr_addr(err, " %02X->%04X fail:%d", reg, value, ret); +#endif + } + if (pval) { + *pval = value; + } + + return ret; +} + +uint16_t fsm_calc_checksum(uint16_t *data, int len) +{ + uint16_t crc = 0; + uint8_t index; + uint8_t b; + int i; + + if (len <= 0) { + return 0; + } + + for (i = 0; i < len; i++) { + b = (uint8_t)(data[i] & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ g_crc16table[index]); + b = (uint8_t)((data[i] >> 8) & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ g_crc16table[index]); + } + return crc; +} + +int fsm_get_srate_bits(fsm_dev_t *fsm_dev, uint32_t srate) +{ + int size; + int idx; + + if (!fsm_dev) { + return -EINVAL; + } + if (srate > 48000 && HIGH8(fsm_dev->version) != FS1860_DEV_ID) { + pr_addr(err, "invalid srate:%d", srate); + return -EINVAL; + } + if (srate == 32000 && fsm_dev->is1603s) { + return 6; // I2SSR=6 + } + size = sizeof(g_srate_tbl)/ sizeof(struct fsm_srate); + for (idx = 0; idx < size; idx++) { + if (srate == g_srate_tbl[idx].srate) + return g_srate_tbl[idx].bf_val; + } + return -EINVAL; +} + +int fsm_access_key(fsm_dev_t *fsm_dev, int access) +{ + uint8_t otpacc; + int ret = 0; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->is1820) { + otpacc = 0x1F; + } else { + otpacc = 0x0B; + } + ret = fsm_reg_write(fsm_dev, otpacc, (access ? FSM_OTP_ACC_KEY2 : 0)); + + return ret; +} + +int fsm_reg_dump(fsm_dev_t *fsm_dev) +{ + char buf[LOG_BUF_SIZE]; + uint16_t value; + int reg_addr; + int reg_end; + int idx = 0; + int ret = 0; + + if (!fsm_dev) { + return -EINVAL; + } + reg_end = (fsm_dev->is1958 ? 0xEF : (fsm_dev->is1820 ? 0xEA : 0xE8)); + for (reg_addr = 0; reg_addr <= reg_end; reg_addr++) { + if (fsm_dev->is1958) { + if (reg_addr == 0x60) reg_addr = 0x80; + else if (reg_addr == 0x81) reg_addr = 0xA1; + else if (reg_addr == 0xD0) + ret |= fsm_access_key(fsm_dev, 1); + } + if (fsm_dev->is1820) { + if (reg_addr == 0x60) reg_addr = 0x80; + } + else if (reg_addr == 0x10) reg_addr = 0x40; + else if (reg_addr == 0xD0) { + ret |= fsm_access_key(fsm_dev, 1); + } + ret |= fsm_reg_read(fsm_dev, reg_addr, &value); + snprintf(buf+idx*8, 9, "%02X:%04X ", reg_addr, value); + idx++; + if (idx == 8 || reg_addr == reg_end) { + buf[idx*8-1] = '\0'; + pr_addr(info, "%s", buf); + idx = 0; + } + } + ret |= fsm_access_key(fsm_dev, 0); + + return ret; +} + +static int fsm_search_list(fsm_dev_t *fsm_dev, struct preset_file *pfile) +{ + dev_list_t *dev_list; + uint8_t *preset; + uint16_t offset; + uint16_t type; + int idx; + + preset = (uint8_t *)pfile; + for (idx = 0; idx < pfile->hdr.ndev; idx++) { + type = pfile->index[idx].type; + if (FSM_DSC_DEV_INFO != type) { + continue; + } + offset = pfile->index[idx].offset; + pr_addr(debug, "offset[%d]: %d", idx, offset); + dev_list = (struct dev_list *)&preset[offset]; + if (fsm_dev->addr == dev_list->addr) { + pr_addr(info, "found dev_list"); + fsm_dev->dev_list = dev_list; + break; + } + } + if (idx == pfile->hdr.ndev) { + pr_addr(err, "not found dev_list"); + fsm_dev->dev_list = NULL; + return -EINVAL; + } + + return 0; +} + +static int fsm_check_dev_type(fsm_dev_t *fsm_dev) +{ + uint16_t dev_type; + + if (!fsm_dev || !fsm_dev->dev_list) { + pr_addr(err, "bad parameter"); + return -EINVAL; + } + dev_type = fsm_dev->dev_list->dev_type; + if (fsm_dev->is1820) { + if (LOW8(fsm_dev->version) != HIGH8(dev_type)) { + pr_addr(err, "type:%02X not match version:%02X", + HIGH8(dev_type), HIGH8(fsm_dev->version)); + return -EINVAL; + } + return 0; + } + if (HIGH8(fsm_dev->version) == 0x06 || HIGH8(fsm_dev->version) == 0x0B) { + // fs1801 series + if (HIGH8(dev_type) != 0x0A && HIGH8(dev_type) != 0x06 + && HIGH8(dev_type) != 0x0B) { + pr_addr(err, "invalid type:%04X", dev_type); + return -EINVAL; + } + return 0; + } + // other series + if (HIGH8(fsm_dev->version) != HIGH8(dev_type)) { + pr_addr(err, "type:%02X not match version:%02X", + HIGH8(dev_type), HIGH8(fsm_dev->version)); + return -EINVAL; + } + + return 0; +} + +int fsm_init_dev_list(fsm_dev_t *fsm_dev) +{ + struct preset_file *pfile; + dev_list_t *dev_list; + int ret; + + pfile = fsm_get_presets(); + if (!fsm_dev || !pfile) { + pr_addr(err, "bad parameter or invalid FW"); + return -EINVAL; + } + + pr_addr(debug, "ndev:%d", pfile->hdr.ndev); + ret = fsm_search_list(fsm_dev, pfile); + if (ret) { + return ret; + } + dev_list = fsm_dev->dev_list; + // assert(dev_list != NULL); + ret = fsm_check_dev_type(fsm_dev); + if (ret) { + pr_addr(err, "type(%04X) not matched version(%04X)", + dev_list->dev_type, fsm_dev->version); + fsm_dev->dev_list = NULL; + return ret; + } + pr_addr(info, "preset ver:%04X", dev_list->preset_ver); + if ((dev_list->preset_ver & BIT(15)) == 0) { // BIT_15 must be 1 + pr_addr(err, "invalid preset version:%04X", + dev_list->preset_ver); + return -EINVAL; + } + pr_addr(debug, "bus : %d, len: %d", + dev_list->bus, dev_list->len); + pr_addr(debug, "type : %04X, npreset: %d", + dev_list->dev_type, dev_list->npreset); + pr_addr(debug, "scene: eq:%04X, reg:%04X", + dev_list->eq_scenes, dev_list->reg_scenes); + fsm_dev->own_scene = dev_list->reg_scenes | dev_list->eq_scenes; + + return 0; +} + +void *fsm_get_list_by_idx(fsm_dev_t *fsm_dev, int idx) +{ + dev_list_t *dev_list; + fsm_index_t *index; + uint8_t *pdata; + + if (fsm_dev == NULL) { + return NULL; + } + dev_list = fsm_dev->dev_list; + if (dev_list == NULL) { + return NULL; + } + index = &dev_list->index[0]; + pdata = (uint8_t *)index; + + return (void *)(&pdata[index[idx].offset]); +} + +void *fsm_get_data_list(fsm_dev_t *fsm_dev, int type) +{ + dev_list_t *dev_list; + fsm_index_t *index; + int i; + + if (fsm_dev == NULL || fsm_dev->dev_list == NULL) { + return NULL; + } + dev_list = fsm_dev->dev_list; + index = dev_list->index; + if (index == NULL) { + return NULL; + } + for (i = 0; i < dev_list->len; i++) { + if (index[i].type == type) { + break; + } + } + if (i >= dev_list->len) { + return NULL; + } + + return (void *)((uint8_t *)index + index[i].offset); +} + +int fsm_get_spk_info(fsm_dev_t *fsm_dev, uint16_t info_type) +{ + info_list_t *info; + + if (fsm_dev == NULL) { + return -EINVAL; + } + info = (info_list_t *)fsm_get_data_list(fsm_dev, FSM_DSC_SPK_INFO); + if (!info || info_type > info->len) { + return -EINVAL; + } + // pr_addr(debug, "spk info len: %d", info->len); + + return info->data[info_type]; +} + +int fsm_init_info(fsm_dev_t *fsm_dev) +{ + if (!fsm_dev || !fsm_dev->dev_list) { + return -EINVAL; + } + fsm_dev->bus = fsm_dev->dev_list->bus; + fsm_dev->addr = fsm_dev->dev_list->addr; + fsm_dev->tmax = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_TMAX); + fsm_dev->tcoef = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_TEMPR_COEF); + fsm_dev->tsel = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_TEMPR_SEL); + pr_addr(info, "tmax:%d, tcoef:%d, tsel:%d", + fsm_dev->tmax, fsm_dev->tcoef, fsm_dev->tsel); + fsm_dev->spkr = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_RES); + fsm_dev->rapp = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_RAPP); + if (IS_PRESET_V3(fsm_dev->dev_list->preset_ver)) { + fsm_dev->pos_mask = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_POSITION); + fsm_dev->compat.RS2RL_RATIO = \ + fsm_get_spk_info(fsm_dev, FSM_INFO_RSRL_RATIO); + } + pr_addr(info, "pos:%02X, rs_ratio:%d", + fsm_dev->pos_mask, fsm_dev->compat.RS2RL_RATIO); + pr_addr(info, "spkr:%d, rapp:%d", + fsm_dev->spkr, fsm_dev->rapp); + + return 0; +} + +void *fsm_get_presets(void) +{ + return g_fsm_config.preset; +} + +void fsm_set_presets(void *preset) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (cfg) { + fsm_free_mem((void **)&cfg->preset); + cfg->preset = preset; + } +} + +/** + * fsm device interface for external reference + */ +int fsm_parse_preset(const void *data, uint32_t size) +{ + struct preset_file *pfile; + struct preset_header *hdr; + uint16_t checksum; + int crc_size; + + if (data == NULL || size == 0) { + pr_err("bad parameter, size:%d", size); + return -EINVAL; + } + pfile = fsm_get_presets(); + if (pfile) { + pr_debug("already had presets, try init"); + fsm_try_init(); + return 0; + } + pfile = (struct preset_file *)fsm_alloc_mem(size); + if (!pfile) { + pr_err("alloc memery failed"); + return -ENOMEM; + } + memcpy(pfile, data, size); + + hdr = &pfile->hdr; + pr_debug("version : %04X", hdr->version); + pr_info("customer: %s", hdr->customer); + pr_info("project : %s", hdr->project); + pr_info("date : %4d%02d%02d-%02d%02d", hdr->date.year, \ + hdr->date.month, hdr->date.day, hdr->date.hour, hdr->date.min); + pr_debug("size : %d", hdr->size); + pr_debug("crc16 : %04X", hdr->crc16); + + crc_size = (size - sizeof(struct preset_header) + 2)/sizeof(uint16_t); + if (hdr->size == 0 || hdr->size != size) { + pr_err("invalid size: hdr:%d, fw:%d", hdr->size, size); + fsm_free_mem((void **)&pfile); + return -EINVAL; + } + checksum = fsm_calc_checksum((uint16_t *)(&(pfile->hdr.ndev)), crc_size); + if (checksum != hdr->crc16) { + pr_err("checksum(%04X) not match(%04X)", checksum, hdr->crc16); + fsm_free_mem((void **)&pfile); + return -EINVAL; + } + else { + pr_info("checksum success!"); + fsm_set_presets(pfile); + } + fsm_try_init(); + + return 0; +} + +int fsm_swap_channel(fsm_dev_t *fsm_dev, int next_angle) +{ + uint16_t left_chn; + uint8_t i2sctrl; + uint16_t chs12; + int ret = 0; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->is1820) { + /* fs1815 not supported */ + return 0; + } + switch (next_angle) { + case 90: + left_chn = (FSM_POS_LBTM | FSM_POS_RBTM); + break; + case 180: + left_chn = (FSM_POS_RTOP | FSM_POS_RBTM); + break; + case 270: + left_chn = (FSM_POS_LTOP | FSM_POS_RTOP); + break; + case 0: + default: + left_chn = (FSM_POS_LTOP | FSM_POS_LBTM); + break; + } + left_chn = (left_chn & fsm_dev->pos_mask); + chs12 = ((left_chn != 0) ? 1 : 2); + if ((g_fsm_config.dev_count == 1) + || (fsm_dev->pos_mask == FSM_POS_MONO)) { + chs12 = 3; + } + if (fsm_dev->is1958) { + i2sctrl = 0x17; + ret = fsm_set_bf(fsm_dev, 0x1317, chs12); // 0x17[4..3] + } else { + i2sctrl = 0x04; + ret = fsm_set_bf(fsm_dev, 0x1304, chs12); // 0x04[4..3] + } + pr_addr(debug, "pos:%02X, CHS12:%d", fsm_dev->pos_mask, chs12); + + return ret; +} + +int fsm_write_reg_tbl(fsm_dev_t *fsm_dev, uint16_t scene) +{ + reg_scene_t *reg_scene; + reg_comm_t *reg_comm; + reg_unit_t *reg; + regs_unit_t *regs; + int ret = 0; + int i; + + if (fsm_dev == NULL) { + return -EINVAL; + } + if (scene == FSM_SCENE_COMMON) { + reg_comm = fsm_get_data_list(fsm_dev, FSM_DSC_REG_COMMON); + if (!reg_comm) { + pr_addr(info, "note: not found reg_common data"); + return 0; + } + reg = reg_comm->reg; + pr_addr(debug, "reg comm len: %d", reg_comm->len); + for (i = 0; i < reg_comm->len; i++) { + ret |= fsm_reg_update_bits(fsm_dev, ®[i]); + } + } else if (scene != FSM_SCENE_UNKNOW) { + reg_scene = fsm_get_data_list(fsm_dev, FSM_DSC_REG_SCENES); + if (!reg_scene) { + pr_addr(info, "note: not found reg_scene data"); + return 0; + } + regs = reg_scene->regs; + pr_addr(debug, "reg scene len: %d", reg_scene->len); + for (i = 0; i < reg_scene->len; i++) { + if ((regs[i].scene & scene) == 0) { + continue; + } + ret |= fsm_reg_update_bits(fsm_dev, ®s[i].reg); + } + } + + FSM_FUNC_EXIT(ret); + return ret; +} + +int fsm_get_rstrim(fsm_dev_t *fsm_dev) +{ + uint16_t reg_rstrim; + uint16_t value; + int ret; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->is1958) { + fsm_dev->rstrim = 0; + return 0; + } else if (fsm_dev->is1820) { + reg_rstrim = 0x07; + } else { + reg_rstrim = 0xE6; + } + if (fsm_dev->rstrim != 0) { + return 0; + } + fsm_access_key(fsm_dev, 1); + ret = fsm_reg_read(fsm_dev, reg_rstrim, &value); + fsm_access_key(fsm_dev, 0); + if (ret || value == 0) { + pr_warning("use default rs_trim value"); + value = FSM_RS_TRIM_DEFAULT; + } + fsm_dev->rstrim = value; + + return 0; +} + +int fsm_check_spkcoef(fsm_dev_t *fsm_dev) +{ + uint8_t spkcoef; + uint16_t value; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + spkcoef = ((fsm_dev->is1958 || fsm_dev->is1820) ? 0x14 : 0x08); + ret = fsm_reg_read(fsm_dev, spkcoef, &value); + if (value == 0) { + // not inited yet + return -EINVAL; + } + + return ret; +} + +int fsm_config_vol(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + uint16_t volctrl; + uint16_t volume; + + if (!fsm_dev || !cfg) { + return -EINVAL; + } + + if (cfg->volume > FSM_VOLUME_MAX) { + cfg->volume = FSM_VOLUME_MAX; + } + // volume = (fsm_dev->state.calibrated ? cfg->volume : 0xDF); // -12dB + if (fsm_dev->is1820) { + /* fs1815 not supported */ + return 0; + } + else if (fsm_dev->is1958) { + volctrl = 0x16; + volume = ((cfg->volume << 1) + 1) << 7; + } else { + volctrl = 0x06; + volume = ((cfg->volume << 8) & 0xFF00); + } + pr_addr(debug, "vol: %04X", volume); + + return fsm_reg_write(fsm_dev, volctrl, volume); +} + +int fsm_ops_dummy(fsm_dev_t *fsm_dev) +{ + return 0; +} + +/* + * fsm stub api + */ + +int fsm_stub_switch_preset(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret = 0; + + if (fsm_dev == NULL) { + return -EINVAL; + } + pr_addr(debug, "%s switching", + (cfg->force_scene ? "force" : "auto")); + if (!cfg->force_scene && fsm_dev->cur_scene == cfg->next_scene) { + pr_addr(debug, "same scene, skip"); + return 0; + } + if (cfg->speaker_on) { + ret |= fsm_stub_shut_down(fsm_dev); + } + if ((fsm_dev->own_scene & cfg->next_scene) == 0) { + return ret; + } + if (fsm_dev->dev_ops.switch_preset) { + ret |= fsm_dev->dev_ops.switch_preset(fsm_dev); + } + ret |= fsm_write_reg_tbl(fsm_dev, cfg->next_scene); + fsm_dev->cur_scene = cfg->next_scene; + if (cfg->speaker_on) { + ret |= fsm_stub_start_up(fsm_dev); + } + + FSM_FUNC_EXIT(ret); + return ret; +} + +int fsm_stub_dev_init(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret; + + if (fsm_dev == NULL || cfg == NULL) { + return -EINVAL; + } + + if (gpio_is_valid(fsm_dev->rst_gpio)) + gpio_direction_output(fsm_dev->rst_gpio, 1); + + if (cfg->force_init) { + pr_addr(info, "force init"); + } + ret = fsm_check_spkcoef(fsm_dev); + if (ret) { + pr_addr(info, "try init device"); + fsm_dev->state.dev_inited = false; + } + if (!cfg->force_init && fsm_dev->state.dev_inited) { + return 0; + } + if (fsm_dev->dev_ops.dev_init) { + return fsm_dev->dev_ops.dev_init(fsm_dev); + } + ret = fsm_init_dev_list(fsm_dev); + if (ret || !fsm_dev->dev_list) { + pr_addr(err, "init dev_list fail:%d", ret); + return ret; + } + fsm_dev->state.dev_inited = true; + ret = fsm_init_info(fsm_dev); + if (fsm_dev->dev_ops.reg_init) { + ret |= fsm_dev->dev_ops.reg_init(fsm_dev); + } + ret |= fsm_get_rstrim(fsm_dev); + ret |= fsm_stub_shut_down(fsm_dev); + fsm_dev->errcode = ret; + + return ret; +} + +int fsm_stub_start_up(fsm_dev_t *fsm_dev) +{ + int ret = 0; + + if (fsm_dev == NULL) { + return -EINVAL; + } + if (fsm_dev->dev_ops.start_up) { + return fsm_dev->dev_ops.start_up(fsm_dev); + } + pr_addr(err, "none ops"); + + return ret; +} + +int fsm_stub_shut_down(fsm_dev_t *fsm_dev) +{ + int ret = 0; + + if (fsm_dev == NULL) { + return -EINVAL; + } + if (fsm_dev->dev_ops.shut_down) { + return fsm_dev->dev_ops.shut_down(fsm_dev); + } + pr_addr(err, "none ops"); + + return ret; +} + +int fsm_stub_dev_deinit(fsm_dev_t *fsm_dev) +{ + if (fsm_dev == NULL) { + return -EINVAL; + } + if (fsm_dev->dev_ops.deinit) { + return fsm_dev->dev_ops.deinit(fsm_dev); + } + pr_addr(err, "none ops"); + + return 0; +} + +int fsm_save_re25(struct fsm_calib_v2 *data) +{ + if (!data) { + return -EINVAL; + } + return -EINVAL; +} + +int fsm_get_re25(struct fsm_calib_v2 *data) +{ + if (!data) { + return -EINVAL; + } + if (data->dev_count > 0) { + // already loaded + return 0; + } + return -EINVAL; +} + +int fsm_dev_count(void) +{ + return g_fsm_config.dev_count; +} + +void fsm_set_i2s_clocks(uint32_t rate, uint32_t bclk) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg) { + return; + } + fsm_mutex_lock(); + cfg->i2s_bclk = bclk; + cfg->i2s_srate = rate; + if (rate == 32000) { + cfg->i2s_bclk += 32; + } + fsm_mutex_unlock(); +} + +void fsm_set_scene(int scene) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + + if (!cfg || scene < 0 || scene >= FSM_SCENE_MAX) { + pr_err("invaild scene:%d", scene); + return; + } + fsm_mutex_lock(); + cfg->next_scene = BIT(scene); + fsm_list_func(fsm_dev, fsm_stub_switch_preset); + fsm_mutex_unlock(); +} + +void fsm_set_volume(int volume) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + + if (!cfg) { + return; + } + fsm_mutex_lock(); + if (volume < 0 || volume > FSM_VOLUME_MAX) { + pr_warning("invalid volume: %d, default 0dB", volume); + volume = FSM_VOLUME_MAX; + } + cfg->volume = volume; + if (!cfg->stream_muted) { + fsm_list_func(fsm_dev, fsm_config_vol); + } + fsm_mutex_unlock(); +} + +void fsm_set_fw_name(char *name) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (cfg) { + cfg->fw_name = name; + } +} + +int fsm_detect_device(fsm_dev_t *fsm_dev, uint8_t dev_id) +{ + if (fsm_dev == NULL) { + return -EINVAL; + } + fsm_dev->use_irq = false; + memset(&fsm_dev->dev_ops, 0, sizeof(fsm_dev->dev_ops)); + switch (dev_id) { + case FS1603_DEV_ID: + //fs1603_ops(fsm_dev); + break; + case FS1818_DEV_ID: + case FS1896_DEV_ID: + fsm_dev->is1801 = true; + //fs1801_ops(fsm_dev); + break; + case FS1958_DEV_ID: + fsm_dev->is1958 = true; + //fs1958_ops(fsm_dev); + break; + case FS1815_DEV_ID: + fsm_dev->is1820 = true; + fsm_reg_read(fsm_dev, 0x02, &fsm_dev->revid); + fs1815_ops(fsm_dev); + break; + default: + pr_addr(err, "invalid dev_id:%02X", dev_id); + return -EINVAL; + } + + return 0; +} + +int fsm_probe(fsm_dev_t *fsm_dev, int addr) +{ + uint16_t id; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + + fsm_mutex_lock(); + if (fsm_dev->addr == 0) + fsm_dev->addr = addr; + do { + ret = fsm_reg_read(fsm_dev, 0x01, &id); + if (ret) { + fsm_dev->addr = 0; + fsm_mutex_unlock(); + return ret; + } + fsm_dev->is1603s = IS_FS1603S(id); + ret = fsm_detect_device(fsm_dev, LOW8(id)); + if (ret) { + pr_addr(err, "Unknown DEVID: %04X", id); + fsm_dev->addr = 0; + fsm_mutex_unlock(); + return ret; + } + } while (0); +#ifndef BUILD_FSTOOL + pr_addr(info, "Found DEVID: %04X", id); +#endif + fsm_dev->ram_scene[FSM_EQ_RAM0] = FSM_SCENE_UNKNOW; + fsm_dev->ram_scene[FSM_EQ_RAM1] = FSM_SCENE_UNKNOW; + fsm_dev->own_scene = FSM_SCENE_UNKNOW; + fsm_dev->cur_scene = FSM_SCENE_UNKNOW; + fsm_dev->version = id; + fsm_dev->acc_count = 0; + + fsm_list_init(fsm_dev); + g_fsm_config.dev_count++; + fsm_mutex_unlock(); + + return 0; +} + +void fsm_remove(fsm_dev_t *fsm_dev) +{ + if (!fsm_dev) { + return; + } + fsm_mutex_lock(); + list_del(&fsm_dev->list); + if (fsm_dev->tdata) { + fsm_free_mem((void **)&fsm_dev->tdata); + } + g_fsm_config.dev_count--; + fsm_mutex_unlock(); +} + +/** + * try init deivce without lock + */ +static int fsm_try_init(void) +{ + fsm_config_t *cfg = fsm_get_config(); + struct preset_file *preset; + fsm_dev_t *fsm_dev; + uint16_t next_scene; + int ret; + + if (cfg->dev_count <= 0) { + pr_err("no found device"); + return -EINVAL; + } + cfg->force_scene = cfg->force_init; + ret = fsm_firmware_init_sync(cfg->fw_name); + preset = fsm_get_presets(); + if (preset == NULL) { + pr_err("invalid firmware, ret:%d", ret); + return -EINVAL; + } + next_scene = cfg->next_scene; + cfg->next_scene = FSM_SCENE_MUSIC; // init all devices + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { + fsm_stub_dev_init(fsm_dev); + } + cfg->next_scene = next_scene; // recovery scene + cfg->force_scene = cfg->force_init = false; + + return 0; +} + +void fsm_init(void) +{ + int ret; + + ret = fsm_hal_open(); + if (ret) { + pr_err("hal open fail:%d", ret); + return; + } + fsm_mutex_lock(); + pr_info("version: %s", FSM_CODE_VERSION); + pr_info("branch : %s", FSM_GIT_BRANCH); + pr_info("date : %s", FSM_CODE_DATE); + ret = fsm_try_init(); + if (ret) { // no device or firmware + pr_err("init failed: %d", ret); + } + g_dev_inited = 1; + pr_debug("done"); + fsm_mutex_unlock(); +} + +static int fsm_get_scene_index(uint16_t scene) +{ + int index = 0; + + while (scene) { + scene = (scene >> 1); + if (scene == 0) { + break; + } + index++; + } + + return index; +} + +int fsm_scene_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_config_t *cfg = fsm_get_config(); + int scene_index; + + if (!cfg) { + ucontrol->value.integer.value[0] = -1; + return 0; + } + scene_index = fsm_get_scene_index(cfg->next_scene); + pr_info("scene: %04X, BIT(%d)", cfg->next_scene, scene_index); + ucontrol->value.integer.value[0] = scene_index; + + return 0; +} + +int fsm_scene_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int next_scene = ucontrol->value.integer.value[0]; + + pr_info("next_scene: %d", next_scene); + fsm_set_scene(next_scene); + + return 0; +} + +int fsm_amp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = atomic_read(&fsm_amp_switch); + + ucontrol->value.integer.value[0] = enable; + pr_info("switch: %s", enable ? "On" : "Off"); + + return 0; +} + +int fsm_amp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_info("switch: %s", enable ? "On" : "Off"); + atomic_set(&fsm_amp_switch, enable); + if (enable) { + fsm_speaker_onn(); + } else { + fsm_speaker_off(); + } + + return 0; +} + +int fsm_init_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_init(); + return 0; +} + +int fsm_init_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + ucontrol->value.integer.value[0] = g_dev_inited; + pr_info("dev %s", (g_dev_inited == 1) ? "initilized" : "init failed"); + + return 0; +} + +const struct snd_kcontrol_new fsm_snd_controls[] = +{ + SOC_SINGLE_EXT("FSM_Scene", SND_SOC_NOPM, 0, FSM_SCENE_MAX, 0, + fsm_scene_get, fsm_scene_put), + SOC_SINGLE_EXT("FSM_Amp_Switch", SND_SOC_NOPM, 0, 1, 0, + fsm_amp_switch_get, fsm_amp_switch_put), + SOC_SINGLE_EXT("FSM_Init", SND_SOC_NOPM, 0, 1, 0, + fsm_init_get, fsm_init_put), +}; + +void fsm_add_codec_controls(struct snd_soc_codec *codec) +{ + snd_soc_add_codec_controls(codec, fsm_snd_controls, + ARRAY_SIZE(fsm_snd_controls)); +} +EXPORT_SYMBOL_GPL(fsm_add_codec_controls); + +void fsm_add_card_controls(struct snd_soc_card *card) +{ + snd_soc_add_card_controls(card, fsm_snd_controls, + ARRAY_SIZE(fsm_snd_controls)); +} +EXPORT_SYMBOL_GPL(fsm_add_card_controls); + +void fsm_speaker_onn(void) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + int ret; + + pr_info("scene: %04X", cfg->next_scene); + fsm_mutex_lock(); + cfg->stream_muted = false; + ret = fsm_try_init(); + if (ret) { // no device or firmware + pr_err("init failed: %d", ret); + fsm_mutex_unlock(); + return; + } + fsm_list_func(fsm_dev, fsm_stub_switch_preset); + fsm_list_func(fsm_dev, fsm_stub_start_up); + cfg->cur_angle = cfg->next_angle; + cfg->speaker_on = true; + pr_debug("done"); + fsm_mutex_unlock(); +} +EXPORT_SYMBOL_GPL(fsm_speaker_onn); + +void fsm_speaker_off(void) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + int ret; + + pr_info("scene: %04X", cfg->next_scene); + fsm_mutex_lock(); + cfg->stream_muted = true; + ret = fsm_try_init(); + if (ret) { + pr_err("try init failed: %d", ret); + fsm_mutex_unlock(); + return; + } + fsm_list_func(fsm_dev, fsm_stub_shut_down); + cfg->speaker_on = false; + fsm_mutex_unlock(); + fsm_set_scene(0); // scene music + pr_debug("done"); +} +EXPORT_SYMBOL_GPL(fsm_speaker_off); + +void fsm_stereo_rotation(int next_angle) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + + if (next_angle >= 360) { // angle range: [0 ~ 359] + next_angle %= 360; + } + if (cfg->cur_angle == next_angle) { + return; + } + fsm_mutex_lock(); + pr_info("scene:%04X, angle:%d", cfg->next_scene, next_angle); + cfg->next_angle = next_angle; + if (cfg->stream_muted || !(cfg->next_scene & FSM_SCENE_MUSIC)) { + fsm_mutex_unlock(); + return; + } + fsm_list_func_arg(fsm_dev, fsm_swap_channel, cfg->next_angle); + cfg->cur_angle = cfg->next_angle; + fsm_mutex_unlock(); +} + +void fsm_dump(void) +{ + fsm_dev_t *fsm_dev = NULL; + fsm_mutex_lock(); + fsm_list_func(fsm_dev, fsm_reg_dump); + fsm_mutex_unlock(); +} + +void fsm_deinit(void) +{ + fsm_dev_t *fsm_dev = NULL; + + fsm_mutex_lock(); + if (fsm_dev_count() <= 0) { + fsm_mutex_unlock(); + return; + } + fsm_list_func(fsm_dev, fsm_stub_dev_deinit); + fsm_firmware_deinit(); + fsm_mutex_unlock(); + fsm_hal_close(); +} diff --git a/asoc/codecs/fs1815/fsm_firmware.c b/asoc/codecs/fs1815/fsm_firmware.c new file mode 100644 index 000000000000..b33be9d25370 --- /dev/null +++ b/asoc/codecs/fs1815/fsm_firmware.c @@ -0,0 +1,136 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2019-02-22 File created. + */ + +#if defined(CONFIG_FSM_FIRMWARE) +#include "fsm_public.h" +#include +#include + +static int g_fsm_fw_init = 0; + +#ifdef FSM_UNUSED_CODE +static void *fsm_devm_kzalloc(struct device *dev, void *buf, size_t size) +{ + char *devm_buf = devm_kzalloc(dev, size, GFP_KERNEL); + + if (!devm_buf) { + return devm_buf; + } else { + memcpy(devm_buf, buf, size); + } + + return devm_buf; +} + +static void fsm_devm_kfree(struct device *dev, void **buf) +{ + if (*buf) { + devm_kfree(dev, *buf); + *buf = NULL; + } +} +#endif + +static void fsm_firmware_inited(const struct firmware *cont, void *context) +{ + struct device *dev = (struct device *)context; + int ret; + + if (dev == NULL || cont == NULL) { + pr_err("bad parameter"); + return; + } + + pr_info("size: %zu", cont->size); + fsm_mutex_lock(); + ret = fsm_parse_preset(cont->data, (uint32_t)cont->size); + fsm_mutex_unlock(); + release_firmware(cont); + if (ret) { + pr_err("parse firmware fail: %d", ret); + g_fsm_fw_init = 0; + } +} + +int fsm_firmware_init(char *fw_name) +{ + struct device *dev; + int ret; + + if (fw_name == NULL) { + pr_err("invalid firmware name"); + return -EINVAL; + } + if (fsm_get_presets() || g_fsm_fw_init) { + return MODULE_INITED; + } + dev = fsm_get_pdev(); + if (dev == NULL) { + pr_err("invalid device"); + return -EINVAL; + } + + pr_info("loading %s in nowait mode", fw_name); + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + fw_name, dev, GFP_KERNEL, + dev, fsm_firmware_inited); + if (ret) { + pr_err("request %s failed: %d\n", fw_name, ret); + return ret; + } + g_fsm_fw_init = 1; + + return ret; +} + +int fsm_firmware_init_sync(char *fw_name) +{ + fsm_config_t *cfg = fsm_get_config(); + const struct firmware *fw_cont; + struct device *dev; + int ret; + + if (fw_name == NULL) { + pr_err("invalid firmware name"); + return -EINVAL; + } + if (cfg->force_fw) { + pr_info("force loading"); + cfg->force_fw = false; + fsm_set_presets(NULL); + } + if (fsm_get_presets()) { + return MODULE_INITED; + } + dev = fsm_get_pdev(); + if (dev == NULL) { + pr_err("invalid device"); + return -EINVAL; + } + + g_fsm_fw_init = 0; + pr_info("loading %s in sync mode", fw_name); + ret = request_firmware(&fw_cont, fw_name, dev); + if (ret) { + pr_err("request %s failed: %d\n", fw_name, ret); + return ret; + } + ret = fsm_parse_preset(fw_cont->data, (uint32_t)fw_cont->size); + release_firmware(fw_cont); + if (ret) { + pr_err("parse firmware fail: %d", ret); + return ret; + } + g_fsm_fw_init = 1; + + return ret; +} + +void fsm_firmware_deinit(void) +{ + fsm_set_presets(NULL); + g_fsm_fw_init = 0; +} +#endif diff --git a/asoc/codecs/fs1815/fsm_i2c.c b/asoc/codecs/fs1815/fsm_i2c.c new file mode 100644 index 000000000000..00004697dd76 --- /dev/null +++ b/asoc/codecs/fs1815/fsm_i2c.c @@ -0,0 +1,673 @@ +/* + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + */ +#include "fsm_public.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#if defined(CONFIG_REGULATOR) +#include +static struct regulator *g_fsm_vdd = NULL; +#endif + +static DEFINE_MUTEX(g_fsm_mutex); +static struct device *g_fsm_pdev = NULL; + +/* customize configrature */ +#include "fsm_firmware.c" +#include "fsm_misc.c" +//#include "fsm_codec.c" + +void fsm_mutex_lock() +{ + mutex_lock(&g_fsm_mutex); +} + +void fsm_mutex_unlock() +{ + mutex_unlock(&g_fsm_mutex); +} + +int fsm_i2c_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pVal) +{ + struct i2c_msg msgs[2]; + uint8_t retries = 0; + uint8_t buffer[2]; + int ret; + + if (!fsm_dev || !fsm_dev->i2c || !pVal) { + return -EINVAL; + } + + // write register address. + msgs[0].addr = fsm_dev->i2c->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = ® + // read register buffer. + msgs[1].addr = fsm_dev->i2c->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 2; + msgs[1].buf = &buffer[0]; + + do { + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_transfer(fsm_dev->i2c->adapter, &msgs[0], ARRAY_SIZE(msgs)); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != ARRAY_SIZE(msgs)) { + fsm_delay_ms(5); + retries++; + } + } while (ret != ARRAY_SIZE(msgs) && retries < FSM_I2C_RETRY); + + if (ret != ARRAY_SIZE(msgs)) { + pr_err("read %02x transfer error: %d", reg, ret); + return -EIO; + } + + *pVal = ((buffer[0] << 8) | buffer[1]); + + return 0; +} + +int fsm_i2c_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) +{ + struct i2c_msg msgs[1]; + uint8_t retries = 0; + uint8_t buffer[3]; + int ret; + + if (!fsm_dev || !fsm_dev->i2c) { + return -EINVAL; + } + + buffer[0] = reg; + buffer[1] = (val >> 8) & 0x00ff; + buffer[2] = val & 0x00ff; + msgs[0].addr = fsm_dev->i2c->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(buffer); + msgs[0].buf = &buffer[0]; + + do { + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_transfer(fsm_dev->i2c->adapter, &msgs[0], ARRAY_SIZE(msgs)); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != ARRAY_SIZE(msgs)) { + fsm_delay_ms(5); + retries++; + } + } while (ret != ARRAY_SIZE(msgs) && retries < FSM_I2C_RETRY); + + if (ret != ARRAY_SIZE(msgs)) { + pr_err("write %02x transfer error: %d", reg, ret); + return -EIO; + } + + return 0; +} + +int fsm_i2c_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, + uint8_t *data, int len) +{ + uint8_t retries = 0; + uint8_t *buf; + int size; + int ret; + + if (!fsm_dev || !fsm_dev->i2c || !data) { + return -EINVAL; + } + + size = sizeof(uint8_t) + len; + buf = (uint8_t *)fsm_alloc_mem(size); + if (!buf) { + pr_err("alloc memery failed"); + return -ENOMEM; + } + + buf[0] = reg; + memcpy(&buf[1], data, len); + do { + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_master_send(fsm_dev->i2c, buf, size); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret < 0) { + fsm_delay_ms(5); + retries++; + } else if (ret == size) { + break; + } + } while (ret != size && retries < FSM_I2C_RETRY); + + fsm_free_mem((void **)&buf); + + if (ret != size) { + pr_err("write %02x transfer error: %d", reg, ret); + return -EIO; + } + + return 0; +} + +bool fsm_set_pdev(struct device *dev) +{ + if (g_fsm_pdev == NULL || dev == NULL) { + g_fsm_pdev = dev; + // pr_debug("dev_name: %s", dev_name(dev)); + return true; + } + return false; // already got device +} + +struct device *fsm_get_pdev(void) +{ + return g_fsm_pdev; +} + +int fsm_vddd_on(struct device *dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret = 0; + + if (!cfg || cfg->vddd_on) { + return 0; + } +#if defined(CONFIG_REGULATOR) + g_fsm_vdd = regulator_get(dev, "fsm_vddd"); + if (IS_ERR(g_fsm_vdd) != 0) { + pr_err("error getting fsm_vddd regulator"); + ret = PTR_ERR(g_fsm_vdd); + g_fsm_vdd = NULL; + return ret; + } + pr_info("enable regulator"); + regulator_set_voltage(g_fsm_vdd, 1800000, 1800000); + ret = regulator_enable(g_fsm_vdd); + if (ret < 0) { + pr_err("enabling fsm_vddd failed: %d", ret); + } +#endif + cfg->vddd_on = 1; + fsm_delay_ms(10); + + return ret; +} + +void fsm_vddd_off(void) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg || !cfg->vddd_on || cfg->dev_count > 0) { + return; + } +#if defined(CONFIG_REGULATOR) + if (g_fsm_vdd) { + pr_info("disable regulator"); + regulator_disable(g_fsm_vdd); + regulator_put(g_fsm_vdd); + g_fsm_vdd = NULL; + } +#endif + cfg->vddd_on = 0; +} + +int fsm_get_amb_tempr(void) +{ + union power_supply_propval psp = { 0 }; + struct power_supply *psy; + int tempr = FSM_DFT_AMB_TEMPR; + int vbat = FSM_DFT_AMB_VBAT; + + psy = power_supply_get_by_name("battery"); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) + if (psy && psy->get_property) { + // battery temperatrue + psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &psp); + tempr = DIV_ROUND_CLOSEST(psp.intval, 10); + psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &psp); + vbat = DIV_ROUND_CLOSEST(psp.intval, 1000); + } +#else + if (psy && psy->desc && psy->desc->get_property) { + // battery temperatrue + psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &psp); + tempr = DIV_ROUND_CLOSEST(psp.intval, 10); + psy->desc->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &psp); + vbat = DIV_ROUND_CLOSEST(psp.intval, 1000); + } +#endif + pr_info("vbat:%d, tempr:%d", vbat, tempr); + + return tempr; +} + +void *fsm_devm_kstrdup(struct device *dev, void *buf, size_t size) +{ + char *devm_buf = devm_kzalloc(dev, size + 1, GFP_KERNEL); + + if (!devm_buf) { + return devm_buf; + } + memcpy(devm_buf, buf, size); + + return devm_buf; +} + +static int fsm_set_irq(fsm_dev_t *fsm_dev, bool enable) +{ + if (!fsm_dev || fsm_dev->irq_id <= 0) { + return -EINVAL; + } + if (enable) + enable_irq(fsm_dev->irq_id); + else + disable_irq(fsm_dev->irq_id); + + return 0; +} + +int fsm_set_monitor(fsm_dev_t *fsm_dev, bool enable) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg || !fsm_dev || !fsm_dev->fsm_wq) { + return -EINVAL; + } + if (!cfg->use_monitor) { + return 0; + } + if (fsm_dev->use_irq) { + return fsm_set_irq(fsm_dev, enable); + } + if (enable) { + queue_delayed_work(fsm_dev->fsm_wq, + &fsm_dev->monitor_work, 5*HZ); + } + else { + if (delayed_work_pending(&fsm_dev->monitor_work)) { + cancel_delayed_work_sync(&fsm_dev->monitor_work); + } + } + + return 0; +} + +static int fsm_ext_reset(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + uint16_t id = 0; + int addr; + int ret; + + if (!cfg || !fsm_dev) { + return -EINVAL; + } + if (cfg->reset_chip) { + return 0; + } + if (gpio_is_valid(fsm_dev->rst_gpio)) { + gpio_set_value(fsm_dev->rst_gpio, 0); + fsm_delay_ms(10); // mdelay + gpio_set_value(fsm_dev->rst_gpio, 1); + fsm_delay_ms(1); // mdelay + // TODO: for sharing reset pin in multi-pa application + // cfg->reset_chip = true; + } + fsm_mutex_lock(); + /* soft reset */ + addr = fsm_dev->i2c->addr; + ret = fsm_reg_read(fsm_dev, 0x01, &id); + if (ret && addr != 0x34) { + fsm_dev->i2c->addr = 0x34; + ret = fsm_reg_read(fsm_dev, 0x01, &id); + } + if (!ret && LOW8(id) == FS1815_DEV_ID) { + fsm_reg_write(fsm_dev, 0x10, 0x0002); + fsm_delay_ms(15); + } else { + ret = -ENODEV; + } + fsm_dev->i2c->addr = addr; + fsm_mutex_unlock(); + + return 0; +} + +static irqreturn_t fsm_irq_hander(int irq, void *data) +{ + fsm_dev_t *fsm_dev = data; + + queue_delayed_work(fsm_dev->fsm_wq, &fsm_dev->interrupt_work, 0); + + return IRQ_HANDLED; +} + +static void fsm_work_monitor(struct work_struct *work) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + //int ret; + + fsm_dev = container_of(work, struct fsm_dev, monitor_work.work); + if (!cfg || cfg->skip_monitor || !fsm_dev) { + return; + } + fsm_mutex_lock(); + //ret = fsm_dev_recover(fsm_dev); + fsm_mutex_unlock(); + if (fsm_dev->rec_count >= 5) { // 5 time max + pr_addr(warning, "recover max time, stop it"); + return; + } + /* reschedule */ + queue_delayed_work(fsm_dev->fsm_wq, &fsm_dev->monitor_work, + 5*HZ); + +} + +static void fsm_work_interrupt(struct work_struct *work) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + //int ret; + + fsm_dev = container_of(work, struct fsm_dev, interrupt_work.work); + if (!cfg || cfg->skip_monitor || !fsm_dev) { + return; + } + fsm_mutex_lock(); + //ret = fsm_dev_recover(fsm_dev); + //fsm_get_spkr_tempr(fsm_dev); + + fsm_mutex_unlock(); +} + +static int fsm_request_irq(fsm_dev_t *fsm_dev) +{ + struct i2c_client *i2c; + int irq_flags; + int ret; + + if (fsm_dev == NULL || fsm_dev->i2c == NULL) { + return -EINVAL; + } + fsm_dev->irq_id = -1; + if (!fsm_dev->use_irq || !gpio_is_valid(fsm_dev->irq_gpio)) { + pr_addr(info, "skip to request irq"); + return 0; + } + i2c = fsm_dev->i2c; + /* register irq handler */ + fsm_dev->irq_id = gpio_to_irq(fsm_dev->irq_gpio); + if (fsm_dev->irq_id <= 0) { + dev_err(&i2c->dev, "invalid irq %d\n", fsm_dev->irq_id); + return -EINVAL; + } + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, fsm_dev->irq_id, + NULL, fsm_irq_hander, irq_flags, "fs16xx", fsm_dev); + if (ret) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + fsm_dev->irq_id, ret); + return ret; + } + disable_irq(fsm_dev->irq_id); + + return 0; +} + +#ifdef CONFIG_OF +static int fsm_parse_dts(struct i2c_client *i2c, fsm_dev_t *fsm_dev) +{ + struct device_node *np = i2c->dev.of_node; + uint8_t dummy_addr; + char *str_name; + int ret; + + if (fsm_dev == NULL || np == NULL) { + return -EINVAL; + } + + str_name = devm_kzalloc(&i2c->dev, 32, GFP_KERNEL); + fsm_dev->rst_gpio = of_get_named_gpio(np, "fsm,rst-gpio", 0); + if (gpio_is_valid(fsm_dev->rst_gpio)) { + snprintf(str_name, 32, "FS1815_SDZ_%02X", i2c->addr); + ret = devm_gpio_request_one(&i2c->dev, fsm_dev->rst_gpio, + GPIOF_OUT_INIT_LOW, str_name); + if (ret) + return ret; + } + + ret = of_property_read_u8(np, "fsm,dummy-addr", &dummy_addr); + if (ret) + dummy_addr = 0; + + fsm_dev->addr = dummy_addr; + + return 0; +} + +static struct of_device_id fsm_match_tbl[] = +{ + { .compatible = "foursemi,fs16xx" }, + { .compatible = "foursemi,fs1815" }, + {}, +}; +MODULE_DEVICE_TABLE(of, fsm_match_tbl); +#endif + +static int fsm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + int ret; + + pr_debug("enter"); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check I2C_FUNC_I2C failed"); + return -EIO; + } + + fsm_dev = devm_kzalloc(&i2c->dev, sizeof(struct fsm_dev), GFP_KERNEL); + if (fsm_dev == NULL) { + dev_err(&i2c->dev, "alloc memory fialed"); + return -ENOMEM; + } + + memset(fsm_dev, 0, sizeof(struct fsm_dev)); + mutex_init(&fsm_dev->i2c_lock); + fsm_dev->i2c = i2c; + +#ifdef CONFIG_OF + ret = fsm_parse_dts(i2c, fsm_dev); + if (ret) { + dev_err(&i2c->dev, "failed to parse DTS node"); + return ret; + } +#endif +#if defined(CONFIG_FSM_REGMAP) + fsm_dev->regmap = fsm_regmap_i2c_init(i2c); + if (fsm_dev->regmap == NULL) { + devm_kfree(&i2c->dev, fsm_dev); + dev_err(&i2c->dev, "regmap init fialed"); + return -EINVAL; + } +#endif + + fsm_vddd_on(&i2c->dev); + fsm_ext_reset(fsm_dev); + ret = fsm_probe(fsm_dev, i2c->addr); + if (ret) { + dev_err(&i2c->dev, "detect device failed"); +#if defined(CONFIG_FSM_REGMAP) + fsm_regmap_i2c_deinit(fsm_dev->regmap); +#endif + gpio_set_value(fsm_dev->rst_gpio, 0); + devm_gpio_free(&i2c->dev, fsm_dev->rst_gpio); + devm_kfree(&i2c->dev, fsm_dev); + return ret; + } + fsm_dev->id = cfg->dev_count - 1; + i2c_set_clientdata(i2c, fsm_dev); + pr_addr(info, "index:%d", fsm_dev->id); + fsm_dev->fsm_wq = create_singlethread_workqueue("fs16xx"); + INIT_DELAYED_WORK(&fsm_dev->monitor_work, fsm_work_monitor); + INIT_DELAYED_WORK(&fsm_dev->interrupt_work, fsm_work_interrupt); + fsm_request_irq(fsm_dev); + + if(fsm_dev->id == 0) { + // reigster only in the first device +#if !defined(CONFIG_FSM_CODEC) + fsm_set_pdev(&i2c->dev); +#endif + fsm_misc_init(); + fsm_sysfs_init(&i2c->dev); + fsm_codec_register(&i2c->dev, fsm_dev->id); + } + + dev_info(&i2c->dev, "i2c probe completed"); + + return 0; +} + +static int fsm_i2c_remove(struct i2c_client *i2c) +{ + fsm_dev_t *fsm_dev = i2c_get_clientdata(i2c); + + pr_debug("enter"); + if (fsm_dev == NULL) { + pr_err("bad parameter"); + return -EINVAL; + } + if (fsm_dev->fsm_wq) { + cancel_delayed_work_sync(&fsm_dev->interrupt_work); + cancel_delayed_work_sync(&fsm_dev->monitor_work); + destroy_workqueue(fsm_dev->fsm_wq); + } +#if defined(CONFIG_FSM_REGMAP) + fsm_regmap_i2c_deinit(fsm_dev->regmap); +#endif + if (fsm_dev->id == 0) { + fsm_codec_unregister(&i2c->dev); + fsm_sysfs_deinit(&i2c->dev); + fsm_misc_deinit(); + fsm_set_pdev(NULL); + } + + fsm_remove(fsm_dev); + fsm_vddd_off(); + if (gpio_is_valid(fsm_dev->irq_gpio)) { + devm_gpio_free(&i2c->dev, fsm_dev->irq_gpio); + } + if (gpio_is_valid(fsm_dev->rst_gpio)) { + devm_gpio_free(&i2c->dev, fsm_dev->rst_gpio); + } + devm_kfree(&i2c->dev, fsm_dev); + dev_info(&i2c->dev, "i2c removed"); + + return 0; +} + +static void fsm_i2c_shutdown(struct i2c_client *i2c) +{ + fsm_dev_t *fsm_dev = i2c_get_clientdata(i2c); + + pr_debug("enter"); + if (fsm_dev == NULL) { + pr_err("bad parameter"); + return; + } + + fsm_stub_shut_down(fsm_dev); + if (gpio_is_valid(fsm_dev->rst_gpio)) + gpio_set_value(fsm_dev->rst_gpio, 0); + + dev_info(&i2c->dev, "i2c shutdowned"); +} + +static const struct i2c_device_id fsm_i2c_id[] = +{ + { "fs16xx", 0 }, + { "fs1815", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fsm_i2c_id); + +static struct i2c_driver fsm_i2c_driver = +{ + .driver = { + .name = FSM_DRV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(fsm_match_tbl), +#endif + }, + .probe = fsm_i2c_probe, + .remove = fsm_i2c_remove, + .shutdown = fsm_i2c_shutdown, + .id_table = fsm_i2c_id, +}; + +int exfsm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return fsm_i2c_probe(i2c, id); +} +EXPORT_SYMBOL(exfsm_i2c_probe); + +int exfsm_i2c_remove(struct i2c_client *i2c) +{ + return fsm_i2c_remove(i2c); +} +EXPORT_SYMBOL(exfsm_i2c_remove); + +int fsm_i2c_init(void) +{ + return i2c_add_driver(&fsm_i2c_driver); +} + +void fsm_i2c_exit(void) +{ + i2c_del_driver(&fsm_i2c_driver); +} + +static int __init fsm_mod_init(void) +{ + int ret; + + ret = fsm_i2c_init(); + if (ret) { + pr_err("init fail: %d", ret); + return ret; + } + + return 0; +} + +static void __exit fsm_mod_exit(void) +{ + fsm_i2c_exit(); +} + +//module_i2c_driver(fsm_i2c_driver); +module_init(fsm_mod_init); +module_exit(fsm_mod_exit); + +MODULE_AUTHOR("FourSemi SW "); +MODULE_DESCRIPTION("FourSemi Smart PA Driver"); +MODULE_VERSION(FSM_CODE_VERSION); +MODULE_ALIAS("foursemi:"FSM_DRV_NAME); +MODULE_LICENSE("GPL"); + diff --git a/asoc/codecs/fs1815/fsm_misc.c b/asoc/codecs/fs1815/fsm_misc.c new file mode 100644 index 000000000000..196207f400b8 --- /dev/null +++ b/asoc/codecs/fs1815/fsm_misc.c @@ -0,0 +1,470 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-16 File created. + */ + +#if defined(CONFIG_FSM_MISC) +#include "fsm_public.h" +#ifdef CONFIG_FSM_NONDSP +#include "fsm_q6afe.h" +#endif +#include +#include +#include +#include +#include + +#define FSM_I2C_MAX_LEN (8192) + +/* + * misc driver for foursemi devices. + */ + +struct fsm_misc { + struct fsm_dev *fsm_dev[FSM_DEV_MAX]; + uint8_t addr; + uint8_t index; +}; + +static int g_misc_opened; + +static int fsm_misc_check_params(unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + if (cmd == FSM_IOC_SET_SLAVE) { + return 0; + } + if (_IOC_TYPE(cmd) != FSM_IOC_MAGIC || _IOC_NR(cmd) >= FSM_IOC_MAXNR) { + return -ENOTTY; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + if (_IOC_DIR(cmd) & _IOC_READ) { + ret = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + } else if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + } +#else + ret = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); +#endif + + return (ret ? -EFAULT : 0); +} + +static int fsm_misc_open(struct inode *inode, struct file *filp) +{ + struct fsm_misc *fsm_misc; + + pr_debug("enter"); + if (xchg(&g_misc_opened, 1)) { + pr_warning("device busy now"); + return -EBUSY; + } + fsm_misc = fsm_alloc_mem(sizeof(struct fsm_misc)); + if (fsm_misc == NULL) { + pr_err("allocat memory fail"); + return -EINVAL; + } + memset(fsm_misc, 0, sizeof(struct fsm_misc)); + filp->private_data = fsm_misc; + + return 0; +} + +static int fsm_misc_release(struct inode *inode, struct file *filp) +{ + struct fsm_misc *fsm_misc; + + pr_debug("enter"); + if (!filp) { + return -EINVAL; + } + fsm_misc = filp->private_data; + g_misc_opened = 0; + fsm_free_mem((void **)&fsm_misc); + + return 0; +} + +static int fsm_misc_set_slave(struct fsm_misc *fsm_misc, uint8_t slave) +{ + int index; + + if (fsm_misc == NULL) { + return -EINVAL; + } + if (slave < FSM_ADDR_BASE || slave >= FSM_ADDR_BASE + FSM_DEV_MAX) { + pr_err("invalid address:%02X", slave); + return -EINVAL; + } + index = slave - FSM_ADDR_BASE; + if (fsm_misc->fsm_dev[index] == 0) { + fsm_misc->fsm_dev[index] = fsm_get_fsm_dev(slave); + if (fsm_misc->fsm_dev[index] == NULL) { + pr_debug("not found device:%02X", slave); + fsm_misc->addr = 0; + fsm_misc->index = 0; + return -EINVAL; + } + } + fsm_misc->index = index; + fsm_misc->addr = slave; + + return 0; +} + +static int fsm_misc_cmd_with_args(unsigned int cmd, uint8_t addr, + struct fsm_misc_args *args) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg || !args) { + pr_err("bad parameters"); + return -EINVAL; + } + switch (cmd) { + case FSM_IOC_SET_SRATE: + fsm_set_i2s_clocks(args->srate, args->bclk); + break; + case FSM_IOC_SET_SCENE: + fsm_set_scene(args->scene); + break; + case FSM_IOC_INIT: + cfg->force_init = args->force_init; + fsm_init(); + cfg->force_init = 0; + break; + case FSM_IOC_CALIBRATE: + break; + case FSM_IOC_F0_TEST: + break; + default: + pr_err("invalid cmd: %X", cmd); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_FSM_NONDSP +int fsm_misc_send_apr(void __user *buf) +{ + uint32_t header[FSM_AFE_HEADER_LEN]; + uint32_t *ptr_data; + struct fsm_afe afe; + int buf_size; + void *buffer; + int ret; + + if (!buf) { + pr_err("bad parameter"); + return -EINVAL; + } + ret = copy_from_user(header, (void *)buf, sizeof(header)); + if (ret) { + pr_err("Copy from user failed! buf = 0x%pK", buf); + return ret; + } + /* buffer: + * [0]: buf_size + * [1]: cmd_size + * [2]: module_id + * [3]: param_id + * [4]: opcode + * [...]: data + */ + buf_size = header[0]; + if (buf_size <= 0) { + pr_err("Invalid buffer size = %d", buf_size); + return -EINVAL; + } + buffer = fsm_alloc_mem(buf_size); + afe.module_id = header[2]; + if (afe.module_id == AFE_MODULE_ID_FSADSP_TX) { + afe.port_id = fsm_afe_get_tx_port(); + } else { + afe.port_id = fsm_afe_get_rx_port(); + } + afe.param_id = header[3]; + ptr_data = (uint32_t *)buffer; + if (header[4]) { // op_set = 1 + ret = copy_from_user(buffer, (void *)buf, buf_size); + if (ret) { + pr_err("copy from user fail:%d", ret); + goto exit; + } + afe.op_set = true; + ret = fsm_afe_send_apr(&afe, &ptr_data[5], + (buf_size - FSM_AFE_HEADER_LEN * sizeof(uint32_t))); + if (ret) { + pr_err("set params fail:%d", ret); + goto exit; + } + } else { + afe.op_set = false; + ret = fsm_afe_send_apr(&afe, &ptr_data[5], + (buf_size - FSM_AFE_HEADER_LEN * sizeof(uint32_t))); + if (ret) { + pr_err("get params fail:%d", ret); + goto exit; + } + // set ptr_data[0] = total size + ptr_data[0] = afe.param_size + FSM_AFE_HEADER_LEN * sizeof(uint32_t); + ret = copy_to_user((void __user *)buf, buffer, ptr_data[0]); + if (ret) { + pr_err("copy to user fail:%d", ret); + goto exit; + } + } +exit: + fsm_free_mem((void **)&buffer); + + return ret; +} +#endif + +static long fsm_misc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct fsm_misc_args misc_args; + struct fsm_misc *fsm_misc; + int count; + int ret; + + if (filp == NULL || filp->private_data == NULL) { + pr_err("invalid parameter"); + return -EINVAL; + } + fsm_misc = filp->private_data; + ret = fsm_misc_check_params(cmd, arg); + if (ret) { + pr_err("invalid params: %X, %lX", cmd, arg); + return ret; + } + + pr_debug("cmd:%X, arg:%lX", cmd, arg); + switch (cmd) { + case FSM_IOC_SET_SLAVE: + case FSM_IOC_SET_ADDR: + ret = fsm_misc_set_slave(fsm_misc, arg & 0xFF); + break; + case FSM_IOC_GET_DEVICE: + count = fsm_dev_count(); + ret = copy_to_user((int *)arg, &count, sizeof(count)); + if (ret) { + pr_err("cmd:%X, copy to user fail:%d", cmd, ret); + return -EFAULT; + } + break; + case FSM_IOC_SET_SRATE: + case FSM_IOC_SET_SCENE: + case FSM_IOC_INIT: + case FSM_IOC_CALIBRATE: + case FSM_IOC_F0_TEST: + ret = copy_from_user(&misc_args, (void *)arg, sizeof(misc_args)); + if (ret) { + pr_err("cmd:%X, copy from user fail:%d", cmd, ret); + return -EFAULT; + } + ret = fsm_misc_cmd_with_args(cmd, fsm_misc->addr, &misc_args); + if (ret) { + pr_err("cmd:%X, error:%d", cmd, ret); + } + break; + case FSM_IOC_SPEAKER_ON: + fsm_speaker_onn(); + break; + case FSM_IOC_SPEAKER_OFF: + fsm_speaker_off(); + break; + case FSM_IOC_GET_RESULT: + break; +#ifdef CONFIG_FSM_NONDSP + case FSM_IOC_SEND_APR: + ret = fsm_misc_send_apr((void __user *)arg); + if (ret) { + pr_err("send apr fail:%d", ret); + } + break; +#endif + default: + pr_err("unknown cmd:%X", cmd); + ret = -EINVAL; + } + + return ret; +} + +#if defined(CONFIG_COMPAT) +static long fsm_misc_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case FSM_IOC_GET_RESULT32: + cmd = FSM_IOC_GET_RESULT; + break; +#ifdef CONFIG_FSM_NONDSP + case FSM_IOC_SEND_APR: + case FSM_IOC_SEND_APR32: + cmd = FSM_IOC_SEND_APR; + break; +#endif + default: + break; + } + return fsm_misc_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static ssize_t fsm_misc_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + fsm_config_t *cfg = fsm_get_config(); + struct fsm_misc *fsm_misc; + struct fsm_dev *fsm_dev; + int retries = FSM_I2C_RETRY; + uint8_t *tmp; + int ret; + + if (cfg == NULL || filp == NULL || filp->private_data == NULL) { + pr_err("invalid parameter"); + return -EINVAL; + } + if (count > FSM_I2C_MAX_LEN) { + count = FSM_I2C_MAX_LEN; + } + tmp = fsm_alloc_mem(count); + if (tmp == NULL) { + return -ENOMEM; + } + + if (cfg->skip_monitor == false) { + cfg->skip_monitor = true; + } + fsm_misc = filp->private_data; + do { + fsm_dev = fsm_misc->fsm_dev[fsm_misc->index]; + if (fsm_dev == NULL || fsm_dev->i2c == NULL) { + ret = -EINVAL; + break; + } + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_master_recv(fsm_dev->i2c, tmp, count); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != count) { + fsm_delay_ms(5); + } + // pr_debug("data: 0x%02x", tmp[0]); + } while((ret != count) && (--retries >= 0)); + + if (ret == count) { + ret = (copy_to_user(buf, tmp, count) ? -EFAULT : ret); + } else { + pr_err("%02X: reading %zu bytes failed:%d", + fsm_misc->addr, count, ret); + } + fsm_free_mem((void **)&tmp); + + return ret; +} + +static ssize_t fsm_misc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + fsm_config_t *cfg = fsm_get_config(); + struct fsm_misc *fsm_misc; + struct fsm_dev *fsm_dev; + int retries = FSM_I2C_RETRY; + uint8_t *tmp; + int ret; + + if (cfg == NULL || filp == NULL || filp->private_data == NULL) { + pr_err("invalid parameter"); + return -EINVAL; + } + if (count > FSM_I2C_MAX_LEN) { + count = FSM_I2C_MAX_LEN; + } + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) { + return PTR_ERR(tmp); + } + + if (cfg->skip_monitor == false) { + cfg->skip_monitor = true; + } + fsm_misc = filp->private_data; + do { + fsm_dev = fsm_misc->fsm_dev[fsm_misc->index]; + if (fsm_dev == NULL || fsm_dev->i2c == NULL) { + ret = -EINVAL; + break; + } + // pr_debug("data: 0x%02x-0x%02x", buf[0], tmp[0]); + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_master_send(fsm_dev->i2c, tmp, count); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != count) { + fsm_delay_ms(5); + } + } while((ret != count) && (--retries >= 0)); + + if (ret != count) { + pr_err("%02X: writing %zu bytes failed:%d", + fsm_misc->addr, count, ret); + } + kfree(tmp); + + return ret; +} + +static const struct file_operations g_fsm_misc_ops = { + .owner = THIS_MODULE, + .open = fsm_misc_open, + .read = fsm_misc_read, + .write = fsm_misc_write, + .release = fsm_misc_release, + .llseek = no_llseek, + .unlocked_ioctl = fsm_misc_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = fsm_misc_compat_ioctl, +#endif +}; + +struct miscdevice g_fsm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = FSM_DRV_NAME, + .fops = &g_fsm_misc_ops, + .this_device = NULL, +}; + +int fsm_misc_init(void) +{ + struct miscdevice *misc = &g_fsm_misc; + int ret; + + pr_debug("enter"); + if (misc->this_device) { + return 0; + } + ret = misc_register(misc); + if (ret) { + misc->this_device = NULL; + } + + FSM_FUNC_EXIT(ret); + return ret; +} + +void fsm_misc_deinit(void) +{ + struct miscdevice *misc = &g_fsm_misc; + + pr_debug("enter"); + if (misc->this_device == NULL) { + return; + } + misc_deregister(misc); +} +#endif diff --git a/asoc/codecs/fs1815/fsm_public.h b/asoc/codecs/fs1815/fsm_public.h new file mode 100644 index 000000000000..885744344658 --- /dev/null +++ b/asoc/codecs/fs1815/fsm_public.h @@ -0,0 +1,241 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-18 File created. + */ + +#ifndef __FSM_PUBLIC_H__ +#define __FSM_PUBLIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fsm-dev.h" +#include +#include + +/* + * module: fsm_regmap + */ +#if defined(CONFIG_FSM_REGMAP) +#include +int fsm_regmap_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_regmap_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *pval, uint32_t len); +int fsm_regmap_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval); +int fsm_regmap_bulkread(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *pval, uint32_t len); +int fsm_regmap_update_bits(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t mask, uint8_t val); +struct regmap *fsm_regmap_i2c_init(struct i2c_client *i2c); +int fsm_regmap_i2c_deinit(struct regmap *map); +#else +#define fsm_regmap_i2c_deinit(...) +#endif +/* + * module: fsm_i2c + */ +#if defined(CONFIG_FSM_I2C) +#include +bool fsm_set_pdev(struct device *dev); +struct device *fsm_get_pdev(void); +void *fsm_devm_kstrdup(struct device *dev, void *buf, size_t size); +int fsm_i2c_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pVal); +int fsm_i2c_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_i2c_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, + uint8_t *data, int len); +int fsm_get_amb_tempr(void); +int fsm_read_efsdata(struct fsm_calib_v2 *data); +int fsm_write_efsdata(struct fsm_calib_v2 *data); +int fsm_i2c_save_re25(struct fsm_calib_v2 *calib_data); +int fsm_i2c_init(void); +void fsm_i2c_exit(void); +#else +#define fsm_get_amb_tempr(...) (FSM_DFT_AMB_TEMPR) +#endif + +void fsm_mutex_lock(void); +void fsm_mutex_unlock(void); + +/* + * module: fsm_misc + */ +#if defined(CONFIG_FSM_MISC) +int fsm_misc_init(void); +void fsm_misc_deinit(void); +#else +#define fsm_misc_init(...) +#define fsm_misc_deinit(...) +#endif + +/* + * module: fsm_sysfs + */ +#if defined(CONFIG_FSM_SYSFS) +int fsm_sysfs_init(struct device *dev); +void fsm_sysfs_deinit(struct device *dev); +#else +#define fsm_sysfs_init(...) +#define fsm_sysfs_deinit(...) +#endif + +/* + * module: fsm_firmware + */ +#if defined(CONFIG_FSM_FIRMWARE) +int fsm_firmware_init(char *fw_name); +int fsm_firmware_init_sync(char *fw_name); +void fsm_firmware_deinit(void); +#elif defined(FSM_HAL_SUPPORT) +int fsm_firmware_init_sync(char *fw_name); +void fsm_firmware_deinit(void); +#else +#define fsm_firmware_init(...) (-1) +#define fsm_firmware_init_sync(...) (-1) +#define fsm_firmware_deinit(...) +#endif + +/* + * module: fsm_codec + */ +#if defined(CONFIG_FSM_CODEC) +int fsm_codec_register(struct device *dev, int id); +void fsm_codec_unregister(struct device *dev); +#else +#define fsm_codec_register(...) +#define fsm_codec_unregister(...) +#endif + +/* + * module: fsm_core + */ +fsm_config_t *fsm_get_config(void); +void *fsm_get_presets(void); +void fsm_set_presets(void *preset); +void fsm_get_version(fsm_version_t *version); +void fsm_delay_ms(uint32_t delay_ms); +void *fsm_alloc_mem(int size); +void fsm_free_mem(void **buf); +uint16_t set_bf_val(uint16_t *pval, const uint16_t bf, const uint16_t bf_val); +uint16_t get_bf_val(const uint16_t bf, const uint16_t val); +int fsm_set_bf(fsm_dev_t *fsm_dev, const uint16_t bf, const uint16_t val); +int fsm_get_bf(fsm_dev_t *fsm_dev, const uint16_t bf, uint16_t *pval); +int fsm_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval); +int fsm_reg_update_bits(fsm_dev_t *fsm_dev, reg_unit_t *reg); +int fsm_reg_update(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_reg_multiread(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval); +int fsm_burst_write(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *data, int len); +uint16_t fsm_calc_checksum(uint16_t *data, int len); +struct fsm_dev *fsm_get_fsm_dev(uint8_t addr); +struct fsm_dev *fsm_get_fsm_dev_by_id(int id); +int fsm_read_vbat(fsm_dev_t *fsm_dev, uint16_t *vbat); +int fsm_get_srate_bits(fsm_dev_t *fsm_dev, uint32_t srate); +int fsm_access_key(fsm_dev_t *fsm_dev, int access); +int fsm_reg_dump(fsm_dev_t *fsm_dev); +int fsm_init_dev_list(fsm_dev_t *fsm_dev); +int fsm_init_info(fsm_dev_t *fsm_dev); +void *fsm_get_list_by_idx(fsm_dev_t *fsm_dev, int idx); +void *fsm_get_data_list(fsm_dev_t *fsm_dev, int type); +int fsm_get_spk_info(fsm_dev_t *fsm_dev, uint16_t info_type); +int zero_bit_counter(uint8_t byte); +int get_otp_counter(uint16_t byte); +int get_otp_max_count(fsm_dev_t *fsm_dev); +void convert_data_to_bytes(uint32_t val, uint8_t *buf); +int get_re25_from_buf(struct fsm_calib_v2 *data, + char *buf, int buf_len); +int set_re25_to_buf(struct fsm_calib_v2 *data, + char *buf, int buf_len); +int fsm_parse_preset(const void *data, uint32_t size); +int fsm_write_preset(fsm_dev_t *fsm_dev); +int fsm_swap_channel(fsm_dev_t *fsm_dev, int next_angle); +int fsm_set_monitor(fsm_dev_t *fsm_dev, bool enable); +int fsm_wait_stable(fsm_dev_t *fsm_dev, int type); +int fsm_parse_otp(fsm_dev_t *fsm_dev, uint16_t value, int *re25, int *count); +int fsm_config_vol(fsm_dev_t *fsm_dev); +int fsm_dev_recover(fsm_dev_t *fsm_dev); +int fsm_get_spkr_tempr(fsm_dev_t *fsm_dev); +int fsm_ops_dummy(fsm_dev_t *fsm_dev); +int fsm_write_reg_tbl(fsm_dev_t *fsm_dev, uint16_t scene); +int fsm_set_cal_scene(fsm_dev_t *fsm_dev); +int fsm_get_livedata(fsm_msg_t *data); +int fsm_save_re25(struct fsm_calib_v2 *data); +int fsm_get_re25(struct fsm_calib_v2 *data); +uint32_t fsm_cal_spkr_zmimp(fsm_dev_t *fsm_dev, uint16_t data); +int fsm_skip_device(fsm_dev_t *fsm_dev); +int fsm_cal_zmdata(fsm_dev_t *fsm_dev); +int fsm_get_rstrim(fsm_dev_t *fsm_dev); +uint16_t fsm_cal_threshold(fsm_dev_t *fsm_dev, + int mt_type, uint16_t zmdata); +int fsm_check_otp(fsm_dev_t *fsm_dev); +int fsm_check_without_otp(fsm_dev_t *fsm_dev); +int fsm_stub_dev_init(fsm_dev_t *fsm_dev); +int fsm_stub_start_up(fsm_dev_t *fsm_dev); +int fsm_stub_check_stable(fsm_dev_t *fsm_dev, int type); +int fsm_stub_store_re25(fsm_dev_t *fsm_dev); +int fsm_stub_pre_calib(fsm_dev_t *fsm_dev); +int fsm_stub_post_calib(fsm_dev_t *fsm_dev); +int fsm_stub_set_mute(fsm_dev_t *fsm_dev, bool mute); +int fsm_stub_set_tsignal(fsm_dev_t *fsm_dev, bool enable); +int fsm_stub_shut_down(fsm_dev_t *fsm_dev); +int fsm_stub_dev_deinit(fsm_dev_t *fsm_dev); + +/* + * module: fsm_hal + */ +#if defined(FSM_HAL_SUPPORT) +int fsm_open_device(void); +void fsm_close_device(void); +int fsm_check_device(char *dev_name); +int fsm_hal_open(void); +int fsm_hal_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *val); +int fsm_hal_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_hal_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *val, uint32_t len); +int fsm_hal_bulkread(fsm_dev_t *fsm_dev, uint8_t reg, uint32_t len, uint8_t *val); +int fsm_hal_get_livedata(fsm_msg_t *data); +int fsm_hal_save_re25(struct fsm_calib_v2 *data); +int fsm_hal_get_re25(struct fsm_calib_v2 *data); +int fstool_reg_read(uint8_t addr, uint8_t reg, uint16_t *val); +int fstool_reg_write(uint8_t addr, uint8_t reg, uint16_t val); +int fstool_bulkwrite(uint8_t addr, uint8_t reg, + uint32_t len, uint8_t *val); +int fstool_bulkread(uint8_t addr, uint8_t reg, + uint32_t len, uint8_t *val); +void fsm_hal_close(void); +#else +#define fsm_hal_open(...) (0) +#define fsm_hal_close(...) +#endif + +void fsm_set_fw_name(char *name); +int fsm_probe(fsm_dev_t *fsm_dev, int addr); +void fsm_remove(fsm_dev_t *fsm_dev); +int fsm_dev_count(void); +void fsm_set_i2s_clocks(uint32_t rate, uint32_t bclk); +void fsm_set_tx_format(uint32_t format); +void fsm_set_scene(int scene); +void fsm_set_volume(int volume); +void fsm_set_cfg_flag(int pos, int mark); +void fsm_init(void); +void fsm_speaker_onn(void); +void fsm_speaker_off(void); +void fsm_stereo_rotation(int next_angle); +void fsm_batv_monitor(void); +void fsm_re25_test(bool force); +void fsm_f0_test(void); +int fsm_test_result(struct fsm_cal_result *result, int size); +void fsm_dump(void); +void fsm_deinit(void); + +void fs1815_ops(fsm_dev_t *fsm_dev); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +#define snd_soc_codec snd_soc_component +#define snd_soc_add_codec_controls snd_soc_add_component_controls +#define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata +#endif +void fsm_add_codec_controls(struct snd_soc_codec *codec); +void fsm_add_card_controls(struct snd_soc_card *card); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/asoc/holi.c b/asoc/holi.c index 5351e2893a98..2c0387557c0c 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -50,6 +50,10 @@ extern int aw87xxx_add_codec_controls(void *codec); #endif +#ifdef CONFIG_SND_SOC_FS1815 + extern int fsm_add_codec_controls(struct snd_soc_component *codec); +#endif + #define SAMPLING_RATE_8KHZ 8000 #define SAMPLING_RATE_11P025KHZ 11025 #define SAMPLING_RATE_16KHZ 16000 @@ -6444,6 +6448,15 @@ static int msm_int_audrx_init(struct snd_soc_pcm_runtime *rtd) }; #endif +#ifdef CONFIG_SND_SOC_FS1815 + ret = fsm_add_codec_controls(component); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, ret %d\n", + __func__, ret); + return ret; + }; +#endif + snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets, ARRAY_SIZE(msm_int_dapm_widgets)); From b1bdb94eb1afd4fd1c9d7f9ed0fc2d2485c9bba1 Mon Sep 17 00:00:00 2001 From: Yunxian He Date: Tue, 20 Jun 2023 11:19:51 +0800 Subject: [PATCH 61/79] Awinic: update communicate with adsp logic main changes: if ADSP is not ready, do not read/write adsp Change-Id: Iec2e44f2d05e5fce19884a2d1e740734b927f1d2 Signed-off-by: Yunxian He Reviewed-on: https://gerrit.mot.com/2649049 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw87xxx/aw87xxx_dsp.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/asoc/codecs/aw87xxx/aw87xxx_dsp.c b/asoc/codecs/aw87xxx/aw87xxx_dsp.c index a699033f0b9a..f623e62ec297 100644 --- a/asoc/codecs/aw87xxx/aw87xxx_dsp.c +++ b/asoc/codecs/aw87xxx/aw87xxx_dsp.c @@ -215,7 +215,7 @@ failed: /******************qcom dsp communication function start**********************/ #ifdef AW_QCOM_OPEN_DSP_PLATFORM -static void aw_check_dsp_ready(void) +static int aw_check_dsp_ready(void) { int ret; @@ -224,19 +224,23 @@ static void aw_check_dsp_ready(void) if (ret != g_rx_topo_id) AW_LOGE("topo id 0x%x", ret); + return ret; } static int aw_qcom_write_data_to_dsp(int32_t param_id, void *data, int data_size) { - int ret = 0; + int ret = -1; + int topo = -1; AW_LOGI("enter"); mutex_lock(&g_dsp_lock); - aw_check_dsp_ready(); - ret = aw_send_afe_cal_apr(param_id, data, - data_size, true); + topo = aw_check_dsp_ready(); + if (topo != 0) + ret = aw_send_afe_cal_apr(param_id, data, + data_size, true); + mutex_unlock(&g_dsp_lock); return ret; } @@ -244,12 +248,14 @@ static int aw_qcom_write_data_to_dsp(int32_t param_id, static int aw_qcom_read_data_from_dsp(int32_t param_id, void *data, int data_size) { - int ret = 0; + int ret = -1; + int topo = -1; AW_LOGI("enter"); mutex_lock(&g_dsp_lock); - aw_check_dsp_ready(); - ret = aw_send_afe_cal_apr(param_id, data, + topo = aw_check_dsp_ready(); + if (topo != 0) + ret = aw_send_afe_cal_apr(param_id, data, data_size, false); mutex_unlock(&g_dsp_lock); return ret; From 6505e846cccd589811cd0400be740d65af3a65ae Mon Sep 17 00:00:00 2001 From: liaohj Date: Wed, 5 Jul 2023 16:42:41 +0800 Subject: [PATCH 62/79] audio:add fs1815 bypass algo code and logic. fogor use fs1815. need set fsadsp algo on/off,rotation,battery and so. add related code to support. Change-Id: Ie7a23814a00ee0962f7e9c20782974162d4374f0 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2666214 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/fs1815/fsm_core.c | 68 +++ dsp/Kbuild | 4 + dsp/q6afe.c | 39 ++ include/dsp/q6fsm-v3.h | 773 ++++++++++++++++++++++++++++++++++ 4 files changed, 884 insertions(+) create mode 100644 include/dsp/q6fsm-v3.h diff --git a/asoc/codecs/fs1815/fsm_core.c b/asoc/codecs/fs1815/fsm_core.c index 0ad45c1f2c83..f532a7037b7f 100644 --- a/asoc/codecs/fs1815/fsm_core.c +++ b/asoc/codecs/fs1815/fsm_core.c @@ -13,6 +13,12 @@ static int fsm_try_init(void); static int g_dev_inited = 0; static atomic_t fsm_amp_switch; +static atomic_t fsm_adsp_enable; + + +//include "q6fsm-v3.h" +extern int q6fsm_set_rotation(int angle); +extern int q6fsm_set_rx_enable(int enable); static LIST_HEAD(fsm_dev_list); #define fsm_list_init(fsm_dev) \ @@ -1613,6 +1619,64 @@ int fsm_init_get(struct snd_kcontrol *kcontrol, return 0; } +int fsm_rotation_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_config_t *cfg = fsm_get_config(); + if (!cfg) { + ucontrol->value.integer.value[0] = -1; + return 0; + } + + ucontrol->value.integer.value[0] = cfg->next_angle; + + return 0; +} + +int fsm_rotation_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_config_t *cfg = fsm_get_config(); + int angle = ucontrol->value.integer.value[0]; + + if(!cfg){ + pr_err("cfg is null"); + return 0; + } + + pr_info("angle: %d", angle); + if(q6fsm_set_rotation(angle) == 0){ + cfg->next_angle = angle; + } + + return 0; +} + +int fsm_adsp_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = atomic_read(&fsm_adsp_enable); + + ucontrol->value.integer.value[0] = enable; + pr_info("get: %s", (enable) ? "Enable" : "Disable"); + + return 0; +} + +int fsm_adsp_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_info("switch: %s", enable ? "Enable" : "Disable"); + + if(q6fsm_set_rx_enable(enable) == 0){ + atomic_set(&fsm_adsp_enable, enable); + } + + return 0; +} + const struct snd_kcontrol_new fsm_snd_controls[] = { SOC_SINGLE_EXT("FSM_Scene", SND_SOC_NOPM, 0, FSM_SCENE_MAX, 0, @@ -1621,6 +1685,10 @@ const struct snd_kcontrol_new fsm_snd_controls[] = fsm_amp_switch_get, fsm_amp_switch_put), SOC_SINGLE_EXT("FSM_Init", SND_SOC_NOPM, 0, 1, 0, fsm_init_get, fsm_init_put), + SOC_SINGLE_EXT("FSM_Rotation", SND_SOC_NOPM, 0, 360, 0, + fsm_rotation_get, fsm_rotation_put), + SOC_SINGLE_EXT("FSM_ADSP_Enable", SND_SOC_NOPM, 0, 1, 0, + fsm_adsp_enable_get, fsm_adsp_enable_put), }; void fsm_add_codec_controls(struct snd_soc_codec *codec) diff --git a/dsp/Kbuild b/dsp/Kbuild index a3468fb4220d..f7fc98567492 100755 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -27,6 +27,10 @@ ifdef CONFIG_SND_SOC_AW87XXX CDEFINES += -DCONFIG_SND_SOC_AW87XXX endif +ifdef CONFIG_SND_SOC_FS1815 +CDEFINES += -DCONFIG_SND_SOC_FS1815 +endif + ifeq ($(CONFIG_SND_SOC_AUTO), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf diff --git a/dsp/q6afe.c b/dsp/q6afe.c index f71bc241f6a2..09d10bdb1356 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -24,6 +24,12 @@ #include "adsp_err.h" #include "q6afecal-hwdep.h" +#ifdef CONFIG_SND_SOC_FS1815 +#include +#endif + + + #ifdef CONFIG_SND_SOC_AWINIC_AW882XX #define AFE_MODULE_ID_AWDSP_TX (0x10013D00) #define AFE_MODULE_ID_AWDSP_RX (0x10013D01) @@ -1159,6 +1165,18 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return 0; } #endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ + +#ifdef CONFIG_SND_SOC_FS1815 + if (!q6fsm_afe_callback(data->payload, data->payload_size)) { + atomic_set(&this_afe.state, 0); + if (afe_token_is_valid(data->token)) { + wake_up(&this_afe.wait[data->token]); + return 0; + } else { + return -EINVAL; + } + } +#endif if (rtac_make_afe_callback(data->payload, data->payload_size)) return 0; @@ -4645,6 +4663,16 @@ static int afe_send_cmd_port_start(u16 port_id) pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__, port_id, ret); +#ifdef CONFIG_SND_SOC_FS1815 + //TODO: + // Set the monitor enable when: + // 1) Speaker PA type is matched + // 2) In speaker playback + // 3) The RX Port is the same as the one of speaker playback + if (port_id == Q6FSM_AFE_RX_PORT) + q6fsm_monitor_switch(true); +#endif + return ret; } @@ -9700,6 +9728,11 @@ int afe_close(int port_id) memset(&stop, 0, sizeof(stop)); +#ifdef CONFIG_SND_SOC_FS1815 + if (port_id == Q6FSM_AFE_RX_PORT) + q6fsm_monitor_switch(false); +#endif + if (this_afe.apr == NULL) { pr_err("%s: AFE is already closed\n", __func__); @@ -12344,11 +12377,17 @@ int __init afe_init(void) this_afe.event_notifier.notifier_call = afe_aud_event_notify; msm_aud_evt_blocking_register_client(&this_afe.event_notifier); +#ifdef CONFIG_SND_SOC_FS1815 + q6fsm_afe_init(); +#endif return 0; } void afe_exit(void) { +#ifdef CONFIG_SND_SOC_FS1815 + q6fsm_afe_deinit(); +#endif if (this_afe.apr) { apr_reset(this_afe.apr); atomic_set(&this_afe.state, 0); diff --git a/include/dsp/q6fsm-v3.h b/include/dsp/q6fsm-v3.h new file mode 100644 index 000000000000..1a6667ec8290 --- /dev/null +++ b/include/dsp/q6fsm-v3.h @@ -0,0 +1,773 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/** + * Copyright (C) Shanghai FourSemi Semiconductor Co.,Ltd 2016-2022. All rights reserved. + * 2022-12-23 File created. + */ + +#ifndef __Q6FSM_V3_H__ +#define __Q6FSM_V3_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define fsm_info(fmt, args...) pr_info("%s: " fmt "\n", __func__, ##args) +#define fsm_dbg(fmt, args...) pr_debug("%s: " fmt "\n", __func__, ##args) +#define fsm_err(fmt, args...) pr_err("%s: " fmt "\n", __func__, ##args) + +#define Q6FSM_VERSION "v1.1.1" + +// TODO update +#define Q6FSM_AFE_RX_PORT (AFE_PORT_ID_RX_CODEC_DMA_RX_0) +#define Q6FSM_AFE_TX_PORT (AFE_PORT_ID_TX_CODEC_DMA_TX_3) + +#define Q6FSM_CHUNK_SIZE (256) + +// FSADSP MODULE ID +#define AFE_MODULE_ID_FSADSP_TX (0x10001110) +#define AFE_MODULE_ID_FSADSP_RX (0x10001111) +// FSADSP PARAM ID +#define CAPI_V2_PARAM_FSADSP_TX_ENABLE 0x11111601 +#define CAPI_V2_PARAM_FSADSP_RX_ENABLE 0x11111611 +#define CAPI_V2_PARAM_FSADSP_MODULE_ENABLE 0x10001FA1 +#define CAPI_V2_PARAM_FSADSP_ROTATION 0x10001FB1 +#define CAPI_V2_PARAM_FSADSP_BSG_VBAT 0x10001FB2 +#define CAPI_V2_PARAM_FSADSP_BSGV2_PARAM 0x10001FB5 //set +#define CAPI_V2_PARAM_FSADSP_BSGV2_CONFIG 0x10001FB6 //get +#define CAPI_V2_PARAM_FSADSP_ROTATION 0x10001FB1 +#define CAPI_V2_PARAM_FSADSP_FADE 0x10001FB7 + +#define Q6FSM_RETRY_TIME (3) +#define Q6FSM_SLEEP_TIME (10) + +struct bsg_config_v2 { + int bsg_mode; + int bsg_enable; + int bsg_interval; + int tc_mode; + int tc_mode_enable; + int tc_interval; +}; + +struct bsg_param_v2 { + int bsg_mode; + int bsg_val; + int tc_mode; + int tc_val; +}; + +struct q6fsm_afe { + struct workqueue_struct *vbat_wq; + struct delayed_work vbat_work; + struct power_supply *psy; + struct mutex cmd_lock; + atomic_t bsg_switch; + unsigned long delay_period; + bool monitor_en; + int bsg_version; + struct bsg_config_v2 config; + int angle; +}; + +typedef enum _fs__chann_t +{ + FS_CH_LEFT = 1 << 0, + FS_CH_RIGHT = 1 << 1, + FS_CH_MONO = (FS_CH_LEFT|FS_CH_RIGHT), +}fs_chann_t; + +typedef struct _fsm_fade_info_t +{ + int fade_type; //0x10 mute, 0x11 fade in,0x12 fade out + int fade_time; // fade length time, unit is ms + int start_db; + fs_chann_t ch; // left or right channel +}fsm_fade_info_t; + +typedef struct _fsm_rotation_info_t +{ + int angle; // 0,90,180,270 + int8_t ch_sequence[8]; +}fsm_rotation_info_t; + + +static struct q6fsm_afe *g_fsm_afe; +static uint32_t *q6fsm_get_buffer; + +static int q6afe_set_params(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size); +static int q6afe_get_params(u16 port_id, + struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr); + +static int q6fsm_afe_callback(void *payload, int size) +{ + uint32_t *payload32 = payload; + + if (payload32[0] != 0) { + pr_err("invalid status: %d", payload32[0]); + return -EINVAL; + } + + fsm_dbg("payload size: %d", size); + switch (payload32[1]) { + case AFE_MODULE_ID_FSADSP_TX: + case AFE_MODULE_ID_FSADSP_RX: + memcpy(q6fsm_get_buffer, payload32 + 1, size - 4); + break; + default: + return -EINVAL; + } + + return 0; +} + +static bool q6fsm_check_dsp_ready(void) +{ + int topo_id; + + topo_id = afe_get_topology(Q6FSM_AFE_RX_PORT); + fsm_dbg("topo_id: 0x%x", topo_id); + + if (topo_id <= 0) + return false; + else + return true; +} + +static int q6fsm_afe_set_params(struct param_hdr_v3 *param_hdr, + uint8_t *params, uint32_t param_size) +{ + u8 *packed_param_data; + int packed_data_size; + int port_id, index; + int retry = 0; + int ret; + + if (param_hdr == NULL || params == NULL) { + fsm_err("Bad parameters"); + return -EINVAL; + } + + if (param_size >= Q6FSM_CHUNK_SIZE) { + fsm_err("Invalid param_size: %d\n", param_size); + return -EINVAL; + } + + if (param_hdr->module_id == AFE_MODULE_ID_FSADSP_RX) + port_id = Q6FSM_AFE_RX_PORT; + else + port_id = Q6FSM_AFE_TX_PORT; + + index = afe_get_port_index(port_id); + if (index < 0) { + fsm_err("invalid port: %x", port_id); + return -EINVAL; + } + + mutex_lock(&g_fsm_afe->cmd_lock); + packed_data_size = sizeof(union param_hdrs) + param_size; + packed_param_data = kzalloc(packed_data_size, GFP_KERNEL); + if (packed_param_data == NULL) { + fsm_err("alloc memory failed!"); + mutex_unlock(&g_fsm_afe->cmd_lock); + return -ENOMEM; + } + + do { + ret = q6common_pack_pp_params(packed_param_data, param_hdr, + params, &packed_data_size); + if (ret) { + fsm_err("Failed to pack data: %d", ret); + break; + } + while (retry++ < Q6FSM_RETRY_TIME) { + if (q6fsm_check_dsp_ready()) { + ret = q6afe_set_params(port_id, index, NULL, + packed_param_data, packed_data_size); + break; + } + msleep(Q6FSM_SLEEP_TIME); + ret = -ETIMEDOUT; + } + } while (0); + if (ret) + fsm_err("Failed to set params: %d", ret); + + mutex_unlock(&g_fsm_afe->cmd_lock); + kfree(packed_param_data); + packed_param_data = NULL; + + return ret; +} + +static int q6fsm_afe_get_params(struct param_hdr_v3 *param_hdr, + uint8_t *params, uint32_t length) +{ + int port_id; + int retry = 0; + int ret; + + if (param_hdr == NULL || params == NULL) { + fsm_err("Bad parameters"); + return -EINVAL; + } + + mutex_lock(&g_fsm_afe->cmd_lock); + q6fsm_get_buffer = kzalloc(length + 16, GFP_KERNEL); + if (q6fsm_get_buffer == NULL) { + fsm_err("Memory allocation failed!"); + mutex_unlock(&g_fsm_afe->cmd_lock); + return -ENOMEM; + } + + if (param_hdr->module_id == AFE_MODULE_ID_FSADSP_RX) + port_id = Q6FSM_AFE_RX_PORT; + else + port_id = Q6FSM_AFE_TX_PORT; + + do { + while (retry++ < Q6FSM_RETRY_TIME) { + if (q6fsm_check_dsp_ready()) { + ret = q6afe_get_params(port_id, NULL, param_hdr); + break; + } + msleep(Q6FSM_SLEEP_TIME); + ret = -ETIMEDOUT; + } + if (!ret) + memcpy(params, &q6fsm_get_buffer[4], length); + else + fsm_err("Get params failed: %d", ret); + } while (0); + + kfree(q6fsm_get_buffer); + q6fsm_get_buffer = NULL; + mutex_unlock(&g_fsm_afe->cmd_lock); + + return ret; +} + +bool q6fsm_get_rx_status(void) +{ + struct param_hdr_v3 param_hdr; + int enable; + int ret; + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(enable); + + ret = q6fsm_afe_get_params(¶m_hdr, + (uint8_t *)&enable, sizeof(enable)); + if (ret) { + fsm_err("Get params failed: %d", ret); + return false; + } + + return !!enable; +} +EXPORT_SYMBOL(q6fsm_get_rx_status); + +int q6fsm_set_rx_enable(int enable) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + struct param_hdr_v3 param_hdr; + uint32_t param; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + if (!q6fsm_get_rx_status()) { + fsm_info("RX module isn't ready"); + return -EINVAL; + } + + param = (uint32_t)enable; + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(int); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)¶m, sizeof(param)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(q6fsm_set_rx_enable); + +int q6fsm_set_rotation(int angle) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + struct param_hdr_v3 param_hdr; + fsm_rotation_info_t param = {0}; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + if (!q6fsm_get_rx_status()) { + fsm_info("RX module isn't ready"); + return -EINVAL; + } + + if(q6fsm->angle == angle){ + fsm_info("no need to rotation angle"); + return 0; + } + + if(angle == 0){ + param.ch_sequence[0] = 0; + param.ch_sequence[1] = 1; + }else if(angle == 90){ + param.ch_sequence[0] = 1; + param.ch_sequence[1] = 0; + }else{ + fsm_err("not support angle:%d",angle); + return -EINVAL; + } + param.angle = angle; + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_ROTATION; + param_hdr.param_size = sizeof(int); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)¶m, sizeof(param)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(q6fsm_set_rotation); + +int q6fsm_get_rotation(int *angle) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + + *angle = q6fsm->angle; + + return 0; +} +EXPORT_SYMBOL(q6fsm_get_rotation); + + +#define PSY_DESC psy->desc + +static int q6fsm_get_batv(struct q6fsm_afe *q6fsm, uint32_t *batv) +{ + union power_supply_propval prop = { 0 }; + struct power_supply *psy; + + if (q6fsm == NULL || batv == NULL) { + fsm_err("Bad paramters"); + return -EINVAL; + } + + q6fsm->psy = power_supply_get_by_name("battery"); + if (q6fsm->psy == NULL) { + fsm_err("get power supply failed"); + return -EINVAL; + } + + psy = q6fsm->psy; + if (PSY_DESC == NULL || PSY_DESC->get_property == NULL) { + fsm_err("Power desc/get_property is null!"); + return -EINVAL; + } + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + *batv = prop.intval; + fsm_dbg("voltage: %d", *batv); + + return 0; +} + +static int q6fsm_get_bsg_config(struct q6fsm_afe *q6fsm) +{ + struct param_hdr_v3 param_hdr; + struct bsg_config_v2 *config; + int interval; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + if (q6fsm->delay_period > 0) + return 0; + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_BSGV2_CONFIG; + param_hdr.param_size = sizeof(q6fsm->config); + ret = q6fsm_afe_get_params(¶m_hdr, + (uint8_t *)&q6fsm->config, sizeof(q6fsm->config)); + if (ret) { + fsm_err("Get params fail:%d", ret); + return ret; + } + + config = &q6fsm->config; + if (!config->bsg_enable && !config->tc_mode_enable) { + fsm_info("BSG/CSG is disabled"); + return 0x1000; /* Not enabled, stop the monitor */ + } + + if (config->bsg_enable) + interval = config->bsg_interval; + if (config->tc_mode_enable) + interval = config->tc_interval; + + fsm_info("interval: %d", interval); + q6fsm->delay_period = interval*HZ/1000; + + return 0; +} + +static int q6fsm_gen_bsg_param(struct q6fsm_afe *q6fsm, + struct bsg_param_v2 *param) +{ + union power_supply_propval prop = { 0 }; + struct power_supply *psy; + + if (q6fsm == NULL || param == NULL) { + fsm_err("Bad paramters"); + return -EINVAL; + } + + q6fsm->psy = power_supply_get_by_name("battery"); + if (q6fsm->psy == NULL) { + fsm_err("get power supply failed"); + return -EINVAL; + } + + psy = q6fsm->psy; + if (PSY_DESC == NULL || PSY_DESC->get_property == NULL) { + fsm_err("Power desc/get_property is null!"); + return -EINVAL; + } + + param->bsg_mode = q6fsm->config.bsg_mode; + if (param->bsg_mode == 1) { + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + param->bsg_val = prop.intval; + } else if (param->bsg_mode == 2) { + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + param->bsg_val = prop.intval; + } else { + param->bsg_val = 0; + } + + param->tc_mode = q6fsm->config.tc_mode; + if (param->tc_mode == 3) { + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_TEMP, &prop); + param->tc_val = prop.intval; + } else { + param->tc_val = 0; + } + + fsm_dbg("bsg:%d-%d, tc:%d-%d", + param->bsg_mode, param->bsg_val, + param->tc_mode, param->tc_val); + + return 0; +} + +static int q6fsm_vbat_monitor_work_v2(struct q6fsm_afe *q6fsm) +{ + struct param_hdr_v3 param_hdr; + struct bsg_param_v2 param; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + ret = q6fsm_get_bsg_config(q6fsm); + if (ret) { + if (ret != 0x1000) + fsm_err("Get bsg config fail:%d", ret); + return ret; + } + + ret = q6fsm_gen_bsg_param(q6fsm, ¶m); + if (ret) { + fsm_err("Get batt voltage fail:%d", ret); + return ret; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_BSGV2_PARAM; + param_hdr.param_size = sizeof(param); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)¶m, sizeof(param)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} + +static int q6fsm_vbat_monitor_work(struct q6fsm_afe *q6fsm) +{ + struct param_hdr_v3 param_hdr; + uint32_t batv; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + if (!q6fsm_get_rx_status()) { + fsm_info("RX module isn't ready"); + return 0; + } + + if (q6fsm->bsg_version == 2) { + ret = q6fsm_vbat_monitor_work_v2(q6fsm); + if (!ret || ret == 0x1000) + return ret; + } + + q6fsm->bsg_version = 1; + q6fsm->delay_period = 2*HZ; + + ret = q6fsm_get_batv(q6fsm, &batv); + if (ret) { + fsm_err("Get batt voltage fail:%d", ret); + return ret; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_BSG_VBAT; + param_hdr.param_size = sizeof(batv); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)&batv, sizeof(batv)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} + +static void q6fsm_vbat_monitor(struct work_struct *work) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + int ret; + + if (q6fsm == NULL) + return; + + ret = q6fsm_vbat_monitor_work(q6fsm); + if (ret) + return; + + /* reschedule */ + queue_delayed_work(q6fsm->vbat_wq, + &q6fsm->vbat_work, q6fsm->delay_period); +} + +static void q6fsm_monitor_switch(bool enable) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + + if (q6fsm == NULL) + return; + + fsm_info("%s", enable ? "enable" : "disable"); + if (enable) { + if (q6fsm->monitor_en) + return; + q6fsm->monitor_en = true; + queue_delayed_work(q6fsm->vbat_wq, &q6fsm->vbat_work, 0); + } else { + if (!q6fsm->monitor_en) + return; + q6fsm->monitor_en = false; + q6fsm->delay_period = 0; + cancel_delayed_work_sync(&q6fsm->vbat_work); + } +} + +static ssize_t q6fsm_rx_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + struct param_hdr_v3 param_hdr; + int len, enable; + int ret; + + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(enable); + + ret = q6fsm_afe_get_params(¶m_hdr, + (uint8_t *)&enable, sizeof(enable)); + if (ret) { + fsm_err("Get params failed: %d", ret); + return ret; + } + len = scnprintf(buf, PAGE_SIZE, "%s\n", + enable ? "On" : "Off"); + + return len; +} + +static ssize_t q6fsm_rx_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct param_hdr_v3 param_hdr; + int enable; + int ret = -1; + + if (buf == NULL) + return -ENOMEM; + + enable = simple_strtoul(buf, NULL, 0); + + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return ret; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(enable); + enable = !!enable; + + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)&enable, sizeof(enable)); + if (ret) { + fsm_err("Set params failed: %d", ret); + return ret; + } + + return len; +} + +static ssize_t q6fsm_bsg_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + int len; + + if (q6fsm == NULL) + return -EINVAL; + + len = scnprintf(buf, PAGE_SIZE, "%s\n", + q6fsm->monitor_en ? "On" : "Off"); + + return len; +} + +static ssize_t q6fsm_bsg_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + int enable; + + if (buf == NULL) + return -ENOMEM; + + enable = simple_strtoul(buf, NULL, 0); + q6fsm_monitor_switch(!!enable); + + return len; +} + +static CLASS_ATTR_RW(q6fsm_rx); +static CLASS_ATTR_RW(q6fsm_bsg); + +static struct attribute *q6fsm_class_attrs[] = { + &class_attr_q6fsm_rx.attr, + &class_attr_q6fsm_bsg.attr, + NULL, +}; +ATTRIBUTE_GROUPS(q6fsm_class); + +/** Device model classes */ +struct class g_q6fsm_class = { + .name = "q6fsm", + .class_groups = q6fsm_class_groups, +}; + +static int __init q6fsm_afe_init(void) +{ + struct q6fsm_afe *q6fsm; + + fsm_info("version: %s", Q6FSM_VERSION); + + q6fsm = kzalloc(sizeof(struct q6fsm_afe), GFP_KERNEL); + if (q6fsm == NULL) { + fsm_err("alloc memery failed"); + return -ENOMEM; + } + + mutex_init(&q6fsm->cmd_lock); + atomic_set(&q6fsm->bsg_switch, 0); + + /* BSG V1 */ + //q6fsm->delay_period = 2*HZ; + //q6fsm->bsg_version = 1; + /* BSG V2 */ + q6fsm->delay_period = 0; /* Get from adsp */ + q6fsm->bsg_version = 2; + q6fsm->monitor_en = false; + q6fsm->vbat_wq = create_singlethread_workqueue("q6fsm-vbat"); + INIT_DELAYED_WORK(&q6fsm->vbat_work, q6fsm_vbat_monitor); + /* path: sys/class/$(CLASS NAME) */ + class_register(&g_q6fsm_class); + + g_fsm_afe = q6fsm; + + return 0; +} + +static void q6fsm_afe_deinit(void) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + + if (q6fsm == NULL) + return; + + cancel_delayed_work_sync(&q6fsm->vbat_work); + destroy_workqueue(q6fsm->vbat_wq); + mutex_destroy(&q6fsm->cmd_lock); + + kfree(g_fsm_afe); + g_fsm_afe = NULL; +} + +#endif // __Q6FSM_V3_H__ From bba0b8f43b7d303fdc72eaca60ff5971e86565a9 Mon Sep 17 00:00:00 2001 From: liaohj Date: Mon, 17 Jul 2023 16:51:43 +0800 Subject: [PATCH 63/79] machie_driver:add aw pri I2S dailink fogo use 4350+aw88261(pri i2s) add aw pri I2S dailink. Change-Id: Ia04386c1244a31a0c6c3043aaee9c86615644d91 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2677610 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/Kbuild | 4 ++++ asoc/holi.c | 51 +++++++++++++++++++++++++++++++++++++++++ asoc/msm_holi_dailink.h | 24 ++++++++++++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/asoc/Kbuild b/asoc/Kbuild index e3b7ea95b2e3..404d0db0702d 100755 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -263,6 +263,10 @@ ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA), y) EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA endif +ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA_PRI), y) + EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA_PRI +endif + CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ -DANI_LITTLE_BIT_ENDIAN \ -DDOT11F_LITTLE_ENDIAN_HOST \ diff --git a/asoc/holi.c b/asoc/holi.c index 2c0387557c0c..2b360733f964 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -5258,6 +5258,22 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { }, }; +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI +static struct snd_soc_dai_link msm_pri_mi2s_tx_hotless_dai_links[] = { + { + .name = "Primary MI2S_TX Hostless", + .stream_name = "Primary MI2S_TX Hostless Capture", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + SND_SOC_DAILINK_REG(sec_mi2s_tx_hostless), + }, +}; +#else static struct snd_soc_dai_link msm_sec_mi2s_tx_hotless_dai_links[] = { { .name = "Secondary MI2S_TX Hostless", @@ -5272,6 +5288,7 @@ static struct snd_soc_dai_link msm_sec_mi2s_tx_hotless_dai_links[] = { SND_SOC_DAILINK_REG(sec_mi2s_tx_hostless), }, }; +#endif static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { {/* hw:x,33 */ @@ -5983,7 +6000,11 @@ static struct snd_soc_dai_link msm_holi_dai_links[ ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link) + ARRAY_SIZE(msm_wcn_btfm_be_dai_links) + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI + ARRAY_SIZE(msm_pri_mi2s_tx_hotless_dai_links)]; +#else ARRAY_SIZE(msm_sec_mi2s_tx_hotless_dai_links)]; +#endif static int msm_populate_dai_link_component_of_node( struct snd_soc_card *card) @@ -6241,6 +6262,26 @@ void awinic_set_dai_name(const char* drvdainame, const char*drvname) } EXPORT_SYMBOL(awinic_set_dai_name); +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI +static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) +{ + int i; + + if (aw_codecs_num != 0) { + for (i = 0; i < len1; i++) { + if((!strcmp(dailink[i].stream_name, "Primary MI2S Playback")) || + (!strcmp(dailink[i].stream_name, "Primary MI2S Capture"))){ + dailink[i].num_codecs = aw_codecs_num; + dailink[i].codecs = aw_codecs; + } + } + } else { + pr_err("aw_codecs is empty\n"); + return -EINVAL; + } + return 0; +} +#else static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) { int i; @@ -6261,6 +6302,8 @@ static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) } #endif +#endif + static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { @@ -6370,10 +6413,18 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) } if (mi2s_audio_intf) { + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI + memcpy(msm_holi_dai_links + total_links, + msm_pri_mi2s_tx_hotless_dai_links, + sizeof(msm_pri_mi2s_tx_hotless_dai_links)); + total_links += ARRAY_SIZE(msm_pri_mi2s_tx_hotless_dai_links); +#else memcpy(msm_holi_dai_links + total_links, msm_sec_mi2s_tx_hotless_dai_links, sizeof(msm_sec_mi2s_tx_hotless_dai_links)); total_links += ARRAY_SIZE(msm_sec_mi2s_tx_hotless_dai_links); +#endif } dailink = msm_holi_dai_links; diff --git a/asoc/msm_holi_dailink.h b/asoc/msm_holi_dailink.h index 7ac3e51891fe..9af412862580 100644 --- a/asoc/msm_holi_dailink.h +++ b/asoc/msm_holi_dailink.h @@ -316,6 +316,18 @@ SND_SOC_DAILINK_DEFS(slimbus_8_tx, "btfm_fm_slim_tx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#if defined(CONFIG_AW882XX_STEREO_SMARTPA_PRI) +SND_SOC_DAILINK_DEFS(pri_mi2s_rx, + DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")), + DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), + COMP_CODEC("aw882xxacf_smartpa.2-0035", "aw882xx-aif-2-35")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +SND_SOC_DAILINK_DEFS(pri_mi2s_tx, + DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.1")), + DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), + COMP_CODEC("aw882xxacf_smartpa.2-0035", "aw882xx-aif-2-35")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#else SND_SOC_DAILINK_DEFS(pri_mi2s_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), @@ -325,8 +337,18 @@ SND_SOC_DAILINK_DEFS(pri_mi2s_tx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.1")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-tx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#endif -#if defined(CONFIG_AW882XX_STEREO_SMARTPA) +#if defined(CONFIG_AW882XX_STEREO_SMARTPA_PRI) +SND_SOC_DAILINK_DEFS(sec_mi2s_rx, + DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.2")), + DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +SND_SOC_DAILINK_DEFS(sec_mi2s_tx, + DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.3")), + DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-tx")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#elif defined(CONFIG_AW882XX_STEREO_SMARTPA) SND_SOC_DAILINK_DEFS(sec_mi2s_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.2")), DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), From 0cabd611967627ff21ec44d329a291bfdca4b7b9 Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 20 Jul 2023 11:05:23 +0800 Subject: [PATCH 64/79] audio/asoc:add PRI_MI2S_TX_HOSTLESS fogo 5g na use pri i2s add PRI_MI2S_TX_HOSTLESS to dailink. Change-Id: Iab8d626cdf6cee6ece6ce248c1a9b8a2b392386b Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2682281 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/holi.c | 2 +- asoc/msm-pcm-routing-v2.c | 23 +++++++++++++++++++++++ asoc/msm_holi_dailink.h | 7 +++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/asoc/holi.c b/asoc/holi.c index 2b360733f964..01b668ff7157 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -5270,7 +5270,7 @@ static struct snd_soc_dai_link msm_pri_mi2s_tx_hotless_dai_links[] = { .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ignore_suspend = 1, .ignore_pmdown_time = 1, - SND_SOC_DAILINK_REG(sec_mi2s_tx_hostless), + SND_SOC_DAILINK_REG(pri_mi2s_tx_hostless), }, }; #else diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 7e42abcc6928..360e33cef137 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -33273,6 +33273,11 @@ static const char * const mi2s_rx_vi_fb_tx_mux_text[] = { "ZERO", "SENARY_TX" }; +static const char * const pri_mi2s_rx_vi_fb_tx_mux_text[] = { + "ZERO", "PRI_MI2S_TX" +}; + + static const char * const sec_mi2s_rx_vi_fb_tx_mux_text[] = { "ZERO", "SEC_MI2S_TX" }; @@ -33309,6 +33314,10 @@ static const int wsa_rx_0_vi_fb_tx_rch_value[] = { MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0 }; +static const int pri_mi2s_rx_vi_fb_tx_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_PRI_MI2S_TX +}; + static const int sec_mi2s_rx_vi_fb_tx_value[] = { MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SECONDARY_MI2S_TX }; @@ -33360,6 +33369,11 @@ static const struct soc_enum mi2s_rx_vi_fb_mux_enum = ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text), mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value); +static const struct soc_enum pri_mi2s_rx_vi_fb_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_PRI_MI2S_RX, 0, 0, + ARRAY_SIZE(pri_mi2s_rx_vi_fb_tx_mux_text), + pri_mi2s_rx_vi_fb_tx_mux_text, pri_mi2s_rx_vi_fb_tx_value); + static const struct soc_enum sec_mi2s_rx_vi_fb_mux_enum = SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SEC_I2S_RX, 0, 0, ARRAY_SIZE(sec_mi2s_rx_vi_fb_tx_mux_text), @@ -33412,6 +33426,11 @@ static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux = mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, spkr_prot_put_vi_lch_port); +static const struct snd_kcontrol_new pri_mi2s_rx_vi_fb_mux = + SOC_DAPM_ENUM_EXT("PRI_MI2S_RX_VI_FB_MUX", + pri_mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + static const struct snd_kcontrol_new sec_mi2s_rx_vi_fb_mux = SOC_DAPM_ENUM_EXT("SEC_MI2S_RX_VI_FB_MUX", sec_mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, @@ -33432,6 +33451,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { /* Widget name equals to Front-End DAI name, * Stream name must contains substring of front-end dai name */ + SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, + &pri_mi2s_rx_vi_fb_mux), SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0), @@ -35622,6 +35643,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets_tdm[] = { #endif static const struct snd_soc_dapm_route intercon[] = { + {"PRI_MI2S_RX_VI_FB_MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, diff --git a/asoc/msm_holi_dailink.h b/asoc/msm_holi_dailink.h index 9af412862580..39e828f84a66 100644 --- a/asoc/msm_holi_dailink.h +++ b/asoc/msm_holi_dailink.h @@ -504,7 +504,14 @@ SND_SOC_DAILINK_DEFS(proxy_rx, DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#if defined(CONFIG_AW882XX_STEREO_SMARTPA_PRI) +SND_SOC_DAILINK_DEFS(pri_mi2s_tx_hostless, + DAILINK_COMP_ARRAY(COMP_CPU("PRI_MI2S_TX_HOSTLESS")), + DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-hostless"))); +#else SND_SOC_DAILINK_DEFS(sec_mi2s_tx_hostless, DAILINK_COMP_ARRAY(COMP_CPU("SEC_MI2S_TX_HOSTLESS")), DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-hostless"))); +#endif From 84171d1ff269299a19d5c408b49988dc3d995b7d Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 27 Jul 2023 12:38:21 +0800 Subject: [PATCH 65/79] audio/dsp:reduce fsm vbat monitor retry times fsm vbat monitor for all screne. and l(CR) handset or headset acdb not fsm topo, will cause block ,then crash. reduce the retry time to avoid crash. and then fsm vbat monitor need judge scene. Change-Id: Iaa288f27a67360b16cbd0a185a80bf6035ecc86c Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2691387 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- include/dsp/q6fsm-v3.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dsp/q6fsm-v3.h b/include/dsp/q6fsm-v3.h index 1a6667ec8290..4c281fedea99 100644 --- a/include/dsp/q6fsm-v3.h +++ b/include/dsp/q6fsm-v3.h @@ -44,7 +44,7 @@ #define CAPI_V2_PARAM_FSADSP_ROTATION 0x10001FB1 #define CAPI_V2_PARAM_FSADSP_FADE 0x10001FB7 -#define Q6FSM_RETRY_TIME (3) +#define Q6FSM_RETRY_TIME (1) #define Q6FSM_SLEEP_TIME (10) struct bsg_config_v2 { @@ -542,7 +542,7 @@ static int q6fsm_vbat_monitor_work(struct q6fsm_afe *q6fsm) if (!q6fsm_get_rx_status()) { fsm_info("RX module isn't ready"); - return 0; + return -EINVAL; } if (q6fsm->bsg_version == 2) { From b9b4bcff833059719a64304893592ddd748a38b0 Mon Sep 17 00:00:00 2001 From: liaohj Date: Tue, 1 Aug 2023 09:04:50 +0800 Subject: [PATCH 66/79] fsm:correct fsm vbat monitor logic fsm vbat monitor will access adsp fsm algo. if fsm algo not ready or not enable, it will block adsp thread then cause timecheck crash. correct fsm vbat monitor logic, just when fsm algo run, then run fsm vbat monitor. Signed-off-by: liaohj Change-Id: Ibbc2c372ea110c9b901a6058d17c502ca06feeb2 Reviewed-on: https://gerrit.mot.com/2695488 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/fs1815/fsm_core.c | 20 +++++++++++++++++++- include/dsp/q6fsm-v3.h | 24 ++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/fs1815/fsm_core.c b/asoc/codecs/fs1815/fsm_core.c index f532a7037b7f..e4ac2306a5b4 100644 --- a/asoc/codecs/fs1815/fsm_core.c +++ b/asoc/codecs/fs1815/fsm_core.c @@ -19,7 +19,7 @@ static atomic_t fsm_adsp_enable; //include "q6fsm-v3.h" extern int q6fsm_set_rotation(int angle); extern int q6fsm_set_rx_enable(int enable); - +extern void q6fsm_fsm_check_scene_status(bool status); static LIST_HEAD(fsm_dev_list); #define fsm_list_init(fsm_dev) \ do { \ @@ -1705,6 +1705,16 @@ void fsm_add_card_controls(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(fsm_add_card_controls); +static int fsm_check_scene_status() +{ + fsm_config_t *cfg = fsm_get_config(); + if(cfg->speaker_on && cfg->next_scene == FSM_SCENE_MUSIC){ + return 0; + } + + return -1; +} + void fsm_speaker_onn(void) { fsm_config_t *cfg = fsm_get_config(); @@ -1724,6 +1734,13 @@ void fsm_speaker_onn(void) fsm_list_func(fsm_dev, fsm_stub_start_up); cfg->cur_angle = cfg->next_angle; cfg->speaker_on = true; + + if(fsm_check_scene_status() == 0) { + q6fsm_fsm_check_scene_status(true); + } else { + q6fsm_fsm_check_scene_status(false); + } + pr_debug("done"); fsm_mutex_unlock(); } @@ -1746,6 +1763,7 @@ void fsm_speaker_off(void) } fsm_list_func(fsm_dev, fsm_stub_shut_down); cfg->speaker_on = false; + q6fsm_fsm_check_scene_status(false); fsm_mutex_unlock(); fsm_set_scene(0); // scene music pr_debug("done"); diff --git a/include/dsp/q6fsm-v3.h b/include/dsp/q6fsm-v3.h index 4c281fedea99..d0561746d712 100644 --- a/include/dsp/q6fsm-v3.h +++ b/include/dsp/q6fsm-v3.h @@ -26,7 +26,7 @@ // TODO update #define Q6FSM_AFE_RX_PORT (AFE_PORT_ID_RX_CODEC_DMA_RX_0) -#define Q6FSM_AFE_TX_PORT (AFE_PORT_ID_TX_CODEC_DMA_TX_3) +#define Q6FSM_AFE_TX_PORT (AFE_PORT_ID_TX_CODEC_DMA_TX_0) #define Q6FSM_CHUNK_SIZE (256) @@ -100,6 +100,7 @@ typedef struct _fsm_rotation_info_t static struct q6fsm_afe *g_fsm_afe; static uint32_t *q6fsm_get_buffer; +static bool fsm_scene_status = false; static int q6afe_set_params(u16 port_id, int index, struct mem_mapping_hdr *mem_hdr, @@ -262,6 +263,11 @@ bool q6fsm_get_rx_status(void) int enable; int ret; + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return -EINVAL; + } + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; param_hdr.instance_id = 0; param_hdr.reserved = 0; @@ -289,8 +295,8 @@ int q6fsm_set_rx_enable(int enable) if (q6fsm == NULL) return -EINVAL; - if (!q6fsm_get_rx_status()) { - fsm_info("RX module isn't ready"); + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); return -EINVAL; } @@ -334,13 +340,14 @@ int q6fsm_set_rotation(int angle) if(angle == 0){ param.ch_sequence[0] = 0; param.ch_sequence[1] = 1; - }else if(angle == 90){ + }else if(angle == 1){ param.ch_sequence[0] = 1; param.ch_sequence[1] = 0; }else{ fsm_err("not support angle:%d",angle); return -EINVAL; } + param.angle = angle; param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; param_hdr.instance_id = 0; @@ -368,6 +375,11 @@ int q6fsm_get_rotation(int *angle) } EXPORT_SYMBOL(q6fsm_get_rotation); +void q6fsm_fsm_check_scene_status(bool status) +{ + fsm_scene_status = status; +} +EXPORT_SYMBOL(q6fsm_fsm_check_scene_status); #define PSY_DESC psy->desc @@ -603,6 +615,10 @@ static void q6fsm_monitor_switch(bool enable) if (enable) { if (q6fsm->monitor_en) return; + if(fsm_scene_status == false){ + fsm_info("no need to start vbat monitor"); + return; + } q6fsm->monitor_en = true; queue_delayed_work(q6fsm->vbat_wq, &q6fsm->vbat_work, 0); } else { From af133a08b2959c190e99df5b9ea77d08ceb65835 Mon Sep 17 00:00:00 2001 From: liaohj Date: Wed, 2 Aug 2023 17:28:06 +0800 Subject: [PATCH 67/79] techpack:add aw8838 driver fogo 5g na need to pass HAC test. so hardware add aw8838 for handset hac output. here add aw8838 driver. Change-Id: I164170a4f7748e767d389dc980e80d3773856892 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2698019 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/Kbuild | 5 + asoc/codecs/Kbuild | 1 + asoc/codecs/aw8838/Android.mk | 42 + asoc/codecs/aw8838/Kbuild | 125 +++ asoc/codecs/aw8838/aw8838.c | 1582 +++++++++++++++++++++++++++ asoc/codecs/aw8838/aw8838.h | 107 ++ asoc/codecs/aw8838/aw8838_monitor.c | 1280 ++++++++++++++++++++++ asoc/codecs/aw8838/aw8838_monitor.h | 201 ++++ asoc/codecs/aw8838/aw8838_reg.h | 258 +++++ 9 files changed, 3601 insertions(+) create mode 100644 asoc/codecs/aw8838/Android.mk create mode 100644 asoc/codecs/aw8838/Kbuild create mode 100644 asoc/codecs/aw8838/aw8838.c create mode 100644 asoc/codecs/aw8838/aw8838.h create mode 100644 asoc/codecs/aw8838/aw8838_monitor.c create mode 100644 asoc/codecs/aw8838/aw8838_monitor.h create mode 100644 asoc/codecs/aw8838/aw8838_reg.h diff --git a/asoc/Kbuild b/asoc/Kbuild index 404d0db0702d..1cdb6c8b84c9 100755 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -267,6 +267,10 @@ ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA_PRI), y) EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA_PRI endif +ifeq ($(CONFIG_SND_SOC_AW8838), y) + EXTRA_CFLAGS += -DCONFIG_SND_SOC_AW8838 +endif + CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ -DANI_LITTLE_BIT_ENDIAN \ -DDOT11F_LITTLE_ENDIAN_HOST \ @@ -305,6 +309,7 @@ KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd934x/Module.symvers KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd937x/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/aw8838/Module.symvers endif ifeq ($(KERNEL_BUILD), 1) obj-y += codecs/ diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 5d6455d37920..b466a29e7010 100755 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -278,6 +278,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += aw882xxacf/ obj-y += aw87xxx/ obj-y += fs1815/ + obj-y += aw8838/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/asoc/codecs/aw8838/Android.mk b/asoc/codecs/aw8838/Android.mk new file mode 100644 index 000000000000..ae691bed003d --- /dev/null +++ b/asoc/codecs/aw8838/Android.mk @@ -0,0 +1,42 @@ +# Android makefile for audio kernel modules +# Assume no targets will be supported + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,msm8953 msm8937 sdm710 qcs605),true) + +LOCAL_PATH := $(call my-dir) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) +AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/vendor/qcom/opensource/audio-kernel +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR) + +# We are actually building audio.ko here, as per the +# requirement we are specifying _audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=audio_aw8838 +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_SELECT)aw8838.ko +LOCAL_MODULE_KBUILD_NAME := audio_aw8838.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### +endif # DLKM check +endif # supported target check diff --git a/asoc/codecs/aw8838/Kbuild b/asoc/codecs/aw8838/Kbuild new file mode 100644 index 000000000000..6cdbc71ffc2e --- /dev/null +++ b/asoc/codecs/aw8838/Kbuild @@ -0,0 +1,125 @@ +#ld either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/vendor/qcom/opensource/audio-kernel/include +EXTRA_CFLAGS += -I$(TOP)/vendor/qcom/opensource/audio-kernel/include/uapi/ + +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/kernel/msm-5.4 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_BENGAL), y) + include $(AUDIO_ROOT)/config/bengalauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/bengalautoconf.h + endif + ifeq ($(CONFIG_ARCH_LITO), y) + include $(AUDIO_ROOT)/config/litoauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ AW882XX ############ + +# for AW8838 Codec +# 添加 aw8838 相关驱动的所有.c 文件 +ifdef CONFIG_SND_SOC_AW8838 + AW8838_OBJS += aw8838.o + AW8838_OBJS += aw8838_monitor.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +# Module information used by KBuild framework +obj-m += audio_aw8838.o +audio_aw8838-y := $(AW8838_OBJS) +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/asoc/codecs/aw8838/aw8838.c b/asoc/codecs/aw8838/aw8838.c new file mode 100644 index 000000000000..159c9efef66c --- /dev/null +++ b/asoc/codecs/aw8838/aw8838.c @@ -0,0 +1,1582 @@ +/* + * aw8838.c aw8838 codec module + * + * Copyright (c) 2018 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8838.h" +#include "aw8838_reg.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW8838_I2C_NAME "aw8838_smartpa" + +#define AW8838_DRIVER_VERSION "v1.0.5" + +#define AW8838_RATES SNDRV_PCM_RATE_8000_48000 +#define AW8838_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AW_I2C_RETRIES 5 +#define AW_I2C_RETRY_DELAY 5 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 5 + +static int aw8838_spk_control = 0; +static int aw8838_rcv_control = 0; + +#define AW8838_MAX_FIRMWARE_LOAD_CNT 20 +static char *aw8838_cfg_name = "aw8838_cfg.bin"; +int aw8838_read_chipid(struct aw8838 *aw8838); +void awinic_set_dai_name(const char* drvdainame, const char*drvname); + +/****************************************************** +* +*distinguish between codecs and components by version +* +******************************************************/ +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_component, + .aw_snd_soc_codec_get_drvdata = snd_soc_component_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_component_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_component, + .aw_snd_soc_register_codec = snd_soc_register_component, +}; +#else +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_codec, + .aw_snd_soc_codec_get_drvdata = snd_soc_codec_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_codec_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_codec, + .aw_snd_soc_register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef AW_KERNEL_VER_OVER_4_19_1 + return dai->component; +#else + return dai->codec; +#endif +} + + /****************************************************** + * + * aw8838 i2c write/read + * + ******************************************************/ +static int i2c_write(struct aw8838 *aw8838, + unsigned char addr, unsigned int reg_data) +{ + int ret = -1; + u8 wbuf[512] = {0}; + + struct i2c_msg msgs[] = { + { + .addr = aw8838->i2c->addr, + .flags = 0, + .len = 3, + .buf = wbuf, + }, + }; + + wbuf[0] = addr; + wbuf[1] = (unsigned char)((reg_data & 0xff00)>>8); + wbuf[2] = (unsigned char)(reg_data & 0x00ff); + + ret = i2c_transfer(aw8838->i2c->adapter, msgs, 1); + if (ret < 0) { + pr_err("%s: i2c write error: %d\n", __func__, ret); + } + + return ret; +} + +static int i2c_read(struct aw8838 *aw8838, + unsigned char addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char rbuf[512] = {0}; + unsigned int get_data = 0; + + struct i2c_msg msgs[] = { + { + .addr = aw8838->i2c->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = aw8838->i2c->addr, + .flags = I2C_M_RD, + .len = 2, + .buf = rbuf, + }, + }; + + ret = i2c_transfer(aw8838->i2c->adapter, msgs, 2); + if (ret < 0) { + pr_err("%s: i2c read error: %d\n", __func__, ret); + return ret; + } + + get_data = (unsigned int)(rbuf[0] & 0x00ff); + get_data <<= 8; + get_data |= (unsigned int)rbuf[1]; + + *reg_data = get_data; + + return ret; +} + +int aw8838_i2c_write(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while(cnt < AW_I2C_RETRIES) { + ret = i2c_write(aw8838, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } + cnt ++; + } + + return ret; +} + +int aw8838_i2c_read(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while(cnt < AW_I2C_RETRIES) { + ret = i2c_read(aw8838, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } + cnt ++; + } + + return ret; +} + +static int aw8838_i2c_write_bits(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + unsigned int reg_val; + + aw8838_i2c_read(aw8838, reg_addr, ®_val); + reg_val &= mask; + reg_val |= reg_data; + aw8838_i2c_write(aw8838, reg_addr, reg_val); + + return 0; +} + +/****************************************************** + * + * aw8838 control + * + ******************************************************/ +int aw8838_get_hmute(struct aw8838 *aw8838) +{ + unsigned int reg_val = 0; + int ret; + + aw_dev_dbg(aw8838->dev, "%s: enter\n", __func__); + + aw8838_i2c_read(aw8838, AW8838_REG_PWMCTRL, ®_val); + if((reg_val & AW8838_BIT_PWMCTRL_HMUTE_ENABLE) == AW8838_BIT_PWMCTRL_HMUTE_DISABLE) + ret = 0; + else + ret = 1; + + return ret; +} + +/*[7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1*/ +uint32_t aw8838_reg_val_to_db(uint32_t value) +{ + return ((value >> 4) * AW8838_VOL_6DB_STEP + (value & 0x0f)); +} + +/*[7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2*/ +static uint32_t aw8838_db_val_to_reg(uint32_t value) +{ + return (((value / AW8838_VOL_6DB_STEP) << 4) + + (value % AW8838_VOL_6DB_STEP)); +} + +int aw8838_set_volume(struct aw8838 *aw8838, uint32_t value) +{ + uint32_t reg_value = 0; + uint32_t real_value = aw8838_db_val_to_reg(value); + + /* cal real value */ + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_value); + + aw_dev_dbg(aw8838->dev, "%s: value %d , 0x%x\n", + __func__, value, real_value); + + /*15 : 8] volume*/ + real_value = (real_value << AW8838_VOL_REG_SHIFT) | (reg_value & 0x00ff); + + /* write value */ + aw8838_i2c_write(aw8838, AW8838_REG_HAGCCFG8, real_value); + + return 0; +} + +int aw8838_get_volume(struct aw8838 *aw8838, uint32_t *value) +{ + uint32_t reg_value = 0; + uint32_t real_value = 0; + + /* read value */ + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_value); + + /*[15 : 8] volume*/ + real_value = reg_value >> AW8838_VOL_REG_SHIFT; + + real_value = aw8838_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw8838_run_cpmd(struct aw8838 *aw8838, bool cpmd_flag) +{ + pr_debug("%s enter\n", __func__); + if(cpmd_flag) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_CPCTRL, + AW8838_BIT_CPCTRL_CP_MD_MASK, AW8838_BIT_CPCTRL_CP_MD_TRANS); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_CPCTRL, + AW8838_BIT_CPCTRL_CP_MD_MASK, aw8838->cpmd_default); + } +} + +static void aw8838_run_cppd(struct aw8838 *aw8838, bool cppd) +{ + pr_debug("%s enter\n", __func__); + if(cppd) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_CPPD_MASK, AW8838_BIT_SYSCTRL_CPPD_PDN); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_CPPD_MASK, AW8838_BIT_SYSCTRL_CPPD_ACTIVE); + } +} + +static void aw8838_run_mute(struct aw8838 *aw8838, bool mute) +{ + pr_debug("%s enter\n", __func__); + + if(mute) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_PWMCTRL, + AW8838_BIT_PWMCTRL_HMUTE_MASK, AW8838_BIT_PWMCTRL_HMUTE_ENABLE); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_PWMCTRL, + AW8838_BIT_PWMCTRL_HMUTE_MASK, AW8838_BIT_PWMCTRL_HMUTE_DISABLE); + } +} + +static void aw8838_run_pwd(struct aw8838 *aw8838, bool pwd) +{ + pr_debug("%s enter\n", __func__); + + if(pwd) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_PW_MASK, AW8838_BIT_SYSCTRL_PW_PDN); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_PW_MASK, AW8838_BIT_SYSCTRL_PW_ACTIVE); + } +} + +static void aw8838_spk_rcv_mode(struct aw8838 *aw8838) +{ + pr_info("%s spk_rcv=%d\n", __func__, aw8838->spk_rcv_mode); + + if(aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_MODE_MASK, AW8838_BIT_SYSCTRL_SPK_MODE); + } else if(aw8838->spk_rcv_mode == AW8838_RECEIVER_MODE){ + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_MODE_MASK, AW8838_BIT_SYSCTRL_RCV_MODE); + } else { + } +} + + +static void aw8838_start(struct aw8838 *aw8838) +{ + unsigned int reg_val = 0; + unsigned int iis_check_max = 5; + unsigned int i = 0; + + pr_info("%s enter\n", __func__); + + if ((aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) && !aw8838_spk_control) { + pr_info("%s: spk can not allow power\n", __func__); + return; + } else if ((aw8838->spk_rcv_mode == AW8838_RECEIVER_MODE) && !aw8838_rcv_control) { + pr_info("%s: rcv can not allow power\n", __func__); + return; + } + + aw8838_run_cpmd(aw8838, false); + aw8838_run_pwd(aw8838, false); + msleep(1); + aw8838_run_cppd(aw8838, false); + msleep(1); + + for(i=0; imonitor); + + return; + } + msleep(2); + } + aw8838_run_pwd(aw8838, true); + pr_err("%s: iis signal check error\n", __func__); +} + +static void aw8838_stop(struct aw8838 *aw8838) +{ + pr_debug("%s enter\n", __func__); + + aw8838_monitor_stop(&aw8838->monitor); + aw8838_run_mute(aw8838, true); + aw8838_run_cpmd(aw8838, true); + aw8838_run_cppd(aw8838, true); + aw8838_run_pwd(aw8838, true); +} + +static void aw8838_container_update(struct aw8838 *aw8838, + struct aw8838_container *aw8838_cont) +{ + int i = 0; + int reg_addr = 0; + int reg_val = 0; + unsigned int cpmd_val; + + pr_debug("%s enter\n", __func__); + + for(i=0; ilen; i+=4) { + reg_addr = (aw8838_cont->data[i+1]<<8) + aw8838_cont->data[i+0]; + reg_val = (aw8838_cont->data[i+3]<<8) + aw8838_cont->data[i+2]; + pr_info("%s: reg=0x%04x, val = 0x%04x\n", __func__, reg_addr, reg_val); + aw8838_i2c_write(aw8838, (unsigned char)reg_addr, (unsigned int)reg_val); + } + + aw8838_i2c_read(aw8838, AW8838_REG_CPCTRL, &cpmd_val); + aw8838->cpmd_default = cpmd_val & (~AW8838_BIT_CPCTRL_CP_MD_MASK); + pr_debug("%s:cpmd_default=0x%04x\n", __func__, aw8838->cpmd_default); + + aw8838_get_volume(aw8838, &aw8838->db_offset); + pr_debug("%s exit\n", __func__); +} + +static void aw8838_cfg_loaded(const struct firmware *cont, void *context) +{ + struct aw8838 *aw8838 = context; + struct aw8838_container *aw8838_cfg; + unsigned int i = 0; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw8838_cfg_name); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, aw8838_cfg_name, + cont ? cont->size : 0); + + for(i=0; isize; i++) { + pr_info("%s: addr:0x%04x, data:0x%02x\n", __func__, i, *(cont->data+i)); + } + + aw8838_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw8838_cfg) { + release_firmware(cont); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw8838_cfg->len = cont->size; + memcpy(aw8838_cfg->data, cont->data, cont->size); + release_firmware(cont); + + aw8838_container_update(aw8838, aw8838_cfg); + + kfree(aw8838_cfg); + + aw8838->init = 1; + pr_info("%s: cfg update complete\n", __func__); + + aw8838_spk_rcv_mode(aw8838); + aw8838_start(aw8838); +} + +static int aw8838_load_cfg(struct aw8838 *aw8838) +{ + pr_info("%s enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8838_cfg_name, aw8838->dev, GFP_KERNEL, + aw8838, aw8838_cfg_loaded); +} + +static void aw8838_cold_start(struct aw8838 *aw8838) +{ + int ret = -1; + + pr_info("%s enter\n", __func__); + + ret = aw8838_load_cfg(aw8838); + if(ret) { + pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); + } +} + +static void aw8838_smartpa_cfg(struct aw8838 *aw8838, bool flag) +{ + pr_info("%s, flag = %d\n", __func__, flag); + + if(flag == true) { + if(aw8838->init == 0) { + pr_info("%s, init = %d\n", __func__, aw8838->init); + aw8838_cold_start(aw8838); + } else { + aw8838_spk_rcv_mode(aw8838); + aw8838_start(aw8838); + } + } else { + aw8838_stop(aw8838); + } +} + +/****************************************************** + * + * kcontrol + * + ******************************************************/ + static const char *const spk_function[] = { "Off", "On" }; + static const char *const rcv_function[] = { "Off", "On" }; + static const DECLARE_TLV_DB_SCALE(digital_gain,0,50,0); + + struct soc_mixer_control aw8838_mixer ={ + .reg = AW8838_REG_HAGCCFG8, + .shift = AW8838_VOL_REG_SHIFT, + .max = AW8838_VOLUME_MAX, + .min = AW8838_VOLUME_MIN, + }; + +static int aw8838_volume_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control*) kcontrol->private_value; + + //set kcontrol info + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max - mc->min; + return 0; +} + +static int aw8838_volume_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + struct soc_mixer_control *mc = (struct soc_mixer_control*) kcontrol->private_value; + + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_val); + ucontrol->value.integer.value[0] = (reg_val >> mc->shift)\ + &(AW8838_BIT_HAGCCFG8_VOL_MASK); + return 0; +} + +static int aw8838_volume_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control*) kcontrol->private_value; + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int value = 0; + unsigned int reg_value = 0; + + //value is right + value = ucontrol->value.integer.value[0]; + if(value > (mc->max-mc->min)|| value <0){ + pr_err("%s:value over range \n",__func__); + return -1; + } + + //smartpa have clk + aw8838_i2c_read(aw8838, AW8838_REG_SYSST, ®_value); + if(!(reg_value&AW8838_BIT_SYSST_PLLS)){ + pr_err("%s: NO I2S CLK ,cat not write reg \n",__func__); + return 0; + } + //cal real value + value = value << mc->shift& (~AW8838_BIT_HAGCCFG8_VOL_MASK); + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_value); + value = value | (reg_value&0x00ff); + + //write value + aw8838_i2c_write(aw8838, AW8838_REG_HAGCCFG8, value); + + return 0; +} + +static struct snd_kcontrol_new aw8838_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "aw8838_rx_volume", + .access= SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = (digital_gain), + .info = aw8838_volume_info, + .get = aw8838_volume_get, + .put = aw8838_volume_put, + .private_value = (unsigned long)&aw8838_mixer, +}; + +static int aw8838_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw8838_spk_control=%d\n", __func__, aw8838_spk_control); + ucontrol->value.integer.value[0] = aw8838_spk_control; + return 0; +} + +static int aw8838_spk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n ", + __func__, ucontrol->value.integer.value[0]); + if(ucontrol->value.integer.value[0] == aw8838_spk_control) + return 1; + + aw8838_spk_control = ucontrol->value.integer.value[0]; + + aw8838->spk_rcv_mode = AW8838_SPEAKER_MODE; + + aw8838_spk_rcv_mode(aw8838); + + if (!aw8838_get_hmute(aw8838)) + aw8838_monitor_start(&aw8838->monitor); + + return 0; +} + +static int aw8838_chipid_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int status = 0; + int ret = 0; + struct aw8838 *aw8838; + + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + ret = aw8838_read_chipid(aw8838); + if(ret < 0) + status = 0; + else + status = 1; + + ucontrol->value.integer.value[0] = status; + + pr_info("chip id get %ld", ucontrol->value.integer.value[0]); + + return 0; +} + + +static int aw8838_rcv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw8838_rcv_control=%d\n", __func__, aw8838_rcv_control); + ucontrol->value.integer.value[0] = aw8838_rcv_control; + return 0; +} +static int aw8838_rcv_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n ", + __func__, ucontrol->value.integer.value[0]); + if(ucontrol->value.integer.value[0] == aw8838_rcv_control) + return 1; + + aw8838_rcv_control = ucontrol->value.integer.value[0]; + + aw8838->spk_rcv_mode = AW8838_RECEIVER_MODE; + + aw8838_spk_rcv_mode(aw8838); + + return 0; +} +static const struct soc_enum aw8838_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rcv_function), rcv_function), +}; + +static struct snd_kcontrol_new aw8838_controls[] = { + SOC_ENUM_EXT("aw8838_speaker_switch", aw8838_snd_enum[0], + aw8838_spk_get, aw8838_spk_set), + SOC_ENUM_EXT("aw8838_receiver_switch", aw8838_snd_enum[1], + aw8838_rcv_get, aw8838_rcv_set), + SOC_SINGLE_EXT("HAC_HW_STATUS", SND_SOC_NOPM, 0, 0x7fffffff, 0, + aw8838_chipid_get, NULL), +}; + +static void aw8838_add_codec_controls(struct aw8838 *aw8838) +{ + pr_info("%s enter\n", __func__); + + aw_componet_codec_ops.aw_snd_soc_add_codec_controls(aw8838->codec, aw8838_controls, + ARRAY_SIZE(aw8838_controls)); + + aw_componet_codec_ops.aw_snd_soc_add_codec_controls(aw8838->codec, &aw8838_volume,1); +} + + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ +static int aw8838_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: enter\n", __func__); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s:capture\n", __func__); + return 0; + } + + aw8838_run_pwd(aw8838, false); + + return 0; +} + +static int aw8838_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + //struct aw8838 *aw8838 = snd_soc_codec_get_drvdata(dai->codec); + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + + pr_info("%s: fmt=0x%x\n", __func__, fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "%s: invalid codec master mode\n", __func__); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, "%s: unsupported DAI format %d\n", __func__, + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw8838_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: freq=%d\n", __func__, freq); + + aw8838->sysclk = freq; + return 0; +} + +static int aw8838_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int rate = 0; + int reg_value = 0; + int width = 0; + /* Supported */ + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, + rate, snd_pcm_format_width(params_format(params))); + return 0; + } + + //get rate param + aw8838->rate=rate = params_rate(params); + pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, rate, + snd_pcm_format_width(params_format(params))); + //match rate + switch(rate) + { + case 8000: + reg_value = AW8838_BIT_I2SCTRL_SR_8K; + break; + case 16000: + reg_value = AW8838_BIT_I2SCTRL_SR_16K; + break; + case 32000: + reg_value = AW8838_BIT_I2SCTRL_SR_32K; + break; + case 44100: + reg_value = AW8838_BIT_I2SCTRL_SR_44P1K; + break; + case 48000: + reg_value = AW8838_BIT_I2SCTRL_SR_48K; + break; + default: + reg_value = AW8838_BIT_I2SCTRL_SR_48K; + pr_err("%s: rate can not support\n", __func__); + break; + } + //set chip rate + if(-1 != reg_value){ + aw8838_i2c_write_bits(aw8838, AW8838_REG_I2SCTRL, + AW8838_BIT_I2SCTRL_SR_MASK, reg_value); + } + + //get bit width + width = params_width(params); + pr_debug("%s: width = %d \n",__func__,width); + switch(width) + { + case 16: + reg_value = AW8838_BIT_I2SCTRL_FMS_16BIT; + break; + case 20: + reg_value = AW8838_BIT_I2SCTRL_FMS_20BIT; + break; + case 24: + reg_value = AW8838_BIT_I2SCTRL_FMS_24BIT; + break; + case 32: + reg_value = AW8838_BIT_I2SCTRL_FMS_32BIT; + break; + default: + reg_value = AW8838_BIT_I2SCTRL_FMS_16BIT; + pr_err("%s: width can not support\n", __func__); + break; + } + //set width + if(-1 != reg_value){ + aw8838_i2c_write_bits(aw8838, AW8838_REG_I2SCTRL, + AW8838_BIT_I2SCTRL_FMS_MASK, reg_value); + } + + return 0; +} + +static int aw8838_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: mute state=%d\n", __func__, mute); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s:capture\n", __func__); + return 0; + } + + if (!(aw8838->flags & AW8838_FLAG_START_ON_MUTE)) + return 0; + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw8838->pstream = 0; + else + aw8838->cstream = 0; + if (aw8838->pstream != 0 || aw8838->cstream != 0) + return 0; + + mutex_lock(&aw8838->cfg_lock); + aw8838_smartpa_cfg(aw8838, false); + mutex_unlock(&aw8838->cfg_lock); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw8838->pstream = 1; + else + aw8838->cstream = 1; + + mutex_lock(&aw8838->cfg_lock); + aw8838_smartpa_cfg(aw8838, true); + mutex_unlock(&aw8838->cfg_lock); + } + + return 0; +} + +static void aw8838_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: enter\n", __func__); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s:capture\n", __func__); + return; + } + + aw8838->rate = 0; + aw8838_run_pwd(aw8838, true); +} + +static const struct snd_soc_dai_ops aw8838_dai_ops = { + .startup = aw8838_startup, + .set_fmt = aw8838_set_fmt, + .set_sysclk = aw8838_set_dai_sysclk, + .hw_params = aw8838_hw_params, + .mute_stream = aw8838_mute, + .shutdown = aw8838_shutdown, +}; + +static struct snd_soc_dai_driver aw8838_dai[] = { + { + .name = "aw8838-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW8838_RATES, + .formats = AW8838_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW8838_RATES, + .formats = AW8838_FORMATS, + }, + .ops = &aw8838_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static int aw8838_probe(aw_snd_soc_codec_t *codec) +{ + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + int ret = 0; + + pr_info("%s enter\n", __func__); + + aw8838->codec = codec; + + aw8838_add_codec_controls(aw8838); + +#if 0 + if (codec->dev->of_node) + dev_set_name(codec->dev, "%s", "aw8838_smartpa"); +#endif + schedule_delayed_work(&aw8838->monitor.load_fw_work, + msecs_to_jiffies(AW_LOAD_MON_FW_DELAY_TIME)); + + pr_info("%s exit\n", __func__); + + return ret; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static void aw8838_remove(struct snd_soc_component *component) +{ + //struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_info("%s enter\n", __func__); + + return; +} +#else +static int aw8838_remove(struct snd_soc_codec *codec) +{ + //struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_info("%s enter\n", __func__); + + return 0; +} +#endif + +static unsigned int aw8838_codec_read(aw_snd_soc_codec_t *codec,unsigned int reg) +{ + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int value =0; + int ret; + pr_debug("%s:enter \n",__func__); + + if(aw8838_reg_access[reg]®_RD_ACCESS){ + ret=aw8838_i2c_read(aw8838,reg,&value); + if(ret<0){ + pr_debug("%s: read register failed \n",__func__); + return ret; + } + }else{ + pr_debug("%s:Register 0x%x NO read access\n",__func__,reg); + return -1; + } + return value; +} + +static int aw8838_codec_write(aw_snd_soc_codec_t *codec,unsigned int reg,unsigned int value) +{ + int ret ; + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_debug("%s:enter ,reg is 0x%x value is 0x%x\n",__func__,reg,value); + + if(aw8838_reg_access[reg]®_WR_ACCESS){ + ret=aw8838_i2c_write(aw8838,reg,value); + return ret; + }else{ + pr_debug("%s: Register 0x%x NO write access \n",__func__,reg); + } + + return -1; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct snd_soc_component_driver soc_codec_dev_aw8838 = { + .probe = aw8838_probe, + .remove = aw8838_remove, + .read = aw8838_codec_read, + .write = aw8838_codec_write, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw8838 = { + .probe = aw8838_probe, + .remove = aw8838_remove, + .read = aw8838_codec_read, + .write = aw8838_codec_write, + .reg_cache_size = AW8838_REG_MAX, + .reg_word_size = 2, +}; +#endif + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw8838_interrupt_setup(struct aw8838 *aw8838) +{ + unsigned int reg_val; + + pr_info("%s enter\n", __func__); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSINTM, ®_val); + reg_val &= (~AW8838_BIT_SYSINTM_UVLOM); + reg_val &= (~AW8838_BIT_SYSINTM_PLLM); + reg_val &= (~AW8838_BIT_SYSINTM_OTHM); + reg_val &= (~AW8838_BIT_SYSINTM_OCDM); + aw8838_i2c_write(aw8838, AW8838_REG_SYSINTM, reg_val); +} + +static void aw8838_interrupt_clear(struct aw8838 *aw8838) +{ + unsigned int reg_val = 0; + + pr_info("%s enter\n", __func__); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSST, ®_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSINTM, ®_val); + pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val); +} + +static irqreturn_t aw8838_irq(int irq, void *data) +{ + struct aw8838 *aw8838 = data; + + pr_info("%s enter\n", __func__); + + aw8838_interrupt_clear(aw8838); + + pr_info("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw8838_parse_dt(struct device *dev, struct aw8838 *aw8838, + struct device_node *np) +{ + aw8838->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw8838->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", __func__); + return -1; + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw8838->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw8838->irq_gpio < 0) { + dev_info(dev, "%s: no irq gpio provided.\n", __func__); + } else { + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + } + + aw8838_parse_monitor_dt(&aw8838->monitor); + + return 0; +} + +int aw8838_hw_reset(struct aw8838 *aw8838) +{ + pr_info("%s enter\n", __func__); + + if (aw8838 && gpio_is_valid(aw8838->reset_gpio)) { + gpio_set_value_cansleep(aw8838->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw8838->reset_gpio, 1); + msleep(1); + } else { + dev_err(aw8838->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +int aw8838_read_chipid(struct aw8838 *aw8838) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg = 0; + + while(cnt < AW_READ_CHIPID_RETRIES) { + ret = aw8838_i2c_read(aw8838, AW8838_REG_ID, ®); + if (ret < 0) { + dev_err(aw8838->dev, "%s: failed to read register AW8838_REG_ID: %d\n", __func__, ret); + return -EIO; + } + switch (reg) { + case 0x1730: + pr_info("%s aw8838 detected\n", __func__); + aw8838->flags |= AW8838_FLAG_SKIP_INTERRUPTS; + aw8838->flags |= AW8838_FLAG_START_ON_MUTE; + aw8838->chipid = AW8838_ID; + pr_info("%s aw8838->flags=0x%x\n", __func__, aw8838->flags); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", __func__, reg ); + break; + } + cnt ++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys bin attribute + * + *****************************************************/ +static ssize_t aw8838_reg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct aw8838 *aw8838 = dev_get_drvdata(dev); + + if (count != 1) { + pr_info("invalid register address"); + return -EINVAL; + } + + aw8838->reg = buf[0]; + + return 1; +} + +static ssize_t aw8838_rw_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct aw8838 *aw8838 = dev_get_drvdata(dev); + u8 *data; + int ret; + int retries = AW_I2C_RETRIES; + + data = kmalloc(count+1, GFP_KERNEL); + if (data == NULL) { + pr_err("can not allocate memory\n"); + return -ENOMEM; + } + + data[0] = aw8838->reg; + memcpy(&data[1], buf, count); + + retry: + ret = i2c_master_send(aw8838->i2c, data, count+1); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(AW_I2C_RETRY_DELAY); + goto retry; + } + } + + kfree(data); + return ret; +} + +static ssize_t aw8838_rw_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct aw8838 *aw8838 = dev_get_drvdata(dev); + struct i2c_msg msgs[] = { + { + .addr = aw8838->i2c->addr, + .flags = 0, + .len = 1, + .buf = &aw8838->reg, + }, + { + .addr = aw8838->i2c->addr, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + }, + }; + int ret; + int retries = AW_I2C_RETRIES; + retry: + ret = i2c_transfer(aw8838->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(AW_I2C_RETRY_DELAY); + goto retry; + } + return ret; + } + /* ret contains the number of i2c messages send */ + return 1 + ((ret > 1) ? count : 0); +} + +static struct bin_attribute dev_attr_rw = { + .attr = { + .name = "rw", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = aw8838_rw_read, + .write = aw8838_rw_write, +}; + +static struct bin_attribute dev_attr_regaddr = { + .attr = { + .name = "regaddr", + .mode = S_IWUSR, + }, + .size = 0, + .read = NULL, + .write = aw8838_reg_write, +}; + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw8838_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if(2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw8838_i2c_write(aw8838, databuf[0], databuf[1]); + } + + return count; +} + +static ssize_t aw8838_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + for(i = 0; i < AW8838_REG_MAX; i ++) { + if(!(aw8838_reg_access[i]®_RD_ACCESS)) + continue; + aw8838_i2c_read(aw8838, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%04x \n", i, reg_val); + } + return len; +} + +static ssize_t aw8838_spk_rcv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if(1 == sscanf(buf, "%d", &databuf[0])) { + aw8838->spk_rcv_mode = databuf[0]; + } + + return count; +} + +static ssize_t aw8838_spk_rcv_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + if(aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) { + len += snprintf(buf+len, PAGE_SIZE-len, "aw8838 spk_rcv: %d, speaker mode\n", aw8838->spk_rcv_mode); + } else if (aw8838->spk_rcv_mode == AW8838_RECEIVER_MODE) { + len += snprintf(buf+len, PAGE_SIZE-len, "aw8838 spk_rcv: %d, receiver mode\n", aw8838->spk_rcv_mode); + } else { + len += snprintf(buf+len, PAGE_SIZE-len, "aw8838 spk_rcv: %d, unknown mode\n", aw8838->spk_rcv_mode); + } + + return len; +} + + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw8838_reg_show, aw8838_reg_store); +static DEVICE_ATTR(spk_rcv, S_IWUSR | S_IRUGO, aw8838_spk_rcv_show, aw8838_spk_rcv_store); + +static struct attribute *aw8838_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_spk_rcv.attr, + NULL +}; + +static struct attribute_group aw8838_attribute_group = { + .attrs = aw8838_attributes +}; + + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw8838_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct aw8838 *aw8838; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + const char *aw8838drvname = AW8838_I2C_NAME; + + pr_info("%s enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw8838 = devm_kzalloc(&i2c->dev, sizeof(struct aw8838), GFP_KERNEL); + if (aw8838 == NULL) + return -ENOMEM; + + aw8838->dev = &i2c->dev; + aw8838->i2c = i2c; + + i2c_set_clientdata(i2c, aw8838); + mutex_init(&aw8838->cfg_lock); + + /* aw8838 rst & int */ + if (np) { + ret = aw8838_parse_dt(&i2c->dev, aw8838, np); + if (ret) { + dev_err(&i2c->dev, "%s: failed to parse device tree node\n", __func__); + goto err_parse_dt; + } + } else { + aw8838->reset_gpio = -1; + aw8838->irq_gpio = -1; + } + + if (gpio_is_valid(aw8838->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8838->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw8838_rst"); + if (ret){ + dev_err(&i2c->dev, "%s: rst request failed\n", __func__); + goto err_gpio_request; + } + } + + if (gpio_is_valid(aw8838->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8838->irq_gpio, + GPIOF_DIR_IN, "aw8838_int"); + if (ret){ + dev_err(&i2c->dev, "%s: int request failed\n", __func__); + goto err_gpio_request; + } + } + + /* hardware reset */ + aw8838_hw_reset(aw8838); + + /* aw8838 chip id */ + ret = aw8838_read_chipid(aw8838); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw8838_read_chipid failed ret=%d\n", __func__, ret); + goto err_id; + } + +#if 0 + /* aw8838 device name */ + if (i2c->dev.of_node) { + dev_set_name(&i2c->dev, "%s", "aw8838_smartpa"); + } else { + dev_err(&i2c->dev, "%s failed to set device name: %d\n", __func__, ret); + } +#endif + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw8838_dai), GFP_KERNEL); + if (!dai) { + goto err_dai_kzalloc; + } + + memcpy(dai, aw8838_dai, sizeof(aw8838_dai)); + pr_info("%s dai->name(%s)\n", __func__, dai->name); + + awinic_set_dai_name(dai->name, aw8838drvname); + + ret = aw_componet_codec_ops.aw_snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aw8838, dai, ARRAY_SIZE(aw8838_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed to register aw8838: %d\n", __func__, ret); + goto err_register_codec; + } + + /* aw8838 irq */ + if (gpio_is_valid(aw8838->irq_gpio) && + !(aw8838->flags & AW8838_FLAG_SKIP_INTERRUPTS)) { + aw8838_interrupt_setup(aw8838); + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw8838->irq_gpio), + NULL, aw8838_irq, irq_flags, + "aw8838", aw8838); + if (ret != 0) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + gpio_to_irq(aw8838->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw8838->flags |= AW8838_FLAG_SKIP_INTERRUPTS; + } + + /* Register the sysfs files for climax backdoor access */ + ret = device_create_bin_file(&i2c->dev, &dev_attr_rw); + if (ret) + dev_info(&i2c->dev, "%s error creating sysfs files: rw\n", __func__); + ret = device_create_bin_file(&i2c->dev, &dev_attr_regaddr); + if (ret) + dev_info(&i2c->dev, "%s error creating sysfs files: regaddr\n", __func__); + + dev_set_drvdata(&i2c->dev, aw8838); + ret = sysfs_create_group(&i2c->dev.kobj, &aw8838_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, "%s error creating sysfs attr files\n", __func__); + goto err_sysfs; + } + + aw8838_monitor_init(&aw8838->monitor); + + pr_info("%s probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + device_remove_bin_file(&i2c->dev, &dev_attr_regaddr); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); + devm_free_irq(&i2c->dev, gpio_to_irq(aw8838->irq_gpio), aw8838); +err_irq: + aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev); +err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; +err_dai_kzalloc: +err_id: + if (gpio_is_valid(aw8838->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8838->reset_gpio); + if (gpio_is_valid(aw8838->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8838->irq_gpio); +err_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw8838); + aw8838 = NULL; + return ret; +} + +static int aw8838_i2c_remove(struct i2c_client *i2c) +{ + struct aw8838 *aw8838 = i2c_get_clientdata(i2c); + + pr_info("%s enter\n", __func__); + + aw8838_monitor_deinit(&aw8838->monitor); + device_remove_bin_file(&i2c->dev, &dev_attr_regaddr); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); + devm_free_irq(&i2c->dev, gpio_to_irq(aw8838->irq_gpio), aw8838); + + aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(aw8838->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8838->irq_gpio); + if (gpio_is_valid(aw8838->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8838->reset_gpio); + + return 0; +} + +static void aw8838_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw8838 *aw8838 = i2c_get_clientdata(i2c); + + pr_info("%s enter!\n", __func__); + aw8838_stop(aw8838); +} + +static const struct i2c_device_id aw8838_i2c_id[] = { + { AW8838_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw8838_i2c_id); + +static struct of_device_id aw8838_dt_match[] = { + { .compatible = "awinic,aw8838_smartpa" }, + { }, +}; + +static struct i2c_driver aw8838_i2c_driver = { + .driver = { + .name = AW8838_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw8838_dt_match), + }, + .probe = aw8838_i2c_probe, + .remove = aw8838_i2c_remove, + .shutdown = aw8838_i2c_shutdown, + .id_table = aw8838_i2c_id, +}; + + +static int __init aw8838_i2c_init(void) +{ + int ret = 0; + + pr_info("aw8838 driver version %s\n", AW8838_DRIVER_VERSION); + + ret = i2c_add_driver(&aw8838_i2c_driver); + if(ret){ + pr_err("fail to add aw8838 device into i2c\n"); + return ret; + } + + return 0; +} +module_init(aw8838_i2c_init); + + +static void __exit aw8838_i2c_exit(void) +{ + i2c_del_driver(&aw8838_i2c_driver); +} +module_exit(aw8838_i2c_exit); + + +MODULE_DESCRIPTION("ASoC AW8838 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/aw8838/aw8838.h b/asoc/codecs/aw8838/aw8838.h new file mode 100644 index 000000000000..b19eee5cdbde --- /dev/null +++ b/asoc/codecs/aw8838/aw8838.h @@ -0,0 +1,107 @@ +#ifndef _AW8838_H_ +#define _AW8838_H_ + +#include "aw8838_monitor.h" +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 + +#define AW8838_FLAG_START_ON_MUTE (1 << 0) +#define AW8838_FLAG_SKIP_INTERRUPTS (1 << 1) +#define AW8838_FLAG_SAAM_AVAILABLE (1 << 2) +#define AW8838_FLAG_STEREO_DEVICE (1 << 3) +#define AW8838_FLAG_MULTI_MIC_INPUTS (1 << 4) + +#define AW8838_NUM_RATES 9 + +#define AW8838_MAX_REGISTER 0xff + + +enum aw8838_chipid{ + AW8838_ID, +}; + +enum aw8838_mode_spk_rcv{ + AW8838_SPEAKER_MODE = 0, + AW8838_RECEIVER_MODE = 1, +}; + +/******************************************** + Compatible with codec and component + *******************************************/ + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*aw_snd_soc_kcontrol_codec)(struct snd_kcontrol *kcontrol); + void *(*aw_snd_soc_codec_get_drvdata)(aw_snd_soc_codec_t *codec); + int (*aw_snd_soc_add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*aw_snd_soc_unregister_codec)(struct device *dev); + int (*aw_snd_soc_register_codec)(struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai); +}; + +struct aw8838 { + struct regmap *regmap; + struct i2c_client *i2c; + aw_snd_soc_codec_t *codec; + struct device *dev; + struct mutex cfg_lock; +#ifdef AW8838_VBAT_MONITOR + struct hrtimer vbat_monitor_timer; + struct work_struct vbat_monitor_work; +#endif + int sysclk; + int rate; + int pstream; + int cstream; + + int reset_gpio; + int irq_gpio; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_dir; +#endif + u8 reg; + + unsigned int flags; + unsigned int chipid; + unsigned int init; + unsigned int spk_rcv_mode; + struct aw8838_monitor monitor; + uint32_t db_offset; + unsigned int cpmd_default; +}; + +struct aw8838_container{ + int len; + unsigned char data[]; +}; + +int aw8838_i2c_read(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int *reg_data); +int aw8838_i2c_write(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int reg_data); +int aw8838_get_volume(struct aw8838 *aw8838, uint32_t *value); +int aw8838_set_volume(struct aw8838 *aw8838, uint32_t value); +uint32_t aw8838_reg_val_to_db(uint32_t value); +int aw8838_get_hmute(struct aw8838 *aw8838); + +#endif diff --git a/asoc/codecs/aw8838/aw8838_monitor.c b/asoc/codecs/aw8838/aw8838_monitor.c new file mode 100644 index 000000000000..1bf958d3ff37 --- /dev/null +++ b/asoc/codecs/aw8838/aw8838_monitor.c @@ -0,0 +1,1280 @@ +/* + * awinic_monitor.c monitor_module + * + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8838.h" +#include "aw8838_reg.h" +#include "aw8838_monitor.h" + + +#define AW_MONITOR_FILE "aw8838_monitor.bin" + +/*DSP communication*/ +#ifdef AW_QCOM_PLATFORM +extern int afe_get_topology(int port_id); +extern int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write); +#else +static int afe_get_topology(int port_id) +{ + return -EPERM; +} +static int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + return 0; +} +#endif + +static int aw_check_dsp_ready(void) +{ + int topo_id; + + topo_id = afe_get_topology(AFE_PORT_ID_AWDSP_RX); + pr_debug("%s: topo_id 0x%x\n", __func__, topo_id); + + if (topo_id == AW_RX_TOPO_ID) + return true; + else + return false; +} + +static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int data_size) +{ + int try = 0; + + while (try < AW_DSP_TRY_TIME) { + if (aw_check_dsp_ready()) { + return aw_send_afe_cal_apr(param_id, data, + data_size, true); + } else { + try++; + msleep(AW_DSP_SLEEP_TIME); + pr_err("%s: afe not ready try again\n", __func__); + } + } + + return -EINVAL; +} + +static int aw_qcom_set_vmax_to_dsp(uint32_t vmax, int channel) +{ + uint32_t param_id; + + if (channel == AW_CHAN_LEFT) + param_id = AFE_PARAM_ID_AWDSP_RX_VMAX_L; + else + param_id = AFE_PARAM_ID_AWDSP_RX_VMAX_R; + + return aw_qcom_write_data_to_dsp(param_id, &vmax, sizeof(vmax)); +} + +static int aw8838_monitor_get_voltage(struct aw8838 *aw8838, + int *vbat) +{ + char name[] = "battery"; + int ret; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + aw_dev_info(aw8838->dev, "%s enter\n", __func__); + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &prop); + if (ret < 0) { + aw_dev_err(aw8838->dev, + "%s: get voltage failed\n", __func__); + return -EINVAL; + } + *vbat = prop.intval / 1000; + aw_dev_info(aw8838->dev, "%s: get vbat is %d\n", + __func__, *vbat); + } else { + aw_dev_err(aw8838->dev, "%s: no struct power supply name : %s\n", + __func__, name); + return -EINVAL; + } + + return 0; +} + +static int aw8838_monitor_get_temperature(struct aw8838 *aw8838, int *temp) +{ +#ifdef AW_TEMP_EN + char name[] = "battery"; + int ret; + union power_supply_propval sys_temp = { 0 }; + struct power_supply *psy = NULL; + + aw_dev_info(aw8838->dev, "%s enter\n", __func__); + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_TEMP, + &sys_temp); + if (ret < 0) { + aw_dev_err(aw8838->dev, + "%s: get temperature failed\n", __func__); + return -EINVAL; + } + *temp = sys_temp.intval / 10; + aw_dev_info(aw8838->dev, "%s: get temperature is %d\n", + __func__, *temp); + } else { + aw_dev_err(aw8838->dev, "%s: no struct power supply name : %s\n", + __func__, name); + return -EINVAL; + } +#else + aw_dev_info(aw8838->dev, "%s colse temp switch\n", __func__); + aw8838->monitor.monitor_cfg.temp_switch = 0; +#endif + return 0; +} + +static int aw8838_monitor_get_temp_and_vol(struct aw8838 *aw8838) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + unsigned int voltage = 0; + int current_temp = 0; + int ret = -1; + +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw8838_monitor_get_voltage(aw8838, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } + + if (monitor->test_temp == 0) { + ret = aw8838_monitor_get_temperature(aw8838, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw8838_monitor_get_voltage(aw8838, &voltage); + if (ret < 0) + return ret; + + ret = aw8838_monitor_get_temperature(aw8838, ¤t_temp); + if (ret < 0) + return ret; +#endif + + monitor->vol_trace.sum_val += voltage; + monitor->temp_trace.sum_val += current_temp; + monitor->samp_count++; + + return 0; +} + +static int aw8838_monitor_trace_data_from_table(struct aw8838 *aw8838, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw8838->dev, "%s: table_info.aw_table is null\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val && + data_trace->sum_val <= table_info.aw_table[i].max_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + + data_trace->pre_val = data_trace->sum_val; + data_trace->sum_val = 0; + return 0; +} + +static int aw8838_monitor_first_get_data_form_table(struct aw8838 *aw8838, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw8838->dev, "%s: table_info.aw_table is null\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + data_trace->pre_val = data_trace->sum_val; + data_trace->sum_val = 0; + return 0; +} + +static int aw8838_monitor_get_data_from_table(struct aw8838 *aw8838, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace, + uint32_t aplha) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + + if (monitor->first_entry == AW_FIRST_ENTRY) { + return aw8838_monitor_first_get_data_form_table(aw8838, + table_info, data_trace); + } else { + data_trace->sum_val = data_trace->sum_val / monitor->samp_count; + data_trace->sum_val = ((int32_t)aplha * data_trace->sum_val + + (1000 - (int32_t)aplha) * data_trace->pre_val) / 1000; + return aw8838_monitor_trace_data_from_table(aw8838, + table_info, data_trace); + } + + return 0; +} + +static int aw8838_monitor_get_data(struct aw8838 *aw8838) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_monitor_trace *vol_trace = &monitor->vol_trace; + struct aw_monitor_trace *temp_trace = &monitor->temp_trace; + int ret; + + if (monitor_cfg->vol_switch) { + ret = aw8838_monitor_get_data_from_table(aw8838, + monitor_cfg->vol_info, vol_trace, + monitor_cfg->vol_aplha); + if (ret < 0) + return ret; + } else { + vol_trace->aw_table.ipeak = IPEAK_NONE; + vol_trace->aw_table.gain = GAIN_NONE; + vol_trace->aw_table.vmax = VMAX_NONE; + } + + if (monitor_cfg->temp_switch) { + ret = aw8838_monitor_get_data_from_table(aw8838, + monitor_cfg->temp_info, temp_trace, + monitor_cfg->temp_aplha); + if (ret < 0) + return ret; + } else { + temp_trace->aw_table.ipeak = IPEAK_NONE; + temp_trace->aw_table.gain = GAIN_NONE; + temp_trace->aw_table.vmax = VMAX_NONE; + } + + aw_dev_info(aw8838->dev, "%s: vol: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, vol_trace->aw_table.ipeak, + vol_trace->aw_table.gain, + vol_trace->aw_table.vmax); + + aw_dev_info(aw8838->dev, "%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, temp_trace->aw_table.ipeak, + temp_trace->aw_table.gain, + temp_trace->aw_table.vmax); + + return 0; +} + +static void aw8838_monitor_get_cfg(struct aw8838 *aw8838, + struct aw_table *set_table) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + struct aw_table *temp_data = &monitor->temp_trace.aw_table; + struct aw_table *vol_data = &monitor->vol_trace.aw_table; + + if (temp_data->ipeak == IPEAK_NONE && vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else if (temp_data->ipeak == IPEAK_NONE) { + memcpy(set_table, vol_data, sizeof(struct aw_table)); + } else if (vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else { + if (monitor->monitor_cfg.logic_switch == AW_MON_LOGIC_OR) { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + temp_data->ipeak : vol_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + vol_data->gain : temp_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + vol_data->vmax : temp_data->vmax); + } else { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + vol_data->ipeak : temp_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + temp_data->gain : vol_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + temp_data->vmax : vol_data->vmax); + } + } +} + +static void aw8838_monitor_set_ipeak(struct aw8838 *aw8838, + uint16_t ipeak) +{ + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (ipeak == IPEAK_NONE || (!monitor_cfg->ipeak_switch)) + return; + + ret = aw8838_i2c_read(aw8838, AW8838_REG_CPCTRL, ®_val); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: read ipeak failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val &= (~AW8838_BIT_CPCTRL_CP_IPEAK_MASK); + + if (read_reg_val == ipeak) { + aw_dev_info(aw8838->dev, "%s: ipeak = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + + reg_val &= AW8838_BIT_CPCTRL_CP_IPEAK_MASK; + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw8838_i2c_write(aw8838, AW8838_REG_CPCTRL, reg_val); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: write ipeak failed\n", __func__); + return; + } + + aw_dev_info(aw8838->dev, "%s: set reg val = 0x%x, ipeak = 0x%x\n", + __func__, reg_val, ipeak); +} + +static void aw8838_monitor_set_gain(struct aw8838 *aw8838, + uint16_t gain) +{ + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + uint32_t read_volume; + uint32_t set_volume; + uint32_t gain_db; + int ret; + + if (gain == GAIN_NONE || (!monitor_cfg->gain_switch)) + return; + + ret = aw8838_get_volume(aw8838, &read_volume); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: read volume failed\n", __func__); + return; + } + + gain_db = aw8838_reg_val_to_db(gain); + + /*add offset*/ + set_volume = gain_db + aw8838->db_offset; + + if (read_volume == set_volume) { + aw_dev_info(aw8838->dev, "%s: set db = %d.%d dB, no change\n", + __func__, GET_DB_INT(read_volume), + GET_DB_DECIMAL(read_volume)); + return; + } + + ret = aw8838_set_volume(aw8838, set_volume); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: set volume failed\n", __func__); + return; + } + + aw_dev_info(aw8838->dev, "%s: set_volume = %d.%d dB\n", + __func__, GET_DB_INT(set_volume), + GET_DB_DECIMAL(set_volume)); + +} + +static void aw8838_monitor_set_vmax(struct aw8838 *aw8838, + uint32_t vmax) +{ + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + int ret; + + if (vmax == VMAX_NONE || (!monitor_cfg->vmax_switch)) + return; + + if ((aw8838->monitor.pre_vmax == vmax) && + (aw8838->monitor.first_entry != AW_FIRST_ENTRY)) { + aw_dev_info(aw8838->dev, "%s: vmax no change\n", + __func__); + return; + } + + /*left sound channel*/ + ret = aw_qcom_set_vmax_to_dsp(vmax, AW_CHAN_LEFT); + if (ret) { + aw_dev_err(aw8838->dev, "%s: dsp_msg_write error\n", + __func__); + return; + } + aw8838->monitor.pre_vmax = vmax; + aw_dev_info(aw8838->dev, "%s: set vmax = 0x%x\n", __func__, vmax); +} + +static int aw8838_monitor_work(struct aw8838 *aw8838) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_table set_table; + int ret = -1; + + ret = aw8838_monitor_get_temp_and_vol(aw8838); + if (ret < 0) + return ret; + + if (monitor->samp_count < monitor_cfg->monitor_count && + (monitor->first_entry == AW_NOT_FIRST_ENTRY)) + return 0; + + ret = aw8838_monitor_get_data(aw8838); + if (ret < 0) + return ret; + + aw8838_monitor_get_cfg(aw8838, &set_table); + + aw_dev_dbg(aw8838->dev, "%s: set_ipeak = 0x%x, set_gain = 0x%x, set_vmax = 0x%x\n", + __func__, set_table.ipeak, set_table.gain, set_table.vmax); + + aw8838_monitor_set_ipeak(aw8838, set_table.ipeak); + + aw8838_monitor_set_gain(aw8838, set_table.gain); + + aw8838_monitor_set_vmax(aw8838, set_table.vmax); + + monitor->samp_count = 0; + + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + + return 0; +} + +static void aw8838_monitor_work_func(struct work_struct *work) +{ + struct aw8838 *aw8838 = container_of(work, + struct aw8838, monitor.delay_work.work); + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + aw_dev_dbg(aw8838->dev, "%s: monitor is_enable %d,scene_mode %d,monitor_status:%d, monitor_switch:%d\n", + __func__, aw8838->monitor.is_enable, aw8838->spk_rcv_mode, + monitor_cfg->monitor_status, monitor_cfg->monitor_switch); + + if (aw8838->monitor.is_enable && + (aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) && + (monitor_cfg->monitor_status == AW_MON_CFG_OK) && + monitor_cfg->monitor_switch) { + if (!aw8838_get_hmute(aw8838)) { + aw8838_monitor_work(aw8838); + schedule_delayed_work(&aw8838->monitor.delay_work, + msecs_to_jiffies(monitor_cfg->monitor_time)); + } + } +} + +void aw8838_monitor_start(struct aw8838_monitor *monitor) +{ + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw_dev_info(aw8838->dev, "%s: enter\n", __func__); + monitor->first_entry = AW_FIRST_ENTRY; + monitor->samp_count = 0; + monitor->vol_trace.sum_val = 0; + monitor->temp_trace.sum_val = 0; + aw8838->monitor.pre_vmax = 0; + + schedule_delayed_work(&aw8838->monitor.delay_work, + msecs_to_jiffies(0)); +} + +void aw8838_monitor_stop(struct aw8838_monitor *monitor) +{ + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw_dev_info(aw8838->dev, "%s: enter\n", __func__); + cancel_delayed_work_sync(&aw8838->monitor.delay_work); +} + + +/***************************************************** + * load monitor config + *****************************************************/ +static int aw_check_monitor_profile(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + int temp_size, vol_size; + + if (cont->len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw8838->dev, + "%s:params size[%d] < struct aw_monitor_hdr size[%d]!\n", + __func__, cont->len, + (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->temp_offset, cont->len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->vol_offset, cont->len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_size:[%d] overflow file size[%d]!!\n", + __func__, temp_size, cont->len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_size:[%d] overflow file size[%d]!\n", + __func__, vol_size, cont->len); + return -ENOMEM; + } + + return 0; +} + +static int aw_check_monitor_profile_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + int temp_size, vol_size; + + if (cont->len < sizeof(struct aw_monitor_hdr_v_0_1_1)) { + aw_dev_err(aw8838->dev, + "%s:params size[%d] < struct aw_monitor_hdr_v_0_1_1 size[%d]!\n", + __func__, cont->len, + (int)sizeof(struct aw_monitor_hdr_v_0_1_1)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->temp_offset, cont->len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->vol_offset, cont->len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_size:[%d] overflow file size[%d]!!\n", + __func__, temp_size, cont->len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_size:[%d] overflow file size[%d]!\n", + __func__, vol_size, cont->len); + return -ENOMEM; + } + + return 0; +} + + +static void aw_parse_monitor_hdr(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + monitor_cfg->monitor_switch = monitor_hdr->monitor_switch; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = monitor_hdr->ipeak_switch; + monitor_cfg->gain_switch = monitor_hdr->gain_switch; + monitor_cfg->vmax_switch = monitor_hdr->vmax_switch; + monitor_cfg->temp_switch = monitor_hdr->temp_switch; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = monitor_hdr->vol_switch; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw8838->dev, "%s: chip name:%s\n", + __func__, monitor_hdr->chip_type); + aw_dev_info(aw8838->dev, "%s: ui ver:0x%x\n", + __func__, monitor_hdr->ui_ver); + + aw_dev_info(aw8838->dev, + "%s:monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d\n", + __func__, monitor_cfg->monitor_switch, + monitor_cfg->monitor_time, monitor_cfg->monitor_count); + + aw_dev_info(aw8838->dev, + "%s:ipeak_switch:%d, gain_switch:%d, vmax_switch:%d\n", + __func__, monitor_cfg->ipeak_switch, + monitor_cfg->gain_switch, monitor_cfg->vmax_switch); + + aw_dev_info(aw8838->dev, + "%s:temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d\n", + __func__, monitor_cfg->temp_switch, + monitor_cfg->temp_aplha, monitor_cfg->vol_switch, + monitor_cfg->vol_aplha); +} + +static void aw_parse_monitor_hdr_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + monitor_cfg->monitor_switch = (monitor_hdr->enable_flag >> MONITOR_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = (monitor_hdr->enable_flag >> MONITOR_IPEAK_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->logic_switch = (monitor_hdr->enable_flag >> MONITOR_LOGIC_BIT) & MONITOR_EN_MASK; + monitor_cfg->gain_switch = (monitor_hdr->enable_flag >> MONITOR_GAIN_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vmax_switch = (monitor_hdr->enable_flag >> MONITOR_VMAX_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_switch = (monitor_hdr->enable_flag >> MONITOR_TEMP_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = (monitor_hdr->enable_flag >> MONITOR_VOL_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw8838->dev, "%s:chip name:%s\n", + __func__, monitor_hdr->chip_type); + aw_dev_info(aw8838->dev, "%s:ui ver:0x%x\n", + __func__, monitor_hdr->ui_ver); + + aw_dev_info(aw8838->dev, "%s:monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d\n", + __func__, monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw8838->dev, "%s: logic_switch:%d, ipeak_switch:%d, gain_switch:%d, vmax_switch:%d\n", + __func__, monitor_cfg->logic_switch, monitor_cfg->ipeak_switch, monitor_cfg->gain_switch, + monitor_cfg->vmax_switch); + + aw_dev_info(aw8838->dev, "%s:temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d\n", + __func__, monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); + +} + +static void aw_populate_data_to_table(struct aw8838 *aw8838, + struct aw_table_info *table_info, const char *offset_ptr) +{ + int i; + + for (i = 0; i < table_info->table_num * AW_TABLE_SIZE; i += AW_TABLE_SIZE) { + table_info->aw_table[i / AW_TABLE_SIZE].min_val = + GET_16_DATA(offset_ptr[1 + i], offset_ptr[i]); + table_info->aw_table[i / AW_TABLE_SIZE].max_val = + GET_16_DATA(offset_ptr[3 + i], offset_ptr[2 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].ipeak = + GET_16_DATA(offset_ptr[5 + i], offset_ptr[4 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].gain = + GET_16_DATA(offset_ptr[7 + i], offset_ptr[6 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].vmax = + GET_32_DATA(offset_ptr[11 + i], offset_ptr[10 + i], + offset_ptr[9 + i], offset_ptr[8 + i]); + } + + for (i = 0; i < table_info->table_num; i++) + aw_dev_info(aw8838->dev, + "min_val:%d, max_val:%d, ipeak:0x%x, gain:0x%x, vmax:0x%x\n", + table_info->aw_table[i].min_val, + table_info->aw_table[i].max_val, + table_info->aw_table[i].ipeak, + table_info->aw_table[i].gain, + table_info->aw_table[i].vmax); + +} + +static int aw_parse_temp_data(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + struct aw_table_info *temp_info = + &aw8838->monitor.monitor_cfg.temp_info; + + aw_dev_info(aw8838->dev, "%s: ===parse temp start ===\n", + __func__); + + if (temp_info->aw_table != NULL) { + kfree(temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = kzalloc((monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!temp_info->aw_table) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_populate_data_to_table(aw8838, temp_info, + &cont->data[monitor_hdr->temp_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse temp end ===\n", + __func__); + return 0; +} + +static int aw_parse_temp_data_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + struct aw_table_info *temp_info = + &aw8838->monitor.monitor_cfg.temp_info; + + aw_dev_info(aw8838->dev, "%s: ===parse temp start ===\n", + __func__); + + if (temp_info->aw_table != NULL) { + kfree(temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = kzalloc((monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!temp_info->aw_table) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_populate_data_to_table(aw8838, temp_info, + &cont->data[monitor_hdr->temp_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse temp end ===\n", + __func__); + return 0; +} + +static int aw_parse_vol_data(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + struct aw_table_info *vol_info = + &aw8838->monitor.monitor_cfg.vol_info; + + aw_dev_info(aw8838->dev, "%s: ===parse vol start ===\n", + __func__); + + if (vol_info->aw_table != NULL) { + kfree(vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = kzalloc((monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!vol_info->aw_table) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_populate_data_to_table(aw8838, vol_info, + &cont->data[monitor_hdr->vol_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse vol end ===\n", + __func__); + return 0; +} + +static int aw_parse_vol_data_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + struct aw_table_info *vol_info = + &aw8838->monitor.monitor_cfg.vol_info; + + aw_dev_info(aw8838->dev, "%s: ===parse vol start ===\n", + __func__); + + if (vol_info->aw_table != NULL) { + kfree(vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = kzalloc((monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!vol_info->aw_table) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_populate_data_to_table(aw8838, vol_info, + &cont->data[monitor_hdr->vol_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse vol end ===\n", + __func__); + return 0; +} + + +static int aw_parse_monitor_data(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + ret = aw_check_monitor_profile(aw8838, cont); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s:check %s failed\n", + __func__, AW_MONITOR_FILE); + return ret; + } + + aw_parse_monitor_hdr(aw8838, cont); + + ret = aw_parse_temp_data(aw8838, cont); + if (ret < 0) + return ret; + + ret = aw_parse_vol_data(aw8838, cont); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + kfree(monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + +static int aw_parse_monitor_data_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + ret = aw_check_monitor_profile_v_0_1_1(aw8838, cont); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s:check %s failed\n", + __func__, AW_MONITOR_FILE); + return ret; + } + + aw_parse_monitor_hdr_v_0_1_1(aw8838, cont); + + ret = aw_parse_temp_data_v_0_1_1(aw8838, cont); + if (ret < 0) + return ret; + + ret = aw_parse_vol_data_v_0_1_1(aw8838, cont); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + kfree(monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +static int aw_monitor_param_check_sum(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + int i, check_sum = 0; + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + + if (cont->len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw8838->dev, "%s:data size smaller than hdr , please check monitor bin\n", + __func__); + return -ENOMEM; + } + + for (i = 4; i < cont->len; i++) + check_sum += (uint8_t)cont->data[i]; + + if (monitor_hdr->check_sum != check_sum) { + aw_dev_err(aw8838->dev, "%s:check_sum[%d] is not equal to actual check_sum[%d]\n", + __func__, monitor_hdr->check_sum, check_sum); + return -ENOMEM; + } + + return 0; +} + +static int aw_parse_monitor_profile(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + int ret; + + ret = aw_monitor_param_check_sum(aw8838, cont); + if (ret < 0) + return ret; + + switch (monitor_hdr->monitor_ver) { + case AW_MONITOR_HDR_VER_0_1_0: + return aw_parse_monitor_data(aw8838, cont); + case AW_MONITOR_HDR_VER_0_1_1: + return aw_parse_monitor_data_v_0_1_1(aw8838, cont); + default: + aw_dev_err(aw8838->dev, "%s:cfg version:0x%x unsupported\n", + __func__, monitor_hdr->monitor_ver); + return -EINVAL; + } +} + +static void aw_monitor_profile_loaded(const struct firmware *cont, + void *context) +{ + struct aw8838 *aw8838 = context; + struct aw8838_container *monitor_cfg = NULL; + int ret; + + if (aw8838->monitor.monitor_cfg.monitor_status == AW_MON_CFG_ST) { + if (!cont) { + aw_dev_err(aw8838->dev, "%s:failed to read %s\n", + __func__, AW_MONITOR_FILE); + goto exit; + } + + aw_dev_info(aw8838->dev, "%s: loaded %s - size: %zu\n", + __func__, AW_MONITOR_FILE, cont ? cont->size : 0); + + monitor_cfg = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!monitor_cfg) { + aw_dev_err(aw8838->dev, "%s: error allocating memory\n", __func__); + goto exit; + } + monitor_cfg->len = cont->size; + memcpy(monitor_cfg->data, cont->data, cont->size); + ret = aw_parse_monitor_profile(aw8838, monitor_cfg); + if (ret < 0) + aw_dev_err(aw8838->dev, "%s:parse monitor cfg failed\n", + __func__); + kfree(monitor_cfg); + monitor_cfg = NULL; + } + +exit: + release_firmware(cont); +} + +int aw8838_load_monitor_profile(struct aw8838_monitor *monitor) +{ + int ret; + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + if (!monitor->is_enable) { + aw_dev_info(aw8838->dev, "%s: monitor flag:%d, monitor bin noload\n", + __func__, monitor->is_enable); + ret = 0; + } else { + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + AW_MONITOR_FILE, + aw8838->dev, GFP_KERNEL, aw8838, + aw_monitor_profile_loaded); + } + + return ret; +} + +static void aw8838_monitor_load_fw_work_func(struct work_struct *work) +{ + struct aw8838_monitor *monitor = container_of(work, + struct aw8838_monitor, load_fw_work.work); + + aw8838_load_monitor_profile(monitor); +} + +void aw8838_deinit_monitor_profile(struct aw8838_monitor *monitor) +{ + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + + monitor_cfg->monitor_status = AW_MON_CFG_ST; + + if (monitor_cfg->temp_info.aw_table != NULL) { + kfree(monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + } + + if (monitor_cfg->vol_info.aw_table != NULL) { + kfree(monitor_cfg->vol_info.aw_table); + monitor_cfg->vol_info.aw_table = NULL; + } + memset(monitor_cfg, 0, sizeof(struct aw_monitor_cfg)); +} + +#ifdef AW_DEBUG +static ssize_t aw8838_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s: vol set =%d\n", __func__, vol); + aw8838->monitor.test_vol = vol; + + return count; +} + +static ssize_t aw8838_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_vol = aw8838->monitor.test_vol; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8838 vol: %d\n", local_vol); + return len; +} + +static ssize_t aw8838_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s: temp set =%d\n", __func__, temp); + aw8838->monitor.test_temp = temp; + + return count; +} + +static ssize_t aw8838_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + int32_t local_temp = aw8838->monitor.test_temp; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8838 temp: %d\n", local_temp); + return len; +} + +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw8838_vol_show, aw8838_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw8838_temp_show, aw8838_temp_store); +#endif + +static ssize_t aw8838_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s:monitor enable set =%d\n", + __func__, enable); + aw8838->monitor.is_enable = enable; + if (enable) + schedule_delayed_work(&aw8838->monitor.delay_work, + msecs_to_jiffies(0)); + + return count; +} + +static ssize_t aw8838_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_enable; + + local_enable = aw8838->monitor.is_enable; + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8838 monitor enable: %d\n", local_enable); + return len; +} + +static ssize_t aw_monitor_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + uint32_t update = 0; + int ret; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &update); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s:monitor update = %d\n", + __func__, update); + + aw8838_monitor_stop(&aw8838->monitor); + + aw8838_deinit_monitor_profile(&aw8838->monitor); + aw8838_load_monitor_profile(&aw8838->monitor); + msleep(100); + aw8838_monitor_start(&aw8838->monitor); + + return count; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw8838_monitor_show, aw8838_monitor_store); +static DEVICE_ATTR(monitor_update, S_IWUSR, + NULL, aw_monitor_update_store); + + +static struct attribute *aw8838_monitor_attr[] = { + &dev_attr_monitor.attr, + &dev_attr_monitor_update.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw8838_monitor_attr_group = { + .attrs = aw8838_monitor_attr, +}; + +void aw8838_monitor_init(struct aw8838_monitor *monitor) +{ + int ret; + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw_dev_info(aw8838->dev, "%s: enter\n", __func__); + +#ifdef AW_DEBUG + monitor->test_vol = 0; + monitor->test_temp = 0; +#endif + + INIT_DELAYED_WORK(&monitor->delay_work, aw8838_monitor_work_func); + INIT_DELAYED_WORK(&monitor->load_fw_work, aw8838_monitor_load_fw_work_func); + + ret = sysfs_create_group(&aw8838->dev->kobj, + &aw8838_monitor_attr_group); + if (ret < 0) + aw_dev_err(aw8838->dev, "%s error creating sysfs attr files\n", + __func__); +} + +void aw8838_monitor_deinit(struct aw8838_monitor *monitor) +{ + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw8838_monitor_stop(monitor); + aw8838_deinit_monitor_profile(monitor); + + sysfs_remove_group(&aw8838->dev->kobj, &aw8838_monitor_attr_group); +} + +/***************************************************** + * device tree parse monitor param + *****************************************************/ +void aw8838_parse_monitor_dt(struct aw8838_monitor *monitor) +{ + int ret; + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + struct device_node *np = aw8838->dev->of_node; + + ret = of_property_read_u32(np, "monitor-flag", &monitor->is_enable); + if (ret) { + monitor->is_enable = AW8838_MONITOR_DEFAULT_FLAG; + aw_dev_info(aw8838->dev, + "%s: monitor-flag get failed ,user default value!\n", + __func__); + } else { + aw_dev_info(aw8838->dev, "%s: monitor-flag = %d\n", + __func__, monitor->is_enable); + } +} + diff --git a/asoc/codecs/aw8838/aw8838_monitor.h b/asoc/codecs/aw8838/aw8838_monitor.h new file mode 100644 index 000000000000..ea94796f3d91 --- /dev/null +++ b/asoc/codecs/aw8838/aw8838_monitor.h @@ -0,0 +1,201 @@ +#ifndef __AWINIC_MONITOR_H__ +#define __AWINIC_MONITOR_H__ + +/*#define AW_DEBUG*/ +/*#define AW_TEMP_EN*/ +/*#define AW_QCOM_PLATFORM*/ + +#define AFE_PORT_ID_AWDSP_RX (0x1006) /*AFE_PORT_ID_QUATERNARY_MI2S_RX*/ +#define AW_RX_TOPO_ID (0x1000FF01) /*RX topology id*/ +#define AW_DSP_SLEEP_TIME (10) +#define AW_DSP_TRY_TIME (3) + + +struct aw_table; + +#define AW_TABLE_SIZE sizeof(struct aw_table) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_L (0X10013D17) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_R (0X10013D18) + + +#define IPEAK_NONE (0xFF) +#define GAIN_NONE (0xFF) +#define VMAX_NONE (0xFFFFFFFF) +#define GET_DB_INT(x) (x / 2) +#define GET_DB_DECIMAL(x) ((x % 2) * 5) +#define AW_LOAD_MON_FW_DELAY_TIME (3000) +#define AW8838_MONITOR_DEFAULT_FLAG (0) + +#define GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | (((uint8_t)y) << 8) | ((uint8_t)z))) +#define GET_16_DATA(x, y) \ + ((uint16_t)((((uint8_t)x) << 8) | (uint8_t)y)) + +#define MONITOR_EN_MASK 0x01 + +enum { + AW_CHAN_LEFT = 0, + AW_CHAN_RIGHT = 1, +}; + +enum { + AW_MON_LOGIC_OR = 0, + AW_MON_LOGIC_AND = 1, +}; + +enum { + MONITOR_EN_BIT = 0, + MONITOR_LOGIC_BIT = 1, + MONITOR_IPEAK_EN_BIT =2, + MONITOR_GAIN_EN_BIT = 3, + MONITOR_VMAX_EN_BIT = 4, + MONITOR_TEMP_EN_BIT = 5, + MONITOR_VOL_EN_BIT = 6, +}; + +enum aw_monitor_hdr_ver { + AW_MONITOR_HDR_VER_0_1_0 = 0x00010000, + AW_MONITOR_HDR_VER_0_1_1 = 0x00010100, +}; + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +struct aw_table { + int16_t min_val; + int16_t max_val; + uint16_t ipeak; + uint16_t gain; + uint32_t vmax; +}; + +struct aw_table_info { + uint8_t table_num; + struct aw_table *aw_table; +}; + + +enum aw_monitor_init { + AW_MON_CFG_ST = 0, + AW_MON_CFG_OK = 1, +}; + +struct aw_monitor_cfg { + uint8_t monitor_status; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t logic_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + struct aw_table_info temp_info; + struct aw_table_info vol_info; +}; + +struct aw_monitor_hdr { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[8]; + uint32_t ui_ver; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; +}; + +struct aw_monitor_hdr_v_0_1_1 { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[16]; + uint32_t ui_ver; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t enable_flag; + // [bit 31:7] + // [bit 6: vol en] + // [bit 5: temp en] + // [bit 4: vmax en] + // [bit 3: gain en] + // [bit 2: ipeak en] + // [bit 1: & or | flag] + // [bit 0: monitor en] + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; + uint32_t reserver[3]; +}; + +struct aw_monitor_trace { + int32_t pre_val; + int32_t sum_val; + struct aw_table aw_table; +}; + +/****************************************************************** +* struct aw882xx monitor +*******************************************************************/ +struct aw8838_monitor { + struct delayed_work delay_work; + struct delayed_work load_fw_work; + struct aw_monitor_cfg monitor_cfg; + uint32_t is_enable; + uint8_t first_entry; + uint8_t samp_count; + uint32_t pre_vmax; + + struct aw_monitor_trace temp_trace; + struct aw_monitor_trace vol_trace; +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/****************************************************************** +* aw8838 monitor functions +*******************************************************************/ +void aw8838_monitor_start(struct aw8838_monitor *monitor); +void aw8838_monitor_stop(struct aw8838_monitor *monitor); +void aw8838_parse_monitor_dt(struct aw8838_monitor *monitor); +void aw8838_monitor_init(struct aw8838_monitor *monitor); +void aw8838_monitor_deinit(struct aw8838_monitor *monitor); + +/******************************************** + * print information control + *******************************************/ +#define aw_dev_err(dev, format, ...) \ + pr_err("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +#define aw_dev_info(dev, format, ...) \ + pr_info("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +#define aw_dev_dbg(dev, format, ...) \ + pr_debug("[%s]" format, dev_name(dev), ##__VA_ARGS__) + + +#endif + diff --git a/asoc/codecs/aw8838/aw8838_reg.h b/asoc/codecs/aw8838/aw8838_reg.h new file mode 100644 index 000000000000..2128511effbe --- /dev/null +++ b/asoc/codecs/aw8838/aw8838_reg.h @@ -0,0 +1,258 @@ +#ifndef _AW8838_REG_H_ +#define _AW8838_REG_H_ + +/******************************************** + * Register List + *******************************************/ +#define AW8838_REG_ID 0x00 +#define AW8838_REG_SYSST 0x01 +#define AW8838_REG_SYSINT 0x02 +#define AW8838_REG_SYSINTM 0x03 +#define AW8838_REG_SYSCTRL 0x04 +#define AW8838_REG_I2SCTRL 0x05 +#define AW8838_REG_I2STXCFG 0x06 +#define AW8838_REG_PWMCTRL 0x08 +#define AW8838_REG_HAGCCFG0 0x10 +#define AW8838_REG_HAGCCFG1 0x11 +#define AW8838_REG_HAGCCFG2 0x12 +#define AW8838_REG_HAGCCFG3 0x13 +#define AW8838_REG_HAGCCFG4 0x14 +#define AW8838_REG_HAGCCFG5 0x15 +#define AW8838_REG_HAGCCFG6 0x16 +#define AW8838_REG_HAGCCFG7 0x17 +#define AW8838_REG_HAGCCFG8 0x18 +#define AW8838_REG_DBGCTRL 0x20 +#define AW8838_REG_I2SCFG 0x21 +#define AW8838_REG_I2SSTAT 0x22 +#define AW8838_REG_I2SCAPCNT 0x23 +#define AW8838_REG_HPFCCFG1 0x50 +#define AW8838_REG_HPFCCFG2 0x51 +#define AW8838_REG_HPFCCFG3 0x52 +#define AW8838_REG_HPFCCFG4 0x53 +#define AW8838_REG_HPFCCFG5 0x54 +#define AW8838_REG_CPCTRL 0x60 +#define AW8838_REG_PLLCTRL1 0x61 +#define AW8838_REG_PLLCTRL2 0x62 +#define AW8838_REG_AMPDBG1 0x63 +#define AW8838_REG_AMPDBG2 0x64 +#define AW8838_REG_TESTCTRL 0x65 + +#define AW8838_REG_MAX 0x6F + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS 1 << 0 +#define REG_WR_ACCESS 1 << 1 + +static const unsigned char aw8838_reg_access[AW8838_REG_MAX]={ + [AW8838_REG_ID ] = REG_RD_ACCESS, + [AW8838_REG_SYSST ] = REG_RD_ACCESS, + [AW8838_REG_SYSINT ] = REG_RD_ACCESS, + [AW8838_REG_SYSINTM ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_SYSCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2STXCFG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_PWMCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG0 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG3 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG4 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG5 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG6 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG7 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG8 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_DBGCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SCFG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SSTAT ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SCAPCNT] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG3 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG4 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG5 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_CPCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_PLLCTRL1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_PLLCTRL2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_AMPDBG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_AMPDBG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_TESTCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, +}; + +/****************************************************** + * Register Detail + *****************************************************/ +// SYSST +#define AW8838_BIT_SYSST_UVLOS ( 1<<14) +#define AW8838_BIT_SYSST_ADPS ( 1<<13) +#define AW8838_BIT_SYSST_OVPS ( 1<<11) +#define AW8838_BIT_SYSST_HVS ( 1<<10) +#define AW8838_BIT_SYSST_VOS ( 1<< 9) +#define AW8838_BIT_SYSST_SWS ( 1<< 8) +#define AW8838_BIT_SYSST_NOCLKS ( 1<< 5) +#define AW8838_BIT_SYSST_CLKS ( 1<< 4) +#define AW8838_BIT_SYSST_OCDS ( 1<< 3) +#define AW8838_BIT_SYSST_OTLS ( 1<< 2) +#define AW8838_BIT_SYSST_OTHS ( 1<< 1) +#define AW8838_BIT_SYSST_PLLS ( 1<< 0) + +// SYSINT +#define AW8838_BIT_SYSINT_UVLOI ( 1<<14) +#define AW8838_BIT_SYSINT_ADPI ( 1<<13) +#define AW8838_BIT_SYSINT_OVPI ( 1<<11) +#define AW8838_BIT_SYSINT_HVI ( 1<<10) +#define AW8838_BIT_SYSINT_VOI ( 1<< 9) +#define AW8838_BIT_SYSINT_SWI ( 1<< 8) +#define AW8838_BIT_SYSINT_NOCLKI ( 1<< 5) +#define AW8838_BIT_SYSINT_CLKI ( 1<< 4) +#define AW8838_BIT_SYSINT_OCDI ( 1<< 3) +#define AW8838_BIT_SYSINT_OTLI ( 1<< 2) +#define AW8838_BIT_SYSINT_OTHI ( 1<< 1) +#define AW8838_BIT_SYSINT_PLLI ( 1<< 0) + +// SYSINTM +#define AW8838_BIT_SYSINTM_UVLOM ( 1<<14) +#define AW8838_BIT_SYSINTM_ADPM ( 1<<13) +#define AW8838_BIT_SYSINTM_OVPM ( 1<<11) +#define AW8838_BIT_SYSINTM_HVM ( 1<<10) +#define AW8838_BIT_SYSINTM_VOM ( 1<< 9) +#define AW8838_BIT_SYSINTM_SWM ( 1<< 8) +#define AW8838_BIT_SYSINTM_NOCLKM ( 1<< 5) +#define AW8838_BIT_SYSINTM_CLKM ( 1<< 4) +#define AW8838_BIT_SYSINTM_OCDM ( 1<< 3) +#define AW8838_BIT_SYSINTM_OTLM ( 1<< 2) +#define AW8838_BIT_SYSINTM_OTHM ( 1<< 1) +#define AW8838_BIT_SYSINTM_PLLM ( 1<< 0) + +// SYSCTRL +#define AW8838_BIT_SYSCTRL_INTMODE_MASK (~( 3<< 8)) +#define AW8838_BIT_SYSCTRL_INT_HIGH_PP ( 3<< 8) +#define AW8838_BIT_SYSCTRL_INT_LOW_PP ( 2<< 8) +#define AW8838_BIT_SYSCTRL_INT_HIGH_OD ( 1<< 8) +#define AW8838_BIT_SYSCTRL_INT_LOW_OD ( 0<< 8) +#define AW8838_BIT_SYSCTRL_MODE_MASK (~( 1<< 7)) +#define AW8838_BIT_SYSCTRL_RCV_MODE ( 1<< 7) +#define AW8838_BIT_SYSCTRL_SPK_MODE ( 0<< 7) +#define AW8838_BIT_SYSCTRL_I2SEN_MASK (~( 1<< 6)) +#define AW8838_BIT_SYSCTRL_I2S_ENABLE ( 1<< 6) +#define AW8838_BIT_SYSCTRL_I2S_DISABLE ( 0<< 6) +#define AW8838_BIT_SYSCTRL_WSINV_MASK (~( 1<< 5)) +#define AW8838_BIT_SYSCTRL_WS_INVERT ( 1<< 5) +#define AW8838_BIT_SYSCTRL_WS_NO_INVERT ( 0<< 5) +#define AW8838_BIT_SYSCTRL_BCKINV_MASK (~( 1<< 4)) +#define AW8838_BIT_SYSCTRL_BCK_INVERT ( 1<< 4) +#define AW8838_BIT_SYSCTRL_BCK_NO_INVERT ( 0<< 4) +#define AW8838_BIT_SYSCTRL_IPLL_MASK (~( 1<< 3)) +#define AW8838_BIT_SYSCTRL_PLL_WORD ( 1<< 3) +#define AW8838_BIT_SYSCTRL_PLL_BIT ( 0<< 3) +#define AW8838_BIT_SYSCTRL_DSPBY_MASK (~( 1<< 2)) +#define AW8838_BIT_SYSCTRL_DSP_BYPASS ( 1<< 2) +#define AW8838_BIT_SYSCTRL_DSP_WORK ( 0<< 2) +#define AW8838_BIT_SYSCTRL_CP_MASK (~( 1<< 1)) +#define AW8838_BIT_SYSCTRL_CP_PDN ( 1<< 1) +#define AW8838_BIT_SYSCTRL_CP_ACTIVE ( 0<< 1) +#define AW8838_BIT_SYSCTRL_PW_MASK (~( 1<< 0)) +#define AW8838_BIT_SYSCTRL_PW_PDN ( 1<< 0) +#define AW8838_BIT_SYSCTRL_PW_ACTIVE ( 0<< 0) +#define AW8838_BIT_SYSCTRL_CPPD_MASK (~( 1<< 1)) +#define AW8838_BIT_SYSCTRL_CPPD_PDN ( 1<< 1) +#define AW8838_BIT_SYSCTRL_CPPD_ACTIVE ( 0<< 1) + +// I2SCTRL +#define AW8838_BIT_I2SCTRL_INPLEV_MASK (~( 1<<13)) +#define AW8838_BIT_I2SCTRL_INPLEV_0DB ( 1<<13) +#define AW8838_BIT_I2SCTRL_INPLEV_NEG_6DB ( 0<<13) +#define AW8838_BIT_I2SCTRL_STEREO_MASK (~( 1<<12)) +#define AW8838_BIT_I2SCTRL_STEREO_ENABLE ( 1<<12) +#define AW8838_BIT_I2SCTRL_STEREO_DISABLE ( 0<<12) +#define AW8838_BIT_I2SCTRL_CHS_MASK (~( 3<<10)) +#define AW8838_BIT_I2SCTRL_CHS_MONO ( 3<<10) +#define AW8838_BIT_I2SCTRL_CHS_RIGHT ( 2<<10) +#define AW8838_BIT_I2SCTRL_CHS_LEFT ( 1<<10) +#define AW8838_BIT_I2SCTRL_MD_MASK (~( 3<< 8)) +#define AW8838_BIT_I2SCTRL_MD_LSB ( 2<< 8) +#define AW8838_BIT_I2SCTRL_MD_MSB ( 1<< 8) +#define AW8838_BIT_I2SCTRL_MD_STD ( 0<< 8) +#define AW8838_BIT_I2SCTRL_FMS_MASK (~( 3<< 6)) +#define AW8838_BIT_I2SCTRL_FMS_32BIT ( 3<< 6) +#define AW8838_BIT_I2SCTRL_FMS_24BIT ( 2<< 6) +#define AW8838_BIT_I2SCTRL_FMS_20BIT ( 1<< 6) +#define AW8838_BIT_I2SCTRL_FMS_16BIT ( 0<< 6) +#define AW8838_BIT_I2SCTRL_BCK_MASK (~( 3<< 4)) +#define AW8838_BIT_I2SCTRL_BCK_64FS ( 2<< 4) +#define AW8838_BIT_I2SCTRL_BCK_48FS ( 1<< 4) +#define AW8838_BIT_I2SCTRL_BCK_32FS ( 0<< 4) +#define AW8838_BIT_I2SCTRL_SR_MASK (~(15<< 0)) +#define AW8838_BIT_I2SCTRL_SR_192K (10<< 0) +#define AW8838_BIT_I2SCTRL_SR_96K ( 9<< 0) +#define AW8838_BIT_I2SCTRL_SR_48K ( 8<< 0) +#define AW8838_BIT_I2SCTRL_SR_44P1K ( 7<< 0) +#define AW8838_BIT_I2SCTRL_SR_32K ( 6<< 0) +#define AW8838_BIT_I2SCTRL_SR_24K ( 5<< 0) +#define AW8838_BIT_I2SCTRL_SR_22K ( 4<< 0) +#define AW8838_BIT_I2SCTRL_SR_16K ( 3<< 0) +#define AW8838_BIT_I2SCTRL_SR_12K ( 2<< 0) +#define AW8838_BIT_I2SCTRL_SR_11K ( 1<< 0) +#define AW8838_BIT_I2SCTRL_SR_8K ( 0<< 0) + + +// I2STXCFG +#define AW8838_BIT_I2STXCFG_FSYNC_MASK (~( 1<<15)) +#define AW8838_BIT_I2STXCFG_FSYNC_BCK_CYCLE ( 1<<15) +#define AW8838_BIT_I2STXCFG_FSYNC_ONE_SLOT ( 0<<15) +#define AW8838_BIT_I2STXCFG_SLOT_NUM_MASK (~( 1<<14)) +#define AW8838_BIT_I2STXCFG_SLOT_NUM_4_TIMES ( 1<<14) +#define AW8838_BIT_I2STXCFG_SLOT_NUM_2_TIMES ( 0<<14) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_MASK (~(15<<12)) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_3 ( 3<<12) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_2 ( 2<<12) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_1 ( 1<<12) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_0 ( 0<<12) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_MASK (~(15<< 8)) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3_2 (12<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3_1 (10<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3_0 ( 9<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_2_1 ( 6<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_2_0 ( 5<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_1_0 ( 3<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3 ( 8<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_2 ( 4<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_1 ( 2<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_0 ( 1<< 8) +#define AW8838_BIT_I2STXCFG_DRVSTREN_MASK (~( 1<< 5)) +#define AW8838_BIT_I2STXCFG_DRVSTREN_8MA ( 1<< 5) +#define AW8838_BIT_I2STXCFG_DRVSTREN_2MA ( 0<< 5) +#define AW8838_BIT_I2STXCFG_DOHZ_MASK (~( 1<< 4)) +#define AW8838_BIT_I2STXCFG_DOHZ_HIZ ( 1<< 4) +#define AW8838_BIT_I2STXCFG_DOHZ_GND ( 0<< 4) +#define AW8838_BIT_I2STXCFG_DSEL_MASK (~( 3<< 2)) +#define AW8838_BIT_I2STXCFG_DSEL_DSP ( 2<< 2) +#define AW8838_BIT_I2STXCFG_DSEL_GAIN ( 1<< 2) +#define AW8838_BIT_I2STXCFG_DSEL_ZERO ( 0<< 2) +#define AW8838_BIT_I2STXCFG_CHS_MASK (~( 1<< 1)) +#define AW8838_BIT_I2STXCFG_CHS_RIGHT ( 1<< 1) +#define AW8838_BIT_I2STXCFG_CHS_LEFT ( 0<< 1) +#define AW8838_BIT_I2STXCFG_TX_MASK (~( 1<< 0)) +#define AW8838_BIT_I2STXCFG_TX_ENABLE ( 1<< 0) +#define AW8838_BIT_I2STXCFG_TX_DISABLE ( 0<< 0) + +// PWMCTRL +#define AW8838_BIT_PWMCTRL_HMUTE_MASK (~( 1<< 0)) +#define AW8838_BIT_PWMCTRL_HMUTE_ENABLE ( 1<< 0) +#define AW8838_BIT_PWMCTRL_HMUTE_DISABLE ( 0<< 0) + +// HAGCCFG8 +#define AW8838_BIT_HAGCCFG8_VOL_MASK (~(255<< 8)) +#define AW8838_VOLUME_MAX (0) +#define AW8838_VOLUME_MIN (-255) +#define AW8838_VOL_REG_SHIFT (8) +#define AW8838_VOL_6DB_STEP (6 * 2) + +//CPCTRL +#define AW8838_BIT_CPCTRL_CP_IPEAK_MASK (~( 1<< 15)) +#define AW8838_BIT_CPCTRL_CP_MD_MASK (~( 3<< 8)) +#define AW8838_BIT_CPCTRL_CP_MD_TRANS ( 0<<8) + +#endif From a4d1ba6cc003ba547a31767c2c33518e384b51a5 Mon Sep 17 00:00:00 2001 From: liaohj Date: Tue, 8 Aug 2023 17:08:07 +0800 Subject: [PATCH 68/79] audio-kernel: fix build fail caused by aw8838 fix build fail caused by aw8838 just use aw8838 product(CONFIG_SND_SOC_AW8838) build aw8838 ko. Change-Id: Id0acae0077cc526afbd2408259544a655951c805 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2703732 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/Kbuild | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index b466a29e7010..01c29baed095 100755 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -278,8 +278,10 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += aw882xxacf/ obj-y += aw87xxx/ obj-y += fs1815/ +ifeq ($(CONFIG_SND_SOC_AW8838), y) obj-y += aw8838/ endif +endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o obj-$(CONFIG_WCD9XXX_CODEC_CORE_V2) += wcd_core_dlkm.o From 0fe0a0e889c8ac26fab4b4b62f086b8b2909c0f5 Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 11 Aug 2023 13:55:33 +0800 Subject: [PATCH 69/79] aw882xxacf: add hac scene algo ctrl fogo use aw883xx as hac deice. and need diff voice handset & hac skt algo params. here add hac scene algo ctrl. Change-Id: Ic66b6c28f7dad1a5d1b6dc697f18ecbee6c5c2c3 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2707275 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Guobin Zhang Submit-Approved: Jira Key --- asoc/codecs/aw882xxacf/aw882xx.c | 63 +++++++++++++++++++++++++++++--- asoc/codecs/aw882xxacf/aw882xx.h | 1 + 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/aw882xxacf/aw882xx.c b/asoc/codecs/aw882xxacf/aw882xx.c index 987ffd52e137..4a8f27af1789 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.c +++ b/asoc/codecs/aw882xxacf/aw882xx.c @@ -69,6 +69,7 @@ struct aw_container *g_awinic_skt_cfg = NULL; #define AW882XX_SCENE_VOIP_ID 4 #define AW882XX_SCENE_DEEPBUFFER_ID 5 #define AW882XX_SCENE_FASTTRACK_ID 6 +#define AW882XX_SCENE_HAC_ID 7 #ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { @@ -91,7 +92,7 @@ struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { { AW882XX_SCENE_HANDSET_ID, "Handset", - 3, + 4, 3, 0, 0, @@ -99,7 +100,7 @@ struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { { AW882XX_SCENE_VOICE_ID, "Voice", - 4, + 5, 4, 0, 0, @@ -107,7 +108,7 @@ struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { { AW882XX_SCENE_VOIP_ID, "Voip", - 5, + 6, 5, 0, 0, @@ -115,7 +116,7 @@ struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { { AW882XX_SCENE_DEEPBUFFER_ID, "deepbuffer", - 6, + 7, 6, 0, 0, @@ -123,11 +124,20 @@ struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { { AW882XX_SCENE_FASTTRACK_ID, "fasttrack", - 7, + 8, 7, 0, 0, }, + { + AW882XX_SCENE_HAC_ID, + "Hac", + 3, + 8, + 0, + 0, + }, + }; #endif @@ -1927,6 +1937,47 @@ static int aw882xx_set_algo_handset_switch(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_get_algo_hac_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_HAC_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_hac_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_HAC_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + + static int aw882xx_get_algo_voice_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2135,6 +2186,8 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_algo_music_deepbuffer_switch, aw882xx_set_algo_music_deepbuffer_switch), SOC_ENUM_EXT("aw882xx_algo_music_fasttrack_switch", aw882xx_snd_enum[1], aw882xx_get_algo_music_fasttrack_switch, aw882xx_set_algo_music_fasttrack_switch), + SOC_ENUM_EXT("aw882xx_algo_hac_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_hac_switch, aw882xx_set_algo_hac_switch), #endif }; diff --git a/asoc/codecs/aw882xxacf/aw882xx.h b/asoc/codecs/aw882xxacf/aw882xx.h index e4299e40948b..47dea41fdf5d 100644 --- a/asoc/codecs/aw882xxacf/aw882xx.h +++ b/asoc/codecs/aw882xxacf/aw882xx.h @@ -61,6 +61,7 @@ enum { AW_ALGO_PROFILE_ID_4, AW_ALGO_PROFILE_ID_5, AW_ALGO_PROFILE_ID_6, + AW_ALGO_PROFILE_ID_7, AW_ALGO_PROFILE_ID_MAX, }; From 1a5de603e99bfd8d9f4b69618ff5b96d91013cac Mon Sep 17 00:00:00 2001 From: liaohj Date: Tue, 22 Aug 2023 10:43:49 +0800 Subject: [PATCH 70/79] fs1815:correct rotation code use fs1815 algo to switch output left/right channel according to rotation states. correct fs1815 rotation code. Change-Id: I90971371ab5645ab346e8aa16b42f2bd43804106 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2716100 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Jiefeng Ou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/codecs/fs1815/fsm_core.c | 17 +++++++++++------ include/dsp/q6fsm-v3.h | 32 ++++++-------------------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/asoc/codecs/fs1815/fsm_core.c b/asoc/codecs/fs1815/fsm_core.c index e4ac2306a5b4..950f4a5b1718 100644 --- a/asoc/codecs/fs1815/fsm_core.c +++ b/asoc/codecs/fs1815/fsm_core.c @@ -15,7 +15,6 @@ static int g_dev_inited = 0; static atomic_t fsm_amp_switch; static atomic_t fsm_adsp_enable; - //include "q6fsm-v3.h" extern int q6fsm_set_rotation(int angle); extern int q6fsm_set_rx_enable(int enable); @@ -1645,6 +1644,10 @@ int fsm_rotation_put(struct snd_kcontrol *kcontrol, } pr_info("angle: %d", angle); + + if(cfg->next_angle == angle) + return 0; + if(q6fsm_set_rotation(angle) == 0){ cfg->next_angle = angle; } @@ -1735,11 +1738,11 @@ void fsm_speaker_onn(void) cfg->cur_angle = cfg->next_angle; cfg->speaker_on = true; - if(fsm_check_scene_status() == 0) { - q6fsm_fsm_check_scene_status(true); - } else { - q6fsm_fsm_check_scene_status(false); - } + if(fsm_check_scene_status() == 0) { + q6fsm_fsm_check_scene_status(true); + } else { + q6fsm_fsm_check_scene_status(false); + } pr_debug("done"); fsm_mutex_unlock(); @@ -1763,6 +1766,8 @@ void fsm_speaker_off(void) } fsm_list_func(fsm_dev, fsm_stub_shut_down); cfg->speaker_on = false; + cfg->next_angle = 0; + atomic_set(&fsm_adsp_enable, 0); q6fsm_fsm_check_scene_status(false); fsm_mutex_unlock(); fsm_set_scene(0); // scene music diff --git a/include/dsp/q6fsm-v3.h b/include/dsp/q6fsm-v3.h index d0561746d712..b313a6294fac 100644 --- a/include/dsp/q6fsm-v3.h +++ b/include/dsp/q6fsm-v3.h @@ -73,7 +73,6 @@ struct q6fsm_afe { bool monitor_en; int bsg_version; struct bsg_config_v2 config; - int angle; }; typedef enum _fs__chann_t @@ -260,7 +259,7 @@ static int q6fsm_afe_get_params(struct param_hdr_v3 *param_hdr, bool q6fsm_get_rx_status(void) { struct param_hdr_v3 param_hdr; - int enable; + uint32_t enable; int ret; if (!q6fsm_check_dsp_ready()) { @@ -287,13 +286,10 @@ EXPORT_SYMBOL(q6fsm_get_rx_status); int q6fsm_set_rx_enable(int enable) { - struct q6fsm_afe *q6fsm = g_fsm_afe; struct param_hdr_v3 param_hdr; uint32_t param; int ret; - if (q6fsm == NULL) - return -EINVAL; if (!q6fsm_check_dsp_ready()) { fsm_info("DSP not ready yet!"); @@ -305,7 +301,7 @@ int q6fsm_set_rx_enable(int enable) param_hdr.instance_id = 0; param_hdr.reserved = 0; param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; - param_hdr.param_size = sizeof(int); + param_hdr.param_size = sizeof(param); ret = q6fsm_afe_set_params(¶m_hdr, (uint8_t *)¶m, sizeof(param)); if (ret) { @@ -319,28 +315,22 @@ EXPORT_SYMBOL(q6fsm_set_rx_enable); int q6fsm_set_rotation(int angle) { - struct q6fsm_afe *q6fsm = g_fsm_afe; struct param_hdr_v3 param_hdr; fsm_rotation_info_t param = {0}; int ret; - if (q6fsm == NULL) - return -EINVAL; if (!q6fsm_get_rx_status()) { fsm_info("RX module isn't ready"); return -EINVAL; } - if(q6fsm->angle == angle){ - fsm_info("no need to rotation angle"); - return 0; - } - if(angle == 0){ + param.angle = 0; param.ch_sequence[0] = 0; param.ch_sequence[1] = 1; }else if(angle == 1){ + param.angle = 180; param.ch_sequence[0] = 1; param.ch_sequence[1] = 0; }else{ @@ -348,14 +338,13 @@ int q6fsm_set_rotation(int angle) return -EINVAL; } - param.angle = angle; param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; param_hdr.instance_id = 0; param_hdr.reserved = 0; param_hdr.param_id = CAPI_V2_PARAM_FSADSP_ROTATION; - param_hdr.param_size = sizeof(int); + param_hdr.param_size = sizeof(fsm_rotation_info_t); ret = q6fsm_afe_set_params(¶m_hdr, - (uint8_t *)¶m, sizeof(param)); + (uint8_t *)¶m, sizeof(fsm_rotation_info_t)); if (ret) { fsm_err("Set params fail:%d", ret); return ret; @@ -365,15 +354,6 @@ int q6fsm_set_rotation(int angle) } EXPORT_SYMBOL(q6fsm_set_rotation); -int q6fsm_get_rotation(int *angle) -{ - struct q6fsm_afe *q6fsm = g_fsm_afe; - - *angle = q6fsm->angle; - - return 0; -} -EXPORT_SYMBOL(q6fsm_get_rotation); void q6fsm_fsm_check_scene_status(bool status) { From 829cbb1de6847fddc9a58c4e1f8ab6406b1d7fb6 Mon Sep 17 00:00:00 2001 From: liaohj Date: Fri, 25 Aug 2023 14:01:03 +0800 Subject: [PATCH 71/79] q6fsm:add protect for fs1815 algo fs1815 algo need acdb include fsm module. if some scene acdb not fsm module, and afe call fsm algo, will casue adsp block and error. add protect for fs1815 algo. Change-Id: Ib4b77e85443049afc42cb4285d2ea79dce94a380 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2720691 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- include/dsp/q6fsm-v3.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/dsp/q6fsm-v3.h b/include/dsp/q6fsm-v3.h index b313a6294fac..0cd53fc0f581 100644 --- a/include/dsp/q6fsm-v3.h +++ b/include/dsp/q6fsm-v3.h @@ -262,6 +262,10 @@ bool q6fsm_get_rx_status(void) uint32_t enable; int ret; + if(fsm_scene_status == false){ + return -EINVAL; + } + if (!q6fsm_check_dsp_ready()) { fsm_info("DSP not ready yet!"); return -EINVAL; @@ -290,6 +294,9 @@ int q6fsm_set_rx_enable(int enable) uint32_t param; int ret; + if(fsm_scene_status == false){ + return -EINVAL; + } if (!q6fsm_check_dsp_ready()) { fsm_info("DSP not ready yet!"); From d667970a4a6264946b120a55db31ccfc117dff1b Mon Sep 17 00:00:00 2001 From: liaohj Date: Thu, 12 Oct 2023 17:43:00 +0800 Subject: [PATCH 72/79] audio:add lock for aw dynamics set dailink for aw driver, we use dynamics set dailink. and for fogo5gna ,there are aw8838 also use the interface. but aw8838 & aw882xx are different ko. so add lock for awinic_set_dai_name function to avoid rewrite ,and lost one dailink. Change-Id: I418a0207598486cc9d600a5030410912476a8e60 Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2765833 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/holi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/asoc/holi.c b/asoc/holi.c index 01b668ff7157..76dcfe93f58a 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -6250,15 +6250,18 @@ static const struct of_device_id holi_asoc_machine_of_match[] = { #ifdef CONFIG_AW882XX_STEREO_SMARTPA struct snd_soc_dai_link_component aw_codecs[4]; static unsigned int aw_codecs_num; +static DEFINE_MUTEX(aw_dailink_mutex); void awinic_set_dai_name(const char* drvdainame, const char*drvname) { + mutex_lock(&aw_dailink_mutex); if (aw_codecs_num < ARRAY_SIZE(aw_codecs)) { aw_codecs[aw_codecs_num].dai_name = drvdainame; aw_codecs[aw_codecs_num].name = drvname; aw_codecs[aw_codecs_num].of_node = NULL; aw_codecs_num++; } + mutex_unlock(&aw_dailink_mutex); } EXPORT_SYMBOL(awinic_set_dai_name); @@ -6273,6 +6276,8 @@ static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) (!strcmp(dailink[i].stream_name, "Primary MI2S Capture"))){ dailink[i].num_codecs = aw_codecs_num; dailink[i].codecs = aw_codecs; + pr_info("%s: aw_codecs_num=%d, codec_name[%d]:%s dai_name[%d]:%s\n", + __func__, aw_codecs_num, i, dailink[i].codecs[i].name, i, dailink[i].codecs[i].dai_name); } } } else { @@ -6292,6 +6297,8 @@ static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) (!strcmp(dailink[i].stream_name, "Secondary MI2S Capture"))){ dailink[i].num_codecs = aw_codecs_num; dailink[i].codecs = aw_codecs; + pr_info("%s: aw_codecs_num=%d, codec_name[%d]:%s dai_name[%d]:%s\n", + __func__, aw_codecs_num, i, dailink[i].codecs[i].name, i, dailink[i].codecs[i].dai_name); } } } else { From 95b9d463676d320e6904774b1a62ae9e2fbc615b Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 31 Jul 2023 14:26:42 +0530 Subject: [PATCH 73/79] ASoC: msm-pcm-host-voice: Check validity of session idx Added check for voice session index. Mot-CRs-fixed: (CR) CVE-Fixed: CVE-2023-33110 CRs-Fixed: 3459076 Change-Id: Ifff36add5d62f2fdc3395de1447075d297f2c2df Signed-off-by: Soumya Managoli Signed-off-by: Ashutosh Verma Reviewed-on: https://gerrit.mot.com/2760359 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/msm-pcm-host-voice-v2.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/asoc/msm-pcm-host-voice-v2.c b/asoc/msm-pcm-host-voice-v2.c index 42c3a59a99ef..3ecec5eb7a0a 100644 --- a/asoc/msm-pcm-host-voice-v2.c +++ b/asoc/msm-pcm-host-voice-v2.c @@ -752,6 +752,13 @@ void hpcm_notify_evt_processing(uint8_t *data, char *session, return; } + if (prtd->mixer_conf.sess_indx < VOICE_INDEX || + prtd->mixer_conf.sess_indx >= MAX_SESSION) { + pr_err("%s:: Invalid session idx %d\n", + __func__, prtd->mixer_conf.sess_indx); + return; + } + if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) { tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point; tmd = &prtd->mixer_conf.tx; From 27ad5dd6a984a3d13c865bc9745fd6e87ce13361 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 24 Jul 2023 16:14:09 +0530 Subject: [PATCH 74/79] dsp: q6lsm: Add check for payload buffer Check get_param_payload buffer ptr before accessing. Mot-CRs-fixed: (CR) CVE-Fixed: CVE-2023-33118 CRs-Fixed: 3538029 Change-Id: I5470983188dffeec14965a5cdec30747b98735e7 Signed-off-by: Soumya Managoli Signed-off-by: Ashutosh Verma >ashverma@motorola.com> Reviewed-on: https://gerrit.mot.com/2761126 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- dsp/q6lsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index e1aad637d8be..bcb8f652ffe8 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2021, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2020, Linux Foundation. All rights reserved. * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -247,6 +247,7 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) ret = -EINVAL; goto done; } + memcpy((u8 *)client->get_param_payload, (u8 *)payload + payload_min_size_expected, param_size); done: From 641e93631b90732d35dc32acaa8732f51d8b732d Mon Sep 17 00:00:00 2001 From: Sandhya Mutha Naga Venkata Date: Thu, 20 Jul 2023 14:40:44 +0530 Subject: [PATCH 75/79] dsp: q6lsm: Address use after free for mmap handle The global declared mmap_handle can be left dangling for case when the handle is freed by the calling function. Fix is to address this. Also add a check to make sure the mmap_handle is accessed legally. Mot-CRs-fixed: (CR) CVE-Fixed: CVE-2023-33120 CRs-Fixed: 3538938 Change-Id: I367f8a41339aa0025b545b125ee820220efedeee Signed-off-by: Soumya Managoli Signed-off-by: Ashutosh Verma Reviewed-on: https://gerrit.mot.com/2761210 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- dsp/q6lsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index bcb8f652ffe8..deaaa9a68f1d 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -539,6 +539,7 @@ static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, if (wait) mutex_unlock(&lsm_common.apr_lock); + mmap_handle_p = NULL; if (mmap_p && *mmap_p == 0) ret = -ENOMEM; mmap_handle_p = NULL; From 1c44947d3549b564cdbb12697b464f39c4bf8c57 Mon Sep 17 00:00:00 2001 From: Hujun Liao Date: Mon, 16 Oct 2023 03:50:28 -0500 Subject: [PATCH 76/79] Revert "(CR):audio:add lock for aw dynamics set dailink" mabye casue (CR) dump issue. revert firstly. This reverts commit e8324261f16fa213c8b7181f88f5e973ac834a0e. Change-Id: I32b86339567985c4442620c297d6a8d84dcaabb6 Reviewed-on: https://gerrit.mot.com/2768237 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/holi.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/asoc/holi.c b/asoc/holi.c index 76dcfe93f58a..01b668ff7157 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -6250,18 +6250,15 @@ static const struct of_device_id holi_asoc_machine_of_match[] = { #ifdef CONFIG_AW882XX_STEREO_SMARTPA struct snd_soc_dai_link_component aw_codecs[4]; static unsigned int aw_codecs_num; -static DEFINE_MUTEX(aw_dailink_mutex); void awinic_set_dai_name(const char* drvdainame, const char*drvname) { - mutex_lock(&aw_dailink_mutex); if (aw_codecs_num < ARRAY_SIZE(aw_codecs)) { aw_codecs[aw_codecs_num].dai_name = drvdainame; aw_codecs[aw_codecs_num].name = drvname; aw_codecs[aw_codecs_num].of_node = NULL; aw_codecs_num++; } - mutex_unlock(&aw_dailink_mutex); } EXPORT_SYMBOL(awinic_set_dai_name); @@ -6276,8 +6273,6 @@ static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) (!strcmp(dailink[i].stream_name, "Primary MI2S Capture"))){ dailink[i].num_codecs = aw_codecs_num; dailink[i].codecs = aw_codecs; - pr_info("%s: aw_codecs_num=%d, codec_name[%d]:%s dai_name[%d]:%s\n", - __func__, aw_codecs_num, i, dailink[i].codecs[i].name, i, dailink[i].codecs[i].dai_name); } } } else { @@ -6297,8 +6292,6 @@ static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) (!strcmp(dailink[i].stream_name, "Secondary MI2S Capture"))){ dailink[i].num_codecs = aw_codecs_num; dailink[i].codecs = aw_codecs; - pr_info("%s: aw_codecs_num=%d, codec_name[%d]:%s dai_name[%d]:%s\n", - __func__, aw_codecs_num, i, dailink[i].codecs[i].name, i, dailink[i].codecs[i].dai_name); } } } else { From 7bd15a4f608d69a4ce76254176ab77fa054b67c7 Mon Sep 17 00:00:00 2001 From: liaohj Date: Mon, 23 Oct 2023 18:10:21 +0800 Subject: [PATCH 77/79] audio:add lock for aw dynamics set dailink for aw driver, we use dynamics set dailink. and for fogo5gna ,there are aw8838 also use the interface. but aw8838 & aw882xx are different ko. so add lock for awinic_set_dai_name function to avoid rewrite ,and lost one dailink. Change-Id: Ie1dca1fb38b4cb9764a62884adc433422d4b80bb Signed-off-by: liaohj Reviewed-on: https://gerrit.mot.com/2774476 Reviewed-by: Wanlong Zhou SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/holi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/asoc/holi.c b/asoc/holi.c index 01b668ff7157..7df117af25cf 100644 --- a/asoc/holi.c +++ b/asoc/holi.c @@ -6250,15 +6250,20 @@ static const struct of_device_id holi_asoc_machine_of_match[] = { #ifdef CONFIG_AW882XX_STEREO_SMARTPA struct snd_soc_dai_link_component aw_codecs[4]; static unsigned int aw_codecs_num; +static DEFINE_MUTEX(aw_dailink_mutex); void awinic_set_dai_name(const char* drvdainame, const char*drvname) { + mutex_lock(&aw_dailink_mutex); if (aw_codecs_num < ARRAY_SIZE(aw_codecs)) { aw_codecs[aw_codecs_num].dai_name = drvdainame; aw_codecs[aw_codecs_num].name = drvname; aw_codecs[aw_codecs_num].of_node = NULL; + pr_info("aw_codecs_num[%d] codec name %s dai name %s\n", + aw_codecs_num, aw_codecs[aw_codecs_num].name, aw_codecs[aw_codecs_num].dai_name); aw_codecs_num++; } + mutex_unlock(&aw_dailink_mutex); } EXPORT_SYMBOL(awinic_set_dai_name); From 617ded8e1ad1549aefff67cacd9cf88351920b8f Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Mon, 24 Apr 2023 12:26:05 +0530 Subject: [PATCH 78/79] soc: swr-mstr-ctrl: allow runtime suspend first before system suspend Allow the swrm to runtime suspend first before entering system suspend. This will prevent suspend being blocked for about 700ms. Change-Id: I8f90a9f1ba4f6af0decc256cace2f3bd2295dece Signed-off-by: Yunxian He Mot-CRs-fixed: (CR) CRs-Fixed: 06687863 Reviewed-on: https://gerrit.mot.com/2784008 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- soc/swr-mstr-ctrl.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index 098f2fe1c36c..cdc2cb327d26 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -3819,7 +3819,21 @@ static int swrm_suspend(struct device *dev) dev_dbg(swrm->dev, "%s: suspending system, state %d, wlock %d\n", __func__, swrm->pm_state, swrm->wlock_holders); - swrm->pm_state = SWRM_PM_ASLEEP; + /* + * before updating the pm_state to ASLEEP, check if device is + * runtime suspended or not. If it is not, then first make it + * runtime suspend, and then update the pm_state to ASLEEP. + */ + mutex_unlock(&swrm->pm_lock); /* release pm_lock before dev suspend */ + swrm_device_suspend(swrm->dev); /* runtime suspend the device */ + mutex_lock(&swrm->pm_lock); /* acquire pm_lock and update state */ + if (swrm->pm_state == SWRM_PM_SLEEPABLE) { + swrm->pm_state = SWRM_PM_ASLEEP; + } else if (swrm->pm_state == SWRM_PM_AWAKE) { + ret = -EBUSY; + mutex_unlock(&swrm->pm_lock); + goto check_ebusy; + } } else if (swrm->pm_state == SWRM_PM_AWAKE) { /* * unlock to wait for pm_state == SWRM_PM_SLEEPABLE @@ -3872,6 +3886,7 @@ static int swrm_suspend(struct device *dev) pm_runtime_enable(dev); } } +check_ebusy: if (ret == -EBUSY) { /* * There is a possibility that some audio stream is active From 1d127faf74a370aaf6cc2032a18bad428ce38e9a Mon Sep 17 00:00:00 2001 From: Marco Zhang Date: Mon, 30 Oct 2023 01:47:22 -0700 Subject: [PATCH 79/79] Revert "asoc: msm-compress : Fix for CTS-on-gsi with gki" This reverts commit d607d15392285e7ba6c70aeb7bde102cdc1468ca. Reason for revert: Change-Id: I65c7d3291240f6354b0921c8a07f892e3a5ff82f Reviewed-on: https://gerrit.mot.com/2790234 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wanlong Zhou Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- asoc/msm-compress-q6-v2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 6097ca2a7ea3..745ff4771efd 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2017,7 +2017,6 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) atomic_set(&prtd->start, 0); atomic_set(&prtd->drain, 0); #if !IS_ENABLED(CONFIG_AUDIO_QGKI) - snd_compr_use_pause_in_draining(cstream); atomic_set(&prtd->partial_drain, 0); #endif atomic_set(&prtd->xrun, 0);