/* * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved. * description: function define * author: audio * create: 2020-10-29 */ /******************************* include files *******************************/ /* unf headers */ #include "hi_log.h" #include "hi_common.h" #include "hi_drv_sys.h" /* drv headers */ #include "hi_type.h" #include "drv_amp_private.h" #include "drv_amp_ioctl.h" #include "hi_drv_amp.h" #include "drv_amp_ext.h" #include "amp_i2s.h" #ifdef __cplusplus #if __cplusplus extern "C" { #endif #endif /* __cplusplus */ #define ENDTBL_FLAG 0xff #define AMP_DEBUG 0 #define MAX_AMP_REG_LEN 20 #define TAS5805_DRC_VAL_LEN 4 #define TAS5805_PEQ_VAL_LEN 4 #define TAS5805_INIT_INTERVAL 65 /* delay 65ms */ #define INVALID_GPIONUM 0xff #define INIT_DELAY 5 #define TAS5805M_REG_00 0x00 #define TAS5805M_REG_03 0x03 #define TAS5805M_REG_35 0x35 #define TAS5805M_REG_7F 0x7f #define TAS5805M_PAGE_00 0x00 #define TAS5805M_BOOK_00 0x00 static amp_global_param *g_tas5805_drv = HI_NULL; typedef struct { hi_u8 addr; hi_u8 value; } tas5805_regmap; static tas5805_regmap g_tas5805_init_sequence[] = { { 0x00, 0x00 }, { 0x7f, 0x00 }, { 0x03, 0x03 }, { 0x78, 0x80 }, { ENDTBL_FLAG, 0x00 }, }; static hi_s32 amp_tas5805_set_mute(hi_bool mute); static hi_s32 amp_write_reg(hi_u8 u1_addr, hi_u8 *u1_buf, hi_u16 u2_byte_cnt) { hi_s32 ret; check_amp_func_init(g_tas5805_drv); check_amp_func_init(g_tas5805_drv->p_i2c_func); check_amp_func_init(g_tas5805_drv->p_i2c_func->pfn_i2c_write); ret = g_tas5805_drv->p_i2c_func->pfn_i2c_write(g_tas5805_drv->i2c_num, g_tas5805_drv->device_addr, u1_addr, 1, u1_buf, u2_byte_cnt); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 i2c write error\n"); hi_err_print_h32(g_tas5805_drv->i2c_num); hi_err_print_h32(g_tas5805_drv->device_addr); hi_err_print_h32(u1_addr); hi_err_print_h32(u1_buf[0]); hi_err_print_h32(u2_byte_cnt); } return ret; } static hi_s32 amp_read_reg(hi_u8 u1_addr, hi_u8 *u1_buf, hi_u8 max_len, hi_u16 u2_byte_cnt) { hi_s32 ret; check_amp_func_init(g_tas5805_drv); check_amp_func_init(g_tas5805_drv->p_i2c_func); check_amp_func_init(g_tas5805_drv->p_i2c_func->pfn_i2c_read); ret = g_tas5805_drv->p_i2c_func->pfn_i2c_read(g_tas5805_drv->i2c_num, g_tas5805_drv->device_addr, u1_addr, 1, u1_buf, u2_byte_cnt); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 read fail!\n"); hi_err_print_err_code(ret); } return ret; } static hi_void amp_power_up(hi_bool power) { hi_s32 ret = HI_SUCCESS; if (g_tas5805_drv == HI_NULL) { HI_LOG_ERR("TAS5805 no init! \n"); return; } if (g_tas5805_drv->p_gpio_func == HI_NULL) { HI_LOG_ERR("GPIO function null \n"); return; } if (g_tas5805_drv->reset_gpio_num == INVALID_GPIONUM) { HI_LOG_WARN("the amp TAS5805 unsupported reset!\n"); return; } if (g_tas5805_drv->p_gpio_func->pfn_gpio_dir_set_bit != HI_NULL) { ret = g_tas5805_drv->p_gpio_func->pfn_gpio_dir_set_bit(g_tas5805_drv->reset_gpio_num, g_tas5805_drv->gpio_output_polarity); if (ret != HI_SUCCESS) { HI_LOG_ERR("call pfn_gpio_dir_set_bit fail!\n"); hi_err_print_err_code(ret); return; } msleep(1); } if (g_tas5805_drv->p_gpio_func->pfn_gpio_write_bit != HI_NULL) { if (power) { ret = g_tas5805_drv->p_gpio_func->pfn_gpio_write_bit(g_tas5805_drv->reset_gpio_num, g_tas5805_drv->reset_polarity); } else { ret = g_tas5805_drv->p_gpio_func->pfn_gpio_write_bit(g_tas5805_drv->reset_gpio_num, !(g_tas5805_drv->reset_polarity)); } if (ret != HI_SUCCESS) { HI_LOG_ERR("call pfn_gpio_write_bit fail!\n"); hi_err_print_err_code(ret); return; } msleep(TAS5805_INIT_INTERVAL); } return; } static hi_void amp_tas5805_hw_mute(hi_bool mute) { hi_s32 ret; ret = amp_tas5805_set_mute(mute); if (ret != HI_SUCCESS) { HI_LOG_ERR("set_mute failed!\n"); } } static hi_s32 amp_tas5805_init(amp_global_param *amp_drv) { hi_s32 ret; hi_u8 index = 0; hi_u8 addr = 0; hi_u8 value = 0; tas5805_regmap *tas5805_init_sequence = g_tas5805_init_sequence; check_amp_null_ptr(amp_drv); g_tas5805_drv = amp_drv; msleep(INIT_DELAY); amp_power_up(HI_TRUE); msleep(INIT_DELAY); for (index = 0;; index++) { addr = tas5805_init_sequence[index].addr; if (addr == ENDTBL_FLAG) { break; } value = tas5805_init_sequence[index].value; ret = amp_write_reg(addr, &value, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 init fail \n"); } #if AMP_DEBUG hi_u8 read_array[MAX_AMP_REG_LEN] = { 0 }; ret = amp_read_reg(addr, read_array, MAX_AMP_REG_LEN, 1); hi_dbg_print_h32(addr); hi_dbg_print_h32(read_array[0]); HI_LOG_DBG("\n"); #endif } return HI_SUCCESS; } static hi_s32 amp_tas5805_resume(amp_global_param *amp_drv) { return amp_tas5805_init(amp_drv); } static hi_void amp_tas5805_de_init(hi_void) { if (g_tas5805_drv == HI_NULL) { /* delete print to a hi_void rmmod error print while tas5805 is not initialized. */ return; } /* power down AMP */ amp_power_up(HI_FALSE); g_tas5805_drv = HI_NULL; } static hi_s32 amp_tas5805_set_mute(hi_bool mute) { hi_s32 ret; hi_u8 i; hi_u8 addr = 0; hi_u8 value = 0; hi_u8 reg03_value = 0; hi_u8 reg35_value = 0; /* sequence need to write 5 registers */ tas5805_regmap mute_sequence[5] = { { TAS5805M_REG_00, TAS5805M_PAGE_00 }, { TAS5805M_REG_7F, TAS5805M_BOOK_00 }, { TAS5805M_REG_00, TAS5805M_PAGE_00 }, { TAS5805M_REG_03, 0x03 }, { TAS5805M_REG_35, 0x11 }, }; check_amp_func_init(g_tas5805_drv); if (mute) { /* mute both left & right channels */ reg03_value = 0x0b; reg35_value = 0x00; } else { /* unmute */ reg03_value = 0x03; reg35_value = 0x11; } mute_sequence[3].value = reg03_value; /* the 3th value write to reg TAS5805M_REG_03 */ mute_sequence[4].value = reg35_value; /* the 4th value write to reg TAS5805M_REG_35 */ /* sequence need to write 5 registers */ for (i = 0; i < 5; i++) { addr = mute_sequence[i].addr; value = mute_sequence[i].value; ret = amp_write_reg(addr, &value, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 write mute reg fail!\n"); return ret; } } return HI_SUCCESS; } static hi_s32 amp_tas5805_get_mute(hi_bool *mute) { hi_s32 ret; hi_u8 gain = 0; check_amp_func_init(g_tas5805_drv); ret = amp_read_reg(TAS5805M_REG_35, &gain, 1, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 read mute reg fail!\n"); return ret; } if (gain == 0x00) { *mute = HI_TRUE; } else { *mute = HI_FALSE; } return HI_SUCCESS; } static hi_s32 amp_set_drc_en(hi_bool en) { hi_s32 ret; hi_u8 i; hi_u8 addr = 0; hi_u8 value = 0; hi_u8 drc_val[TAS5805_DRC_VAL_LEN] = { 0 }; /* sequence need to write 4 registers */ tas5805_regmap sequence[4] = { { TAS5805M_REG_00, TAS5805M_PAGE_00 }, { TAS5805M_REG_7F, TAS5805M_BOOK_00 }, { TAS5805M_REG_00, TAS5805M_PAGE_00 }, { 0x66, 0x00 }, }; check_amp_func_init(g_tas5805_drv); ret = amp_read_reg(0x66, drc_val, TAS5805_DRC_VAL_LEN, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 read drc reg fail!\n"); return HI_FAILURE; } if (en == HI_TRUE) { drc_val[0] &= ~0x02; /* BIT1 = 0 NOT bypass DRC */ } else { drc_val[0] |= 0x02; /* BIT1 = 1 bypass DRC */ } sequence[3].value = drc_val[0]; /* the 3th value write to reg 0x66 */ /* sequence need to write 4 registers */ for (i = 0; i < 4; i++) { addr = sequence[i].addr; value = sequence[i].value; ret = amp_write_reg(addr, &value, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 write drc reg fail!\n"); return ret; } } return HI_SUCCESS; } static hi_s32 amp_get_drc_en(hi_bool *en) { hi_s32 ret; hi_u8 drc_val[TAS5805_DRC_VAL_LEN] = { 0 }; check_amp_func_init(g_tas5805_drv); ret = amp_read_reg(0x66, drc_val, TAS5805_DRC_VAL_LEN, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 read drc reg fail!\n"); return ret; } if ((drc_val[0] & 0x02) == 0x02) { /* BIT1 = 1 bypass DRC */ *en = HI_FALSE; } else { *en = HI_TRUE; } return HI_SUCCESS; } static hi_s32 amp_set_peq_en(hi_bool en) { hi_s32 ret; hi_u8 i; hi_u8 addr = 0; hi_u8 value = 0; hi_u8 peq_val[TAS5805_PEQ_VAL_LEN] = { 0 }; /* sequence need to write 4 registers */ tas5805_regmap sequence[4] = { { TAS5805M_REG_00, TAS5805M_PAGE_00 }, { TAS5805M_REG_7F, TAS5805M_BOOK_00 }, { TAS5805M_REG_00, TAS5805M_PAGE_00 }, { 0x66, 0x00 }, }; check_amp_func_init(g_tas5805_drv); ret = amp_read_reg(0x66, peq_val, TAS5805_PEQ_VAL_LEN, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 read peq reg fail!\n"); return ret; } if (en == HI_TRUE) { peq_val[0] &= ~0x01; /* BIT0 = 0 not bypass EQ */ } else { peq_val[0] |= 0x01; /* BIT0 = 1 bypass EQ */ } sequence[3].value = peq_val[0]; /* the 3th value write to reg 0x66 */ /* sequence need to write 4 registers */ for (i = 0; i < 4; i++) { addr = sequence[i].addr; value = sequence[i].value; ret = amp_write_reg(addr, &value, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 write peq reg fail!\n"); return ret; } } return HI_SUCCESS; } static hi_s32 amp_get_peq_en(hi_bool *en) { hi_s32 ret; hi_u8 peq_val[TAS5805_PEQ_VAL_LEN] = { 0 }; check_amp_func_init(g_tas5805_drv); ret = amp_read_reg(0x66, peq_val, TAS5805_PEQ_VAL_LEN, 1); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5707 read peq reg fail!\n"); return ret; } if ((peq_val[0] & 0x01) == 0x01) { /* BIT0 = 1 bypass EQ */ *en = HI_FALSE; } else { *en = HI_TRUE; } return HI_SUCCESS; } static hi_s32 amp_tas5805_set_sub_woofer_vol(hi_u32 volume) { return HI_SUCCESS; } static hi_s32 amp_tas5805_get_sub_woofer_vol(hi_u32 *volume) { return HI_SUCCESS; } static hi_s32 amp_tas5805_check_reg_addr(hi_u32 reg_addr) { if (reg_addr > ENDTBL_FLAG) { HI_LOG_ERR("invalid reg addr\n"); hi_err_print_h32(reg_addr); return HI_FAILURE; } return HI_SUCCESS; } static hi_void amp_tas5805_check_byte_size(hi_u32 *byte_size) { if (*byte_size > MAX_AMP_REG_LEN) { HI_LOG_ERR("TAS5805 reg length MAX is 20\n"); *byte_size = MAX_AMP_REG_LEN; } } static hi_s32 amp_tas5805_write_reg(hi_u32 reg_addr, hi_u32 byte_size, HI_VIRT_ADDR_T value_vir_addr) { hi_s32 ret; hi_u8 reg_array[MAX_AMP_REG_LEN] = { 0 }; check_amp_func_init(g_tas5805_drv); ret = amp_tas5805_check_reg_addr(reg_addr); if (ret != HI_SUCCESS) { return ret; } amp_tas5805_check_byte_size(&byte_size); ret = copy_from_user(reg_array, (hi_u8 *)(uintptr_t)value_vir_addr, byte_size); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 copy_from_user fail \n"); return ret; } ret = amp_write_reg(reg_addr, (hi_u8 *)reg_array, byte_size); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 write I2C fail \n"); return ret; } return HI_SUCCESS; } static hi_s32 amp_tas5805_read_reg(hi_u32 reg_addr, hi_u32 byte_size, HI_VIRT_ADDR_T value_vir_addr) { hi_s32 ret; hi_u8 reg_array[MAX_AMP_REG_LEN] = { 0 }; check_amp_func_init(g_tas5805_drv); ret = amp_tas5805_check_reg_addr(reg_addr); if (ret != HI_SUCCESS) { return ret; } amp_tas5805_check_byte_size(&byte_size); ret = amp_read_reg(reg_addr, reg_array, MAX_AMP_REG_LEN, byte_size); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 read I2C fail \n"); return ret; } ret = copy_to_user((hi_u8 *)(uintptr_t)value_vir_addr, reg_array, byte_size); if (ret != HI_SUCCESS) { HI_LOG_ERR("TAS5805 copy_to_user fail \n"); return ret; } return HI_SUCCESS; } static hi_void amp_tas5805_read_proc_base(hi_void *p) { HI_CHIP_VERSION_E chip_version = HI_CHIP_VERSION_BUTT; HI_CHIP_TYPE_E chip_type = HI_CHIP_TYPE_BUTT; hi_drv_sys_get_chip_version(&chip_type, &chip_version); osal_proc_print(p, "\n==============================================hi3751v%x AMP PROC====" "==========================================\n", (hi_u32)chip_version); osal_proc_print(p, "AMP modul: TI TAS5805\n"); osal_proc_print(p, "I2C num: %d\n", g_tas5805_drv->i2c_num); osal_proc_print(p, "I2C addr: 0x%x\n", g_tas5805_drv->device_addr); osal_proc_print(p, "mute GPIO group: %d\n", g_tas5805_drv->hw_mute_gpio_num / AMP_GPIO_BITS_PER_GROUP); osal_proc_print(p, "mute GPIO bit: %d\n", g_tas5805_drv->hw_mute_gpio_num % AMP_GPIO_BITS_PER_GROUP); osal_proc_print(p, "mute polarity: %d\n", g_tas5805_drv->hw_mute_polarity); osal_proc_print(p, "reset GPIO group: %d\n", g_tas5805_drv->reset_gpio_num / AMP_GPIO_BITS_PER_GROUP); osal_proc_print(p, "reset GPIO bit: %d\n", g_tas5805_drv->reset_gpio_num % AMP_GPIO_BITS_PER_GROUP); osal_proc_print(p, "reset polarity: %d\n", g_tas5805_drv->reset_polarity); } static hi_s32 amp_tas5805_read_proc(hi_void *p, hi_void *data) { hi_s32 ret; hi_bool mute_en = HI_FALSE; hi_bool drc_en = HI_FALSE; hi_bool peq_en = HI_FALSE; check_amp_func_init(g_tas5805_drv); amp_tas5805_read_proc_base(p); osal_proc_print(p, "\n-------------------------------------------------TAS5805 STATUS-------" "------------------------------------------\n"); ret = amp_tas5805_get_mute(&mute_en); if (ret != HI_SUCCESS) { HI_LOG_ERR("AMP get_mute failed!\n"); } osal_proc_print(p, "MUTE: %s\n", mute_en ? "ON" : "OFF"); ret = amp_get_drc_en(&drc_en); if (ret != HI_SUCCESS) { HI_LOG_ERR("AMP get_drc_en failed!\n"); } osal_proc_print(p, "DRC: %s\n", (drc_en) ? "ON" : "OFF"); ret = amp_get_peq_en(&peq_en); if (ret != HI_SUCCESS) { HI_LOG_ERR("AMP get_peq_en failed!\n"); } osal_proc_print(p, "PEQ: %s\n", (peq_en) ? "ON" : "OFF"); osal_proc_print(p, "============================================================" "====================================================\n"); osal_proc_print(p, "get setting command: echo help > /proc/msp/amp\n"); return HI_SUCCESS; } static hi_s32 amp_tas5805_get_enanle(hi_bool *set_en, char *cmd) { if (strstr(cmd, "1")) { *set_en = HI_TRUE; } else if (strstr(cmd, "0")) { *set_en = HI_FALSE; } else { return HI_FAILURE; } return HI_SUCCESS; } static hi_s32 amp_tas5805_proc_set_mute(unsigned int argc, char (*argv)[PROC_CMD_SINGEL_LENGTH_MAX]) { hi_s32 ret; hi_bool get_en = HI_FALSE; hi_bool set_en = HI_FALSE; if (argc != 2) { /* 2 is argc num. */ return HI_FAILURE; } ret = amp_tas5805_get_mute(&get_en); if (ret != HI_SUCCESS) { return HI_FAILURE; } ret = amp_tas5805_get_enanle(&set_en, argv[1]); if (ret != HI_SUCCESS) { return HI_FAILURE; } ret = amp_tas5805_set_mute(set_en); if (ret != HI_SUCCESS) { return HI_FAILURE; } osal_printk("set amp mute %s success:%d(%s) -> %d(%s)\n", (set_en ? "ON" : "OFF"), (hi_u32)get_en, (get_en ? "ON" : "OFF"), (hi_u32)set_en, (set_en ? "ON" : "OFF")); return HI_SUCCESS; } static hi_s32 amp_tas5805_proc_set_drc(unsigned int argc, char (*argv)[PROC_CMD_SINGEL_LENGTH_MAX]) { hi_s32 ret; hi_bool get_en = HI_FALSE; hi_bool set_en = HI_FALSE; if (argc != 2) { /* 2 is argc num. */ return HI_FAILURE; } ret = amp_get_drc_en(&get_en); if (ret != HI_SUCCESS) { return HI_FAILURE; } ret = amp_tas5805_get_enanle(&set_en, argv[1]); if (ret != HI_SUCCESS) { return HI_FAILURE; } ret = amp_set_drc_en(set_en); if (ret != HI_SUCCESS) { return HI_FAILURE; } osal_printk("set amp drc %s success:%d(%s) -> %d(%s)\n", (set_en ? "ON" : "OFF"), (hi_u32)get_en, (get_en ? "ON" : "OFF"), (hi_u32)set_en, (set_en ? "ON" : "OFF")); return HI_SUCCESS; } static hi_s32 amp_tas5805_proc_set_peq(unsigned int argc, char (*argv)[PROC_CMD_SINGEL_LENGTH_MAX]) { hi_s32 ret; hi_bool get_en = HI_FALSE; hi_bool set_en = HI_FALSE; if (argc != 2) { /* 2 is argc num. */ return HI_FAILURE; } ret = amp_get_peq_en(&get_en); if (ret != HI_SUCCESS) { return HI_FAILURE; } ret = amp_tas5805_get_enanle(&set_en, argv[1]); if (ret != HI_SUCCESS) { return HI_FAILURE; } ret = amp_set_peq_en(set_en); if (ret != HI_SUCCESS) { return HI_FAILURE; } osal_printk("set amp peq %s success:%d(%s) -> %d(%s)\n", (set_en ? "ON" : "OFF"), (hi_u32)get_en, (get_en ? "ON" : "OFF"), (hi_u32)set_en, (set_en ? "ON" : "OFF")); return HI_SUCCESS; } static hi_s32 amp_tas5805_write_proc(amp_cmd_proc cmd, unsigned int argc, char (*argv)[PROC_CMD_SINGEL_LENGTH_MAX], hi_void *private) { check_amp_func_init(g_tas5805_drv); if (cmd == AMP_CMD_PROC_SET_MUTE) { if (amp_tas5805_proc_set_mute(argc, argv) != HI_SUCCESS) { goto CMD_FAULT; } } if (cmd == AMP_CMD_PROC_SET_DRC) { if (amp_tas5805_proc_set_drc(argc, argv) != HI_SUCCESS) { goto CMD_FAULT; } } if (cmd == AMP_CMD_PROC_SET_PEQ) { if (amp_tas5805_proc_set_peq(argc, argv) != HI_SUCCESS) { goto CMD_FAULT; } } if (cmd == AMP_CMD_PROC_SET_HELP) { amp_proc_show_help(); } return HI_SUCCESS; CMD_FAULT: HI_LOG_ERR("proc cmd is fault\n"); amp_proc_show_help(); return HI_FAILURE; } amp_drv_func g_tas5805_func = { .b_ext_m_clk_flag = HI_FALSE, .amp_read_proc = amp_tas5805_read_proc, .amp_write_proc = amp_tas5805_write_proc, .amp_read_reg = amp_tas5805_read_reg, .amp_write_reg = amp_tas5805_write_reg, .amp_get_sub_woofer_vol = amp_tas5805_get_sub_woofer_vol, .amp_set_sub_woofer_vol = amp_tas5805_set_sub_woofer_vol, .amp_get_mute = amp_tas5805_get_mute, .amp_set_mute = amp_tas5805_set_mute, .amp_deinit = amp_tas5805_de_init, .amp_init = amp_tas5805_init, .amp_resume = amp_tas5805_resume, .amp_hw_mute = amp_tas5805_hw_mute, .amp_soft_reset = HI_NULL, }; amp_drv_func *amp_get_tas5805_func_handle(hi_void) { return &g_tas5805_func; } #ifdef __cplusplus #if __cplusplus } #endif #endif /* end of #ifdef __cplusplus */