From 782c8273ed046cbaa8e7447b628ce80f35cf96dc Mon Sep 17 00:00:00 2001 From: Vijay Kumar Maddula Date: Fri, 28 Jul 2023 22:11:57 +0530 Subject: [PATCH] asoc: lahaina: add 16 channel support on tdm interface Added support for 16 channel playback and capture support on tdm interface Change-Id: Idbfe55f5ee29c3f01a379e24e85288321dbeee3b --- asoc/lahaina.c | 729 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 651 insertions(+), 78 deletions(-) diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 191f4b9960ee..82b18769c339 100644 --- a/asoc/lahaina.c +++ b/asoc/lahaina.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -76,6 +76,7 @@ #define WCD_MBHC_HS_V_MAX 1600 #define TDM_CHANNEL_MAX 8 +#define TDM_SLOT_OFFSET_MAX 32 #define MI2S_NUM_CHANNELS 2 @@ -107,7 +108,7 @@ enum { TDM_PORT_MAX, }; -#define TDM_MAX_SLOTS 8 +#define TDM_MAX_SLOTS 16 #define TDM_SLOT_WIDTH_BITS 32 #define TDM_SLOT_WIDTH_BYTES TDM_SLOT_WIDTH_BITS/8 @@ -667,6 +668,59 @@ static struct dev_config afe_loopback_tx_cfg[] = { [AFE_LOOPBACK_TX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; +/* TDM default slot config */ +struct tdm_slot_cfg { + u32 width; + u32 num; +}; + +static struct tdm_slot_cfg tdm_slot[TDM_INTERFACE_MAX] = { + /* PRI TDM */ + {32, 8}, + /* SEC TDM */ + {32, 8}, + /* TERT TDM */ + {32, 8}, + /* QUAT TDM */ + {32, 8}, + /* QUIN TDM */ + {32, 8}, + /* SEN TDM*/ + {32, 8} +}; + +static unsigned int tdm_rx_slot_offset + [TDM_INTERFACE_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEC TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* TERT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUIN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF} +}; + +static unsigned int tdm_tx_slot_offset + [TDM_INTERFACE_MAX][TDM_SLOT_OFFSET_MAX] = { + /* PRI TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEC TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* TERT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUAT TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* QUIN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF}, + /* SEN TDM */ + {0, 4, 8, 12, 16, 20, 24, 0xFFFF} +}; + static int msm_vi_feed_tx_ch = 2; static const char *const vi_feed_ch_text[] = {"One", "Two"}; static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE", @@ -682,12 +736,23 @@ static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; -static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", - "KHZ_48", "KHZ_176P4", - "KHZ_352P8"}; +static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", + "KHZ_16", "KHZ_22P05", + "KHZ_32", "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96", "KHZ_176P4", + "KHZ_192", "KHZ_352P8", "KHZ_384"}; +static const char *const tdm_slot_num_text[] = {"One", "Two", "Four", + "Eight", "Sixteen", "ThirtyTwo"}; +static const char *const tdm_slot_width_text[] = {"16", "24", "32"}; static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"}; -static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four", - "Five", "Six", "Seven", "Eight"}; +static char const *tdm_ch_text[] = { + "One", "Two", "Three", "Four", "Five", "Six", "Seven", + "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", + "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", + "Nineteen", "Twenty", "TwentyOne", "TwentyTwo", "TwentyThree", + "TwentyFour", "TwentyFive", "TwentySix", "TwentySeven", + "TwentyEight", "TwentyNine", "Thirty", "ThirtyOne", "ThirtyTwo" +}; static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"}; static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", "KHZ_22P05", "KHZ_32", "KHZ_44P1", @@ -739,6 +804,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text); static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_num, tdm_slot_num_text); +static SOC_ENUM_SINGLE_EXT_DECL(tdm_slot_width, tdm_slot_width_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text); @@ -1650,34 +1717,43 @@ static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol, return 1; } +static int tdm_get_mode(struct snd_kcontrol *kcontrol) +{ + int mode; + + if (strnstr(kcontrol->id.name, "PRI", + sizeof(kcontrol->id.name))) { + mode = TDM_PRI; + } else if (strnstr(kcontrol->id.name, "SEC", + sizeof(kcontrol->id.name))) { + mode = TDM_SEC; + } else if (strnstr(kcontrol->id.name, "TERT", + sizeof(kcontrol->id.name))) { + mode = TDM_TERT; + } else if (strnstr(kcontrol->id.name, "QUAT", + sizeof(kcontrol->id.name))) { + mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + mode = TDM_QUIN; + } else if (strnstr(kcontrol->id.name, "SEN", + sizeof(kcontrol->id.name))) { + mode = TDM_SEN; + } else { + pr_err("%s: unsupported mode in: %s", + __func__, kcontrol->id.name); + return -EINVAL; + } + return mode; +} + 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 if (strnstr(kcontrol->id.name, "QUIN", - sizeof(kcontrol->id.name))) { - port->mode = TDM_QUIN; - } else if (strnstr(kcontrol->id.name, "SEN", - sizeof(kcontrol->id.name))) { - port->mode = TDM_SEN; - } else { - pr_err("%s: unsupported mode in: %s\n", - __func__, kcontrol->id.name); - return -EINVAL; - } - + port->mode = tdm_get_mode(kcontrol); + if (port->mode < 0) + return port->mode; if (strnstr(kcontrol->id.name, "RX_0", sizeof(kcontrol->id.name)) || strnstr(kcontrol->id.name, "TX_0", @@ -2072,6 +2148,263 @@ static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, return ret; } +static int tdm_get_slot_num_val(int slot_num) +{ + int slot_num_val; + + switch (slot_num) { + case 1: + slot_num_val = 0; + break; + case 2: + slot_num_val = 1; + break; + case 4: + slot_num_val = 2; + break; + case 8: + slot_num_val = 3; + break; + case 16: + slot_num_val = 4; + break; + case 32: + slot_num_val = 5; + break; + default: + slot_num_val = 5; + break; + } + return slot_num_val; +} + +static int tdm_get_slot_num(int value) +{ + int slot_num; + + switch (value) { + case 0: + slot_num = 1; + break; + case 1: + slot_num = 2; + break; + case 2: + slot_num = 4; + break; + case 3: + slot_num = 8; + break; + case 4: + slot_num = 16; + break; + case 5: + slot_num = 32; + break; + default: + slot_num = 8; + break; + } + return slot_num; +} + +static int tdm_slot_num_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + ucontrol->value.enumerated.item[0] = + tdm_get_slot_num_val(tdm_slot[mode].num); + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_slot_num_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + tdm_slot[mode].num = + tdm_get_slot_num(ucontrol->value.enumerated.item[0]); + pr_debug("%s: mode = %d, tdm_slot_num = %d, item = %d\n", __func__, + mode, tdm_slot[mode].num, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_get_slot_width_val(int slot_width) +{ + int slot_width_val; + + switch (slot_width) { + case 16: + slot_width_val = 0; + break; + case 24: + slot_width_val = 1; + break; + case 32: + slot_width_val = 2; + break; + default: + slot_width_val = 2; + break; + } + return slot_width_val; +} + +static int tdm_slot_width_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + ucontrol->value.enumerated.item[0] = + tdm_get_slot_width_val(tdm_slot[mode].width); + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_get_slot_width(int value) +{ + int slot_width; + + switch (value) { + case 0: + slot_width = 16; + break; + case 1: + slot_width = 24; + break; + case 2: + slot_width = 32; + break; + default: + slot_width = 32; + break; + } + return slot_width; +} + +static int tdm_slot_width_put(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + tdm_slot[mode].width = + tdm_get_slot_width(ucontrol->value.enumerated.item[0]); + pr_debug("%s: mode = %d, tdm_slot_width = %d, item = %d\n", __func__, + mode, tdm_slot[mode].width, + ucontrol->value.enumerated.item[0]); + return 0; +} + +static int tdm_rx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_rx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} + +static int tdm_rx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_rx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} +static int tdm_tx_slot_mapping_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_tx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + ucontrol->value.integer.value[i] = slot_offset[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} + +static int tdm_tx_slot_mapping_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int *slot_offset; + int i; + int mode = tdm_get_mode(kcontrol); + + if (mode < 0) { + pr_err("%s: unsupported control: %s", + __func__, kcontrol->id.name); + return mode; + } + slot_offset = tdm_tx_slot_offset[mode]; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + slot_offset[i] = ucontrol->value.integer.value[i]; + pr_debug("%s: offset %d, value %d\n", + __func__, i, slot_offset[i]); + } + return 0; +} + static int tdm_slot_map_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2899,6 +3232,112 @@ err: return ret; } +static int lahaina_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + if (cpu_dai->id == AFE_PORT_ID_PRIMARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_PRIMARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_PRI][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_PRI][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_SEC][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEC][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_TERTIARY_TDM_RX) { + 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; + } else if (cpu_dai->id == AFE_PORT_ID_TERTIARY_TDM_TX) { + 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; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUINARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SENARY_TDM_RX) { + channels->min = channels->max = + tdm_rx_cfg[TDM_SEN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_SEN][TDM_0].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_SEN][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_SENARY_TDM_TX) { + channels->min = channels->max = + tdm_tx_cfg[TDM_SEN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_SEN][TDM_0].bit_format); + rate->min = rate->max = + tdm_tx_cfg[TDM_SEN][TDM_0].sample_rate; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + static int cdc_dma_get_port_idx(struct snd_kcontrol *kcontrol) { int idx = 0; @@ -4175,6 +4614,30 @@ static const struct snd_kcontrol_new msm_common_snd_controls[] = { SOC_ENUM_EXT("SEN_TDM_TX_0 Channels", tdm_tx_chs, tdm_tx_ch_get, tdm_tx_ch_put), + SOC_ENUM_EXT("PRI_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("SEC_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("TERT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUAT_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("QUIN_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("SEN_TDM SlotNumber", tdm_slot_num, + tdm_slot_num_get, tdm_slot_num_put), + SOC_ENUM_EXT("PRI_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("SEC_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("TERT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUAT_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("QUIN_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), + SOC_ENUM_EXT("SEN_TDM SlotWidth", tdm_slot_width, + tdm_slot_width_get, tdm_slot_width_put), SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs, msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs, @@ -4228,6 +4691,42 @@ static const struct snd_kcontrol_new msm_common_snd_controls[] = { msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, TDM_MAX_SLOTS + MAX_PATH, NULL, tdm_slot_map_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("PRI_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEC_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("TERT_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("QUIN_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEN_TDM_TX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_tx_slot_mapping_get, tdm_tx_slot_mapping_put), + SOC_SINGLE_MULTI_EXT("SEN_TDM_RX SlotMapping", + SND_SOC_NOPM, 0, 0xFFFF, 0, TDM_SLOT_OFFSET_MAX, + tdm_rx_slot_mapping_get, tdm_rx_slot_mapping_put), }; static const struct snd_kcontrol_new msm_snd_controls[] = { @@ -4806,55 +5305,113 @@ static int lahaina_tdm_snd_hw_params(struct snd_pcm_substream *substream, int channels, slots; unsigned int slot_mask, rate, clk_freq; unsigned int *slot_offset; - struct tdm_dev_config *config; - unsigned int path_dir = 0, interface = 0, channel_interface = 0; - struct msm_asoc_mach_data *pdata = NULL; + int offset_channels = 0; + int i; pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - pdata = snd_soc_card_get_drvdata(rtd->card); - slots = pdata->tdm_max_slots; - if (cpu_dai->id < AFE_PORT_ID_TDM_PORT_RANGE_START) { + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + channels = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_rx_slot_offset[TDM_PRI]; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + channels = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_rx_slot_offset[TDM_SEC]; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + channels = tdm_rx_cfg[TDM_TERT][TDM_0].channels; + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_rx_slot_offset[TDM_TERT]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: + channels = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_rx_slot_offset[TDM_QUAT]; + break; + case AFE_PORT_ID_QUINARY_TDM_RX: + channels = tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_rx_slot_offset[TDM_QUIN]; + break; + case AFE_PORT_ID_SENARY_TDM_RX: + channels = tdm_rx_cfg[TDM_SEN][TDM_0].channels; + slots = tdm_slot[TDM_SEN].num; + slot_width = tdm_slot[TDM_SEN].width; + slot_offset = tdm_rx_slot_offset[TDM_SEN]; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + channels = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + slots = tdm_slot[TDM_PRI].num; + slot_width = tdm_slot[TDM_PRI].width; + slot_offset = tdm_tx_slot_offset[TDM_PRI]; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + channels = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + slots = tdm_slot[TDM_SEC].num; + slot_width = tdm_slot[TDM_SEC].width; + slot_offset = tdm_tx_slot_offset[TDM_SEC]; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + channels = tdm_tx_cfg[TDM_TERT][TDM_0].channels; + slots = tdm_slot[TDM_TERT].num; + slot_width = tdm_slot[TDM_TERT].width; + slot_offset = tdm_tx_slot_offset[TDM_TERT]; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + channels = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + slots = tdm_slot[TDM_QUAT].num; + slot_width = tdm_slot[TDM_QUAT].width; + slot_offset = tdm_tx_slot_offset[TDM_QUAT]; + break; + case AFE_PORT_ID_QUINARY_TDM_TX: + channels = tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + slots = tdm_slot[TDM_QUIN].num; + slot_width = tdm_slot[TDM_QUIN].width; + slot_offset = tdm_tx_slot_offset[TDM_QUIN]; + break; + case AFE_PORT_ID_SENARY_TDM_TX: + channels = tdm_tx_cfg[TDM_SEN][TDM_0].channels; + slots = tdm_slot[TDM_SEN].num; + slot_width = tdm_slot[TDM_SEN].width; + slot_offset = tdm_tx_slot_offset[TDM_SEN]; + break; + default: pr_err("%s: dai id 0x%x not supported\n", __func__, cpu_dai->id); return -EINVAL; } - /* RX or TX */ - path_dir = cpu_dai->id % MAX_PATH; - - /* PRI, SEC, TERT, QUAT, QUIN, ... */ - interface = (cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) - / (MAX_PATH * TDM_PORT_MAX); - - /* 0, 1, 2, .. 7 */ - channel_interface = - ((cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) / MAX_PATH) - % TDM_PORT_MAX; - - pr_debug("%s: path dir: %u, interface %u, channel interface %u\n", - __func__, path_dir, interface, channel_interface); - - config = ((struct tdm_dev_config *) tdm_cfg[interface]) + - (path_dir * TDM_PORT_MAX) + channel_interface; - if (!config) { - pr_err("%s: tdm config is NULL\n", __func__); - return -EINVAL; + for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) + offset_channels++; + else + break; } - slot_offset = config->tdm_slot_offset; - if (!slot_offset) { - pr_err("%s: slot offset is NULL\n", __func__); + + if (offset_channels == 0) { + pr_err("%s: invalid offset_channels %d\n", + __func__, offset_channels); return -EINVAL; } - if (path_dir) - channels = tdm_tx_cfg[interface][channel_interface].channels; - else - channels = tdm_rx_cfg[interface][channel_interface].channels; + 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) { /*2 slot config - bits 0 and 1 set for the first two slots */ - slot_mask = 0x0000FFFF >> (16 - slots); + slot_mask = 0xFFFFFFFF >> (32-channels); pr_debug("%s: tdm rx slot_width %d slots %d slot_mask %x\n", __func__, slot_width, slots, slot_mask); @@ -4878,7 +5435,7 @@ static int lahaina_tdm_snd_hw_params(struct snd_pcm_substream *substream, } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { /*2 slot config - bits 0 and 1 set for the first two slots */ - slot_mask = 0x0000FFFF >> (16 - slots); + slot_mask = 0xFFFFFFFF >> (32-channels); pr_debug("%s: tdm tx slot_width %d slots %d slot_mask %x\n", __func__, slot_width, slots, slot_mask); @@ -4909,6 +5466,12 @@ static int lahaina_tdm_snd_hw_params(struct snd_pcm_substream *substream, rate = params_rate(params); clk_freq = rate * slot_width * slots; + if (clk_freq > 24576000) { + ret = -EINVAL; + pr_err("%s: clk frequency > 24.576MHz %d\n", + __func__, clk_freq); + goto end; + } ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); if (ret < 0) pr_err("%s: failed to set tdm clk, err:%d\n", @@ -6540,13 +7103,17 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .ignore_suspend = 1, SND_SOC_DAILINK_REG(usb_audio_tx), }, +}; + + +static struct snd_soc_dai_link msm_tdm_be_dai_links[] = { { .name = LPASS_BE_PRI_TDM_RX_0, .stream_name = "Primary TDM0 Playback", .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6558,7 +7125,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(pri_tdm_tx_0), @@ -6569,7 +7136,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6581,7 +7148,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(sec_tdm_tx_0), @@ -6592,7 +7159,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6604,7 +7171,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(tert_tdm_tx_0), @@ -6615,7 +7182,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6627,7 +7194,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(quat_tdm_tx_0), @@ -6638,7 +7205,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6650,7 +7217,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_QUIN_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(quin_tdm_tx_0), @@ -6661,7 +7228,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_SEN_TDM_RX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -6673,7 +7240,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .no_pcm = 1, .dpcm_capture = 1, .id = MSM_BACKEND_DAI_SEN_TDM_TX_0, - .be_hw_params_fixup = msm_be_hw_params_fixup, + .be_hw_params_fixup = lahaina_tdm_be_hw_params_fixup, .ops = &lahaina_tdm_be_ops, .ignore_suspend = 1, SND_SOC_DAILINK_REG(sen_tdm_tx_0), @@ -7286,6 +7853,7 @@ static struct snd_soc_dai_link msm_lahaina_dai_links[ ARRAY_SIZE(msm_bolero_fe_dai_links) + ARRAY_SIZE(msm_common_misc_fe_dai_links) + ARRAY_SIZE(msm_common_be_dai_links) + + ARRAY_SIZE(msm_tdm_be_dai_links) + ARRAY_SIZE(msm_mi2s_be_dai_links) + #ifndef CONFIG_AUXPCM_DISABLE ARRAY_SIZE(msm_auxpcm_be_dai_links) + @@ -7656,6 +8224,11 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) sizeof(msm_common_be_dai_links)); total_links += ARRAY_SIZE(msm_common_be_dai_links); + memcpy(msm_lahaina_dai_links + total_links, + msm_tdm_be_dai_links, + sizeof(msm_tdm_be_dai_links)); + total_links += ARRAY_SIZE(msm_tdm_be_dai_links); + memcpy(msm_lahaina_dai_links + total_links, msm_rx_tx_cdc_dma_be_dai_links, sizeof(msm_rx_tx_cdc_dma_be_dai_links));