/* * aw87xxx.c aw87xxx pa module * * Copyright (c) 2021 AWINIC Technology CO., LTD * * Author: Barry * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aw87xxx.h" #include "aw_device.h" #include "aw_log.h" #include "aw_monitor.h" #include "aw_acf_bin.h" #include "aw_bin_parse.h" /***************************************************************** * aw87xxx marco ******************************************************************/ #define AW87XXX_I2C_NAME "aw87xxx_pa" #define AW87XXX_DRIVER_VERSION "v2.2.0" #define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" /************************************************************************* * aw87xxx variable ************************************************************************/ static LIST_HEAD(g_aw87xxx_list); static DEFINE_MUTEX(g_aw87xxx_mutex_lock); unsigned int g_aw87xxx_dev_cnt = 0; #ifdef AW_KERNEL_VER_OVER_4_19_1 static struct aw_componet_codec_ops aw_componet_codec_ops = { .add_codec_controls = snd_soc_add_component_controls, .unregister_codec = snd_soc_unregister_component, }; #else static struct aw_componet_codec_ops aw_componet_codec_ops = { .add_codec_controls = snd_soc_add_codec_controls, .unregister_codec = snd_soc_unregister_codec, }; #endif /************************************************************************ * * aw87xxx device update profile * ************************************************************************/ static int aw87xxx_update_off_prof(struct aw87xxx *aw87xxx, char *profile) { int ret = 0; struct aw_prof_desc *prof_desc = NULL; struct aw_data_container *data_container = NULL; struct aw_device *aw_dev = &aw87xxx->aw_dev; AW_DEV_LOGD(aw87xxx->dev, "enter"); mutex_lock(&aw87xxx->reg_lock); aw_monitor_stop(&aw87xxx->monitor); prof_desc = aw_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); if (prof_desc == NULL) goto no_bin_pwr_off; if (!prof_desc->prof_st) goto no_bin_pwr_off; data_container = &prof_desc->data_container; AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", profile, data_container->len); if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); } else { if (aw_dev->ops.pwr_off_func) { ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); goto pwr_off_failed; } } else { ret = aw_dev_default_pwr_off(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); goto pwr_off_failed; } } } aw87xxx->current_profile = prof_desc->prof_name; mutex_unlock(&aw87xxx->reg_lock); return 0; pwr_off_failed: no_bin_pwr_off: aw_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); aw87xxx->current_profile = aw87xxx->prof_off_name; mutex_unlock(&aw87xxx->reg_lock); return ret; } int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) { int ret = -EINVAL; struct aw_prof_desc *prof_desc = NULL; struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; struct aw_data_container *data_container = NULL; struct aw_device *aw_dev = &aw87xxx->aw_dev; AW_DEV_LOGD(aw87xxx->dev, "enter"); if (!prof_info->status) { AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); return -EINVAL; } if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) return aw87xxx_update_off_prof(aw87xxx, profile); mutex_lock(&aw87xxx->reg_lock); prof_desc = aw_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); if (prof_desc == NULL) { AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); mutex_unlock(&aw87xxx->reg_lock); return -EINVAL; } if (!prof_desc->prof_st) { AW_DEV_LOGE(aw87xxx->dev, "not found data container"); mutex_unlock(&aw87xxx->reg_lock); return -EINVAL; } data_container = &prof_desc->data_container; AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", profile, data_container->len); aw_monitor_stop(&aw87xxx->monitor); if (aw_dev->ops.pwr_on_func) { ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); mutex_unlock(&aw87xxx->reg_lock); return aw87xxx_update_off_prof(aw87xxx, aw87xxx->prof_off_name); } } else { ret = aw_dev_default_pwr_on(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); mutex_unlock(&aw87xxx->reg_lock); return aw87xxx_update_off_prof(aw87xxx, aw87xxx->prof_off_name); } } aw87xxx->current_profile = prof_desc->prof_name; aw_monitor_start(&aw87xxx->monitor); mutex_unlock(&aw87xxx->reg_lock); AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] succeed", profile); return 0; } char *aw87xxx_show_current_profile(int dev_index) { struct list_head *pos = NULL; struct aw87xxx *aw87xxx = NULL; list_for_each(pos, &g_aw87xxx_list) { aw87xxx = list_entry(pos, struct aw87xxx, list); if (aw87xxx == NULL) { AW_LOGE("struct aw87xxx not ready"); return NULL; } if (aw87xxx->dev_index == dev_index) { AW_DEV_LOGI(aw87xxx->dev, "current profile is [%s]", aw87xxx->current_profile); return aw87xxx->current_profile; } } AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); return NULL; } EXPORT_SYMBOL(aw87xxx_show_current_profile); int aw87xxx_set_profile(int dev_index, char *profile) { struct list_head *pos = NULL; struct aw87xxx *aw87xxx = NULL; list_for_each(pos, &g_aw87xxx_list) { aw87xxx = list_entry(pos, struct aw87xxx, list); if (aw87xxx == NULL) { AW_LOGE("struct aw87xxx not ready"); return -EINVAL; } if (profile && aw87xxx->dev_index == dev_index) return aw87xxx_update_profile(aw87xxx, profile); } AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); return -EINVAL; } EXPORT_SYMBOL(aw87xxx_set_profile); /************************************************************************ * * aw87xxx esd update profile * ************************************************************************/ static int aw87xxx_esd_update_off_prof(struct aw87xxx *aw87xxx, char *profile) { int ret = 0; struct aw_prof_desc *prof_desc = NULL; struct aw_data_container *data_container = NULL; struct aw_device *aw_dev = &aw87xxx->aw_dev; AW_DEV_LOGD(aw87xxx->dev, "enter"); prof_desc = aw_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); if (prof_desc == NULL) goto no_bin_pwr_off; if (!prof_desc->prof_st) goto no_bin_pwr_off; data_container = &prof_desc->data_container; AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", profile, data_container->len); if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); } else { if (aw_dev->ops.pwr_off_func) { ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); goto pwr_off_failed; } } else { ret = aw_dev_default_pwr_off(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); goto pwr_off_failed; } } } aw87xxx->current_profile = prof_desc->prof_name; return 0; pwr_off_failed: no_bin_pwr_off: aw_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); aw87xxx->current_profile = aw87xxx->prof_off_name; return ret; } int aw87xxx_esd_update_profile(struct aw87xxx *aw87xxx, char *profile) { int ret = -EINVAL; struct aw_prof_desc *prof_desc = NULL; struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; struct aw_data_container *data_container = NULL; struct aw_device *aw_dev = &aw87xxx->aw_dev; AW_DEV_LOGD(aw87xxx->dev, "enter"); if (!prof_info->status) { AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); return -EINVAL; } if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) return aw87xxx_esd_update_off_prof(aw87xxx, profile); prof_desc = aw_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); if (prof_desc == NULL) { AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); return -EINVAL; } if (!prof_desc->prof_st) { AW_DEV_LOGE(aw87xxx->dev, "not found data container"); return -EINVAL; } data_container = &prof_desc->data_container; AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", profile, data_container->len); if (aw_dev->ops.pwr_on_func) { ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); return ret; } } else { ret = aw_dev_default_pwr_on(aw_dev, data_container); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); return ret; } } aw87xxx->current_profile = prof_desc->prof_name; AW_DEV_LOGD(aw87xxx->dev, "recover load profile[%s] succeed", profile); return 0; } /**************************************************************************** * * aw87xxx Kcontrols * ****************************************************************************/ static int aw87xxx_profile_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { int count = 0; char *name = NULL; char *profile_name = NULL; struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; if (aw87xxx == NULL) { AW_LOGE("get struct aw87xxx failed"); return -EINVAL; } uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; /*make sure have prof */ count = aw_acf_get_profile_count(aw87xxx->dev, &aw87xxx->acf_info); if (count <= 0) { uinfo->value.enumerated.items = 0; AW_DEV_LOGE(aw87xxx->dev, "get count[%d] failed", count); return 0; } uinfo->value.enumerated.items = count; if (uinfo->value.enumerated.item >= count) uinfo->value.enumerated.item = count - 1; name = uinfo->value.enumerated.name; count = uinfo->value.enumerated.item; profile_name = aw_acf_get_prof_name_form_index(aw87xxx->dev, &aw87xxx->acf_info, count); if (profile_name == NULL) { strlcpy(uinfo->value.enumerated.name, "NULL", strlen("NULL") + 1); return 0; } strlcpy(name, profile_name, sizeof(uinfo->value.enumerated.name)); return 0; } static int aw87xxx_profile_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret = -1; char *profile_name = NULL; int index = ucontrol->value.integer.value[0]; struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; struct acf_bin_info *acf_info = &aw87xxx->acf_info; if (aw87xxx == NULL) { AW_LOGE("get struct aw87xxx failed"); return -EINVAL; } profile_name = aw_acf_get_prof_name_form_index(aw87xxx->dev, acf_info, index); if (!profile_name) { AW_DEV_LOGE(aw87xxx->dev, "not found profile name,index=[%d]", index); return -EINVAL; } AW_DEV_LOGI(aw87xxx->dev, "set profile [%s]", profile_name); ret = aw87xxx_update_profile(aw87xxx, profile_name); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "set dev_index[%d] profile failed, profile = %s", aw87xxx->dev_index, profile_name); return ret; } return 0; } static int aw87xxx_profile_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int index = 0; char *profile; struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; if (aw87xxx == NULL) { AW_LOGE("get struct aw87xxx failed"); return -EINVAL; } if (!aw87xxx->current_profile) { AW_DEV_LOGE(aw87xxx->dev, "profile not init"); return -EINVAL; } profile = aw87xxx->current_profile; AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); index = aw_acf_get_prof_index_form_name(aw87xxx->dev, &aw87xxx->acf_info, aw87xxx->current_profile); if (index < 0) { AW_DEV_LOGE(aw87xxx->dev, "get profile index failed"); return index; } ucontrol->value.integer.value[0] = index; return 0; } static int aw87xxx_vmax_get_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = INT_MIN; uinfo->value.integer.max = AW_VMAX_MAX; return 0; } static int aw87xxxx_vmax_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int ret = -1; int vmax_val = 0; struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; if (aw87xxx == NULL) { AW_LOGE("get struct aw87xxx failed"); return -EINVAL; } ret = aw_monitor_no_dsp_get_vmax(&aw87xxx->monitor, &vmax_val); if (ret < 0) return ret; ucontrol->value.integer.value[0] = vmax_val; AW_DEV_LOGI(aw87xxx->dev, "get vmax = [0x%x]", vmax_val); return 0; } static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, void *codec) { struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; char *kctl_name[AW87XXX_KCONTROL_NUM]; int kcontrol_num = AW87XXX_KCONTROL_NUM; int ret = -1; AW_DEV_LOGD(aw87xxx->dev, "enter"); aw87xxx->codec = soc_codec; aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, sizeof(struct snd_kcontrol_new) * kcontrol_num, GFP_KERNEL); if (aw87xxx_kcontrol == NULL) { AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); return -ENOMEM; } kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, GFP_KERNEL); if (kctl_name[0] == NULL) return -ENOMEM; snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_profile_switch_%d", aw87xxx->dev_index); aw87xxx_kcontrol[0].name = kctl_name[0]; aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; aw87xxx_kcontrol[0].info = aw87xxx_profile_switch_info; aw87xxx_kcontrol[0].get = aw87xxx_profile_switch_get; aw87xxx_kcontrol[0].put = aw87xxx_profile_switch_put; aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; kctl_name[1] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); if (kctl_name[1] == NULL) return -ENOMEM; snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_vmax_get_%d", aw87xxx->dev_index); aw87xxx_kcontrol[1].name = kctl_name[1]; aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; aw87xxx_kcontrol[1].access = SNDRV_CTL_ELEM_ACCESS_READ; aw87xxx_kcontrol[1].info = aw87xxx_vmax_get_info; aw87xxx_kcontrol[1].get = aw87xxxx_vmax_get; aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, aw87xxx_kcontrol, kcontrol_num); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", ret); return ret; } AW_DEV_LOGI(aw87xxx->dev, "add codec controls[%s,%s]", aw87xxx_kcontrol[0].name, aw87xxx_kcontrol[1].name); return 0; } /**************************************************************************** * *aw87xxx kcontrol create * ****************************************************************************/ int aw87xxx_add_codec_controls(void *codec) { struct list_head *pos = NULL; struct aw87xxx *aw87xxx = NULL; int ret = -1; list_for_each(pos, &g_aw87xxx_list) { aw87xxx = list_entry(pos, struct aw87xxx, list); if (aw87xxx == NULL) { AW_LOGE("struct aw87xxx not ready"); return ret; } ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, codec); if (ret < 0) return ret; } return 0; } EXPORT_SYMBOL(aw87xxx_add_codec_controls); /**************************************************************************** * * aw87xxx firmware cfg load * ***************************************************************************/ static void aw87xxx_fw_cfg_free(struct aw87xxx *aw87xxx) { AW_DEV_LOGD(aw87xxx->dev, "enter"); aw_acf_profile_free(aw87xxx->dev, &aw87xxx->acf_info); aw_monitor_cfg_free(&aw87xxx->monitor); } static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) { char *profile = NULL; profile = aw_acf_get_prof_off_name(aw87xxx->dev, &aw87xxx->acf_info); if (profile == NULL) { AW_DEV_LOGE(aw87xxx->dev, "get profile off name failed"); return -EINVAL; } snprintf(aw87xxx->prof_off_name, AW_PROFILE_STR_MAX, "%s", profile); aw87xxx->current_profile = profile; AW_DEV_LOGI(aw87xxx->dev, "init profile name [%s]", aw87xxx->current_profile); return 0; } static void aw87xxx_fw_load_retry(struct aw87xxx *aw87xxx) { struct acf_bin_info *acf_info = &aw87xxx->acf_info; int ram_timer_val = 2000; AW_DEV_LOGD(aw87xxx->dev, "failed to read [%s]", aw87xxx->fw_name); if (acf_info->load_count < AW_LOAD_FW_RETRIES) { AW_DEV_LOGD(aw87xxx->dev, "restart hrtimer to load firmware"); schedule_delayed_work(&aw87xxx->fw_load_work, msecs_to_jiffies(ram_timer_val)); } else { acf_info->load_count = 0; AW_DEV_LOGE(aw87xxx->dev, "can not load firmware,please check name or file exists"); return; } acf_info->load_count++; } static void aw87xxx_fw_load(const struct firmware *fw, void *context) { int ret = -1; struct aw87xxx *aw87xxx = context; struct acf_bin_info *acf_info = &aw87xxx->acf_info; AW_DEV_LOGD(aw87xxx->dev, "enter"); if (!fw) { aw87xxx_fw_load_retry(aw87xxx); return; } AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %ld", aw87xxx->fw_name, (u_long)(fw ? fw->size : 0)); mutex_lock(&aw87xxx->reg_lock); acf_info->fw_data = vmalloc(fw->size); if (!acf_info->fw_data) { AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); goto exit_vmalloc_failed; } memset(acf_info->fw_data, 0, fw->size); memcpy(acf_info->fw_data, fw->data, fw->size); acf_info->fw_size = fw->size; ret = aw_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed"); goto exit_acf_parse_failed; } ret = aw87xxx_init_default_prof(aw87xxx); if (ret < 0) { aw87xxx_fw_cfg_free(aw87xxx); goto exit_acf_parse_failed; } AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); mutex_unlock(&aw87xxx->reg_lock); release_firmware(fw); return; exit_acf_parse_failed: exit_vmalloc_failed: release_firmware(fw); mutex_unlock(&aw87xxx->reg_lock); } static void aw87xxx_fw_load_work_routine(struct work_struct *work) { struct aw87xxx *aw87xxx = container_of(work, struct aw87xxx, fw_load_work.work); struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; AW_DEV_LOGD(aw87xxx->dev, "enter"); if (prof_info->status == AW_ACF_WAIT) { const struct firmware *fw; int ret; ret = request_firmware(&fw, aw87xxx->fw_name, aw87xxx->dev); if (!ret) { AW_DEV_LOGD(aw87xxx->dev, "loader firmware %s success\n", aw87xxx->fw_name); aw87xxx_fw_load(fw, aw87xxx); } } } static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) { #ifdef AW_CFG_UPDATE_DELAY int cfg_timer_val = AW_CFG_UPDATE_DELAY_TIMER; #else int cfg_timer_val = 0; #endif AW_DEV_LOGI(aw87xxx->dev, "enter"); snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); aw_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work_routine); schedule_delayed_work(&aw87xxx->fw_load_work, msecs_to_jiffies(cfg_timer_val)); } /**************************************************************************** * *aw87xxx attribute node * ****************************************************************************/ static ssize_t aw87xxx_attr_get_reg(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; int ret = 0; unsigned int i = 0; unsigned char reg_val = 0; struct aw87xxx *aw87xxx = dev_get_drvdata(dev); struct aw_device *aw_dev = &aw87xxx->aw_dev; mutex_lock(&aw87xxx->reg_lock); for (i = 0; i < aw_dev->reg_max_addr; i++) { if (!(aw_dev->reg_access[i] & AW_DEV_REG_RD_ACCESS)) continue; ret = aw_dev_i2c_read_byte(&aw87xxx->aw_dev, i, ®_val); if (ret < 0) { len += snprintf(buf + len, PAGE_SIZE - len, "read reg [0x%x] failed\n", i); AW_DEV_LOGE(aw87xxx->dev, "read reg [0x%x] failed", i); } else { len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02X=0x%02X\n", i, reg_val); AW_DEV_LOGD(aw87xxx->dev, "reg:0x%02X=0x%02X", i, reg_val); } } mutex_unlock(&aw87xxx->reg_lock); return len; } static ssize_t aw87xxx_attr_set_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { unsigned int databuf[2] = { 0 }; int ret = 0; struct aw87xxx *aw87xxx = dev_get_drvdata(dev); mutex_lock(&aw87xxx->reg_lock); if (sscanf(buf, "0x%x 0x%x", &databuf[0], &databuf[1]) == 2) { if (databuf[0] >= aw87xxx->aw_dev.reg_max_addr) { AW_DEV_LOGE(aw87xxx->dev, "set reg[0x%x] error,is out of reg_addr_max[0x%x]", databuf[0], aw87xxx->aw_dev.reg_max_addr); mutex_unlock(&aw87xxx->reg_lock); return -EINVAL; } ret = aw_dev_i2c_write_byte(&aw87xxx->aw_dev, databuf[0], databuf[1]); if (ret < 0) AW_DEV_LOGE(aw87xxx->dev, "set [0x%x]=0x%x failed", databuf[0], databuf[1]); else AW_DEV_LOGD(aw87xxx->dev, "set [0x%x]=0x%x succeed", databuf[0], databuf[1]); } else { AW_DEV_LOGE(aw87xxx->dev, "i2c write cmd input error"); } mutex_unlock(&aw87xxx->reg_lock); return len; } static ssize_t aw87xxx_attr_get_profile(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; unsigned int i = 0; struct aw87xxx *aw87xxx = dev_get_drvdata(dev); struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; if (!prof_info->status) { len += snprintf(buf + len, PAGE_SIZE - len, "profile_cfg not load\n"); return len; } AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); for (i = 0; i < prof_info->count; i++) { if (!strncmp(aw87xxx->current_profile, prof_info->prof_name_list[i], AW_PROFILE_STR_MAX)) len += snprintf(buf + len, PAGE_SIZE - len, ">%s\n", prof_info->prof_name_list[i]); else len += snprintf(buf + len, PAGE_SIZE - len, " %s\n", prof_info->prof_name_list[i]); } return len; } static ssize_t aw87xxx_attr_set_profile(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { char profile[AW_PROFILE_STR_MAX] = {0}; int ret = 0; struct aw87xxx *aw87xxx = dev_get_drvdata(dev); if (sscanf(buf, "%s", profile) == 1) { AW_DEV_LOGD(aw87xxx->dev, "set profile [%s]", profile); ret = aw87xxx_update_profile(aw87xxx, profile); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "set profile[%s] failed", profile); return ret; } } return len; } static ssize_t aw87xxx_attr_get_hwen(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; struct aw87xxx *aw87xxx = dev_get_drvdata(dev); int hwen = aw87xxx->aw_dev.hwen_status; if (hwen >= AW_DEV_HWEN_INVALID) len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: invalid\n"); else if (hwen == AW_DEV_HWEN_ON) len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: on\n"); else if (hwen == AW_DEV_HWEN_OFF) len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: off\n"); return len; } static ssize_t aw87xxx_attr_set_hwen(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int ret = -1; unsigned int state; struct aw87xxx *aw87xxx = dev_get_drvdata(dev); ret = kstrtouint(buf, 0, &state); if (ret) { AW_DEV_LOGE(aw87xxx->dev, "fail to channelge str to int"); return ret; } mutex_lock(&aw87xxx->reg_lock); if (state == AW_DEV_HWEN_OFF) aw_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /*OFF*/ else if (state == AW_DEV_HWEN_ON) aw_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /*ON*/ else AW_DEV_LOGE(aw87xxx->dev, "input [%d] error, hwen_on=[%d],hwen_off=[%d]", state, AW_DEV_HWEN_ON, AW_DEV_HWEN_OFF); mutex_unlock(&aw87xxx->reg_lock); return len; } int aw87xxx_awrw_write(struct aw87xxx *aw87xxx, const char *buf, size_t count) { int i = 0, ret = -1; char *data_buf = NULL; int buf_len = 0; int temp_data = 0; int data_str_size = 0; char *reg_data; struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; AW_DEV_LOGD(aw87xxx->dev, "enter"); /* one addr or one data string Composition of Contains two bytes of symbol(0X)*/ /* and two byte of hexadecimal data*/ data_str_size = 2 + 2 * AWRW_DATA_BYTES; /* The buf includes the first address of the register to be written and all data */ buf_len = AWRW_ADDR_BYTES + packet->reg_num * AWRW_DATA_BYTES; AW_DEV_LOGI(aw87xxx->dev, "buf_len = %d,reg_num = %d", buf_len, packet->reg_num); data_buf = vmalloc(buf_len); if (data_buf == NULL) { AW_DEV_LOGE(aw87xxx->dev, "alloc memory failed"); return -ENOMEM; } memset(data_buf, 0, buf_len); data_buf[0] = packet->reg_addr; reg_data = data_buf + 1; AW_DEV_LOGD(aw87xxx->dev, "reg_addr: 0x%02x", data_buf[0]); /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ for (i = 0; i < packet->reg_num; i++) { ret = sscanf(buf + AWRW_HDR_LEN + 1 + i * (data_str_size + 1), "0x%x", &temp_data); if (ret != 1) { AW_DEV_LOGE(aw87xxx->dev, "sscanf failed,ret=%d", ret); return ret; } reg_data[i] = temp_data; AW_DEV_LOGD(aw87xxx->dev, "[%d] : 0x%02x", i, reg_data[i]); } mutex_lock(&aw87xxx->reg_lock); ret = i2c_master_send(aw87xxx->aw_dev.i2c, data_buf, buf_len); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "write failed"); vfree(data_buf); data_buf = NULL; mutex_unlock(&aw87xxx->reg_lock); return -EFAULT; } mutex_unlock(&aw87xxx->reg_lock); vfree(data_buf); data_buf = NULL; AW_DEV_LOGD(aw87xxx->dev, "down"); return 0; } static int aw87xxx_awrw_data_check(struct aw87xxx *aw87xxx, int *data, size_t count) { struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; int req_data_len = 0; int act_data_len = 0; int data_str_size = 0; if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { AW_DEV_LOGE(aw87xxx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); return -EINVAL; } /* one data string Composition of Contains two bytes of symbol(0x)*/ /* and two byte of hexadecimal data*/ data_str_size = 2 + 2 * AWRW_DATA_BYTES; act_data_len = count - AWRW_HDR_LEN - 1; /* There is a comma(,) or space between each piece of data */ if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ req_data_len = (data_str_size + 1) * packet->reg_num; if (req_data_len > act_data_len) { AW_DEV_LOGE(aw87xxx->dev, "data_len checkfailed,requeset data_len [%d],actaul data_len [%d]", req_data_len, act_data_len); return -EINVAL; } } return 0; } /* flag addr_bytes data_bytes reg_num reg_addr*/ static int aw87xxx_awrw_parse_buf(struct aw87xxx *aw87xxx, const char *buf, size_t count, int *wr_status) { int data[AWRW_HDR_MAX] = {0}; struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; int ret = -1; if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", &data[AWRW_HDR_WR_FLAG], &data[AWRW_HDR_ADDR_BYTES], &data[AWRW_HDR_DATA_BYTES], &data[AWRW_HDR_REG_NUM], &data[AWRW_HDR_REG_ADDR]) == 5) { packet->reg_addr = data[AWRW_HDR_REG_ADDR]; packet->reg_num = data[AWRW_HDR_REG_NUM]; *wr_status = data[AWRW_HDR_WR_FLAG]; ret = aw87xxx_awrw_data_check(aw87xxx, data, count); if (ret < 0) return ret; return 0; } return -EINVAL; } static ssize_t aw87xxx_attr_awrw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aw87xxx *aw87xxx = dev_get_drvdata(dev); struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; int wr_status = 0; int ret = -1; if (count < AWRW_HDR_LEN) { AW_DEV_LOGE(aw87xxx->dev, "data count too smaller, please check write format"); AW_DEV_LOGE(aw87xxx->dev, "string %s,count=%ld", buf, (u_long)count); return -EINVAL; } AW_DEV_LOGI(aw87xxx->dev, "string:[%s],count=%ld", buf, (u_long)count); ret = aw87xxx_awrw_parse_buf(aw87xxx, buf, count, &wr_status); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "can not parse string"); return ret; } if (wr_status == AWRW_FLAG_WRITE) { ret = aw87xxx_awrw_write(aw87xxx, buf, count); if (ret < 0) return ret; } else if (wr_status == AWRW_FLAG_READ) { packet->status = AWRW_I2C_ST_READ; AW_DEV_LOGI(aw87xxx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", packet->reg_addr, packet->reg_num); } else { AW_DEV_LOGE(aw87xxx->dev, "please check str format, unsupport read_write_status: %d", wr_status); return -EINVAL; } return count; } static ssize_t aw87xxx_attr_awrw_show(struct device *dev, struct device_attribute *attr, char *buf) { struct aw87xxx *aw87xxx = dev_get_drvdata(dev); struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; int data_len = 0; size_t len = 0; int ret = -1, i = 0; char *reg_data = NULL; if (packet->status != AWRW_I2C_ST_READ) { AW_DEV_LOGE(aw87xxx->dev, "please write read cmd first"); return -EINVAL; } data_len = AWRW_DATA_BYTES * packet->reg_num; reg_data = (char *)vmalloc(data_len); if (reg_data == NULL) { AW_DEV_LOGE(aw87xxx->dev, "memory alloc failed"); ret = -EINVAL; goto exit; } mutex_lock(&aw87xxx->reg_lock); ret = aw_dev_i2c_read_msg(&aw87xxx->aw_dev, packet->reg_addr, (char *)reg_data, data_len); if (ret < 0) { ret = -EFAULT; mutex_unlock(&aw87xxx->reg_lock); goto exit; } mutex_unlock(&aw87xxx->reg_lock); AW_DEV_LOGI(aw87xxx->dev, "reg_addr 0x%02x, reg_num %d", packet->reg_addr, packet->reg_num); for (i = 0; i < data_len; i++) { len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x,", reg_data[i]); AW_DEV_LOGI(aw87xxx->dev, "0x%02x", reg_data[i]); } ret = len; exit: if (reg_data) { vfree(reg_data); reg_data = NULL; } packet->status = AWRW_I2C_ST_NONE; return ret; } static ssize_t aw87xxx_drv_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; len += snprintf(buf + len, PAGE_SIZE - len, "driver_ver: %s \n", AW87XXX_DRIVER_VERSION); return len; } static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw87xxx_attr_get_reg, aw87xxx_attr_set_reg); static DEVICE_ATTR(profile, S_IWUSR | S_IRUGO, aw87xxx_attr_get_profile, aw87xxx_attr_set_profile); static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, aw87xxx_attr_get_hwen, aw87xxx_attr_set_hwen); static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, aw87xxx_attr_awrw_show, aw87xxx_attr_awrw_store); static DEVICE_ATTR(drv_ver, S_IRUGO, aw87xxx_drv_ver_show, NULL); static struct attribute *aw87xxx_attributes[] = { &dev_attr_reg.attr, &dev_attr_profile.attr, &dev_attr_hwen.attr, &dev_attr_awrw.attr, &dev_attr_drv_ver.attr, NULL }; static struct attribute_group aw87xxx_attribute_group = { .attrs = aw87xxx_attributes }; /**************************************************************************** * *aw87xxx device probe * ****************************************************************************/ int aw87xxx_dtsi_dev_index_check(struct aw87xxx *cur_aw87xxx) { struct list_head *pos = NULL; struct aw87xxx *list_aw87xxx = NULL; list_for_each(pos, &g_aw87xxx_list) { list_aw87xxx = list_entry(pos, struct aw87xxx, list); if (list_aw87xxx == NULL) continue; if (list_aw87xxx->dev_index == cur_aw87xxx->dev_index) { AW_DEV_LOGE(cur_aw87xxx->dev, "dev_index has already existing,check failed"); return -EINVAL; } } return 0; } static int aw87xxx_dtsi_parse(struct aw87xxx *aw87xxx, struct device_node *dev_node) { int ret = -1; int32_t dev_index = -EINVAL; ret = of_property_read_u32(dev_node, "dev_index", &dev_index); if (ret < 0) { AW_DEV_LOGI(aw87xxx->dev, "dev_index parse failed, user default[%d], ret=%d", g_aw87xxx_dev_cnt, ret); aw87xxx->dev_index = g_aw87xxx_dev_cnt; } else { aw87xxx->dev_index = dev_index; AW_DEV_LOGI(aw87xxx->dev, "parse dev_index=[%d]", aw87xxx->dev_index); } ret = aw87xxx_dtsi_dev_index_check(aw87xxx); if (ret < 0) return ret; ret = of_get_named_gpio(dev_node, "reset-gpio", 0); if (ret < 0) { AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; } else { aw87xxx->aw_dev.rst_gpio = ret; aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%d] parse succeed", ret); if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { ret = devm_gpio_request_one(aw87xxx->dev, aw87xxx->aw_dev.rst_gpio, GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "reset request failed"); return ret; } } return 0; } /* In order to compatible with stereo share only one reset gpio */ ret = of_get_named_gpio(dev_node, "reset-shared-gpio", 0); if (ret < 0) { AW_DEV_LOGI(aw87xxx->dev, "no reset shared gpio provided, hardware reset unavailable"); aw87xxx->aw_dev.rst_shared_gpio = AW_NO_RESET_GPIO; } else { aw87xxx->aw_dev.rst_shared_gpio = ret; AW_DEV_LOGI(aw87xxx->dev, "reset shared gpio[%d] parse succeed", ret); if (gpio_is_valid(aw87xxx->aw_dev.rst_shared_gpio)) { ret = devm_gpio_request_one(aw87xxx->dev, aw87xxx->aw_dev.rst_shared_gpio, GPIOF_OUT_INIT_LOW, "aw87xxx_reset-shared"); if (ret < 0) { AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_reset-shared reset request failed"); return ret; } msleep(20); gpio_set_value_cansleep(aw87xxx->aw_dev.rst_shared_gpio, AW_GPIO_HIGHT_LEVEL); } } return 0; } static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) { struct aw87xxx *aw87xxx = NULL; aw87xxx = devm_kzalloc(&client->dev, sizeof(struct aw87xxx), GFP_KERNEL); if (aw87xxx == NULL) { AW_DEV_LOGE(&client->dev, "failed to devm_kzalloc aw87xxx"); return NULL; } memset(aw87xxx, 0, sizeof(struct aw87xxx)); aw87xxx->dev = &client->dev; aw87xxx->aw_dev.dev = &client->dev; aw87xxx->aw_dev.i2c_bus = client->adapter->nr; aw87xxx->aw_dev.i2c_addr = client->addr; aw87xxx->aw_dev.i2c = client; aw87xxx->aw_dev.hwen_status = false; aw87xxx->aw_dev.reg_access = NULL; aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; aw87xxx->off_bin_status = AW87XXX_NO_OFF_BIN; aw87xxx->codec = NULL; aw87xxx->current_profile = aw87xxx->prof_off_name; mutex_init(&aw87xxx->reg_lock); AW_DEV_LOGI(&client->dev, "struct aw87xxx devm_kzalloc and init down"); return aw87xxx; } static int aw87xxx_probe(struct snd_soc_component *component) { struct aw87xxx *aw87xxx = dev_get_drvdata(component->dev); int ret; ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, component); if (ret < 0) { dev_err(component->dev, "%s: aw87xxx_add_codec_controls failed, ret= %d\n", __func__, ret); return ret; }; return 0; } static const struct snd_soc_component_driver aw87xxx_component_driver = { .probe = aw87xxx_probe, }; static int aw87xxx_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device_node *dev_node = client->dev.of_node; struct aw87xxx *aw87xxx = NULL; int ret = -1; dev_info(&client->dev, "%s\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { AW_DEV_LOGE(&client->dev, "check_functionality failed"); return -ENODEV; } /* aw87xxx i2c_dev struct init */ aw87xxx = aw87xxx_malloc_init(client); if (aw87xxx == NULL) return -ENOMEM; i2c_set_clientdata(client, aw87xxx); /* aw87xxx dev_node parse */ ret = aw87xxx_dtsi_parse(aw87xxx, dev_node); if (ret < 0) goto exit; /*hw power on PA*/ aw_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /* aw87xxx devices private attributes init */ ret = aw_dev_init(&aw87xxx->aw_dev); if (ret < 0) goto exit; ret = devm_snd_soc_register_component(aw87xxx->dev, &aw87xxx_component_driver, NULL, 0); if (ret < 0) { dev_err(aw87xxx->dev, "%s() register codec error %d\n", __func__, ret); goto exit; } /*product register reset */ aw_dev_soft_reset(&aw87xxx->aw_dev); /*hw power off */ aw_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /* create debug attrbute nodes */ ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); if (ret < 0) AW_DEV_LOGE(aw87xxx->dev, "failed to create sysfs nodes, will not allowed to use"); /* cfg_load init */ aw87xxx_fw_load_init(aw87xxx); /*monitor init*/ aw_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); /*add device to total list */ mutex_lock(&g_aw87xxx_mutex_lock); g_aw87xxx_dev_cnt++; list_add(&aw87xxx->list, &g_aw87xxx_list); mutex_unlock(&g_aw87xxx_mutex_lock); AW_DEV_LOGI(aw87xxx->dev, "succeed"); return 0; exit: AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); return ret; } static void aw87xxx_i2c_remove(struct i2c_client *client) { struct aw87xxx *aw87xxx = i2c_get_clientdata(client); aw_monitor_exit(&aw87xxx->monitor); /*rm attr node*/ sysfs_remove_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); aw87xxx_fw_cfg_free(aw87xxx); mutex_lock(&g_aw87xxx_mutex_lock); g_aw87xxx_dev_cnt--; list_del(&aw87xxx->list); mutex_unlock(&g_aw87xxx_mutex_lock); } static void aw87xxx_i2c_shutdown(struct i2c_client *client) { struct aw87xxx *aw87xxx = i2c_get_clientdata(client); AW_DEV_LOGI(&client->dev, "enter"); /*soft and hw power off*/ aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); } static const struct i2c_device_id aw87xxx_i2c_id[] = { {AW87XXX_I2C_NAME, 0}, {}, }; static const struct of_device_id extpa_of_match[] = { {.compatible = "awinic,aw87xxx_pa"}, {}, }; static struct i2c_driver aw87xxx_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = AW87XXX_I2C_NAME, .of_match_table = extpa_of_match, }, .probe = aw87xxx_i2c_probe, .remove = aw87xxx_i2c_remove, .shutdown = aw87xxx_i2c_shutdown, .id_table = aw87xxx_i2c_id, }; static int __init aw87xxx_pa_init(void) { int ret; AW_LOGI("driver version: %s", AW87XXX_DRIVER_VERSION); ret = i2c_add_driver(&aw87xxx_i2c_driver); if (ret < 0) { AW_LOGE("Unable to register driver, ret= %d", ret); return ret; } return 0; } static void __exit aw87xxx_pa_exit(void) { AW_LOGI("enter"); i2c_del_driver(&aw87xxx_i2c_driver); } module_init(aw87xxx_pa_init); module_exit(aw87xxx_pa_exit); MODULE_AUTHOR(""); MODULE_DESCRIPTION("awinic aw87xxx pa driver"); MODULE_LICENSE("GPL v2");