1de7027c9SMarco Giunta // SPDX-License-Identifier: GPL-2.0-only 2de7027c9SMarco Giunta // 3de7027c9SMarco Giunta // AW88399 HDA side codec driver 4de7027c9SMarco Giunta // 5de7027c9SMarco Giunta // Based on cs35l41_hda.c and aw88399.c 6de7027c9SMarco Giunta // 7de7027c9SMarco Giunta 8de7027c9SMarco Giunta #include <linux/acpi.h> 9de7027c9SMarco Giunta #include <linux/gpio/consumer.h> 10de7027c9SMarco Giunta #include <linux/i2c.h> 11de7027c9SMarco Giunta #include <linux/module.h> 12de7027c9SMarco Giunta #include <linux/mutex.h> 13de7027c9SMarco Giunta #include <linux/pm_runtime.h> 14de7027c9SMarco Giunta #include <linux/string.h> 15de7027c9SMarco Giunta #include <sound/hda_codec.h> 16de7027c9SMarco Giunta #include "../generic.h" 17de7027c9SMarco Giunta #include "hda_component.h" 18de7027c9SMarco Giunta #include "aw88399_hda.h" 19de7027c9SMarco Giunta 20de7027c9SMarco Giunta #define AW88399_HDA_I2C_BASE_ADDR 0x34 21de7027c9SMarco Giunta 22de7027c9SMarco Giunta static void aw88399_hda_playback_hook(struct device *dev, int action) 23de7027c9SMarco Giunta { 24de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 25de7027c9SMarco Giunta struct aw88399 *core = aw88399->core; 26de7027c9SMarco Giunta int ret = 0; 27de7027c9SMarco Giunta 28de7027c9SMarco Giunta dev_dbg(aw88399->dev, "Playback action: %d\n", action); 29de7027c9SMarco Giunta 30de7027c9SMarco Giunta switch (action) { 31de7027c9SMarco Giunta case HDA_GEN_PCM_ACT_OPEN: 32de7027c9SMarco Giunta pm_runtime_get_sync(dev); 33de7027c9SMarco Giunta aw88399->playing = true; 34de7027c9SMarco Giunta break; 35de7027c9SMarco Giunta case HDA_GEN_PCM_ACT_PREPARE: 36de7027c9SMarco Giunta if (core) 37de7027c9SMarco Giunta aw88399_start(core, AW88399_SYNC_START); 38de7027c9SMarco Giunta break; 39de7027c9SMarco Giunta case HDA_GEN_PCM_ACT_CLEANUP: 40de7027c9SMarco Giunta if (aw88399->aw_dev) 41de7027c9SMarco Giunta ret = aw88399_stop(aw88399->aw_dev); 42de7027c9SMarco Giunta if (ret) 43de7027c9SMarco Giunta dev_err(aw88399->dev, "Failed to stop amplifier: %d\n", ret); 44de7027c9SMarco Giunta break; 45de7027c9SMarco Giunta case HDA_GEN_PCM_ACT_CLOSE: 46de7027c9SMarco Giunta if (aw88399->aw_dev) 47de7027c9SMarco Giunta aw88399_stop(aw88399->aw_dev); 48de7027c9SMarco Giunta aw88399->playing = false; 49de7027c9SMarco Giunta pm_runtime_mark_last_busy(dev); 50de7027c9SMarco Giunta pm_runtime_put_autosuspend(dev); 51de7027c9SMarco Giunta break; 52de7027c9SMarco Giunta default: 53de7027c9SMarco Giunta dev_warn(aw88399->dev, "Unsupported action: %d\n", action); 54de7027c9SMarco Giunta break; 55de7027c9SMarco Giunta } 56de7027c9SMarco Giunta } 57de7027c9SMarco Giunta 58de7027c9SMarco Giunta static int aw88399_hda_bind(struct device *dev, struct device *master, void *master_data) 59de7027c9SMarco Giunta { 60de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 61de7027c9SMarco Giunta struct hda_component_parent *parent = master_data; 62de7027c9SMarco Giunta struct hda_component *comp; 63de7027c9SMarco Giunta 64de7027c9SMarco Giunta comp = hda_component_from_index(parent, aw88399->index); 65de7027c9SMarco Giunta if (!comp) 66de7027c9SMarco Giunta return -EINVAL; 67de7027c9SMarco Giunta 68de7027c9SMarco Giunta if (comp->dev) 69de7027c9SMarco Giunta return -EBUSY; 70de7027c9SMarco Giunta 71de7027c9SMarco Giunta comp->dev = dev; 72de7027c9SMarco Giunta 73de7027c9SMarco Giunta strscpy(comp->name, dev_name(dev), sizeof(comp->name)); 74de7027c9SMarco Giunta 75de7027c9SMarco Giunta comp->playback_hook = aw88399_hda_playback_hook; 76de7027c9SMarco Giunta 77de7027c9SMarco Giunta dev_info(aw88399->dev, 78de7027c9SMarco Giunta "AW88399 Bound - SSID: %s, channel: %d\n", 79de7027c9SMarco Giunta aw88399->acpi_subsystem_id, aw88399->channel); 80de7027c9SMarco Giunta 81de7027c9SMarco Giunta return 0; 82de7027c9SMarco Giunta } 83de7027c9SMarco Giunta 84de7027c9SMarco Giunta static void aw88399_hda_unbind(struct device *dev, struct device *master, void *master_data) 85de7027c9SMarco Giunta { 86de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 87de7027c9SMarco Giunta struct hda_component_parent *parent = master_data; 88de7027c9SMarco Giunta struct hda_component *comp; 89de7027c9SMarco Giunta 90de7027c9SMarco Giunta comp = hda_component_from_index(parent, aw88399->index); 91de7027c9SMarco Giunta if (comp && (comp->dev == dev)) 92de7027c9SMarco Giunta memset(comp, 0, sizeof(*comp)); 93de7027c9SMarco Giunta 94de7027c9SMarco Giunta dev_dbg(aw88399->dev, "Unbound from HDA codec\n"); 95de7027c9SMarco Giunta } 96de7027c9SMarco Giunta 97de7027c9SMarco Giunta static const struct component_ops aw88399_hda_comp_ops = { 98de7027c9SMarco Giunta .bind = aw88399_hda_bind, 99de7027c9SMarco Giunta .unbind = aw88399_hda_unbind, 100de7027c9SMarco Giunta }; 101de7027c9SMarco Giunta 102de7027c9SMarco Giunta static int aw88399_hda_index_from_i2c(struct aw88399_hda *aw88399) 103de7027c9SMarco Giunta { 104de7027c9SMarco Giunta return to_i2c_client(aw88399->dev)->addr - AW88399_HDA_I2C_BASE_ADDR; 105de7027c9SMarco Giunta } 106de7027c9SMarco Giunta 107de7027c9SMarco Giunta static int aw88399_hda_init(struct aw88399_hda *aw88399) 108de7027c9SMarco Giunta { 109de7027c9SMarco Giunta struct device *dev = aw88399->dev; 110de7027c9SMarco Giunta struct i2c_client *i2c = to_i2c_client(dev); 111de7027c9SMarco Giunta struct aw88399 *core; 112de7027c9SMarco Giunta int ret; 113de7027c9SMarco Giunta 114de7027c9SMarco Giunta core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); 115de7027c9SMarco Giunta if (!core) 116de7027c9SMarco Giunta return -ENOMEM; 117de7027c9SMarco Giunta 118de7027c9SMarco Giunta mutex_init(&core->lock); 119de7027c9SMarco Giunta core->reset_gpio = aw88399->reset_gpio; 120de7027c9SMarco Giunta core->regmap = aw88399->regmap; 121de7027c9SMarco Giunta core->bsts_unreliable = aw88399->bsts_unreliable; 122de7027c9SMarco Giunta 123de7027c9SMarco Giunta aw88399_hw_reset(core); 124de7027c9SMarco Giunta 125de7027c9SMarco Giunta ret = aw88399_init(core, i2c, aw88399->regmap); 126de7027c9SMarco Giunta if (ret) 127de7027c9SMarco Giunta return ret; 128de7027c9SMarco Giunta 129de7027c9SMarco Giunta /* Set channel BEFORE loading firmware so ACF parser sees correct value */ 130de7027c9SMarco Giunta if (core->aw_pa) 131de7027c9SMarco Giunta aw88399_dev_set_channel(core, aw88399->channel); 132de7027c9SMarco Giunta 133de7027c9SMarco Giunta ret = aw88399_request_firmware_file(core); 134de7027c9SMarco Giunta if (ret) 135de7027c9SMarco Giunta return ret; 136de7027c9SMarco Giunta 137de7027c9SMarco Giunta aw88399->core = core; 138de7027c9SMarco Giunta aw88399->aw_dev = core->aw_pa; 139de7027c9SMarco Giunta 140de7027c9SMarco Giunta return 0; 141de7027c9SMarco Giunta } 142de7027c9SMarco Giunta 143494978aeSMarco Giunta static int aw88399_swap_channels(struct aw88399_hda *aw88399) 144494978aeSMarco Giunta { 145494978aeSMarco Giunta /* 146494978aeSMarco Giunta * Certain Lenovo Legion laptops have their 147494978aeSMarco Giunta * I2C wiring reversed: 0x34 is physically the right speaker, 148494978aeSMarco Giunta * 0x35 is the left. Swap channels to correct L/R assignment. 149494978aeSMarco Giunta * This is a model-specific hardware wiring issue, not a driver bug. 150494978aeSMarco Giunta */ 151494978aeSMarco Giunta aw88399->channel = 1 - aw88399->channel; 152494978aeSMarco Giunta dev_dbg(aw88399->dev, 153494978aeSMarco Giunta "Channel swap applied: index %d -> channel %d\n", 154494978aeSMarco Giunta aw88399->index, aw88399->channel); 155494978aeSMarco Giunta return 0; 156494978aeSMarco Giunta } 157494978aeSMarco Giunta 158494978aeSMarco Giunta static int aw88399_skip_bsts_check(struct aw88399_hda *aw88399) 159494978aeSMarco Giunta { 160494978aeSMarco Giunta /* 161494978aeSMarco Giunta * BSTS (boost-finished) status bit does not reliably report on 162494978aeSMarco Giunta * some hardware. On certain Lenovo Legion laptops, both amps 163494978aeSMarco Giunta * report BSTS=0 (boost not finished) during normal playback 164494978aeSMarco Giunta * despite clean audio output. Skip BSTS in the startup status 165494978aeSMarco Giunta * check to avoid false init failures. 166494978aeSMarco Giunta */ 167494978aeSMarco Giunta aw88399->bsts_unreliable = true; 168494978aeSMarco Giunta dev_dbg(aw88399->dev, "BSTS status check disabled\n"); 169494978aeSMarco Giunta return 0; 170494978aeSMarco Giunta } 171494978aeSMarco Giunta 172494978aeSMarco Giunta static int aw88399_apply_legion_quirks(struct aw88399_hda *aw88399) 173494978aeSMarco Giunta { 174494978aeSMarco Giunta aw88399_swap_channels(aw88399); 175494978aeSMarco Giunta aw88399_skip_bsts_check(aw88399); 176494978aeSMarco Giunta return 0; 177494978aeSMarco Giunta } 178494978aeSMarco Giunta 179de7027c9SMarco Giunta struct aw88399_prop_model { 180de7027c9SMarco Giunta const char *ssid; 181de7027c9SMarco Giunta int (*apply_prop)(struct aw88399_hda *aw88399); 182de7027c9SMarco Giunta }; 183de7027c9SMarco Giunta 184de7027c9SMarco Giunta static const struct aw88399_prop_model aw88399_prop_model_table[] = { 185494978aeSMarco Giunta { "17AA3906", aw88399_apply_legion_quirks }, 186494978aeSMarco Giunta { "17AA3907", aw88399_apply_legion_quirks }, 187494978aeSMarco Giunta { "17AA3927", aw88399_apply_legion_quirks }, 188494978aeSMarco Giunta { "17AA3928", aw88399_apply_legion_quirks }, 189*fde30db1SMarco Giunta { "17AA3936", aw88399_apply_legion_quirks }, 190*fde30db1SMarco Giunta { "17AA3937", aw88399_apply_legion_quirks }, 191494978aeSMarco Giunta { "17AA3938", aw88399_apply_legion_quirks }, 192494978aeSMarco Giunta { "17AA3939", aw88399_apply_legion_quirks }, 193de7027c9SMarco Giunta { } 194de7027c9SMarco Giunta }; 195de7027c9SMarco Giunta 196de7027c9SMarco Giunta static int aw88399_hda_acpi_probe(struct aw88399_hda *aw88399) 197de7027c9SMarco Giunta { 198de7027c9SMarco Giunta struct acpi_device *adev; 199de7027c9SMarco Giunta const char *sub; 200de7027c9SMarco Giunta const struct aw88399_prop_model *model; 201de7027c9SMarco Giunta 202de7027c9SMarco Giunta aw88399->index = aw88399_hda_index_from_i2c(aw88399); 203de7027c9SMarco Giunta aw88399->channel = aw88399->index; 204de7027c9SMarco Giunta aw88399->acpi_subsystem_id = NULL; 205de7027c9SMarco Giunta 206de7027c9SMarco Giunta adev = acpi_dev_get_first_match_dev("AWDZ8399", NULL, -1); 207de7027c9SMarco Giunta if (!adev) { 208de7027c9SMarco Giunta dev_err(aw88399->dev, "Failed to find an ACPI device for AWDZ8399\n"); 209de7027c9SMarco Giunta return -ENODEV; 210de7027c9SMarco Giunta } 211de7027c9SMarco Giunta 2125713fea9STakashi Iwai struct device *physdev __free(put_device) = 2135713fea9STakashi Iwai get_device(acpi_get_first_physical_node(adev)); 214de7027c9SMarco Giunta acpi_dev_put(adev); 215de7027c9SMarco Giunta if (!physdev) 216de7027c9SMarco Giunta return -ENODEV; 217de7027c9SMarco Giunta 218de7027c9SMarco Giunta sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); 219de7027c9SMarco Giunta if (IS_ERR_OR_NULL(sub)) 220de7027c9SMarco Giunta return 0; 221de7027c9SMarco Giunta 222de7027c9SMarco Giunta aw88399->acpi_subsystem_id = devm_kstrdup(aw88399->dev, sub, GFP_KERNEL); 223de7027c9SMarco Giunta kfree(sub); 224de7027c9SMarco Giunta if (!aw88399->acpi_subsystem_id) 225de7027c9SMarco Giunta return -ENOMEM; 226de7027c9SMarco Giunta 227de7027c9SMarco Giunta for (model = aw88399_prop_model_table; model->ssid; model++) { 228de7027c9SMarco Giunta if (!strcasecmp(model->ssid, aw88399->acpi_subsystem_id)) { 229de7027c9SMarco Giunta dev_info(aw88399->dev, 230de7027c9SMarco Giunta "Applying properties for SSID %s\n", 231de7027c9SMarco Giunta aw88399->acpi_subsystem_id); 232de7027c9SMarco Giunta return model->apply_prop(aw88399); 233de7027c9SMarco Giunta } 234de7027c9SMarco Giunta } 235de7027c9SMarco Giunta 236de7027c9SMarco Giunta return 0; 237de7027c9SMarco Giunta } 238de7027c9SMarco Giunta 239de7027c9SMarco Giunta int aw88399_hda_probe(struct device *dev, struct regmap *regmap) 240de7027c9SMarco Giunta { 241de7027c9SMarco Giunta struct aw88399_hda *aw88399; 242de7027c9SMarco Giunta int ret; 243de7027c9SMarco Giunta 244de7027c9SMarco Giunta aw88399 = devm_kzalloc(dev, sizeof(*aw88399), GFP_KERNEL); 245de7027c9SMarco Giunta if (!aw88399) 246de7027c9SMarco Giunta return -ENOMEM; 247de7027c9SMarco Giunta 248de7027c9SMarco Giunta if (IS_ERR(regmap)) 249de7027c9SMarco Giunta return dev_err_probe(dev, PTR_ERR(regmap), "Failed to obtain regmap\n"); 250de7027c9SMarco Giunta 251de7027c9SMarco Giunta aw88399->dev = dev; 252de7027c9SMarco Giunta aw88399->regmap = regmap; 253de7027c9SMarco Giunta dev_set_drvdata(dev, aw88399); 254de7027c9SMarco Giunta 255de7027c9SMarco Giunta aw88399->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 256de7027c9SMarco Giunta if (IS_ERR(aw88399->reset_gpio)) 257de7027c9SMarco Giunta return dev_err_probe(dev, PTR_ERR(aw88399->reset_gpio), 258de7027c9SMarco Giunta "Failed to get reset GPIO\n"); 259de7027c9SMarco Giunta 260de7027c9SMarco Giunta ret = aw88399_hda_acpi_probe(aw88399); 261de7027c9SMarco Giunta if (ret) 262de7027c9SMarco Giunta return dev_err_probe(dev, ret, "ACPI probe failed\n"); 263de7027c9SMarco Giunta 264de7027c9SMarco Giunta ret = aw88399_hda_init(aw88399); 265de7027c9SMarco Giunta if (ret) 266de7027c9SMarco Giunta return dev_err_probe(dev, ret, "Chip initialization failed\n"); 267de7027c9SMarco Giunta 268de7027c9SMarco Giunta /* Enable runtime PM */ 269de7027c9SMarco Giunta pm_runtime_set_autosuspend_delay(dev, 3000); 270de7027c9SMarco Giunta pm_runtime_use_autosuspend(dev); 271de7027c9SMarco Giunta pm_runtime_mark_last_busy(dev); 272de7027c9SMarco Giunta pm_runtime_set_active(dev); 273de7027c9SMarco Giunta pm_runtime_enable(dev); 274de7027c9SMarco Giunta 275de7027c9SMarco Giunta ret = component_add(dev, &aw88399_hda_comp_ops); 276de7027c9SMarco Giunta if (ret) { 277de7027c9SMarco Giunta pm_runtime_disable(dev); 278de7027c9SMarco Giunta return dev_err_probe(dev, ret, "Failed to register component\n"); 279de7027c9SMarco Giunta } 280de7027c9SMarco Giunta 281de7027c9SMarco Giunta dev_info(dev, "AW88399 HDA side codec registered successfully\n"); 282de7027c9SMarco Giunta 283de7027c9SMarco Giunta return 0; 284de7027c9SMarco Giunta } 285de7027c9SMarco Giunta EXPORT_SYMBOL_NS_GPL(aw88399_hda_probe, "SND_HDA_SCODEC_AW88399"); 286de7027c9SMarco Giunta 287de7027c9SMarco Giunta void aw88399_hda_remove(struct device *dev) 288de7027c9SMarco Giunta { 289de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 290de7027c9SMarco Giunta 291de7027c9SMarco Giunta pm_runtime_disable(dev); 292de7027c9SMarco Giunta 293de7027c9SMarco Giunta if (aw88399->aw_dev) 294de7027c9SMarco Giunta aw88399_stop(aw88399->aw_dev); 295de7027c9SMarco Giunta 296de7027c9SMarco Giunta component_del(dev, &aw88399_hda_comp_ops); 297de7027c9SMarco Giunta 298de7027c9SMarco Giunta dev_dbg(aw88399->dev, "AW88399 HDA side codec removed\n"); 299de7027c9SMarco Giunta } 300de7027c9SMarco Giunta EXPORT_SYMBOL_NS_GPL(aw88399_hda_remove, "SND_HDA_SCODEC_AW88399"); 301de7027c9SMarco Giunta 302de7027c9SMarco Giunta static int aw88399_hda_runtime_suspend(struct device *dev) 303de7027c9SMarco Giunta { 304de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 305de7027c9SMarco Giunta 306de7027c9SMarco Giunta dev_dbg(aw88399->dev, "Runtime suspend\n"); 307de7027c9SMarco Giunta 308de7027c9SMarco Giunta if (aw88399->aw_dev && aw88399->playing) 309de7027c9SMarco Giunta aw88399_stop(aw88399->aw_dev); 310de7027c9SMarco Giunta 311de7027c9SMarco Giunta return 0; 312de7027c9SMarco Giunta } 313de7027c9SMarco Giunta 314de7027c9SMarco Giunta static int aw88399_hda_runtime_resume(struct device *dev) 315de7027c9SMarco Giunta { 316de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 317de7027c9SMarco Giunta 318de7027c9SMarco Giunta dev_dbg(aw88399->dev, "Runtime resume\n"); 319de7027c9SMarco Giunta 320de7027c9SMarco Giunta if (aw88399->core && aw88399->aw_dev && aw88399->playing) 321de7027c9SMarco Giunta aw88399_start(aw88399->core, AW88399_SYNC_START); 322de7027c9SMarco Giunta 323de7027c9SMarco Giunta return 0; 324de7027c9SMarco Giunta } 325de7027c9SMarco Giunta 326de7027c9SMarco Giunta static int aw88399_hda_system_suspend(struct device *dev) 327de7027c9SMarco Giunta { 328de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 329de7027c9SMarco Giunta int ret; 330de7027c9SMarco Giunta 331de7027c9SMarco Giunta dev_dbg(aw88399->dev, "System suspend\n"); 332de7027c9SMarco Giunta 333de7027c9SMarco Giunta if (aw88399->aw_dev && aw88399->playing) 334de7027c9SMarco Giunta aw88399_stop(aw88399->aw_dev); 335de7027c9SMarco Giunta 336de7027c9SMarco Giunta if (aw88399->core) 337de7027c9SMarco Giunta aw88399->core->fw_needs_reload = true; 338de7027c9SMarco Giunta 339de7027c9SMarco Giunta ret = pm_runtime_force_suspend(dev); 340de7027c9SMarco Giunta if (ret) 341de7027c9SMarco Giunta dev_err(aw88399->dev, "Runtime force suspend failed: %d\n", ret); 342de7027c9SMarco Giunta 343de7027c9SMarco Giunta return ret; 344de7027c9SMarco Giunta } 345de7027c9SMarco Giunta 346de7027c9SMarco Giunta static int aw88399_hda_system_resume(struct device *dev) 347de7027c9SMarco Giunta { 348de7027c9SMarco Giunta struct aw88399_hda *aw88399 = dev_get_drvdata(dev); 349de7027c9SMarco Giunta int ret; 350de7027c9SMarco Giunta 351de7027c9SMarco Giunta dev_dbg(aw88399->dev, "System resume\n"); 352de7027c9SMarco Giunta 353de7027c9SMarco Giunta if (aw88399->aw_dev) 354de7027c9SMarco Giunta aw88399_hw_reset(aw88399->core); 355de7027c9SMarco Giunta 356de7027c9SMarco Giunta ret = pm_runtime_force_resume(dev); 357de7027c9SMarco Giunta if (ret) 358de7027c9SMarco Giunta dev_err(aw88399->dev, "Runtime force resume failed: %d\n", ret); 359de7027c9SMarco Giunta 360de7027c9SMarco Giunta return ret; 361de7027c9SMarco Giunta } 362de7027c9SMarco Giunta 363de7027c9SMarco Giunta const struct dev_pm_ops aw88399_hda_pm_ops = { 364de7027c9SMarco Giunta RUNTIME_PM_OPS(aw88399_hda_runtime_suspend, aw88399_hda_runtime_resume, NULL) 365de7027c9SMarco Giunta SYSTEM_SLEEP_PM_OPS(aw88399_hda_system_suspend, aw88399_hda_system_resume) 366de7027c9SMarco Giunta }; 367de7027c9SMarco Giunta EXPORT_SYMBOL_NS_GPL(aw88399_hda_pm_ops, "SND_HDA_SCODEC_AW88399"); 368de7027c9SMarco Giunta 369de7027c9SMarco Giunta MODULE_DESCRIPTION("AW88399 HDA driver"); 370de7027c9SMarco Giunta MODULE_AUTHOR("Yakov Till <yakov.till@gmail.com>"); 371de7027c9SMarco Giunta MODULE_AUTHOR("Marco Giunta <marco_giunta@outlook.it>"); 372de7027c9SMarco Giunta MODULE_LICENSE("GPL"); 373de7027c9SMarco Giunta MODULE_FIRMWARE("aw88399_acf.bin"); 374