static struct pci_device_id hifier_ids[] __devinitdata = {
        { OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
        { OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
+       { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
        { }
 };
 MODULE_DEVICE_TABLE(pci, hifier_ids);
        .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
+static int __devinit get_hifier_model(struct oxygen *chip,
+                                     const struct pci_device_id *id)
+{
+       chip->model = model_hifier;
+       return 0;
+}
+
 static int __devinit hifier_probe(struct pci_dev *pci,
                                  const struct pci_device_id *pci_id)
 {
                ++dev;
                return -ENOENT;
        }
-       err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE, &model_hifier, 0);
+       err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
+                              hifier_ids, get_hifier_model);
        if (err >= 0)
                ++dev;
        return err;
 
 
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
-static int generic_probe(struct oxygen *chip, unsigned long driver_data)
-{
-       if (driver_data == MODEL_MERIDIAN) {
-               chip->model.init = meridian_init;
-               chip->model.resume = meridian_resume;
-               chip->model.set_adc_params = set_ak5385_params;
-               chip->model.device_config = PLAYBACK_0_TO_I2S |
-                                           PLAYBACK_1_TO_SPDIF |
-                                           CAPTURE_0_FROM_I2S_2 |
-                                           CAPTURE_1_FROM_SPDIF;
-       }
-       if (driver_data == MODEL_MERIDIAN || driver_data == MODEL_HALO) {
-               chip->model.misc_flags = OXYGEN_MISC_MIDI;
-               chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
-       }
-       return 0;
-}
-
 static const struct oxygen_model model_generic = {
        .shortname = "C-Media CMI8788",
        .longname = "C-Media Oxygen HD Audio",
        .chip = "CMI8788",
-       .probe = generic_probe,
        .init = generic_init,
        .cleanup = generic_cleanup,
        .resume = generic_resume,
        .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
+static int __devinit get_oxygen_model(struct oxygen *chip,
+                                     const struct pci_device_id *id)
+{
+       chip->model = model_generic;
+       switch (id->driver_data) {
+       case MODEL_MERIDIAN:
+               chip->model.init = meridian_init;
+               chip->model.resume = meridian_resume;
+               chip->model.set_adc_params = set_ak5385_params;
+               chip->model.device_config = PLAYBACK_0_TO_I2S |
+                                           PLAYBACK_1_TO_SPDIF |
+                                           CAPTURE_0_FROM_I2S_2 |
+                                           CAPTURE_1_FROM_SPDIF;
+               break;
+       }
+       if (id->driver_data == MODEL_MERIDIAN ||
+           id->driver_data == MODEL_HALO) {
+               chip->model.misc_flags = OXYGEN_MISC_MIDI;
+               chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
+       }
+       return 0;
+}
+
 static int __devinit generic_oxygen_probe(struct pci_dev *pci,
                                          const struct pci_device_id *pci_id)
 {
                return -ENOENT;
        }
        err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
-                              &model_generic, pci_id->driver_data);
+                              oxygen_ids, get_oxygen_model);
        if (err >= 0)
                ++dev;
        return err;
 
        .subvendor = sv, \
        .subdevice = sd
 
+#define BROKEN_EEPROM_DRIVER_DATA ((unsigned long)-1)
+#define OXYGEN_PCI_SUBID_BROKEN_EEPROM \
+       OXYGEN_PCI_SUBID(PCI_VENDOR_ID_CMEDIA, 0x8788), \
+       .driver_data = BROKEN_EEPROM_DRIVER_DATA
+
 struct pci_dev;
+struct pci_device_id;
 struct snd_card;
 struct snd_pcm_substream;
 struct snd_pcm_hardware;
        const char *shortname;
        const char *longname;
        const char *chip;
-       int (*probe)(struct oxygen *chip, unsigned long driver_data);
        void (*init)(struct oxygen *chip);
        int (*control_filter)(struct snd_kcontrol_new *template);
        int (*mixer_init)(struct oxygen *chip);
        void (*ac97_switch)(struct oxygen *chip,
                            unsigned int reg, unsigned int mute);
        const unsigned int *dac_tlv;
+       unsigned long private_data;
        size_t model_data_size;
        unsigned int device_config;
        u8 dac_channels;
 
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
                     struct module *owner,
-                    const struct oxygen_model *model,
-                    unsigned long driver_data);
+                    const struct pci_device_id *ids,
+                    int (*get_model)(struct oxygen *chip,
+                                     const struct pci_device_id *id
+                                    )
+                   );
 void oxygen_pci_remove(struct pci_dev *pci);
 #ifdef CONFIG_PM
 int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
 void oxygen_reset_uart(struct oxygen *chip);
 void oxygen_write_uart(struct oxygen *chip, u8 data);
 
+u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index);
+
 static inline void oxygen_set_bits8(struct oxygen *chip,
                                    unsigned int reg, u8 value)
 {
 
        _write_uart(chip, 0, data);
 }
 EXPORT_SYMBOL(oxygen_write_uart);
