1433 lines
38 KiB
C

/*
* aw87xxx.c aw87xxx pa module
*
* Copyright (c) 2021 AWINIC Technology CO., LTD
*
* Author: Barry <zhaozhongbo@awinic.com>
*
* 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 <linux/i2c.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/gameport.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/kthread.h>
#include <uapi/sound/asound.h>
#include <sound/control.h>
#include <sound/soc.h>
#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, &reg_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("<zhaozhongbo@awinic.com>");
MODULE_DESCRIPTION("awinic aw87xxx pa driver");
MODULE_LICENSE("GPL v2");