适配新七寸屏和AW83791芯片

This commit is contained in:
2026-11-03 10:16:35 +08:00
parent e7f64a6add
commit a9748ac49c
43 changed files with 2584 additions and 87 deletions
@@ -1290,6 +1290,12 @@ static int aw87xxx_acf_bin_load(struct aw87xxx *aw87xxx)
ret = request_firmware(&cont, aw87xxx->fw_name, aw87xxx->dev);
if ((ret) || (!cont)) {
AW_DEV_LOGD(aw87xxx->dev, "load [%s] failed!", aw87xxx->fw_name);
if (aw87xxx->aw_dev.chipid == AW_DEV_CHIPID_C1) {
AW_DEV_LOGI(aw87xxx->dev,
"no ACF for AW87391, use safe built-in defaults");
return aw87391_acf_load_builtin(aw87xxx->dev,
&aw87xxx->acf_info);
}
if (acf_info->load_count == AW_READ_CHIPID_RETRIES) {
acf_info->load_count = 0;
} else {
@@ -1319,7 +1325,14 @@ static int aw87xxx_acf_bin_load(struct aw87xxx *aw87xxx)
ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info);
if (ret < 0) {
AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed");
if (aw87xxx->aw_dev.chipid == AW_DEV_CHIPID_C1) {
AW_DEV_LOGI(aw87xxx->dev,
"ACF has no AW87391 profile, use safe built-in defaults");
ret = aw87391_acf_load_builtin(aw87xxx->dev,
&aw87xxx->acf_info);
} else {
AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed");
}
goto exit_acf_parse_failed;
}
@@ -80,6 +80,55 @@ static char *g_profile_name[] = {"Music", "Voice", "Voip",
static char *g_power_off_name[] = {"Off", "OFF", "off", "oFF", "power_down"};
/*
* Safe AW87391 fallback profiles derived from the public register definitions
* in the AW87391 V1.2 data sheet. The board's legacy ACF contains AW87390
* (chip-id 0x76) data only; those values must never be written to AW87391
* (chip-id 0xc1), whose public register map is different.
*
* Keep EN_SW clear while programming and write it last when powering on.
* Only public registers 0x00..0x05 are touched. Keep the board's legacy 8.0 V
* charge-pump limit (0x06 on AW87391) and use the conservative default AGC2
* limit of 1.2 W into 8 ohm. A product-specific ACF, when supplied, is still
* preferred and bypasses this fallback entirely.
*/
static uint8_t g_aw87391_music_profile[] = {
0x00, 0xaa,
AW87XXX_DELAY_REG_ADDR, 0x01,
0x02, 0x06,
0x03, 0x02,
0x04, 0x47,
0x05, 0x4e,
0x01, 0x3f,
};
static uint8_t g_aw87391_off_profile[] = {
0x01, 0x3e,
};
static uint8_t g_aw87391_receiver_profile[] = {
0x00, 0xaa,
AW87XXX_DELAY_REG_ADDR, 0x01,
0x02, 0x06,
0x03, 0x00,
0x04, 0x47,
0x05, 0x4e,
0x01, 0x37,
};
struct aw87391_builtin_profile {
const char *name;
uint8_t *data;
uint32_t len;
};
static const struct aw87391_builtin_profile g_aw87391_builtin_profiles[] = {
{ "Music", g_aw87391_music_profile, ARRAY_SIZE(g_aw87391_music_profile) },
{ "Off", g_aw87391_off_profile, ARRAY_SIZE(g_aw87391_off_profile) },
{ "Receiver", g_aw87391_receiver_profile,
ARRAY_SIZE(g_aw87391_receiver_profile) },
};
static char *aw_get_prof_name(int profile)
{
if (profile < 0 || profile >= AW_PROFILE_MAX)
@@ -1464,6 +1513,47 @@ void aw87xxx_acf_profile_free(struct device *dev, struct acf_bin_info *acf_info)
}
}
int aw87391_acf_load_builtin(struct device *dev,
struct acf_bin_info *acf_info)
{
struct aw_prof_info *prof_info = &acf_info->prof_info;
int count = ARRAY_SIZE(g_aw87391_builtin_profiles);
int i;
aw87xxx_acf_profile_free(dev, acf_info);
prof_info->prof_name_list = devm_kcalloc(dev, count,
AW_PROFILE_STR_MAX, GFP_KERNEL);
if (!prof_info->prof_name_list)
return -ENOMEM;
prof_info->prof_desc = devm_kcalloc(dev, count,
sizeof(*prof_info->prof_desc), GFP_KERNEL);
if (!prof_info->prof_desc) {
devm_kfree(dev, prof_info->prof_name_list);
prof_info->prof_name_list = NULL;
return -ENOMEM;
}
for (i = 0; i < count; i++) {
snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s",
g_aw87391_builtin_profiles[i].name);
prof_info->prof_desc[i].prof_name = prof_info->prof_name_list[i];
prof_info->prof_desc[i].prof_st = AW_PROFILE_OK;
prof_info->prof_desc[i].data_container.data =
g_aw87391_builtin_profiles[i].data;
prof_info->prof_desc[i].data_container.len =
g_aw87391_builtin_profiles[i].len;
}
prof_info->count = count;
prof_info->prof_type = AW_DDE_DEV_DEFAULT_TYPE_ID;
prof_info->status = AW_ACF_UPDATE;
AW_DEV_LOGI(dev, "loaded built-in AW87391 safe profiles");
return 0;
}
int aw_parse_single_bin(struct device *dev, struct acf_bin_info *acf_info,
struct aw_data_container aw_fw_data, int prof_index)
{
@@ -193,6 +193,8 @@ char *aw87xxx_ctos_get_prof_name(int profile_id);
void aw87xxx_acf_profile_free(struct device *dev,
struct acf_bin_info *acf_info);
int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info);
int aw87391_acf_load_builtin(struct device *dev,
struct acf_bin_info *acf_info);
struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev,
struct acf_bin_info *acf_info, char *profile_name);
int aw87xxx_acf_get_prof_index_form_name(struct device *dev,
@@ -226,8 +226,9 @@ static struct sprd_headset *sprd_hdst;
static void sprd_enable_hmicbias_polling(bool enable, bool force_disable);
/*
* When remove headphone, disconnect the headphone
* dapm DA path in codec driver.
* Keep the UMP9620 "HP Virt" DAPM path in sync with jack state.
* SND_SOC_DAPM_SWITCH_E("HP Virt", ...) exposes the mixer control as
* "HP Virt Switch".
*/
static int dapm_jack_switch_control(struct snd_soc_component *codec, bool on)
{
@@ -235,7 +236,7 @@ static int dapm_jack_switch_control(struct snd_soc_component *codec, bool on)
struct snd_soc_card *card = codec ? codec->card : NULL;
struct snd_ctl_elem_id id = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Virt HP Jack Switch",
.name = "HP Virt Switch",
};
struct snd_ctl_elem_value ucontrol = {
.value.integer.value[0] = on,