+
+u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index)
+{
+       unsigned int timeout;
+
+       oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
+                     index | OXYGEN_EEPROM_DIR_READ);
+       for (timeout = 0; timeout < 100; ++timeout) {
+               udelay(1);
+               if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
+                     & OXYGEN_EEPROM_BUSY))
+                       break;
+       }
+       return oxygen_read16(chip, OXYGEN_EEPROM_DATA);
+}
 
 #define oxygen_proc_init(chip)
 #endif
 
+static const struct pci_device_id *
+oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
+{
+       u16 subdevice;
+
+       /*
+        * Make sure the EEPROM pins are available, i.e., not used for SPI.
+        * (This function is called before we initialize or use SPI.)
+        */
+       oxygen_clear_bits8(chip, OXYGEN_FUNCTION,
+                          OXYGEN_FUNCTION_ENABLE_SPI_4_5);
+       /*
+        * Read the subsystem device ID directly from the EEPROM, because the
+        * chip didn't if the first EEPROM word was overwritten.
+        */
+       subdevice = oxygen_read_eeprom(chip, 2);
+       /*
+        * We use only the subsystem device ID for searching because it is
+        * unique even without the subsystem vendor ID, which may have been
+        * overwritten in the EEPROM.
+        */
+       for (; ids->vendor; ++ids)
+               if (ids->subdevice == subdevice &&
+                   ids->driver_data != BROKEN_EEPROM_DRIVER_DATA)
+                       return ids;
+       return NULL;
+}
+
 static void oxygen_init(struct oxygen *chip)
 {
        unsigned int i;
 
 int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
                     struct module *owner,
-                    const struct oxygen_model *model,
-                    unsigned long driver_data)
+                    const struct pci_device_id *ids,
+                    int (*get_model)(struct oxygen *chip,
+                                     const struct pci_device_id *id
+                                    )
+                   )
 {
        struct snd_card *card;
        struct oxygen *chip;
+       const struct pci_device_id *pci_id;
        int err;
 
        err = snd_card_create(index, id, owner, sizeof(*chip), &card);
        chip->card = card;
        chip->pci = pci;
        chip->irq = -1;
-       chip->model = *model;
        spin_lock_init(&chip->reg_lock);
        mutex_init(&chip->mutex);
        INIT_WORK(&chip->spdif_input_bits_work,
        }
        chip->addr = pci_resource_start(pci, 0);
 
+       pci_id = oxygen_search_pci_id(chip, ids);
+       if (!pci_id) {
+               err = -ENODEV;
+               goto err_pci_regions;
+       }
+       err = get_model(chip, pci_id);
+       if (err < 0)
+               goto err_pci_regions;
+
        if (chip->model.model_data_size) {
                chip->model_data = kmalloc(chip->model.model_data_size,
                                           GFP_KERNEL);
        snd_card_set_dev(card, &pci->dev);
        card->private_free = oxygen_card_free;
 
-       if (chip->model.probe) {
-               err = chip->model.probe(chip, driver_data);
-               if (err < 0)
-                       goto err_card;
-       }
        oxygen_init(chip);
        chip->model.init(chip);
 
 
        { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
        { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
        { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
+       { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
        { }
 };
 MODULE_DEVICE_TABLE(pci, xonar_ids);
 #define I2C_DEVICE_CS4362A     0x30    /* 001100, AD0=0, /W=0 */
 
 struct xonar_data {
-       unsigned int model;
        unsigned int anti_pop_delay;
        unsigned int dacs;
        u16 output_enable_bit;
        struct xonar_data *data = chip->model_data;
 
        data->anti_pop_delay = 300;
+       data->dacs = 4;
        data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
        data->pcm1796_oversampling = PCM1796_OS_64;
-       if (data->model == MODEL_D2X) {
-               data->ext_power_reg = OXYGEN_GPIO_DATA;
-               data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
-               data->ext_power_bit = GPIO_D2X_EXT_POWER;
-               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-                                   GPIO_D2X_EXT_POWER);
-       }
 
        pcm1796_init(chip);
 
        snd_component_add(chip->card, "CS5381");
 }
 
+static void xonar_d2x_init(struct oxygen *chip)
+{
+       struct xonar_data *data = chip->model_data;
+
+       data->ext_power_reg = OXYGEN_GPIO_DATA;
+       data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+       data->ext_power_bit = GPIO_D2X_EXT_POWER;
+       oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
+
+       xonar_d2_init(chip);
+}
+
 static void update_cs4362a_volumes(struct oxygen *chip)
 {
        u8 mute;
        data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
        data->cs4362a_fm = CS4362A_FM_SINGLE |
                CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
-       if (data->model == MODEL_DX) {
-               data->ext_power_reg = OXYGEN_GPI_DATA;
-               data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-               data->ext_power_bit = GPI_DX_EXT_POWER;
-       }
 
        oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
                       OXYGEN_2WIRE_LENGTH_8 |
        snd_component_add(chip->card, "CS5361");
 }
 
+static void xonar_dx_init(struct oxygen *chip)
+{
+       struct xonar_data *data = chip->model_data;
+
+       data->ext_power_reg = OXYGEN_GPI_DATA;
+       data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+       data->ext_power_bit = GPI_DX_EXT_POWER;
+
+       xonar_d1_init(chip);
+}
+
 static void xonar_hdav_init(struct oxygen *chip)
 {
        struct xonar_data *data = chip->model_data;
                       OXYGEN_2WIRE_SPEED_FAST);
 
        data->anti_pop_delay = 100;
+       data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1;
        data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
        data->ext_power_reg = OXYGEN_GPI_DATA;
        data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
        return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
 }
 
-static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data)
-{
-       static const char *const names[] = {
-               [MODEL_D1]      = "Xonar D1",
-               [MODEL_DX]      = "Xonar DX",
-               [MODEL_D2]      = "Xonar D2",
-               [MODEL_D2X]     = "Xonar D2X",
-               [MODEL_HDAV]    = "Xonar HDAV1.3",
-               [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
-       };
-       static const u8 dacs[] = {
-               [MODEL_D1]      = 2,
-               [MODEL_DX]      = 2,
-               [MODEL_D2]      = 4,
-               [MODEL_D2X]     = 4,
-               [MODEL_HDAV]    = 1,
-               [MODEL_HDAV_H6] = 4,
-       };
-       struct xonar_data *data = chip->model_data;
-
-       data->model = driver_data;
-       if (data->model == MODEL_HDAV) {
-               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-                                   GPIO_HDAV_DB_MASK);
-               switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
-                       GPIO_HDAV_DB_MASK) {
-               case GPIO_HDAV_DB_H6:
-                       data->model = MODEL_HDAV_H6;
-                       break;
-               case GPIO_HDAV_DB_XX:
-                       snd_printk(KERN_ERR "unknown daughterboard\n");
-                       return -ENODEV;
-               }
-       }
-
-       data->dacs = dacs[data->model];
-       chip->model.shortname = names[data->model];
-       return 0;
-}
-
 static const struct oxygen_model model_xonar_d2 = {
        .longname = "Asus Virtuoso 200",
        .chip = "AV200",
-       .probe = xonar_model_probe,
        .init = xonar_d2_init,
        .control_filter = xonar_d2_control_filter,
        .mixer_init = xonar_d2_mixer_init,
 static const struct oxygen_model model_xonar_d1 = {
        .longname = "Asus Virtuoso 100",
        .chip = "AV200",
-       .probe = xonar_model_probe,
        .init = xonar_d1_init,
        .control_filter = xonar_d1_control_filter,
        .mixer_init = xonar_d1_mixer_init,
 static const struct oxygen_model model_xonar_hdav = {
        .longname = "Asus Virtuoso 200",
        .chip = "AV200",
-       .probe = xonar_model_probe,
        .init = xonar_hdav_init,
        .cleanup = xonar_hdav_cleanup,
        .suspend = xonar_hdav_suspend,
        .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 };
 
-static int __devinit xonar_probe(struct pci_dev *pci,
-                                const struct pci_device_id *pci_id)
+static int __devinit get_xonar_model(struct oxygen *chip,
+                                    const struct pci_device_id *id)
 {
        static const struct oxygen_model *const models[] = {
                [MODEL_D1]      = &model_xonar_d1,
                [MODEL_D2X]     = &model_xonar_d2,
                [MODEL_HDAV]    = &model_xonar_hdav,
        };
+       static const char *const names[] = {
+               [MODEL_D1]      = "Xonar D1",
+               [MODEL_DX]      = "Xonar DX",
+               [MODEL_D2]      = "Xonar D2",
+               [MODEL_D2X]     = "Xonar D2X",
+               [MODEL_HDAV]    = "Xonar HDAV1.3",
+               [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6",
+       };
+       unsigned int model = id->driver_data;
+
+       if (model >= ARRAY_SIZE(models) || !models[model])
+               return -EINVAL;
+       chip->model = *models[model];
+
+       switch (model) {
+       case MODEL_D2X:
+               chip->model.init = xonar_d2x_init;
+               break;
+       case MODEL_DX:
+               chip->model.init = xonar_dx_init;
+               break;
+       case MODEL_HDAV:
+               oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+                                   GPIO_HDAV_DB_MASK);
+               switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+                       GPIO_HDAV_DB_MASK) {
+               case GPIO_HDAV_DB_H6:
+                       model = MODEL_HDAV_H6;
+                       break;
+               case GPIO_HDAV_DB_XX:
+                       snd_printk(KERN_ERR "unknown daughterboard\n");
+                       return -ENODEV;
+               }
+               break;
+       }
+
+       chip->model.shortname = names[model];
+       chip->model.private_data = model;
+       return 0;
+}
+
+static int __devinit xonar_probe(struct pci_dev *pci,
+                                const struct pci_device_id *pci_id)
+{
        static int dev;
        int err;
 
                ++dev;
                return -ENOENT;
        }
-       BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));
        err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
-                              models[pci_id->driver_data],
-                              pci_id->driver_data);
+                              xonar_ids, get_xonar_model);
        if (err >= 0)
                ++dev;
        return err;