#define CS35L41_FIRMWARE_ROOT "cirrus/"
 #define CS35L41_PART "cs35l41"
-#define FW_NAME "CSPL"
 
 #define HALO_STATE_DSP_CTL_NAME                "HALO_STATE"
 #define HALO_STATE_DSP_CTL_TYPE                5
        struct hda_cs_dsp_ctl_info info;
 
        info.device_name = cs35l41->amp_name;
-       info.fw_type = HDA_CS_DSP_FW_SPK_PROT;
+       info.fw_type = cs35l41->firmware_type;
        info.card = cs35l41->codec->card;
 
        return hda_cs_dsp_control_add(cs_ctl, &info);
 
        if (spkid > -1 && ssid && amp_name)
                *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART,
-                                     dsp_name, "spk-prot", ssid, spkid, amp_name, filetype);
+                                     dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+                                     ssid, spkid, amp_name, filetype);
        else if (spkid > -1 && ssid)
                *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART,
-                                     dsp_name, "spk-prot", ssid, spkid, filetype);
+                                     dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+                                     ssid, spkid, filetype);
        else if (ssid && amp_name)
                *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART,
-                                     dsp_name, "spk-prot", ssid, amp_name,
-                                     filetype);
+                                     dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+                                     ssid, amp_name, filetype);
        else if (ssid)
                *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART,
-                                     dsp_name, "spk-prot", ssid, filetype);
+                                     dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+                                     ssid, filetype);
        else
                *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART,
-                                     dsp_name, "spk-prot", filetype);
+                                     dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
+                                     filetype);
 
        if (*filename == NULL)
                return -ENOMEM;
                dev_warn(cs35l41->dev, "No Coefficient File available.\n");
 
        ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename,
-                             FW_NAME);
+                             hda_cs_dsp_fw_ids[cs35l41->firmware_type]);
        if (ret)
                goto err_release;
 
 {
        struct cs_dsp *dsp = &cs35l41->cs_dsp;
 
+       cancel_work_sync(&cs35l41->fw_load_work);
        cs35l41_shutdown_dsp(cs35l41);
        cs_dsp_remove(dsp);
        cs35l41->halo_initialized = false;
 
        switch (action) {
        case HDA_GEN_PCM_ACT_OPEN:
+               cs35l41->playback_started = true;
                if (cs35l41->firmware_running) {
                        regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
                                               ARRAY_SIZE(cs35l41_hda_config_dsp));
                                           0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
                }
                cs35l41_irq_release(cs35l41);
+               cs35l41->playback_started = false;
                break;
        default:
                dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action);
        return ret;
 }
 
+static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
+{
+       pm_runtime_get_sync(cs35l41->dev);
+
+       if (cs35l41->firmware_running && !load) {
+               dev_dbg(cs35l41->dev, "Unloading Firmware\n");
+               cs35l41_shutdown_dsp(cs35l41);
+       } else if (!cs35l41->firmware_running && load) {
+               dev_dbg(cs35l41->dev, "Loading Firmware\n");
+               cs35l41_smart_amp(cs35l41);
+       } else {
+               dev_dbg(cs35l41->dev, "Unable to Load firmware.\n");
+       }
+
+       pm_runtime_mark_last_busy(cs35l41->dev);
+       pm_runtime_put_autosuspend(cs35l41->dev);
+}
+
+static int cs35l41_fw_load_ctl_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = cs35l41->request_fw_load;
+       return 0;
+}
+
+static void cs35l41_fw_load_work(struct work_struct *work)
+{
+       struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work);
+
+       mutex_lock(&cs35l41->fw_mutex);
+
+       /* Recheck if playback is ongoing, mutex will block playback during firmware loading */
+       if (cs35l41->playback_started)
+               dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n");
+       else
+               cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);
+
+       cs35l41->fw_request_ongoing = false;
+       mutex_unlock(&cs35l41->fw_mutex);
+}
+
+static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+       unsigned int ret = 0;
+
+       mutex_lock(&cs35l41->fw_mutex);
+
+       if (cs35l41->request_fw_load == ucontrol->value.integer.value[0])
+               goto err;
+
+       if (cs35l41->fw_request_ongoing) {
+               dev_dbg(cs35l41->dev, "Existing request not complete\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       /* Check if playback is ongoing when initial request is made */
+       if (cs35l41->playback_started) {
+               dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       cs35l41->fw_request_ongoing = true;
+       cs35l41->request_fw_load = ucontrol->value.integer.value[0];
+       schedule_work(&cs35l41->fw_load_work);
+
+err:
+       mutex_unlock(&cs35l41->fw_mutex);
+
+       return ret;
+}
+
+static int cs35l41_fw_type_ctl_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = cs35l41->firmware_type;
+
+       return 0;
+}
+
+static int cs35l41_fw_type_ctl_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+       if (ucontrol->value.enumerated.item[0] < HDA_CS_DSP_NUM_FW) {
+               cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(hda_cs_dsp_fw_ids), hda_cs_dsp_fw_ids);
+}
+
+static int cs35l41_create_controls(struct cs35l41_hda *cs35l41)
+{
+       char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       char fw_load_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       struct snd_kcontrol_new fw_type_ctl = {
+               .name = fw_type_ctl_name,
+               .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+               .info = cs35l41_fw_type_ctl_info,
+               .get = cs35l41_fw_type_ctl_get,
+               .put = cs35l41_fw_type_ctl_put,
+       };
+       struct snd_kcontrol_new fw_load_ctl = {
+               .name = fw_load_ctl_name,
+               .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+               .info = snd_ctl_boolean_mono_info,
+               .get = cs35l41_fw_load_ctl_get,
+               .put = cs35l41_fw_load_ctl_put,
+       };
+       int ret;
+
+       scnprintf(fw_type_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Type",
+                 cs35l41->amp_name);
+       scnprintf(fw_load_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Load",
+                 cs35l41->amp_name);
+
+       ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41));
+       if (ret) {
+               dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret);
+               return ret;
+       }
+
+       dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name);
+
+       ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41));
+       if (ret) {
+               dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret);
+               return ret;
+       }
+
+       dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name);
+
+       return 0;
+}
+
 static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
 {
        struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
        struct hda_component *comps = master_data;
+       int ret = 0;
 
        if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS)
                return -EINVAL;
        cs35l41->codec = comps->codec;
        strscpy(comps->name, dev_name(dev), sizeof(comps->name));
 
+       cs35l41->firmware_type = HDA_CS_DSP_FW_SPK_PROT;
+
+       cs35l41->request_fw_load = true;
        mutex_lock(&cs35l41->fw_mutex);
        if (cs35l41_smart_amp(cs35l41) < 0)
                dev_warn(cs35l41->dev, "Cannot Run Firmware, reverting to dsp bypass...\n");
        mutex_unlock(&cs35l41->fw_mutex);
 
+       ret = cs35l41_create_controls(cs35l41);
+
        comps->playback_hook = cs35l41_hda_playback_hook;
        comps->suspend_hook = cs35l41_hda_suspend_hook;
        comps->resume_hook = cs35l41_hda_resume_hook;
        pm_runtime_mark_last_busy(dev);
        pm_runtime_put_autosuspend(dev);
 
-       return 0;
+       return ret;
 }
 
 static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data)
        if (ret)
                goto err;
 
+       INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
        mutex_init(&cs35l41->fw_mutex);
 
        pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);