| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * MediaTek 8365 ALSA SoC Audio DAI I2S Control |
| * |
| * Copyright (c) 2024 MediaTek Inc. |
| * Authors: Jia Zeng <jia.zeng@mediatek.com> |
| * Alexandre Mergnat <amergnat@baylibre.com> |
| */ |
| |
| #include <linux/bitops.h> |
| #include <linux/regmap.h> |
| #include <sound/pcm_params.h> |
| #include "mt8365-afe-clk.h" |
| #include "mt8365-afe-common.h" |
| |
| #define IIR_RATIOVER 9 |
| #define IIR_INV_COEF 10 |
| #define IIR_NO_NEED 11 |
| |
| struct mtk_afe_i2s_priv { |
| bool adda_link; |
| int i2s_out_on_ref_cnt; |
| int id; |
| int low_jitter_en; |
| int mclk_id; |
| int share_i2s_id; |
| unsigned int clk_id_in; |
| unsigned int clk_id_in_m_sel; |
| unsigned int clk_id_out; |
| unsigned int clk_id_out_m_sel; |
| unsigned int clk_in_mult; |
| unsigned int clk_out_mult; |
| unsigned int config_val_in; |
| unsigned int config_val_out; |
| unsigned int dynamic_bck; |
| unsigned int reg_off_in; |
| unsigned int reg_off_out; |
| }; |
| |
| /* This enum is merely for mtk_afe_i2s_priv declare */ |
| enum { |
| DAI_I2S0 = 0, |
| DAI_I2S3, |
| DAI_I2S_NUM, |
| }; |
| |
| static const struct mtk_afe_i2s_priv mt8365_i2s_priv[DAI_I2S_NUM] = { |
| [DAI_I2S0] = { |
| .id = MT8365_AFE_IO_I2S, |
| .mclk_id = MT8365_I2S0_MCK, |
| .share_i2s_id = -1, |
| .clk_id_in = MT8365_CLK_AUD_I2S2_M, |
| .clk_id_out = MT8365_CLK_AUD_I2S1_M, |
| .clk_id_in_m_sel = MT8365_CLK_I2S2_M_SEL, |
| .clk_id_out_m_sel = MT8365_CLK_I2S1_M_SEL, |
| .clk_in_mult = 256, |
| .clk_out_mult = 256, |
| .adda_link = true, |
| .config_val_out = AFE_I2S_CON1_I2S2_TO_PAD, |
| .reg_off_in = AFE_I2S_CON2, |
| .reg_off_out = AFE_I2S_CON1, |
| }, |
| [DAI_I2S3] = { |
| .id = MT8365_AFE_IO_2ND_I2S, |
| .mclk_id = MT8365_I2S3_MCK, |
| .share_i2s_id = -1, |
| .clk_id_in = MT8365_CLK_AUD_I2S0_M, |
| .clk_id_out = MT8365_CLK_AUD_I2S3_M, |
| .clk_id_in_m_sel = MT8365_CLK_I2S0_M_SEL, |
| .clk_id_out_m_sel = MT8365_CLK_I2S3_M_SEL, |
| .clk_in_mult = 256, |
| .clk_out_mult = 256, |
| .adda_link = false, |
| .config_val_in = AFE_I2S_CON_FROM_IO_MUX, |
| .reg_off_in = AFE_I2S_CON, |
| .reg_off_out = AFE_I2S_CON3, |
| }, |
| }; |
| |
| static const u32 *get_iir_coef(unsigned int input_fs, |
| unsigned int output_fs, unsigned int *count) |
| { |
| static const u32 IIR_COEF_48_TO_44p1[30] = { |
| 0x061fb0, 0x0bd256, 0x061fb0, 0xe3a3e6, 0xf0a300, 0x000003, |
| 0x0e416d, 0x1bb577, 0x0e416d, 0xe59178, 0xf23637, 0x000003, |
| 0x0c7d72, 0x189060, 0x0c7d72, 0xe96f09, 0xf505b2, 0x000003, |
| 0x126054, 0x249143, 0x126054, 0xe1fc0c, 0xf4b20a, 0x000002, |
| 0x000000, 0x323c85, 0x323c85, 0xf76d4e, 0x000000, 0x000002, |
| }; |
| |
| static const u32 IIR_COEF_44p1_TO_32[42] = { |
| 0x0a6074, 0x0d237a, 0x0a6074, 0xdd8d6c, 0xe0b3f6, 0x000002, |
| 0x0e41f8, 0x128d48, 0x0e41f8, 0xefc14e, 0xf12d7a, 0x000003, |
| 0x0cfa60, 0x11e89c, 0x0cfa60, 0xf1b09e, 0xf27205, 0x000003, |
| 0x15b69c, 0x20e7e4, 0x15b69c, 0xea799a, 0xe9314a, 0x000002, |
| 0x0f79e2, 0x1a7064, 0x0f79e2, 0xf65e4a, 0xf03d8e, 0x000002, |
| 0x10c34f, 0x1ffe4b, 0x10c34f, 0x0bbecb, 0xf2bc4b, 0x000001, |
| 0x000000, 0x23b063, 0x23b063, 0x07335f, 0x000000, 0x000002, |
| }; |
| |
| static const u32 IIR_COEF_48_TO_32[42] = { |
| 0x0a2a9b, 0x0a2f05, 0x0a2a9b, 0xe73873, 0xe0c525, 0x000002, |
| 0x0dd4ad, 0x0e765a, 0x0dd4ad, 0xf49808, 0xf14844, 0x000003, |
| 0x18a8cd, 0x1c40d0, 0x18a8cd, 0xed2aab, 0xe542ec, 0x000002, |
| 0x13e044, 0x1a47c4, 0x13e044, 0xf44aed, 0xe9acc7, 0x000002, |
| 0x1abd9c, 0x2a5429, 0x1abd9c, 0xff3441, 0xe0fc5f, 0x000001, |
| 0x0d86db, 0x193e2e, 0x0d86db, 0x1a6f15, 0xf14507, 0x000001, |
| 0x000000, 0x1f820c, 0x1f820c, 0x0a1b1f, 0x000000, 0x000002, |
| }; |
| |
| static const u32 IIR_COEF_32_TO_16[48] = { |
| 0x122893, 0xffadd4, 0x122893, 0x0bc205, 0xc0ee1c, 0x000001, |
| 0x1bab8a, 0x00750d, 0x1bab8a, 0x06a983, 0xe18a5c, 0x000002, |
| 0x18f68e, 0x02706f, 0x18f68e, 0x0886a9, 0xe31bcb, 0x000002, |
| 0x149c05, 0x054487, 0x149c05, 0x0bec31, 0xe5973e, 0x000002, |
| 0x0ea303, 0x07f24a, 0x0ea303, 0x115ff9, 0xe967b6, 0x000002, |
| 0x0823fd, 0x085531, 0x0823fd, 0x18d5b4, 0xee8d21, 0x000002, |
| 0x06888e, 0x0acbbb, 0x06888e, 0x40b55c, 0xe76dce, 0x000001, |
| 0x000000, 0x2d31a9, 0x2d31a9, 0x23ba4f, 0x000000, 0x000001, |
| }; |
| |
| static const u32 IIR_COEF_96_TO_44p1[48] = { |
| 0x08b543, 0xfd80f4, 0x08b543, 0x0e2332, 0xe06ed0, 0x000002, |
| 0x1b6038, 0xf90e7e, 0x1b6038, 0x0ec1ac, 0xe16f66, 0x000002, |
| 0x188478, 0xfbb921, 0x188478, 0x105859, 0xe2e596, 0x000002, |
| 0x13eff3, 0xffa707, 0x13eff3, 0x13455c, 0xe533b7, 0x000002, |
| 0x0dc239, 0x03d458, 0x0dc239, 0x17f120, 0xe8b617, 0x000002, |
| 0x0745f1, 0x05d790, 0x0745f1, 0x1e3d75, 0xed5f18, 0x000002, |
| 0x05641f, 0x085e2b, 0x05641f, 0x48efd0, 0xe3e9c8, 0x000001, |
| 0x000000, 0x28f632, 0x28f632, 0x273905, 0x000000, 0x000001, |
| }; |
| |
| static const u32 IIR_COEF_44p1_TO_16[48] = { |
| 0x0998fb, 0xf7f925, 0x0998fb, 0x1e54a0, 0xe06605, 0x000002, |
| 0x0d828e, 0xf50f97, 0x0d828e, 0x0f41b5, 0xf0a999, 0x000003, |
| 0x17ebeb, 0xee30d8, 0x17ebeb, 0x1f48ca, 0xe2ae88, 0x000002, |
| 0x12fab5, 0xf46ddc, 0x12fab5, 0x20cc51, 0xe4d068, 0x000002, |
| 0x0c7ac6, 0xfbd00e, 0x0c7ac6, 0x2337da, 0xe8028c, 0x000002, |
| 0x060ddc, 0x015b3e, 0x060ddc, 0x266754, 0xec21b6, 0x000002, |
| 0x0407b5, 0x04f827, 0x0407b5, 0x52e3d0, 0xe0149f, 0x000001, |
| 0x000000, 0x1f9521, 0x1f9521, 0x2ac116, 0x000000, 0x000001, |
| }; |
| |
| static const u32 IIR_COEF_48_TO_16[48] = { |
| 0x0955ff, 0xf6544a, 0x0955ff, 0x2474e5, 0xe062e6, 0x000002, |
| 0x0d4180, 0xf297f4, 0x0d4180, 0x12415b, 0xf0a3b0, 0x000003, |
| 0x0ba079, 0xf4f0b0, 0x0ba079, 0x1285d3, 0xf1488b, 0x000003, |
| 0x12247c, 0xf1033c, 0x12247c, 0x2625be, 0xe48e0d, 0x000002, |
| 0x0b98e0, 0xf96d1a, 0x0b98e0, 0x27e79c, 0xe7798a, 0x000002, |
| 0x055e3b, 0xffed09, 0x055e3b, 0x2a2e2d, 0xeb2854, 0x000002, |
| 0x01a934, 0x01ca03, 0x01a934, 0x2c4fea, 0xee93ab, 0x000002, |
| 0x000000, 0x1c46c5, 0x1c46c5, 0x2d37dc, 0x000000, 0x000001, |
| }; |
| |
| static const u32 IIR_COEF_96_TO_16[48] = { |
| 0x0805a1, 0xf21ae3, 0x0805a1, 0x3840bb, 0xe02a2e, 0x000002, |
| 0x0d5dd8, 0xe8f259, 0x0d5dd8, 0x1c0af6, 0xf04700, 0x000003, |
| 0x0bb422, 0xec08d9, 0x0bb422, 0x1bfccc, 0xf09216, 0x000003, |
| 0x08fde6, 0xf108be, 0x08fde6, 0x1bf096, 0xf10ae0, 0x000003, |
| 0x0ae311, 0xeeeda3, 0x0ae311, 0x37c646, 0xe385f5, 0x000002, |
| 0x044089, 0xfa7242, 0x044089, 0x37a785, 0xe56526, 0x000002, |
| 0x00c75c, 0xffb947, 0x00c75c, 0x378ba3, 0xe72c5f, 0x000002, |
| 0x000000, 0x0ef76e, 0x0ef76e, 0x377fda, 0x000000, 0x000001, |
| }; |
| |
| static const struct { |
| const u32 *coef; |
| unsigned int cnt; |
| } iir_coef_tbl_list[8] = { |
| /* 0: 0.9188 */ |
| { IIR_COEF_48_TO_44p1, ARRAY_SIZE(IIR_COEF_48_TO_44p1) }, |
| /* 1: 0.7256 */ |
| { IIR_COEF_44p1_TO_32, ARRAY_SIZE(IIR_COEF_44p1_TO_32) }, |
| /* 2: 0.6667 */ |
| { IIR_COEF_48_TO_32, ARRAY_SIZE(IIR_COEF_48_TO_32) }, |
| /* 3: 0.5 */ |
| { IIR_COEF_32_TO_16, ARRAY_SIZE(IIR_COEF_32_TO_16) }, |
| /* 4: 0.4594 */ |
| { IIR_COEF_96_TO_44p1, ARRAY_SIZE(IIR_COEF_96_TO_44p1) }, |
| /* 5: 0.3628 */ |
| { IIR_COEF_44p1_TO_16, ARRAY_SIZE(IIR_COEF_44p1_TO_16) }, |
| /* 6: 0.3333 */ |
| { IIR_COEF_48_TO_16, ARRAY_SIZE(IIR_COEF_48_TO_16) }, |
| /* 7: 0.1667 */ |
| { IIR_COEF_96_TO_16, ARRAY_SIZE(IIR_COEF_96_TO_16) }, |
| }; |
| |
| static const u32 freq_new_index[16] = { |
| 0, 1, 2, 99, 3, 4, 5, 99, 6, 7, 8, 9, 10, 11, 12, 99 |
| }; |
| |
| static const u32 iir_coef_tbl_matrix[13][13] = { |
| {/*0*/ |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*1*/ |
| 1, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*2*/ |
| 2, 0, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*3*/ |
| 3, IIR_INV_COEF, IIR_INV_COEF, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*4*/ |
| 5, 3, IIR_INV_COEF, 2, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*5*/ |
| 6, 4, 3, 2, 0, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*6*/ |
| IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, 3, IIR_INV_COEF, |
| IIR_INV_COEF, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*7*/ |
| IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, 5, 3, |
| IIR_INV_COEF, 1, IIR_NO_NEED, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*8*/ |
| 7, IIR_INV_COEF, IIR_INV_COEF, 6, 4, 3, 2, 0, IIR_NO_NEED, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*9*/ |
| IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, |
| IIR_INV_COEF, IIR_INV_COEF, 5, 3, IIR_INV_COEF, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*10*/ |
| IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, 7, IIR_INV_COEF, |
| IIR_INV_COEF, 6, 4, 3, 0, |
| IIR_NO_NEED, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| { /*11*/ |
| IIR_RATIOVER, IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, |
| IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, IIR_INV_COEF, |
| IIR_INV_COEF, 3, IIR_INV_COEF, IIR_NO_NEED, IIR_NO_NEED |
| }, |
| {/*12*/ |
| IIR_RATIOVER, IIR_RATIOVER, IIR_INV_COEF, IIR_INV_COEF, |
| IIR_INV_COEF, IIR_INV_COEF, 7, IIR_INV_COEF, |
| IIR_INV_COEF, 4, 3, 0, IIR_NO_NEED |
| }, |
| }; |
| |
| const u32 *coef = NULL; |
| unsigned int cnt = 0; |
| u32 i = freq_new_index[input_fs]; |
| u32 j = freq_new_index[output_fs]; |
| |
| if (i < 13 && j < 13) { |
| u32 k = iir_coef_tbl_matrix[i][j]; |
| |
| if (k >= IIR_NO_NEED) { |
| } else if (k == IIR_RATIOVER) { |
| } else if (k == IIR_INV_COEF) { |
| } else { |
| coef = iir_coef_tbl_list[k].coef; |
| cnt = iir_coef_tbl_list[k].cnt; |
| } |
| } |
| *count = cnt; |
| return coef; |
| } |
| |
| static int mt8365_dai_set_config(struct mtk_base_afe *afe, |
| struct mtk_afe_i2s_priv *i2s_data, |
| bool is_input, unsigned int rate, |
| int bit_width) |
| { |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mt8365_be_dai_data *be = |
| &afe_priv->be_data[i2s_data->id - MT8365_AFE_BACKEND_BASE]; |
| unsigned int val, reg_off; |
| int fs = mt8365_afe_fs_timing(rate); |
| |
| if (fs < 0) |
| return -EINVAL; |
| |
| val = AFE_I2S_CON_LOW_JITTER_CLK | AFE_I2S_CON_FORMAT_I2S; |
| val |= FIELD_PREP(AFE_I2S_CON_RATE_MASK, fs); |
| |
| if (is_input) { |
| reg_off = i2s_data->reg_off_in; |
| if (i2s_data->adda_link) |
| val |= i2s_data->config_val_in; |
| } else { |
| reg_off = i2s_data->reg_off_out; |
| val |= i2s_data->config_val_in; |
| } |
| |
| /* 1:bck=32lrck(16bit) or bck=64lrck(32bit) 0:fix bck=64lrck */ |
| if (i2s_data->dynamic_bck) { |
| if (bit_width > 16) |
| val |= AFE_I2S_CON_WLEN_32BIT; |
| else |
| val &= ~(u32)AFE_I2S_CON_WLEN_32BIT; |
| } else { |
| val |= AFE_I2S_CON_WLEN_32BIT; |
| } |
| |
| if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) == |
| SND_SOC_DAIFMT_CBM_CFM) { |
| val |= AFE_I2S_CON_SRC_SLAVE; |
| val &= ~(u32)AFE_I2S_CON_FROM_IO_MUX;//from consys |
| } |
| |
| regmap_update_bits(afe->regmap, reg_off, ~(u32)AFE_I2S_CON_EN, val); |
| |
| if (i2s_data->adda_link && is_input) |
| regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1); |
| |
| return 0; |
| } |
| |
| int mt8365_afe_set_i2s_out(struct mtk_base_afe *afe, |
| unsigned int rate, int bit_width) |
| { |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mtk_afe_i2s_priv *i2s_data = |
| afe_priv->dai_priv[MT8365_AFE_IO_I2S]; |
| |
| return mt8365_dai_set_config(afe, i2s_data, false, rate, bit_width); |
| } |
| |
| static int mt8365_afe_set_2nd_i2s_asrc(struct mtk_base_afe *afe, |
| unsigned int rate_in, |
| unsigned int rate_out, |
| unsigned int width, |
| unsigned int mono, |
| int o16bit, int tracking) |
| { |
| int ifs, ofs = 0; |
| unsigned int val = 0; |
| unsigned int mask = 0; |
| const u32 *coef; |
| u32 iir_stage; |
| unsigned int coef_count = 0; |
| |
| ifs = mt8365_afe_fs_timing(rate_in); |
| |
| if (ifs < 0) |
| return -EINVAL; |
| |
| ofs = mt8365_afe_fs_timing(rate_out); |
| |
| if (ofs < 0) |
| return -EINVAL; |
| |
| val = FIELD_PREP(O16BIT, o16bit) | FIELD_PREP(IS_MONO, mono); |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2, |
| O16BIT | IS_MONO, val); |
| |
| coef = get_iir_coef(ifs, ofs, &coef_count); |
| iir_stage = ((u32)coef_count / 6) - 1; |
| |
| if (coef) { |
| unsigned int i; |
| |
| /* CPU control IIR coeff SRAM */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0, |
| COEFF_SRAM_CTRL, COEFF_SRAM_CTRL); |
| |
| /* set to 0, IIR coeff SRAM addr */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON13, |
| 0xffffffff, 0x0); |
| |
| for (i = 0; i < coef_count; ++i) |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON12, |
| 0xffffffff, coef[i]); |
| |
| /* disable IIR coeff SRAM access */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0, |
| COEFF_SRAM_CTRL, |
| ~COEFF_SRAM_CTRL); |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2, |
| CLR_IIR_HISTORY | IIR_EN | IIR_STAGE_MASK, |
| CLR_IIR_HISTORY | IIR_EN | |
| FIELD_PREP(IIR_STAGE_MASK, iir_stage)); |
| } else { |
| /* disable IIR */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON2, |
| IIR_EN, ~IIR_EN); |
| } |
| |
| /* CON3 setting (RX OFS) */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON3, |
| 0x00FFFFFF, rx_frequency_palette(ofs)); |
| /* CON4 setting (RX IFS) */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON4, |
| 0x00FFFFFF, rx_frequency_palette(ifs)); |
| |
| /* CON5 setting */ |
| if (tracking) { |
| val = CALI_64_CYCLE | |
| CALI_AUTORST | |
| AUTO_TUNE_FREQ5 | |
| COMP_FREQ_RES | |
| CALI_BP_DGL | |
| CALI_AUTO_RESTART | |
| CALI_USE_FREQ_OUT | |
| CALI_SEL_01; |
| |
| mask = CALI_CYCLE_MASK | |
| CALI_AUTORST | |
| AUTO_TUNE_FREQ5 | |
| COMP_FREQ_RES | |
| CALI_SEL_MASK | |
| CALI_BP_DGL | |
| AUTO_TUNE_FREQ4 | |
| CALI_AUTO_RESTART | |
| CALI_USE_FREQ_OUT | |
| CALI_ON; |
| |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5, |
| mask, val); |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5, |
| CALI_ON, CALI_ON); |
| } else { |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON5, |
| 0xffffffff, 0x0); |
| } |
| /* CON6 setting fix 8125 */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON6, |
| 0x0000ffff, 0x1FBD); |
| /* CON9 setting (RX IFS) */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON9, |
| 0x000fffff, AutoRstThHi(ifs)); |
| /* CON10 setting (RX IFS) */ |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON10, |
| 0x000fffff, AutoRstThLo(ifs)); |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0, |
| CHSET_STR_CLR, CHSET_STR_CLR); |
| |
| return 0; |
| } |
| |
| static int mt8365_afe_set_2nd_i2s_asrc_enable(struct mtk_base_afe *afe, |
| bool enable) |
| { |
| if (enable) |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0, |
| ASM_ON, ASM_ON); |
| else |
| regmap_update_bits(afe->regmap, AFE_ASRC_2CH_CON0, |
| ASM_ON, ~ASM_ON); |
| return 0; |
| } |
| |
| void mt8365_afe_set_i2s_out_enable(struct mtk_base_afe *afe, bool enable) |
| { |
| int i; |
| unsigned long flags; |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mtk_afe_i2s_priv *i2s_data = NULL; |
| |
| for (i = 0; i < DAI_I2S_NUM; i++) { |
| if (mt8365_i2s_priv[i].adda_link) |
| i2s_data = afe_priv->dai_priv[mt8365_i2s_priv[i].id]; |
| } |
| |
| if (!i2s_data) |
| return; |
| |
| spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags); |
| |
| if (enable) { |
| i2s_data->i2s_out_on_ref_cnt++; |
| if (i2s_data->i2s_out_on_ref_cnt == 1) |
| regmap_update_bits(afe->regmap, AFE_I2S_CON1, |
| 0x1, enable); |
| } else { |
| i2s_data->i2s_out_on_ref_cnt--; |
| if (i2s_data->i2s_out_on_ref_cnt == 0) |
| regmap_update_bits(afe->regmap, AFE_I2S_CON1, |
| 0x1, enable); |
| else if (i2s_data->i2s_out_on_ref_cnt < 0) |
| i2s_data->i2s_out_on_ref_cnt = 0; |
| } |
| |
| spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); |
| } |
| |
| static void mt8365_dai_set_enable(struct mtk_base_afe *afe, |
| struct mtk_afe_i2s_priv *i2s_data, |
| bool is_input, bool enable) |
| { |
| unsigned int reg_off; |
| |
| if (is_input) { |
| reg_off = i2s_data->reg_off_in; |
| } else { |
| if (i2s_data->adda_link) { |
| mt8365_afe_set_i2s_out_enable(afe, enable); |
| return; |
| } |
| reg_off = i2s_data->reg_off_out; |
| } |
| regmap_update_bits(afe->regmap, reg_off, |
| 0x1, enable); |
| } |
| |
| static int mt8365_dai_i2s_startup(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *dai) |
| { |
| struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mtk_afe_i2s_priv *i2s_data = afe_priv->dai_priv[dai->id]; |
| struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE]; |
| bool i2s_in_slave = |
| (substream->stream == SNDRV_PCM_STREAM_CAPTURE) && |
| ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) == |
| SND_SOC_DAIFMT_CBM_CFM); |
| |
| mt8365_afe_enable_main_clk(afe); |
| |
| if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| clk_prepare_enable(afe_priv->clocks[i2s_data->clk_id_out]); |
| |
| if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && !i2s_in_slave) |
| clk_prepare_enable(afe_priv->clocks[i2s_data->clk_id_in]); |
| |
| if (i2s_in_slave) |
| mt8365_afe_enable_top_cg(afe, MT8365_TOP_CG_I2S_IN); |
| |
| return 0; |
| } |
| |
| static void mt8365_dai_i2s_shutdown(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *dai) |
| { |
| struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mtk_afe_i2s_priv *i2s_data = afe_priv->dai_priv[dai->id]; |
| struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE]; |
| bool reset_i2s_out_change = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); |
| bool reset_i2s_in_change = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); |
| bool i2s_in_slave = |
| (substream->stream == SNDRV_PCM_STREAM_CAPTURE) && |
| ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) == |
| SND_SOC_DAIFMT_CBM_CFM); |
| |
| if (be->prepared[substream->stream]) { |
| if (reset_i2s_out_change) |
| mt8365_dai_set_enable(afe, i2s_data, false, false); |
| |
| if (reset_i2s_in_change) |
| mt8365_dai_set_enable(afe, i2s_data, true, false); |
| |
| if (substream->runtime->rate % 8000) |
| mt8365_afe_disable_apll_associated_cfg(afe, MT8365_AFE_APLL1); |
| else |
| mt8365_afe_disable_apll_associated_cfg(afe, MT8365_AFE_APLL2); |
| |
| if (reset_i2s_out_change) |
| be->prepared[SNDRV_PCM_STREAM_PLAYBACK] = false; |
| |
| if (reset_i2s_in_change) |
| be->prepared[SNDRV_PCM_STREAM_CAPTURE] = false; |
| } |
| |
| if (reset_i2s_out_change) |
| mt8365_afe_disable_clk(afe, |
| afe_priv->clocks[i2s_data->clk_id_out]); |
| |
| if (reset_i2s_in_change && !i2s_in_slave) |
| mt8365_afe_disable_clk(afe, |
| afe_priv->clocks[i2s_data->clk_id_in]); |
| |
| if (i2s_in_slave) |
| mt8365_afe_disable_top_cg(afe, MT8365_TOP_CG_I2S_IN); |
| |
| mt8365_afe_disable_main_clk(afe); |
| } |
| |
| static int mt8365_dai_i2s_prepare(struct snd_pcm_substream *substream, |
| struct snd_soc_dai *dai) |
| { |
| struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mtk_afe_i2s_priv *i2s_data = afe_priv->dai_priv[dai->id]; |
| struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE]; |
| bool apply_i2s_out_change = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); |
| bool apply_i2s_in_change = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); |
| unsigned int rate = substream->runtime->rate; |
| int bit_width = snd_pcm_format_width(substream->runtime->format); |
| int ret; |
| |
| if (be->prepared[substream->stream]) { |
| dev_info(afe->dev, "%s '%s' prepared already\n", |
| __func__, snd_pcm_stream_str(substream)); |
| return 0; |
| } |
| |
| if (apply_i2s_out_change) { |
| ret = mt8365_dai_set_config(afe, i2s_data, false, rate, bit_width); |
| if (ret) |
| return ret; |
| } |
| |
| if (apply_i2s_in_change) { |
| if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) |
| == SND_SOC_DAIFMT_CBM_CFM) { |
| ret = mt8365_afe_set_2nd_i2s_asrc(afe, 32000, rate, |
| (unsigned int)bit_width, |
| 0, 0, 1); |
| if (ret < 0) |
| return ret; |
| } |
| ret = mt8365_dai_set_config(afe, i2s_data, true, rate, bit_width); |
| if (ret) |
| return ret; |
| } |
| |
| if (rate % 8000) |
| mt8365_afe_enable_apll_associated_cfg(afe, MT8365_AFE_APLL1); |
| else |
| mt8365_afe_enable_apll_associated_cfg(afe, MT8365_AFE_APLL2); |
| |
| if (apply_i2s_out_change) { |
| mt8365_afe_set_clk_parent(afe, |
| afe_priv->clocks[i2s_data->clk_id_out_m_sel], |
| ((rate % 8000) ? |
| afe_priv->clocks[MT8365_CLK_AUD1] : |
| afe_priv->clocks[MT8365_CLK_AUD2])); |
| |
| mt8365_afe_set_clk_rate(afe, |
| afe_priv->clocks[i2s_data->clk_id_out], |
| rate * i2s_data->clk_out_mult); |
| |
| mt8365_dai_set_enable(afe, i2s_data, false, true); |
| be->prepared[SNDRV_PCM_STREAM_PLAYBACK] = true; |
| } |
| |
| if (apply_i2s_in_change) { |
| mt8365_afe_set_clk_parent(afe, |
| afe_priv->clocks[i2s_data->clk_id_in_m_sel], |
| ((rate % 8000) ? |
| afe_priv->clocks[MT8365_CLK_AUD1] : |
| afe_priv->clocks[MT8365_CLK_AUD2])); |
| |
| mt8365_afe_set_clk_rate(afe, |
| afe_priv->clocks[i2s_data->clk_id_in], |
| rate * i2s_data->clk_in_mult); |
| |
| mt8365_dai_set_enable(afe, i2s_data, true, true); |
| |
| if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) |
| == SND_SOC_DAIFMT_CBM_CFM) |
| mt8365_afe_set_2nd_i2s_asrc_enable(afe, true); |
| |
| be->prepared[SNDRV_PCM_STREAM_CAPTURE] = true; |
| } |
| return 0; |
| } |
| |
| static int mt8365_afe_2nd_i2s_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params, |
| struct snd_soc_dai *dai) |
| { |
| struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); |
| unsigned int width_val = params_width(params) > 16 ? |
| (AFE_CONN_24BIT_O00 | AFE_CONN_24BIT_O01) : 0; |
| |
| if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| regmap_update_bits(afe->regmap, AFE_CONN_24BIT, |
| AFE_CONN_24BIT_O00 | AFE_CONN_24BIT_O01, width_val); |
| |
| return 0; |
| } |
| |
| static int mt8365_afe_2nd_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
| { |
| struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| struct mt8365_be_dai_data *be = &afe_priv->be_data[dai->id - MT8365_AFE_BACKEND_BASE]; |
| |
| be->fmt_mode = 0; |
| |
| switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| case SND_SOC_DAIFMT_I2S: |
| be->fmt_mode |= SND_SOC_DAIFMT_I2S; |
| break; |
| case SND_SOC_DAIFMT_LEFT_J: |
| be->fmt_mode |= SND_SOC_DAIFMT_LEFT_J; |
| break; |
| default: |
| dev_err(afe->dev, "invalid audio format for 2nd i2s!\n"); |
| return -EINVAL; |
| } |
| |
| if (((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) && |
| ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_IF) && |
| ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_IB_NF) && |
| ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_IB_IF)) { |
| dev_err(afe->dev, "invalid audio format for 2nd i2s!\n"); |
| return -EINVAL; |
| } |
| |
| be->fmt_mode |= (fmt & SND_SOC_DAIFMT_INV_MASK); |
| |
| if (((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)) |
| be->fmt_mode |= (fmt & SND_SOC_DAIFMT_MASTER_MASK); |
| |
| return 0; |
| } |
| |
| static const struct snd_soc_dai_ops mt8365_afe_i2s_ops = { |
| .startup = mt8365_dai_i2s_startup, |
| .shutdown = mt8365_dai_i2s_shutdown, |
| .prepare = mt8365_dai_i2s_prepare, |
| }; |
| |
| static const struct snd_soc_dai_ops mt8365_afe_2nd_i2s_ops = { |
| .startup = mt8365_dai_i2s_startup, |
| .shutdown = mt8365_dai_i2s_shutdown, |
| .hw_params = mt8365_afe_2nd_i2s_hw_params, |
| .prepare = mt8365_dai_i2s_prepare, |
| .set_fmt = mt8365_afe_2nd_i2s_set_fmt, |
| }; |
| |
| static struct snd_soc_dai_driver mtk_dai_i2s_driver[] = { |
| { |
| .name = "I2S", |
| .id = MT8365_AFE_IO_I2S, |
| .playback = { |
| .stream_name = "I2S Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = SNDRV_PCM_RATE_8000_192000, |
| .formats = SNDRV_PCM_FMTBIT_S16_LE | |
| SNDRV_PCM_FMTBIT_S24_LE | |
| SNDRV_PCM_FMTBIT_S32_LE, |
| }, |
| .capture = { |
| .stream_name = "I2S Capture", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = SNDRV_PCM_RATE_8000_192000, |
| .formats = SNDRV_PCM_FMTBIT_S16_LE | |
| SNDRV_PCM_FMTBIT_S24_LE | |
| SNDRV_PCM_FMTBIT_S32_LE, |
| }, |
| .ops = &mt8365_afe_i2s_ops, |
| }, { |
| .name = "2ND I2S", |
| .id = MT8365_AFE_IO_2ND_I2S, |
| .playback = { |
| .stream_name = "2ND I2S Playback", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = SNDRV_PCM_RATE_8000_192000, |
| .formats = SNDRV_PCM_FMTBIT_S16_LE | |
| SNDRV_PCM_FMTBIT_S24_LE | |
| SNDRV_PCM_FMTBIT_S32_LE, |
| }, |
| .capture = { |
| .stream_name = "2ND I2S Capture", |
| .channels_min = 1, |
| .channels_max = 2, |
| .rates = SNDRV_PCM_RATE_8000_192000, |
| .formats = SNDRV_PCM_FMTBIT_S16_LE | |
| SNDRV_PCM_FMTBIT_S24_LE | |
| SNDRV_PCM_FMTBIT_S32_LE, |
| }, |
| .ops = &mt8365_afe_2nd_i2s_ops, |
| } |
| }; |
| |
| static const char * const fmi2sin_text[] = { |
| "OPEN", "FM_2ND_I2S_IN" |
| }; |
| |
| static SOC_ENUM_SINGLE_VIRT_DECL(fmi2sin_enum, fmi2sin_text); |
| |
| static const struct snd_kcontrol_new fmi2sin_mux = |
| SOC_DAPM_ENUM("FM 2ND I2S Source", fmi2sin_enum); |
| |
| static const struct snd_kcontrol_new i2s_o03_o04_enable_ctl = |
| SOC_DAPM_SINGLE_VIRT("Switch", 1); |
| |
| static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = { |
| SND_SOC_DAPM_SWITCH("I2S O03_O04", SND_SOC_NOPM, 0, 0, |
| &i2s_o03_o04_enable_ctl), |
| SND_SOC_DAPM_MUX("FM 2ND I2S Mux", SND_SOC_NOPM, 0, 0, &fmi2sin_mux), |
| SND_SOC_DAPM_INPUT("2ND I2S In"), |
| }; |
| |
| static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = { |
| {"I2S O03_O04", "Switch", "O03"}, |
| {"I2S O03_O04", "Switch", "O04"}, |
| {"I2S Playback", NULL, "I2S O03_O04"}, |
| {"2ND I2S Playback", NULL, "O00"}, |
| {"2ND I2S Playback", NULL, "O01"}, |
| {"2ND I2S Capture", NULL, "2ND I2S In"}, |
| {"FM 2ND I2S Mux", "FM_2ND_I2S_IN", "2ND I2S Capture"}, |
| }; |
| |
| static int mt8365_dai_i2s_set_priv(struct mtk_base_afe *afe) |
| { |
| int i, ret; |
| struct mt8365_afe_private *afe_priv = afe->platform_priv; |
| |
| for (i = 0; i < DAI_I2S_NUM; i++) { |
| ret = mt8365_dai_set_priv(afe, mt8365_i2s_priv[i].id, |
| sizeof(*afe_priv), |
| &mt8365_i2s_priv[i]); |
| if (ret) |
| return ret; |
| } |
| return 0; |
| } |
| |
| int mt8365_dai_i2s_register(struct mtk_base_afe *afe) |
| { |
| struct mtk_base_afe_dai *dai; |
| |
| dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); |
| if (!dai) |
| return -ENOMEM; |
| |
| list_add(&dai->list, &afe->sub_dais); |
| |
| dai->dai_drivers = mtk_dai_i2s_driver; |
| dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_i2s_driver); |
| dai->dapm_widgets = mtk_dai_i2s_widgets; |
| dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_i2s_widgets); |
| dai->dapm_routes = mtk_dai_i2s_routes; |
| dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_i2s_routes); |
| |
| /* set all dai i2s private data */ |
| return mt8365_dai_i2s_set_priv(afe); |
| } |