diff options
Diffstat (limited to 'sound/soc')
| -rw-r--r-- | sound/soc/codecs/wcd-dsp-mgr.c | 388 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd-mbhc-v2.c | 2 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd-spi.c | 31 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd9330.c | 12 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd9335.c | 6 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c | 203 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h | 12 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd934x/wcd934x-routing.h | 4 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd934x/wcd934x.c | 92 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd9xxx-mbhc.c | 4 | ||||
| -rw-r--r-- | sound/soc/codecs/wcd_cpe_core.c | 2 | ||||
| -rw-r--r-- | sound/soc/codecs/wsa881x.c | 2 | ||||
| -rw-r--r-- | sound/soc/msm/msm-dai-fe.c | 68 | ||||
| -rw-r--r-- | sound/soc/msm/msmcobalt.c | 827 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 14 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 24 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c | 41 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h | 4 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/q6asm.c | 471 |
19 files changed, 2053 insertions, 154 deletions
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c index 69246ac9cc87..ee8b27dbec64 100644 --- a/sound/soc/codecs/wcd-dsp-mgr.c +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -16,6 +16,8 @@ #include <linux/stringify.h> #include <linux/of.h> #include <linux/component.h> +#include <linux/dma-mapping.h> +#include <soc/qcom/ramdump.h> #include <sound/wcd-dsp-mgr.h> #include "wcd-dsp-utils.h" @@ -75,6 +77,32 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); #define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) +/* SSR relate status macros */ +#define WDSP_SSR_STATUS_WDSP_READY BIT(0) +#define WDSP_SSR_STATUS_CDC_READY BIT(1) +#define WDSP_SSR_STATUS_READY \ + (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY) +#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ) + +enum wdsp_ssr_type { + + /* Init value, indicates there is no SSR in progress */ + WDSP_SSR_TYPE_NO_SSR = 0, + + /* + * Indicates WDSP crashed. The manager driver internally + * decides when to perform WDSP restart based on the + * users of wdsp. Hence there is no explicit WDSP_UP. + */ + WDSP_SSR_TYPE_WDSP_DOWN, + + /* Indicates codec hardware is down */ + WDSP_SSR_TYPE_CDC_DOWN, + + /* Indicates codec hardware is up, trigger to restart WDSP */ + WDSP_SSR_TYPE_CDC_UP, +}; + struct wdsp_cmpnt { /* OF node of the phandle */ @@ -96,6 +124,21 @@ struct wdsp_cmpnt { struct wdsp_cmpnt_ops *ops; }; +struct wdsp_ramdump_data { + + /* Ramdump device */ + void *rd_dev; + + /* DMA address of the dump */ + dma_addr_t rd_addr; + + /* Virtual address of the dump */ + void *rd_v_addr; + + /* Data provided through error interrupt */ + struct wdsp_err_intr_arg err_data; +}; + struct wdsp_mgr_priv { /* Manager driver's struct device pointer */ @@ -130,8 +173,35 @@ struct wdsp_mgr_priv { /* Lock for serializing ops called by components */ struct mutex api_mutex; + + struct wdsp_ramdump_data dump_data; + + /* SSR related */ + enum wdsp_ssr_type ssr_type; + struct mutex ssr_mutex; + struct work_struct ssr_work; + u16 ready_status; + struct completion ready_compl; }; +static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type) +{ + switch (type) { + case WDSP_SSR_TYPE_NO_SSR: + return "NO_SSR"; + case WDSP_SSR_TYPE_WDSP_DOWN: + return "WDSP_DOWN"; + case WDSP_SSR_TYPE_CDC_DOWN: + return "CDC_DOWN"; + case WDSP_SSR_TYPE_CDC_UP: + return "CDC_UP"; + default: + pr_err("%s: Invalid ssr_type %d\n", + __func__, type); + return "Invalid"; + } +} + static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) { switch (type) { @@ -148,6 +218,26 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) } } +static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value) +{ + wdsp->ready_status &= ~(value); + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); +} + +static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value, bool mark_complete) +{ + wdsp->ready_status |= value; + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); + + if (mark_complete && + wdsp->ready_status == WDSP_SSR_STATUS_READY) { + WDSP_DBG(wdsp, "marking ready completion"); + complete(&wdsp->ready_compl); + } +} + static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, enum wdsp_event_type event, void *data) @@ -199,6 +289,18 @@ static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, return ret; } +static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); + } +} + static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) { struct wdsp_cmpnt *cmpnt; @@ -230,6 +332,8 @@ static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); } + } else { + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL); } return ret; @@ -272,6 +376,7 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, struct wdsp_cmpnt *ctl; struct wdsp_img_segment *seg = NULL; enum wdsp_event_type pre, post; + long status; int ret; ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); @@ -279,9 +384,11 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, if (type == WDSP_ELF_FLAG_RE) { pre = WDSP_EVENT_PRE_DLOAD_CODE; post = WDSP_EVENT_POST_DLOAD_CODE; + status = WDSP_STATUS_CODE_DLOADED; } else if (type == WDSP_ELF_FLAG_WRITE) { pre = WDSP_EVENT_PRE_DLOAD_DATA; post = WDSP_EVENT_POST_DLOAD_DATA; + status = WDSP_STATUS_DATA_DLOADED; } else { WDSP_ERR(wdsp, "Invalid type %u", type); return -EINVAL; @@ -312,6 +419,8 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, } } + WDSP_SET_STATUS(wdsp, status); + /* Notify all components that image is downloaded */ wdsp_broadcast_event_downseq(wdsp, post, NULL); @@ -321,42 +430,47 @@ done: return ret; } -static void wdsp_load_fw_image(struct work_struct *work) +static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp) { - struct wdsp_mgr_priv *wdsp; - struct wdsp_cmpnt *cmpnt; - int ret, idx; - - wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); - if (!wdsp) { - pr_err("%s: Invalid private_data\n", __func__); - goto done; - } + int ret; + bool is_initialized; - /* Initialize the components first */ - ret = wdsp_init_components(wdsp); - if (IS_ERR_VALUE(ret)) - goto done; + is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED); - /* Set init done status */ - WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + if (!is_initialized) { + /* Components are not initialized yet, initialize them */ + ret = wdsp_init_components(wdsp); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "INIT failed, err = %d", ret); + goto done; + } + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } /* Download the read-execute sections of image */ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); if (IS_ERR_VALUE(ret)) { WDSP_ERR(wdsp, "Error %d to download code sections", ret); - for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { - cmpnt = WDSP_GET_COMPONENT(wdsp, idx); - if (cmpnt->ops && cmpnt->ops->deinit) - cmpnt->ops->deinit(cmpnt->cdev, - cmpnt->priv_data); - } - WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + goto done; } - - WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); done: - return; + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret); } static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) @@ -377,8 +491,6 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) goto done; } - WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); - wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, @@ -399,6 +511,21 @@ static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) { int ret; + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* + * If Disable happened while SSR is in progress, then set the SSR + * ready status indicating WDSP is now ready. Ignore the disable + * event here and let the SSR handler go through shutdown. + */ + if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) { + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + return 0; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + /* Make sure wdsp is in good state */ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); @@ -478,8 +605,190 @@ static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, return cmpnt->cdev; } +static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_img_section img_section; + struct wdsp_err_intr_arg *data = &wdsp->dump_data.err_data; + struct ramdump_segment rd_seg; + int ret = 0; + + if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN || + !data->mem_dumps_enabled) { + WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s", + wdsp_get_ssr_type_string(wdsp->ssr_type), + !(data->mem_dumps_enabled) ? "disabled" : "enabled"); + goto done; + } + + if (data->dump_size == 0 || + data->remote_start_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx", + data->remote_start_addr, data->dump_size); + goto done; + } + + if (!wdsp->dump_data.rd_dev) { + WDSP_ERR(wdsp, "Ramdump device is not setup"); + goto done; + } + + WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx", + wdsp->base_addr, data->remote_start_addr, data->dump_size); + + /* Allocate memory for dumps */ + wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev, + data->dump_size, + &wdsp->dump_data.rd_addr, + GFP_KERNEL); + if (!wdsp->dump_data.rd_v_addr) + goto done; + + img_section.addr = data->remote_start_addr - wdsp->base_addr; + img_section.size = data->dump_size; + img_section.data = wdsp->dump_data.rd_v_addr; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_READ_SECTION, + &img_section); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x", + img_section.size, img_section.addr); + goto err_read_dumps; + } + + rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr; + rd_seg.size = img_section.size; + rd_seg.v_address = wdsp->dump_data.rd_v_addr; + + ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret); + +err_read_dumps: + dma_free_coherent(wdsp->mdev, data->dump_size, + wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr); +done: + return; +} + +static void wdsp_ssr_work_fn(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + wdsp_collect_ramdumps(wdsp); + + /* In case of CDC_DOWN event, the DSP is already shutdown */ + if (wdsp->ssr_type != WDSP_SSR_TYPE_CDC_DOWN) { + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (IS_ERR_VALUE(ret)) + WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret); + } + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + ret = wait_for_completion_timeout(&wdsp->ready_compl, + WDSP_SSR_READY_WAIT_TIMEOUT); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + if (ret == 0) { + WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x", + wdsp->ready_status); + goto done; + } + + /* Data sections are to downloaded per WDSP boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + /* + * Even though code section could possible be retained on DSP + * crash, go ahead and still re-download just to avoid any + * memory corruption from previous crash. + */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); + + /* If codec went down, then all components must be re-initialized */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_DOWN) { + wdsp_deinit_components(wdsp); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (IS_ERR_VALUE(ret)) { + WDSP_ERR(wdsp, "Failed to dload code sections err = %d", + ret); + goto done; + } + + /* SSR handling is finished, mark SSR type as NO_SSR */ + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); +} + +static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, + enum wdsp_ssr_type ssr_type) +{ + enum wdsp_ssr_type current_ssr_type; + struct wdsp_err_intr_arg *err_data; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + current_ssr_type = wdsp->ssr_type; + WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s", + wdsp_get_ssr_type_string(current_ssr_type), + wdsp_get_ssr_type_string(ssr_type)); + wdsp->ssr_type = ssr_type; + + if (arg) { + err_data = (struct wdsp_err_intr_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + memset(&wdsp->dump_data.err_data, 0, + sizeof(wdsp->dump_data.err_data)); + } + + switch (ssr_type) { + + case WDSP_SSR_TYPE_WDSP_DOWN: + case WDSP_SSR_TYPE_CDC_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY); + if (ssr_type == WDSP_SSR_TYPE_CDC_DOWN) + __wdsp_clr_ready_locked(wdsp, + WDSP_SSR_STATUS_CDC_READY); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, + NULL); + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_UP: + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true); + break; + + default: + WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type); + /* Revert back the ssr_type for undefined events */ + wdsp->ssr_type = current_ssr_type; + break; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return 0; +} + static int wdsp_intr_handler(struct device *wdsp_dev, - enum wdsp_intr intr) + enum wdsp_intr intr, void *arg) { struct wdsp_mgr_priv *wdsp; int ret; @@ -495,6 +804,9 @@ static int wdsp_intr_handler(struct device *wdsp_dev, ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, WDSP_EVENT_IPC1_INTR, NULL); break; + case WDSP_ERR_INTR: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN); + break; default: ret = -EINVAL; break; @@ -585,6 +897,11 @@ static int wdsp_mgr_bind(struct device *dev) wdsp->ops = &wdsp_ops; + /* Setup ramdump device */ + wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev); + if (!wdsp->dump_data.rd_dev) + dev_info(dev, "%s: create_ramdump_device failed\n", __func__); + ret = component_bind_all(dev, wdsp->ops); if (IS_ERR_VALUE(ret)) WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); @@ -616,6 +933,11 @@ static void wdsp_mgr_unbind(struct device *dev) component_unbind_all(dev, wdsp->ops); + if (wdsp->dump_data.rd_dev) { + destroy_ramdump_device(wdsp->dump_data.rd_dev); + wdsp->dump_data.rd_dev = NULL; + } + /* Clear all status bits */ wdsp->status = 0x00; @@ -746,6 +1068,12 @@ static int wdsp_mgr_probe(struct platform_device *pdev) INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); INIT_LIST_HEAD(wdsp->seg_list); mutex_init(&wdsp->api_mutex); + mutex_init(&wdsp->ssr_mutex); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; + wdsp->ready_status = WDSP_SSR_STATUS_READY; + INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn); + init_completion(&wdsp->ready_compl); + arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0); dev_set_drvdata(mdev, wdsp); ret = component_master_add_with_match(mdev, &wdsp_master_ops, @@ -759,6 +1087,7 @@ static int wdsp_mgr_probe(struct platform_device *pdev) err_master_add: mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); err_dt_parse: devm_kfree(mdev, wdsp->seg_list); devm_kfree(mdev, wdsp); @@ -775,6 +1104,7 @@ static int wdsp_mgr_remove(struct platform_device *pdev) component_master_del(mdev, &wdsp_master_ops); mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); devm_kfree(mdev, wdsp->seg_list); devm_kfree(mdev, wdsp); dev_set_drvdata(mdev, NULL); diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index d207d9ccda34..3cbc1e7821cf 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -2318,7 +2318,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, schedule_delayed_work(&mbhc->mbhc_firmware_dwork, usecs_to_jiffies(FW_READ_TIMEOUT)); else - pr_err("%s: Skipping to read mbhc fw, 0x%p %p\n", + pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n", __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } pr_debug("%s: leave %d\n", __func__, rc); diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c index 3049d87c6c05..60efcb174740 100644 --- a/sound/soc/codecs/wcd-spi.c +++ b/sound/soc/codecs/wcd-spi.c @@ -639,12 +639,10 @@ static int wcd_spi_init(struct spi_device *spi) WCD_SPI_SLAVE_TRNS_LEN, 0xFFFF0000, (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16); -done: - return ret; - err_wr_en: wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, WCD_SPI_CLK_FLAG_IMMEDIATE); +done: return ret; } @@ -813,6 +811,27 @@ static int wdsp_spi_dload_section(struct spi_device *spi, return ret; } +static int wdsp_spi_read_section(struct spi_device *spi, void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, msg.remote_addr, msg.len); + + ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ); + if (IS_ERR_VALUE(ret)) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + static int wdsp_spi_event_handler(struct device *dev, void *priv_data, enum wdsp_event_type event, void *data) @@ -824,6 +843,7 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, __func__, event); switch (event) { + case WDSP_EVENT_PRE_DLOAD_CODE: case WDSP_EVENT_PRE_DLOAD_DATA: ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, WCD_SPI_CLK_FLAG_IMMEDIATE); @@ -846,6 +866,11 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, case WDSP_EVENT_DLOAD_SECTION: ret = wdsp_spi_dload_section(spi, data); break; + + case WDSP_EVENT_READ_SECTION: + ret = wdsp_spi_read_section(spi, data); + break; + default: dev_dbg(&spi->dev, "%s: Unhandled event %d\n", __func__, event); diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c index a8d6e0fa4732..fa396aa55ac9 100644 --- a/sound/soc/codecs/wcd9330.c +++ b/sound/soc/codecs/wcd9330.c @@ -5474,7 +5474,7 @@ static int tomtom_set_channel_map(struct snd_soc_dai *dai, struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec); struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent); if (!tx_slot || !rx_slot) { - pr_err("%s: Invalid tx_slot=%p, rx_slot=%p\n", + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", __func__, tx_slot, rx_slot); return -EINVAL; } @@ -5519,7 +5519,7 @@ static int tomtom_get_channel_map(struct snd_soc_dai *dai, case AIF2_PB: case AIF3_PB: if (!rx_slot || !rx_num) { - pr_err("%s: Invalid rx_slot %p or rx_num %p\n", + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", __func__, rx_slot, rx_num); return -EINVAL; } @@ -5538,7 +5538,7 @@ static int tomtom_get_channel_map(struct snd_soc_dai *dai, case AIF4_VIFEED: case AIF4_MAD_TX: if (!tx_slot || !tx_num) { - pr_err("%s: Invalid tx_slot %p or tx_num %p\n", + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", __func__, tx_slot, tx_num); return -EINVAL; } @@ -8228,7 +8228,7 @@ static void tomtom_compute_impedance(struct wcd9xxx_mbhc *mbhc, s16 *l, s16 *r, struct tomtom_priv *tomtom; if (!mbhc) { - pr_err("%s: Invalid parameters mbhc = %p\n", + pr_err("%s: Invalid parameters mbhc = %pK\n", __func__, mbhc); return; } @@ -8287,7 +8287,7 @@ static void tomtom_zdet_error_approx(struct wcd9xxx_mbhc *mbhc, uint32_t *zl, const int shift = TOMTOM_ZDET_ERROR_APPROX_SHIFT; if (!zl || !zr || !mbhc) { - pr_err("%s: Invalid parameters zl = %p zr = %p, mbhc = %p\n", + pr_err("%s: Invalid parameters zl = %pK zr = %pK, mbhc = %pK\n", __func__, zl, zr, mbhc); return; } @@ -8602,7 +8602,7 @@ static int tomtom_codec_fll_enable(struct snd_soc_codec *codec, struct wcd9xxx *wcd9xxx; if (!codec || !codec->control_data) { - pr_err("%s: Invalid codec handle, %p\n", + pr_err("%s: Invalid codec handle, %pK\n", __func__, codec); return -EINVAL; } diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 2cc895cf1d32..46b8e7f72eb8 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -11080,7 +11080,7 @@ static int tasha_get_channel_map(struct snd_soc_dai *dai, case AIF4_PB: case AIF_MIX1_PB: if (!rx_slot || !rx_num) { - pr_err("%s: Invalid rx_slot %p or rx_num %p\n", + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", __func__, rx_slot, rx_num); return -EINVAL; } @@ -11099,7 +11099,7 @@ static int tasha_get_channel_map(struct snd_soc_dai *dai, case AIF4_MAD_TX: case AIF4_VIFEED: if (!tx_slot || !tx_num) { - pr_err("%s: Invalid tx_slot %p or tx_num %p\n", + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", __func__, tx_slot, tx_num); return -EINVAL; } @@ -11137,7 +11137,7 @@ static int tasha_set_channel_map(struct snd_soc_dai *dai, core = dev_get_drvdata(dai->codec->dev->parent); if (!tx_slot || !rx_slot) { - pr_err("%s: Invalid tx_slot=%p, rx_slot=%p\n", + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", __func__, tx_slot, rx_slot); return -EINVAL; } diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index 225b3a755f66..e649770297f1 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -28,19 +28,22 @@ #define WCD_MEM_ENABLE_MAX_RETRIES 20 #define WCD_DSP_BOOT_TIMEOUT_MS 3000 #define WCD_SYSFS_ENTRY_MAX_LEN 8 - -#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ -{ \ - dev_dbg(codec->dev, "mutex_lock(%s)\n", \ - __func__); \ - mutex_lock(&lock); \ +#define WCD_PROCFS_ENTRY_MAX_LEN 16 +#define WCD_934X_RAMDUMP_START_ADDR 0x20100000 +#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) + +#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ } -#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ -{ \ - dev_dbg(codec->dev, "mutex_unlock(%s)\n",\ - __func__); \ - mutex_unlock(&lock); \ +#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ } struct wcd_cntl_attribute { @@ -147,6 +150,97 @@ static struct kobj_type wcd_cntl_ktype = { .sysfs_ops = &wcd_cntl_sysfs_ops, }; +static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl, + u8 online) +{ + struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry; + unsigned long ret; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + ssr_entry->offline = !online; + /* Make sure the write is complete */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + dev_dbg(cntl->codec->dev, + "%s: requested %u, offline %u offline_change %u, ret = %ldn", + __func__, online, ssr_entry->offline, + ssr_entry->offline_change, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); +} + +static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry, + void *file_priv_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_PROCFS_ENTRY_MAX_LEN]; + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + ssize_t ret; + u8 offline; + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + if (!cntl) { + pr_err("%s: Invalid private data for SSR procfs entry\n", + __func__); + return -EINVAL; + } + + ssr_entry = &cntl->ssr_entry; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + offline = ssr_entry->offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__, + offline ? "true" : "false"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buf, count, &pos, buffer, len); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + unsigned int ret = 0; + + if (!entry || !entry->private_data) { + pr_err("%s: %s is NULL\n", __func__, + (!entry) ? "entry" : "private_data"); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + ssr_entry = &cntl->ssr_entry; + + dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static struct snd_info_entry_ops wdsp_ssr_entry_ops = { + .read = wdsp_ssr_entry_read, + .poll = wdsp_ssr_entry_poll, +}; + static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl) { struct snd_soc_codec *codec = cntl->codec; @@ -553,7 +647,8 @@ static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data) if (cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_handler) - ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR); + ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR, + NULL); else ret = -EINVAL; @@ -568,8 +663,10 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data) { struct wcd_dsp_cntl *cntl = data; struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_intr_arg arg; u16 status = 0; u8 reg_val; + int ret = 0; reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A); status = status | reg_val; @@ -580,6 +677,23 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data) dev_info(codec->dev, "%s: error interrupt status = 0x%x\n", __func__, status); + if ((status & cntl->irqs.fatal_irqs) && + (cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_handler)) { + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; + arg.dump_size = WCD_934X_RAMDUMP_SIZE; + ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_ERR_INTR, + &arg); + if (IS_ERR_VALUE(ret)) + dev_err(cntl->codec->dev, + "%s: Failed to handle fatal irq 0x%x\n", + __func__, status & cntl->irqs.fatal_irqs); + wcd_cntl_change_online_state(cntl, 0); + } else { + dev_err(cntl->codec->dev, "%s: Invalid intr_handler\n", + __func__); + } + return IRQ_HANDLED; } @@ -591,10 +705,15 @@ static int wcd_control_handler(struct device *dev, void *priv_data, int ret = 0; switch (event) { + case WDSP_EVENT_POST_INIT: case WDSP_EVENT_POST_DLOAD_CODE: case WDSP_EVENT_DLOAD_FAILED: case WDSP_EVENT_POST_SHUTDOWN: + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + /* Disable CPAR */ wcd_cntl_cpar_ctrl(cntl, false); /* Disable all the clocks */ @@ -605,12 +724,8 @@ static int wcd_control_handler(struct device *dev, void *priv_data, __func__, ret); break; - case WDSP_EVENT_PRE_DLOAD_CODE: - - wcd_cntl_enable_memory(cntl); - break; - case WDSP_EVENT_PRE_DLOAD_DATA: + case WDSP_EVENT_PRE_DLOAD_CODE: /* Enable all the clocks */ ret = wcd_cntl_clocks_enable(cntl); @@ -623,6 +738,9 @@ static int wcd_control_handler(struct device *dev, void *priv_data, /* Enable CPAR */ wcd_cntl_cpar_ctrl(cntl, true); + + if (event == WDSP_EVENT_PRE_DLOAD_CODE) + wcd_cntl_enable_memory(cntl); break; case WDSP_EVENT_DO_BOOT: @@ -697,6 +815,8 @@ static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) debugfs_create_u32("debug_mode", S_IRUGO | S_IWUSR, cntl->entry, &cntl->debug_mode); + debugfs_create_bool("ramdump_enable", S_IRUGO | S_IWUSR, + cntl->entry, &cntl->ramdump_enable); done: return; } @@ -827,6 +947,10 @@ static int wcd_ctrl_component_bind(struct device *dev, void *data) { struct wcd_dsp_cntl *cntl; + struct snd_soc_codec *codec; + struct snd_card *card; + struct snd_info_entry *entry; + char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; int ret = 0; if (!dev || !master || !data) { @@ -844,13 +968,47 @@ static int wcd_ctrl_component_bind(struct device *dev, cntl->m_dev = master; cntl->m_ops = data; - if (cntl->m_ops->register_cmpnt_ops) - ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, - &control_ops); + if (!cntl->m_ops->register_cmpnt_ops) { + dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n", + __func__); + ret = -EINVAL; + goto done; + } - if (ret) + ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops); + if (ret) { dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", __func__, ret); + goto done; + } + + codec = cntl->codec; + card = codec->component.card->snd_card; + snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", + cntl->dsp_instance, "_state"); + entry = snd_info_create_card_entry(card, proc_name, card->proc_root); + if (!entry) { + /* Do not treat this as Fatal error */ + dev_err(dev, "%s: Failed to create procfs entry %s\n", + __func__, proc_name); + goto done; + } + + cntl->ssr_entry.entry = entry; + cntl->ssr_entry.offline = 1; + entry->size = WCD_PROCFS_ENTRY_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wdsp_ssr_entry_ops; + entry->private_data = cntl; + ret = snd_info_register(entry); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: Failed to register entry %s, err = %d\n", + __func__, proc_name, ret); + snd_info_free_entry(entry); + /* Let bind still happen even if creating the entry failed */ + ret = 0; + } +done: return ret; } @@ -929,6 +1087,8 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec, memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs)); init_completion(&control->boot_complete); mutex_init(&control->clk_mutex); + mutex_init(&control->ssr_mutex); + init_waitqueue_head(&control->ssr_entry.offline_poll_wait); /* * The default state of WDSP is in SVS mode. @@ -981,6 +1141,7 @@ void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) component_del(codec->dev, &wcd_ctrl_component_ops); mutex_destroy(&control->clk_mutex); + mutex_destroy(&control->ssr_mutex); kfree(*cntl); *cntl = NULL; } diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h index 3d6db776a0b5..cd6697b3d641 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -54,6 +54,13 @@ struct wcd_dsp_params { u32 dsp_instance; }; +struct wdsp_ssr_entry { + u8 offline; + u8 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + struct wcd_dsp_cntl { /* Handle to codec */ struct snd_soc_codec *codec; @@ -77,6 +84,7 @@ struct wcd_dsp_cntl { /* Debugfs related */ struct dentry *entry; u32 debug_mode; + bool ramdump_enable; /* WDSP manager drivers data */ struct device *m_dev; @@ -88,6 +96,10 @@ struct wcd_dsp_cntl { /* Keep track of WDSP boot status */ bool is_wdsp_booted; + + /* SSR related */ + struct wdsp_ssr_entry ssr_entry; + struct mutex ssr_mutex; }; void wcd_dsp_cntl_init(struct snd_soc_codec *codec, diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h index ac3031ffe615..940fdf89d361 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-routing.h +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -21,6 +21,7 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, /* Virtual input widget Mixer */ {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, @@ -65,6 +66,8 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"}, {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, @@ -121,6 +124,7 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + {"MAD_BROADCAST", "Switch", "MAD_INP MUX"}, {"MAD_CPE1", "Switch", "MAD_INP MUX"}, {"MAD_CPE2", "Switch", "MAD_INP MUX"}, diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index b4a73d17c322..9e18c17d6f1c 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -186,6 +186,7 @@ enum { AIF3_CAP, AIF4_PB, AIF4_VIFEED, + AIF4_MAD_TX, NUM_CODEC_DAIS, }; @@ -301,13 +302,13 @@ static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = { }; static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { - 0, /* AIF1_PB */ - BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF1_CAP */ - 0, /* AIF2_PB */ - BIT(AIF1_CAP) | BIT(AIF3_CAP), /* AIF2_CAP */ - 0, /* AIF3_PB */ - BIT(AIF1_CAP) | BIT(AIF2_CAP), /* AIF3_CAP */ - 0, /* AIF4_PB */ + 0, /* AIF1_PB */ + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ + 0, /* AIF3_PB */ + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ + 0, /* AIF4_PB */ }; /* Codec supports 2 IIR filters */ @@ -1271,6 +1272,8 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, return 0; } break; + case AIF4_MAD_TX: + break; default: dev_err(codec->dev, "Unknown AIF %d\n", dai_id); mutex_unlock(&tavil_p->codec_mutex); @@ -2474,7 +2477,7 @@ done: return ret; } -static int tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) { int rc = 0; @@ -2519,6 +2522,29 @@ done: return rc; } +static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40); + rc = __tavil_codec_enable_mad(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } + + dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event); + return rc; +} + static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -2533,7 +2559,7 @@ static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, goto done; snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20); - rc = tavil_codec_enable_mad(codec, true); + rc = __tavil_codec_enable_mad(codec, true); if (IS_ERR_VALUE(rc)) { tavil->mad_switch_cnt--; goto done; @@ -2546,7 +2572,7 @@ static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, goto done; snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00); - tavil_codec_enable_mad(codec, false); + __tavil_codec_enable_mad(codec, false); break; } done: @@ -5789,6 +5815,11 @@ static const struct snd_kcontrol_new aif3_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, slim_rx_mux_get, slim_rx_mux_put); WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text, @@ -6110,6 +6141,9 @@ static const struct snd_kcontrol_new mad_cpe1_switch = static const struct snd_kcontrol_new mad_cpe2_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct snd_kcontrol_new adc_us_mux0_switch = SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); @@ -6532,10 +6566,16 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), SND_SOC_DAPM_INPUT("VIINPUT"), @@ -6670,6 +6710,10 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch, tavil_codec_ape_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, &mad_cpe1_switch, tavil_codec_cpe_mad_ctl, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), @@ -6826,6 +6870,7 @@ static int tavil_get_channel_map(struct snd_soc_dai *dai, case AIF1_CAP: case AIF2_CAP: case AIF3_CAP: + case AIF4_MAD_TX: case AIF4_VIFEED: if (!tx_slot || !tx_num) { dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n", @@ -6864,6 +6909,7 @@ static int tavil_set_channel_map(struct snd_soc_dai *dai, { struct tavil_priv *tavil; struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; tavil = snd_soc_codec_get_drvdata(dai->codec); core = dev_get_drvdata(dai->codec->dev->parent); @@ -6878,6 +6924,12 @@ static int tavil_set_channel_map(struct snd_soc_dai *dai, wcd9xxx_init_slimslave(core, core->slim->laddr, tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX13 for MAD data channel */ + dai_data = &tavil->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[WCD934X_TX13].list, + &dai_data->wcd9xxx_ch_list); + return 0; } @@ -7208,7 +7260,7 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); - int ret; + int ret = 0; dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, dai->name, dai->id, params_rate(params), @@ -7238,7 +7290,9 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, tavil->dai[dai->id].rate = params_rate(params); break; case SNDRV_PCM_STREAM_CAPTURE: - ret = tavil_set_decimator_rate(dai, params_rate(params)); + if (dai->id != AIF4_MAD_TX) + ret = tavil_set_decimator_rate(dai, + params_rate(params)); if (ret) { dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n", __func__, ret); @@ -7395,6 +7449,20 @@ static struct snd_soc_dai_driver tavil_dai[] = { }, .ops = &tavil_vi_dai_ops, }, + { + .name = "tavil_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = WCD934X_FORMATS_S16_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tavil_dai_ops, + }, }; static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c index 52ca82fba8e9..2012e4617ee1 100644 --- a/sound/soc/codecs/wcd9xxx-mbhc.c +++ b/sound/soc/codecs/wcd9xxx-mbhc.c @@ -4675,7 +4675,7 @@ int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc, schedule_delayed_work(&mbhc->mbhc_firmware_dwork, usecs_to_jiffies(FW_READ_TIMEOUT)); else - pr_debug("%s: Skipping to read mbhc fw, 0x%p %p\n", + pr_debug("%s: Skipping to read mbhc fw, 0x%pK %pK\n", __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); } @@ -5073,7 +5073,7 @@ static int wcd9xxx_remeasure_z_values(struct wcd9xxx_mbhc *mbhc, right = !!(r); dev_dbg(codec->dev, "%s: Remeasuring impedance values\n", __func__); - dev_dbg(codec->dev, "%s: l: %p, r: %p, left=%d, right=%d\n", __func__, + dev_dbg(codec->dev, "%s: l: %pK, r: %pK, left=%d, right=%d\n", __func__, l, r, left, right); /* Remeasure V2 values */ diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index e9f167fa643b..3aa9ac8d40b6 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -473,7 +473,7 @@ static int wcd_cpe_load_fw(struct wcd_cpe_core *core, bool load_segment; if (!core || !core->cpe_handle) { - pr_err("%s: Error CPE core %p\n", __func__, + pr_err("%s: Error CPE core %pK\n", __func__, core); return -EINVAL; } diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 28fd8930adb2..d7f4044b71ee 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -917,7 +917,7 @@ int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port, if (!port || !ch_mask || !ch_rate || (num_port > WSA881X_MAX_SWR_PORTS)) { dev_err(codec->dev, - "%s: Invalid port=%p, ch_mask=%p, ch_rate=%p\n", + "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n", __func__, port, ch_mask, ch_rate); return -EINVAL; } diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index 4cb62a6b3e7d..ee9dcacdd5c9 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -96,7 +96,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -108,8 +109,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rates = (SNDRV_PCM_RATE_8000_384000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE| - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 4, .rate_min = 8000, @@ -127,7 +129,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -140,7 +143,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -210,7 +214,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 6, .rate_min = 8000, @@ -222,7 +227,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rates = (SNDRV_PCM_RATE_8000_384000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE), + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -240,7 +247,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -259,7 +267,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -271,8 +280,9 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rates = (SNDRV_PCM_RATE_8000_48000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE| - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -290,7 +300,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -303,7 +314,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -321,7 +333,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -340,7 +353,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -353,7 +367,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2220,7 +2235,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2239,7 +2255,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2258,7 +2275,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2277,7 +2295,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2296,7 +2315,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2315,7 +2335,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -2334,7 +2355,8 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, .channels_max = 8, .rate_min = 8000, diff --git a/sound/soc/msm/msmcobalt.c b/sound/soc/msm/msmcobalt.c index d7b0e151f0bf..5c8d91bfe400 100644 --- a/sound/soc/msm/msmcobalt.c +++ b/sound/soc/msm/msmcobalt.c @@ -71,6 +71,9 @@ #define WCN_CDC_SLIM_RX_CH_MAX 2 #define WCN_CDC_SLIM_TX_CH_MAX 3 +#define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 8 + enum { SLIM_RX_0 = 0, SLIM_RX_1, @@ -170,6 +173,131 @@ struct msm_asoc_wcd93xx_codec { void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec); }; +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum { + TDM_PRI = 0, + TDM_SEC, + TDM_TERT, + TDM_QUAT, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + } +}; + +/* TDM default config */ +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* TERT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* QUAT TDM */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + } +}; + +/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */ +static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = { + {0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */ + {AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */ +}; + /* Default configuration of slimbus channels */ static struct dev_config slim_rx_cfg[] = { [SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, @@ -254,7 +382,8 @@ static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; static const char *const vi_feed_ch_text[] = {"One", "Two"}; -static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"}; +static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", + "S32_LE"}; static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"}; static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", @@ -269,9 +398,15 @@ static char const *ch_text[] = {"Two", "Three", "Four", "Five", static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", "KHZ_22P05", "KHZ_32", "KHZ_44P1", "KHZ_48", - "KHZ_96", "KHZ_192"}; + "KHZ_96", "KHZ_192", "KHZ_384"}; static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192"}; +static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", + "Five", "Six", "Seven", "Eight"}; +static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", + "KHZ_44P1", "KHZ_48", "KHZ_96", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", @@ -308,6 +443,12 @@ 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); static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate, ext_disp_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text); @@ -511,6 +652,9 @@ static int slim_get_bit_format_val(int bit_format) int val = 0; switch (bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + val = 3; + break; case SNDRV_PCM_FORMAT_S24_3LE: val = 2; break; @@ -539,6 +683,9 @@ static int slim_get_bit_format(int val) case 2: bit_fmt = SNDRV_PCM_FORMAT_S24_3LE; break; + case 3: + bit_fmt = SNDRV_PCM_FORMAT_S32_LE; + break; default: bit_fmt = SNDRV_PCM_FORMAT_S16_LE; break; @@ -876,6 +1023,9 @@ static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol, int sample_rate_val; switch (usb_rx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; case SAMPLING_RATE_192KHZ: sample_rate_val = 8; break; @@ -916,6 +1066,9 @@ static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (ucontrol->value.integer.value[0]) { + case 9: + usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; case 8: usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ; break; @@ -958,6 +1111,9 @@ static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (usb_rx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; case SNDRV_PCM_FORMAT_S24_3LE: ucontrol->value.integer.value[0] = 2; break; @@ -982,6 +1138,9 @@ static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol, int rc = 0; switch (ucontrol->value.integer.value[0]) { + case 3: + usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; case 2: usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; break; @@ -1024,6 +1183,9 @@ static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol, int sample_rate_val; switch (usb_tx_cfg.sample_rate) { + case SAMPLING_RATE_384KHZ: + sample_rate_val = 9; + break; case SAMPLING_RATE_192KHZ: sample_rate_val = 8; break; @@ -1066,6 +1228,9 @@ static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (ucontrol->value.integer.value[0]) { + case 9: + usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ; + break; case 8: usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ; break; @@ -1108,6 +1273,9 @@ static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { switch (usb_tx_cfg.bit_format) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 3; + break; case SNDRV_PCM_FORMAT_S24_3LE: ucontrol->value.integer.value[0] = 2; break; @@ -1132,6 +1300,9 @@ static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol, int rc = 0; switch (ucontrol->value.integer.value[0]) { + case 3: + usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE; + break; case 2: usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE; break; @@ -1328,6 +1499,45 @@ static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, return 1; } +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLING_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 6: + sample_rate = SAMPLING_RATE_192KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + case 8: + sample_rate = SAMPLING_RATE_384KHZ; + break; + default: + sample_rate = SAMPLING_RATE_48KHZ; + break; + } + return sample_rate; +} + static int aux_pcm_get_sample_rate(int value) { int sample_rate; @@ -1344,6 +1554,45 @@ static int aux_pcm_get_sample_rate(int value) return sample_rate; } +static int tdm_get_sample_rate_val(int sample_rate) +{ + int sample_rate_val = 0; + + switch (sample_rate) { + case SAMPLING_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLING_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_48KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 5; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 6; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_384KHZ: + sample_rate_val = 8; + break; + default: + sample_rate_val = 4; + break; + } + return sample_rate_val; +} + static int aux_pcm_get_sample_rate_val(int sample_rate) { int sample_rate_val; @@ -1360,6 +1609,361 @@ static int aux_pcm_get_sample_rate_val(int sample_rate) return sample_rate_val; } +static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, + struct tdm_port *port) +{ + if (port) { + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + port->mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + port->mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUAT; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + + if (strnstr(kcontrol->id.name, "RX_0", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_0", + sizeof(kcontrol->id.name))) { + port->channel = TDM_0; + } else if (strnstr(kcontrol->id.name, "RX_1", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_1", + sizeof(kcontrol->id.name))) { + port->channel = TDM_1; + } else if (strnstr(kcontrol->id.name, "RX_2", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_2", + sizeof(kcontrol->id.name))) { + port->channel = TDM_2; + } else if (strnstr(kcontrol->id.name, "RX_3", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_3", + sizeof(kcontrol->id.name))) { + port->channel = TDM_3; + } else if (strnstr(kcontrol->id.name, "RX_4", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_4", + sizeof(kcontrol->id.name))) { + port->channel = TDM_4; + } else if (strnstr(kcontrol->id.name, "RX_5", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_5", + sizeof(kcontrol->id.name))) { + port->channel = TDM_5; + } else if (strnstr(kcontrol->id.name, "RX_6", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_6", + sizeof(kcontrol->id.name))) { + port->channel = TDM_6; + } else if (strnstr(kcontrol->id.name, "RX_7", + sizeof(kcontrol->id.name)) || + strnstr(kcontrol->id.name, "TX_7", + sizeof(kcontrol->id.name))) { + port->channel = TDM_7; + } else { + pr_err("%s: unsupported channel in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + } else + return -EINVAL; + return 0; +} + +static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_rx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val( + tdm_tx_cfg[port.mode][port.channel].sample_rate); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].sample_rate = + tdm_get_sample_rate(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].sample_rate, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_get_format(int value) +{ + int format = 0; + + switch (value) { + case 0: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + case 1: + format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 2: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int tdm_get_format_val(int format) +{ + int value = 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 2; + break; + default: + value = 0; + break; + } + return value; +} + +static int tdm_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_rx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = tdm_get_format_val( + tdm_tx_cfg[port.mode][port.channel].bit_format); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].bit_format = + tdm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].bit_format, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + + ucontrol->value.enumerated.item[0] = + tdm_rx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_rx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__, + tdm_rx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + +static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + ucontrol->value.enumerated.item[0] = + tdm_tx_cfg[port.mode][port.channel].channels - 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels - 1, + ucontrol->value.enumerated.item[0]); + } + return ret; +} + +static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tdm_port port; + int ret = tdm_get_port_idx(kcontrol, &port); + + if (ret) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + } else { + tdm_tx_cfg[port.mode][port.channel].channels = + ucontrol->value.enumerated.item[0] + 1; + + pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__, + tdm_tx_cfg[port.mode][port.channel].channels, + ucontrol->value.enumerated.item[0] + 1); + } + return ret; +} + static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) { int idx; @@ -1758,6 +2362,24 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate, ext_disp_rx_sample_rate_get, ext_disp_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate, aux_pcm_rx_sample_rate_get, aux_pcm_rx_sample_rate_put), @@ -2095,6 +2717,22 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = SAMPLING_RATE_48KHZ; break; + case MSM_BACKEND_DAI_TERT_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_TERT_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_TERT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_TERT][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate; + break; + case MSM_BACKEND_DAI_AUXPCM_RX: rate->min = rate->max = aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; @@ -2612,7 +3250,7 @@ static void *def_tasha_mbhc_cal(void) return NULL; #define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y)) - S(v_hs_max, 1500); + S(v_hs_max, 1600); #undef S #define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y)) S(num_btn, WCD_MBHC_DEF_BUTTONS); @@ -3176,6 +3814,157 @@ static struct snd_soc_ops msm_aux_pcm_be_ops = { .shutdown = msm_aux_pcm_snd_shutdown, }; +static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width, + int slots) +{ + unsigned int slot_mask = 0; + int i, j; + unsigned int *slot_offset; + + for (i = TDM_0; i < TDM_PORT_MAX; i++) { + slot_offset = tdm_slot_offset[i]; + + for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) { + if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID) + slot_mask |= + (1 << ((slot_offset[j] * 8) / slot_width)); + else + break; + } + } + + return slot_mask; +} + +static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + int channels, slot_width, slots; + unsigned int slot_mask; + unsigned int *slot_offset; + int offset_channels = 0; + int i; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S16_LE: + /* + * up to 8 channels HW config should + * use 32 bit slot width for max support of + * stream bit width. (slot_width > bit_width) + */ + slot_width = 32; + break; + default: + pr_err("%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + slots = 8; + slot_mask = tdm_param_set_slot_mask(cpu_dai->id, + slot_width, + slots); + if (!slot_mask) { + pr_err("%s: invalid slot_mask 0x%x\n", + __func__, slot_mask); + return -EINVAL; + } + break; + default: + pr_err("%s: invalid param channels %d\n", + __func__, channels); + return -EINVAL; + } + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + case AFE_PORT_ID_SECONDARY_TDM_RX: + case AFE_PORT_ID_TERTIARY_TDM_RX: + case AFE_PORT_ID_QUATERNARY_TDM_RX: + case AFE_PORT_ID_PRIMARY_TDM_TX: + case AFE_PORT_ID_SECONDARY_TDM_TX: + case AFE_PORT_ID_TERTIARY_TDM_TX: + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slot_offset = tdm_slot_offset[TDM_0]; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; + } + + if (offset_channels == 0) { + pr_err("%s: slot offset not supported, offset_channels %d\n", + __func__, offset_channels); + return -EINVAL; + } + + if (channels > offset_channels) { + pr_err("%s: channels %d exceed offset_channels %d\n", + __func__, channels, offset_channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, slot_offset); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0, + slots, slot_width); + if (ret < 0) { + pr_err("%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + slot_offset, 0, NULL); + if (ret < 0) { + pr_err("%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } +end: + return ret; +} + static struct snd_soc_ops msm_be_ops = { .hw_params = msm_snd_hw_params, }; @@ -3192,6 +3981,10 @@ static struct snd_soc_ops msm_wcn_ops = { .hw_params = msm_wcn_hw_params, }; +static struct snd_soc_ops msm_tdm_be_ops = { + .hw_params = msm_tdm_snd_hw_params +}; + /* Digital audio interface glue - connects codec <---> CPU */ static struct snd_soc_dai_link msm_common_dai_links[] = { /* FrontEnd DAI Links */ @@ -3992,6 +4785,34 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, }, + { + .name = LPASS_BE_TERT_TDM_RX_0, + .stream_name = "Tertiary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36896", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_TERT_TDM_TX_0, + .stream_name = "Tertiary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.36897", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, }; static struct snd_soc_dai_link msm_tasha_be_dai_links[] = { diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 841bb5bce13f..770bd12eb501 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -709,6 +709,10 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, } switch (prtd->codec_param.codec.format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + sample_word_size = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: bit_width = 24; sample_word_size = 32; @@ -723,14 +727,16 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, sample_word_size = 16; break; } - ret = q6asm_media_format_block_pcm_format_support_v3( + ret = q6asm_media_format_block_pcm_format_support_v4( prtd->audio_client, prtd->sample_rate, prtd->num_channels, bit_width, stream_id, use_default_chmap, chmap, - sample_word_size); + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_err("%s: CMD Format block failed\n", __func__); @@ -1010,7 +1016,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream) } else { pr_debug("%s: stream_id %d bits_per_sample %d\n", __func__, ac->stream_id, bits_per_sample); - ret = q6asm_stream_open_write_v3(ac, + ret = q6asm_stream_open_write_v4(ac, prtd->codec, bits_per_sample, ac->stream_id, prtd->gapless_state.use_dsp_gapless_mode); @@ -1942,7 +1948,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) pr_debug("%s: open_write stream_id %d bits_per_sample %d", __func__, stream_id, bits_per_sample); - rc = q6asm_stream_open_write_v3(prtd->audio_client, + rc = q6asm_stream_open_write_v4(prtd->audio_client, prtd->codec, bits_per_sample, stream_id, prtd->gapless_state.use_dsp_gapless_mode); diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index a89d88eac41e..e661415a08ca 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -1607,8 +1607,13 @@ static int msm_dai_q6_usb_audio_hw_params(struct snd_pcm_hw_params *params, dai_data->port_config.usb_audio.bit_width = 16; break; case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: dai_data->port_config.usb_audio.bit_width = 24; break; + case SNDRV_PCM_FORMAT_S32_LE: + dai_data->port_config.usb_audio.bit_width = 32; + break; + default: dev_err(dai->dev, "%s: invalid format %d\n", __func__, params_format(params)); @@ -2570,11 +2575,12 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = { SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, - .rate_max = 192000, + .rate_max = 384000, .rate_min = 8000, }, .ops = &msm_dai_q6_ops, @@ -2591,11 +2597,12 @@ static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = { SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, - .rate_max = 192000, + .rate_max = 384000, .rate_min = 8000, }, .ops = &msm_dai_q6_ops, @@ -5825,11 +5832,6 @@ static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, pr_debug("%s: dev_name: %s\n", __func__, dev_name(dai->dev)); - if (params_rate(params) != 48000) { - dev_err(dai->dev, "%s: invalid param rate %d\n", - __func__, params_rate(params)); - return -EINVAL; - } if ((params_channels(params) == 0) || (params_channels(params) > 8)) { dev_err(dai->dev, "%s: invalid param channels %d\n", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index 4e93780c4da0..c5baf0e63732 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -67,10 +67,11 @@ static struct snd_pcm_hardware msm_pcm_hardware_capture = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), - .rates = SNDRV_PCM_RATE_8000_48000, + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 384000, .channels_min = 1, .channels_max = 4, .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * @@ -90,10 +91,11 @@ static struct snd_pcm_hardware msm_pcm_hardware_playback = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S24_3LE), - .rates = SNDRV_PCM_RATE_8000_192000, + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_8000_384000, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 8, .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * @@ -108,7 +110,7 @@ static struct snd_pcm_hardware msm_pcm_hardware_playback = { /* Conventional and unconventional sample rate supported */ static unsigned int supported_sample_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, - 88200, 96000, 176400, 192000 + 88200, 96000, 176400, 192000, 384000 }; static struct snd_pcm_hw_constraint_list constraints_sample_rates = { @@ -313,6 +315,10 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode); switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: bits_per_sample = 24; sample_word_size = 32; @@ -328,7 +334,7 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) break; } - ret = q6asm_open_write_v3(prtd->audio_client, + ret = q6asm_open_write_v4(prtd->audio_client, FORMAT_LINEAR_PCM, bits_per_sample); if (ret < 0) { @@ -353,11 +359,12 @@ static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream) return ret; } - ret = q6asm_media_format_block_multi_ch_pcm_v3( + ret = q6asm_media_format_block_multi_ch_pcm_v4( prtd->audio_client, runtime->rate, runtime->channels, !prtd->set_channel_map, prtd->channel_map, bits_per_sample, - sample_word_size); + sample_word_size, ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_info("%s: CMD Format block failed\n", __func__); @@ -402,6 +409,8 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) || (params_format(params) == SNDRV_PCM_FORMAT_S24_3LE)) bits_per_sample = 24; + else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; /* ULL mode is not supported in capture path */ if (pdata->perf_mode == LEGACY_PCM_MODE) @@ -413,7 +422,7 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) __func__, params_channels(params), prtd->audio_client->perf_mode); - ret = q6asm_open_read_v3(prtd->audio_client, FORMAT_LINEAR_PCM, + ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM, bits_per_sample); if (ret < 0) { pr_err("%s: q6asm_open_read failed\n", __func__); @@ -459,6 +468,10 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) return 0; switch (runtime->format) { + case SNDRV_PCM_FORMAT_S32_LE: + bits_per_sample = 32; + sample_word_size = 32; + break; case SNDRV_PCM_FORMAT_S24_LE: bits_per_sample = 24; sample_word_size = 32; @@ -477,11 +490,13 @@ static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream) pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n", __func__, prtd->samp_rate, prtd->channel_mode, bits_per_sample, sample_word_size); - ret = q6asm_enc_cfg_blk_pcm_format_support_v3(prtd->audio_client, + ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client, prtd->samp_rate, prtd->channel_mode, bits_per_sample, - sample_word_size); + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); if (ret < 0) pr_debug("%s: cmd cfg pcm was block failed", __func__); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h index 72418ea56bb9..8fe31394eef0 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h @@ -59,11 +59,11 @@ struct msm_audio_in_frame_info { #define PLAYBACK_MIN_NUM_PERIODS 2 #define PLAYBACK_MAX_NUM_PERIODS 8 -#define PLAYBACK_MAX_PERIOD_SIZE 12288 +#define PLAYBACK_MAX_PERIOD_SIZE 122880 #define PLAYBACK_MIN_PERIOD_SIZE 128 #define CAPTURE_MIN_NUM_PERIODS 2 #define CAPTURE_MAX_NUM_PERIODS 8 -#define CAPTURE_MAX_PERIOD_SIZE 61440 +#define CAPTURE_MAX_PERIOD_SIZE 122880 #define CAPTURE_MIN_PERIOD_SIZE 320 struct msm_audio { diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index b4257f990aa5..88c27339b299 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -183,6 +183,25 @@ static inline void q6asm_update_token(u32 *token, u8 session_id, u8 stream_id, *token = asm_token.token; } +static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver) +{ + uint32_t pcm_format_id; + + switch (media_format_block_ver) { + case PCM_MEDIA_FORMAT_V4: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4; + break; + case PCM_MEDIA_FORMAT_V3: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + case PCM_MEDIA_FORMAT_V2: + default: + pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + } + return pcm_format_id; +} + /* * q6asm_get_buf_index_from_token: * Retrieve buffer index from token. @@ -2263,7 +2282,7 @@ static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr, static int __q6asm_open_read(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, - bool use_v3_format) + uint32_t pcm_format_block_ver) { int rc = 0x00; struct asm_stream_cmd_open_read_v3 open; @@ -2306,10 +2325,7 @@ static int __q6asm_open_read(struct audio_client *ac, switch (format) { case FORMAT_LINEAR_PCM: open.mode_flags |= 0x00; - if (use_v3_format) - open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; - else - open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver); break; case FORMAT_MPEG4_AAC: open.mode_flags |= BUFFER_META_ENABLE; @@ -2372,14 +2388,14 @@ int q6asm_open_read(struct audio_client *ac, uint32_t format) { return __q6asm_open_read(ac, format, 16, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/); } int q6asm_open_read_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { return __q6asm_open_read(ac, format, bits_per_sample, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/); } /* @@ -2393,10 +2409,25 @@ int q6asm_open_read_v3(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample) { return __q6asm_open_read(ac, format, bits_per_sample, - true /*use_v3_format*/); + PCM_MEDIA_FORMAT_V3/*media fmt block ver*/); } EXPORT_SYMBOL(q6asm_open_read_v3); +/* + * asm_open_read_v4 - Opens audio capture session + * + * @ac: Client session handle + * @format: encoder format + * @bits_per_sample: bit width of capture session + */ +int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/); +} +EXPORT_SYMBOL(q6asm_open_read_v4); + int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format, uint32_t passthrough_flag) { @@ -2488,7 +2519,8 @@ fail_cmd: static int __q6asm_open_write(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, uint32_t stream_id, - bool is_gapless_mode, bool use_v3_format) + bool is_gapless_mode, + uint32_t pcm_format_block_ver) { int rc = 0x00; struct asm_stream_cmd_open_write_v3 open; @@ -2564,11 +2596,7 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, } switch (format) { case FORMAT_LINEAR_PCM: - if (use_v3_format) - open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; - else - open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; - + open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver); break; case FORMAT_MPEG4_AAC: open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2; @@ -2647,7 +2675,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format) { return __q6asm_open_write(ac, format, 16, ac->stream_id, false /*gapless*/, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); } int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, @@ -2655,7 +2683,7 @@ int q6asm_open_write_v2(struct audio_client *ac, uint32_t format, { return __q6asm_open_write(ac, format, bits_per_sample, ac->stream_id, false /*gapless*/, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); } /* @@ -2670,17 +2698,33 @@ int q6asm_open_write_v3(struct audio_client *ac, uint32_t format, { return __q6asm_open_write(ac, format, bits_per_sample, ac->stream_id, false /*gapless*/, - true /*use_v3_format*/); + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); } EXPORT_SYMBOL(q6asm_open_write_v3); +/* + * q6asm_open_write_v4 - Opens audio playback session + * + * @ac: Client session handle + * @format: decoder format + * @bits_per_sample: bit width of playback session + */ +int q6asm_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false /*gapless*/, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_open_write_v4); + int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format, uint16_t bits_per_sample, int32_t stream_id, bool is_gapless_mode) { return __q6asm_open_write(ac, format, bits_per_sample, stream_id, is_gapless_mode, - false /*use_v3_format*/); + PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/); } /* @@ -2698,10 +2742,29 @@ int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format, { return __q6asm_open_write(ac, format, bits_per_sample, stream_id, is_gapless_mode, - true /*use_v3_format*/); + PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/); } EXPORT_SYMBOL(q6asm_stream_open_write_v3); +/* + * q6asm_stream_open_write_v4 - Creates audio stream for playback + * + * @ac: Client session handle + * @format: asm playback format + * @bits_per_sample: bit width of requested stream + * @stream_id: stream id of stream to be associated with this session + * @is_gapless_mode: true if gapless mode needs to be enabled + */ +int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, int32_t stream_id, + bool is_gapless_mode) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + stream_id, is_gapless_mode, + PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/); +} +EXPORT_SYMBOL(q6asm_stream_open_write_v4); + static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, uint32_t wr_format, bool is_meta_data_mode, uint32_t bits_per_sample, @@ -3525,6 +3588,108 @@ fail_cmd: } /* + * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @use_back_flavor: to configure back left and right channel + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, bool use_default_chmap, + bool use_back_flavor, u8 *channel_map, + uint16_t sample_word_size, uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg; + struct asm_enc_cfg_blk_param_v2 enc_fg_blk; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int rc; + + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + rc = -EINVAL; + goto fail_cmd; + } + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&enc_cfg, 0, sizeof(enc_cfg)); + q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE); + atomic_set(&ac->cmd_state, -1); + enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) - + sizeof(enc_cfg.encdec); + enc_cfg.encblk.frames_per_buf = frames_per_buf; + enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size - + sizeof(enc_fg_blk); + enc_cfg.num_channels = channels; + enc_cfg.bits_per_sample = bits_per_sample; + enc_cfg.sample_rate = rate; + enc_cfg.is_signed = 1; + enc_cfg.sample_word_size = sample_word_size; + enc_cfg.endianness = endianness; + enc_cfg.mode = mode; + channel_mapping = enc_cfg.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + pr_debug("%s: setting default channel map for %d channels", + __func__, channels); + if (q6asm_map_channels(channel_mapping, channels, + use_back_flavor)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + pr_debug("%s: Using pre-defined channel map", __func__); + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg); + if (rc < 0) { + pr_err("%s: Command open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout opcode[0x%x]\n", + __func__, enc_cfg.hdr.opcode); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4); + +/* * q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters * * @ac: Client session handle @@ -3700,6 +3865,18 @@ fail_cmd: return rc; } +static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, true, false, NULL, + sample_word_size, endianness, mode); +} + static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, uint32_t rate, uint32_t channels, uint16_t bits_per_sample, @@ -3749,6 +3926,31 @@ int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac, } EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3); +/* + * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels, + bits_per_sample, sample_word_size, + endianness, mode); +} +EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4); + int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, uint32_t rate, uint32_t channels) { @@ -4381,6 +4583,91 @@ fail_cmd: return rc; } +static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id); + atomic_set(&ac->cmd_state, -1); + /* + * Updated the token field with stream/session for compressed playback + * Platform driver must know the the stream with which the command is + * associated + */ + if (ac->io_mode & COMPRESSED_STREAM_IO) + fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) | + (stream_id & 0xFF); + + pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x\n", + __func__, fmt.hdr.token, stream_id, ac->session); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + rc = -EINVAL; + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} + int q6asm_media_format_block_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels) { @@ -4448,6 +4735,47 @@ int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac, } EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3); +/* + * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder + * configuration parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @stream_id: stream id of stream to be associated with this session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample, + int stream_id, + bool use_default_chmap, + char *channel_map, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + if (!use_default_chmap && (channel_map == NULL)) { + pr_err("%s: No valid chan map and can't use default\n", + __func__); + return -EINVAL; + } + return __q6asm_media_format_block_pcm_v4(ac, rate, + channels, bits_per_sample, stream_id, + use_default_chmap, channel_map, + sample_word_size, endianness, + mode); + +} +EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4); + + static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, bool use_default_chmap, char *channel_map, @@ -4581,6 +4909,78 @@ fail_cmd: return rc; } +static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, + uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt; + u8 *channel_mapping; + int rc; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__, + ac->session, rate, channels, + bits_per_sample, sample_word_size); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + atomic_set(&ac->cmd_state, -1); + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.param.num_channels = channels; + fmt.param.bits_per_sample = bits_per_sample; + fmt.param.sample_rate = rate; + fmt.param.is_signed = 1; + fmt.param.sample_word_size = sample_word_size; + fmt.param.endianness = endianness; + fmt.param.mode = mode; + channel_mapping = fmt.param.channel_mapping; + + memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (use_default_chmap) { + if (q6asm_map_channels(channel_mapping, channels, false)) { + pr_err("%s: map channels failed %d\n", + __func__, channels); + rc = -EINVAL; + goto fail_cmd; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt); + if (rc < 0) { + pr_err("%s: Comamnd open failed %d\n", __func__, rc); + goto fail_cmd; + } + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout. waited for format update\n", __func__); + rc = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + return 0; +fail_cmd: + return rc; +} int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, uint32_t rate, uint32_t channels, @@ -4628,6 +5028,39 @@ int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, } EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3); +/* + * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration + * parameters + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @bits_per_sample: bit width of encoder session + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @sample_word_size: Size in bits of the word that holds a sample of a channel + * @endianness: endianness of the pcm data + * @mode: Mode to provide additional info about the pcm input data + */ +int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample, + uint16_t sample_word_size, + uint16_t endianness, + uint16_t mode) +{ + return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels, + use_default_chmap, + channel_map, + bits_per_sample, + sample_word_size, + endianness, + mode); +} +EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4); + static int __q6asm_media_format_block_multi_aac(struct audio_client *ac, struct asm_aac_cfg *cfg, int stream_id) { |
