AC97 is a bus for sound usage. It enables for a AC97 AC-Link to link one
controller to 0 to 4 AC97 codecs.

The goal of this new implementation is to implement a device/driver
model for AC97, with an automatic scan of the bus and automatic
discovery of AC97 codec devices.

Signed-off-by: Robert Jarzmik <robert.jarz...@free.fr>
---
 include/sound/ac97/codec.h      |  98 ++++++++++++
 include/sound/ac97/compat.h     |  21 +++
 include/sound/ac97/controller.h |  39 +++++
 sound/ac97/Kconfig              |   9 ++
 sound/ac97/Makefile             |   5 +
 sound/ac97/ac97_core.h          |  10 ++
 sound/ac97/bus.c                | 330 ++++++++++++++++++++++++++++++++++++++++
 sound/ac97/codec.c              |  15 ++
 sound/ac97/snd_ac97_compat.c    | 104 +++++++++++++
 9 files changed, 631 insertions(+)
 create mode 100644 include/sound/ac97/codec.h
 create mode 100644 include/sound/ac97/compat.h
 create mode 100644 include/sound/ac97/controller.h
 create mode 100644 sound/ac97/Kconfig
 create mode 100644 sound/ac97/Makefile
 create mode 100644 sound/ac97/ac97_core.h
 create mode 100644 sound/ac97/bus.c
 create mode 100644 sound/ac97/codec.c
 create mode 100644 sound/ac97/snd_ac97_compat.c

diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h
new file mode 100644
index 000000000000..4b8b3e570892
--- /dev/null
+++ b/include/sound/ac97/codec.h
@@ -0,0 +1,98 @@
+/*
+ *  Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef AC97_CODEC_H
+#define AC97_CODEC_H
+
+#include <linux/device.h>
+
+#define AC97_ID(vendor_id1, vendor_id2) \
+       (((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff))
+#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \
+       { .id = ((vendor_id1 & 0xffff) << 16) | (vendor_id2 & 0xffff), \
+         .mask = ((mask_id1 & 0xffff) << 16) | (mask_id2 & 0xffff), \
+         .data = _data }
+
+#define to_ac97_device(d) container_of(d, struct ac97_codec_device, dev)
+#define to_ac97_driver(d) container_of(d, struct ac97_codec_driver, driver)
+
+struct ac97_controller;
+
+/**
+ * struct ac97_id - matches a codec device and driver on an ac97 bus
+ * @id: The significant bits if the codec vendor ID1 and ID2
+ * @mask: Bitmask specifying which bits of the id field are significant when
+ *       matching. A driver binds to a device when :
+ *        ((vendorID1 << 8 | vendorID2) & (mask_id1 << 8 | mask_id2)) == id.
+ * @data: Private data used by the driver.
+ */
+struct ac97_id {
+       unsigned int            id;
+       unsigned int            mask;
+       void                    *data;
+};
+
+/**
+ * ac97_codec_device - a ac97 codec
+ * @dev: the code device
+ * @vendor_id: the vendor_id of the codec, as sensed on the AC-link
+ * @num: the codec number, 0 is primary, 1 is first slave, etc ...
+ * @ac97_ctrl: ac97 digital controller on the same AC-link
+ *
+ * This is the device instanciated for each codec living on a AC-link. There 
are
+ * normally 0 to 4 codec devices per AC-link, and all of them are controlled by
+ * an AC97 digital controller.
+ */
+struct ac97_codec_device {
+       struct device           dev;    /* Must stay first member */
+       unsigned int            vendor_id;
+       unsigned int            num;
+       struct list_head        list;
+       struct ac97_controller  *ac97_ctrl;
+};
+
+/**
+ * ac97_codec_driver - a ac97 codec driver
+ * @driver: the device driver structure
+ * @probe: the function called when a ac97_codec_device is matched
+ * @remove: the function called when the device is unbound/removed
+ * @suspend: suspend function (might be NULL)
+ * @resume: resume function (might be NULL)
+ * @shutdown: shutdown function (might be NULL)
+ * @id_table: ac97 vendor_id match table, { } member terminated
+ */
+struct ac97_codec_driver {
+       struct device_driver    driver;
+       int                     (*probe)(struct ac97_codec_device *);
+       int                     (*remove)(struct ac97_codec_device *);
+       int                     (*suspend)(struct ac97_codec_device *);
+       int                     (*resume)(struct ac97_codec_device *);
+       void                    (*shutdown)(struct ac97_codec_device *);
+       struct ac97_id          *id_table;
+};
+
+int ac97_codec_driver_register(struct ac97_codec_driver *drv);
+void ac97_codec_driver_unregister(struct ac97_codec_driver *drv);
+
+static inline struct device *
+ac97_codec_dev2dev(const struct ac97_codec_device *adev)
+{
+       return (struct device *)(adev);
+}
+
+static inline void *ac97_get_drvdata(const struct ac97_codec_device *adev)
+{
+       return dev_get_drvdata(ac97_codec_dev2dev(adev));
+}
+
+static inline void ac97_set_drvdata(const struct ac97_codec_device *adev,
+                                   void *data)
+{
+       dev_set_drvdata(ac97_codec_dev2dev(adev), data);
+}
+
+#endif
diff --git a/include/sound/ac97/compat.h b/include/sound/ac97/compat.h
new file mode 100644
index 000000000000..bf611f572f2d
--- /dev/null
+++ b/include/sound/ac97/compat.h
@@ -0,0 +1,21 @@
+/*
+ *  Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file is for backward compatibility with snd_ac97 structure and its
+ * multiple usages, such as the snd_ac97_bus and snd_ac97_build_ops.
+ *
+ */
+#ifndef AC97_COMPAT_H
+#define AC97_COMPAT_H
+
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+struct snd_ac97 *compat_alloc_snd_ac97_codec(struct snd_soc_codec *codec);
+void compat_release_snd_ac97_codec(struct snd_ac97 *ac97);
+
+#endif
diff --git a/include/sound/ac97/controller.h b/include/sound/ac97/controller.h
new file mode 100644
index 000000000000..f1e5e645f5ef
--- /dev/null
+++ b/include/sound/ac97/controller.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef AC97_CONTROLLER_H
+#define AC97_CONTROLLER_H
+
+#include <linux/list.h>
+
+struct device;
+struct ac97_codec_device;
+
+struct ac97_controller_ops {
+       void (*reset)(struct ac97_codec_device *ac97);
+       void (*warm_reset)(struct ac97_codec_device *ac97);
+       int (*write)(struct ac97_codec_device *ac97, unsigned short reg,
+                    unsigned short val);
+       int (*read)(struct ac97_codec_device *ac97, unsigned short reg);
+       void (*wait)(struct ac97_codec_device *ac97);
+       void (*init)(struct ac97_codec_device *ac97);
+};
+
+struct ac97_controller {
+       const struct ac97_controller_ops *ops;
+       struct list_head controllers;
+       struct device *dev;
+       int bus_idx;
+       int bound_codecs;
+       struct list_head codecs;
+};
+
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
+                                    struct device *dev);
+int ac97_digital_controller_unregister(const struct device *dev);
+
+#endif
diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig
new file mode 100644
index 000000000000..fd2c2d031e62
--- /dev/null
+++ b/sound/ac97/Kconfig
@@ -0,0 +1,9 @@
+#
+# PCI configuration
+#
+
+config AC97
+       bool "AC97 bus"
+       help
+          Say Y here if you want to have AC97 devices, which are sound oriented
+          devices around an AC-Link.
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile
new file mode 100644
index 000000000000..5575909d46e2
--- /dev/null
+++ b/sound/ac97/Makefile
@@ -0,0 +1,5 @@
+#
+# make for AC97 bus drivers
+#
+
+obj-y  += bus.o codec.o snd_ac97_compat.o
diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h
new file mode 100644
index 000000000000..db6e27288357
--- /dev/null
+++ b/sound/ac97/ac97_core.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
+                              int codec_num);
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
new file mode 100644
index 000000000000..f9bf5632d4aa
--- /dev/null
+++ b/sound/ac97/bus.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <sound/ac97/regs.h>
+
+#define AC97_BUS_MAX_CODECS 4
+
+/*
+ * Protects ac97_controllers and each ac97_controller structure.
+ */
+static DEFINE_MUTEX(ac97_controllers_mutex);
+static LIST_HEAD(ac97_controllers);
+static int ac97_bus_idx;
+
+struct bus_type ac97_bus_type;
+
+static struct ac97_codec_device *
+ac97_codec_find(struct ac97_controller *ac97_ctrl, int codec_num)
+{
+       struct ac97_codec_device *codec;
+
+       list_for_each_entry(codec, &ac97_ctrl->codecs, list)
+               if (codec->num == codec_num)
+                       return codec;
+
+       return NULL;
+}
+
+static void ac97_codec_release(struct device *dev)
+{
+       struct ac97_codec_device *codec;
+
+       codec = container_of(dev, struct ac97_codec_device, dev);
+       list_del(&codec->list);
+       kfree(codec);
+}
+
+static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
+                  unsigned int vendor_id)
+{
+       struct ac97_codec_device *codec;
+       char *codec_name;
+       int ret;
+
+       codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+       if (!codec)
+               return -ENOMEM;
+
+       codec->vendor_id = vendor_id;
+       codec->dev.release = ac97_codec_release;
+       codec->dev.bus = &ac97_bus_type;
+       codec->dev.parent = ac97_ctrl->dev;
+       codec->num = idx;
+       codec->ac97_ctrl = ac97_ctrl;
+       INIT_LIST_HEAD(&codec->list);
+       list_move_tail(&codec->list, &ac97_ctrl->codecs);
+
+       codec_name = kasprintf(GFP_KERNEL, "%s:%d", dev_name(ac97_ctrl->dev),
+                              idx);
+       codec->dev.init_name = codec_name;
+
+       ret = device_register(&codec->dev);
+       kfree(codec_name);
+
+       return ret;
+}
+
+unsigned int ac97_bus_scan_one(struct ac97_controller *ac97,
+                                     int codec_num)
+{
+       struct ac97_codec_device codec;
+       unsigned short vid1, vid2;
+       int ret;
+
+       codec.dev = *ac97->dev;
+       codec.num = codec_num;
+       ret = ac97->ops->read(&codec, AC97_VENDOR_ID1);
+       vid1 = (ret & 0xffff);
+       if (ret < 0)
+               return 0;
+
+       ret = ac97->ops->read(&codec, AC97_VENDOR_ID2);
+       vid2 = (ret & 0xffff);
+       if (ret < 0)
+               return 0;
+
+       dev_dbg(&codec.dev, "%s(codec_num=%d): vendor_id=0x%08x\n",
+               __func__, codec_num, AC97_ID(vid1, vid2));
+       return AC97_ID(vid1, vid2);
+}
+
+static int ac97_bus_scan(struct ac97_controller *ac97_ctrl)
+{
+       int ret, i;
+       unsigned int vendor_id;
+
+       for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
+               if (ac97_codec_find(ac97_ctrl, i))
+                       continue;
+               vendor_id = ac97_bus_scan_one(ac97_ctrl, i);
+               if (!vendor_id)
+                       continue;
+
+               ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
+               if (ret < 0)
+                       return ret;
+       }
+       return 0;
+}
+
+void ac97_rescan_all_controllers(void)
+{
+       struct ac97_controller *ac97_ctrl;
+       int ret;
+
+       mutex_lock(&ac97_controllers_mutex);
+       list_for_each_entry(ac97_ctrl, &ac97_controllers, controllers) {
+               ret = ac97_bus_scan(ac97_ctrl);
+               if (ret)
+                       dev_warn(ac97_ctrl->dev, "scan failed: %d\n", ret);
+       }
+       mutex_unlock(&ac97_controllers_mutex);
+}
+EXPORT_SYMBOL(ac97_rescan_all_controllers);
+
+static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
+{
+       struct ac97_codec_device codec;
+
+       memset(&codec, 0, sizeof(codec));
+       codec.dev = *ac97_ctrl->dev;
+
+       ac97_ctrl->ops->reset(&codec);
+       return 0;
+}
+
+/**
+ * ac97_codec_driver_register - register an AC97 codec driver
+ * @dev: AC97 driver codec to register
+ *
+ * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
+ * controller.
+ *
+ * Returns 0 on success or error code
+ */
+int ac97_codec_driver_register(struct ac97_codec_driver *drv)
+{
+       int ret;
+
+       drv->driver.bus = &ac97_bus_type;
+
+       ret = driver_register(&drv->driver);
+       if (!ret)
+               ac97_rescan_all_controllers();
+
+       return ret;
+}
+EXPORT_SYMBOL(ac97_codec_driver_register);
+
+/**
+ * ac97_codec_driver_unregister - unregister an AC97 codec driver
+ * @dev: AC97 codec driver to unregister
+ *
+ * Unregister a previously registered ac97 codec driver.
+ */
+void ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(ac97_codec_driver_unregister);
+
+static int ac97_dc_codecs_unregister(struct ac97_controller *ac97_ctrl)
+{
+       struct ac97_codec_device *codec, *tmp;
+
+       list_for_each_entry_safe(codec, tmp, &ac97_ctrl->codecs, list)
+               put_device(&codec->dev);
+
+       return 0;
+}
+
+/**
+ * ac97_digital_controller_register - register an ac97 controller
+ * @ops: the ac97 bus operations
+ * @dev: the device providing the ac97 DC function
+ *
+ * Register a digital controller which can control up to 4 ac97 codecs. This is
+ * the controller side of the AC97 AC-link, while the slave side are the 
codecs.
+ *
+ * Returns a positive bus index upon success, negative value upon error
+ */
+int ac97_digital_controller_register(const struct ac97_controller_ops *ops,
+                                    struct device *dev)
+{
+       struct ac97_controller *ac97_ctrl;
+
+       ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
+       if (!ac97_ctrl)
+               return -ENOMEM;
+
+       mutex_lock(&ac97_controllers_mutex);
+       ac97_ctrl->ops = ops;
+       ac97_ctrl->bus_idx = ac97_bus_idx++;
+       ac97_ctrl->dev = dev;
+       INIT_LIST_HEAD(&ac97_ctrl->codecs);
+       list_add(&ac97_ctrl->controllers, &ac97_controllers);
+       mutex_unlock(&ac97_controllers_mutex);
+
+       ac97_bus_reset(ac97_ctrl);
+       ac97_bus_scan(ac97_ctrl);
+
+       return ac97_ctrl->bus_idx;
+}
+EXPORT_SYMBOL(ac97_digital_controller_register);
+
+/**
+ * ac97_digital_controller_unregister - unregister an ac97 controller
+ * @dev: the device previously provided to ac97_digital_controller_register()
+ *
+ * Returns 0 on success, negative upon error
+ */
+int ac97_digital_controller_unregister(const struct device *dev)
+{
+       struct ac97_controller *ac97_ctrl, *tmp;
+       int ret = -ENODEV;
+
+       mutex_lock(&ac97_controllers_mutex);
+       list_for_each_entry_safe(ac97_ctrl, tmp, &ac97_controllers,
+                                controllers) {
+               if (ac97_ctrl->dev != dev)
+                       continue;
+               if (ac97_ctrl->bound_codecs)
+                       ret = -EBUSY;
+               else
+                       ret = ac97_dc_codecs_unregister(ac97_ctrl);
+               if (!ret)
+                       list_del(&ac97_ctrl->controllers);
+       }
+
+       mutex_unlock(&ac97_controllers_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(ac97_digital_controller_unregister);
+
+static const struct dev_pm_ops ac97_pm = {
+       .suspend        = pm_generic_suspend,
+       .resume         = pm_generic_resume,
+       .freeze         = pm_generic_freeze,
+       .thaw           = pm_generic_thaw,
+       .poweroff       = pm_generic_poweroff,
+       .restore        = pm_generic_restore,
+};
+
+static ssize_t vendor_id_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct ac97_codec_device *codec = to_ac97_device(dev);
+
+       return sprintf(buf, "%08x", codec->vendor_id);
+}
+
+static struct device_attribute ac97_dev_attrs[] = {
+       __ATTR_RO(vendor_id),
+       __ATTR_NULL,
+};
+
+int ac97_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct ac97_codec_device *adev = to_ac97_device(dev);
+       struct ac97_codec_driver *adrv = to_ac97_driver(drv);
+       struct ac97_id *id = adrv->id_table;
+
+       if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
+               return false;
+
+       do {
+               if ((id->id & id->mask) == (adev->vendor_id & id->mask))
+                       return true;
+       } while (++id->id);
+
+       return false;
+}
+
+static int ac97_bus_probe(struct device *dev)
+{
+       struct ac97_codec_device *adev = to_ac97_device(dev);
+       struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+
+       return adrv->probe(adev);
+}
+
+static int ac97_bus_remove(struct device *dev)
+{
+       struct ac97_codec_device *adev = to_ac97_device(dev);
+       struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+
+       return adrv->remove(adev);
+}
+
+struct bus_type ac97_bus_type = {
+       .name           = "ac97",
+       .dev_attrs      = ac97_dev_attrs,
+       .match          = ac97_bus_match,
+       .pm             = &ac97_pm,
+       .probe          = ac97_bus_probe,
+       .remove         = ac97_bus_remove,
+};
+EXPORT_SYMBOL(ac97_bus_type);
+
+static int __init ac97_bus_init(void)
+{
+       return bus_register(&ac97_bus_type);
+}
+subsys_initcall(ac97_bus_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jarzmik <robert.jarz...@free.fr>");
diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c
new file mode 100644
index 000000000000..a835f03744bf
--- /dev/null
+++ b/sound/ac97/codec.c
@@ -0,0 +1,15 @@
+/*
+ *  Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sound/ac97_codec.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/soc.h> /* For compat_ac97_* */
+
diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c
new file mode 100644
index 000000000000..7e2f01c96fc9
--- /dev/null
+++ b/sound/ac97/snd_ac97_compat.c
@@ -0,0 +1,104 @@
+/*
+ *  Copyright (C) 2016 Robert Jarzmik <robert.jarz...@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <sound/soc.h>
+
+#include "ac97_core.h"
+
+static void compat_ac97_reset(struct snd_ac97 *ac97)
+{
+       struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+       struct ac97_controller *actrl = adev->ac97_ctrl;
+
+       if (actrl->ops->reset)
+               actrl->ops->reset(adev);
+}
+
+static void compat_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+       struct ac97_controller *actrl = adev->ac97_ctrl;
+
+       if (actrl->ops->warm_reset)
+               actrl->ops->warm_reset(adev);
+}
+
+static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+                             unsigned short val)
+{
+       struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+       struct ac97_controller *actrl = adev->ac97_ctrl;
+
+       actrl->ops->write(adev, reg, val);
+}
+
+static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
+                                      unsigned short reg)
+{
+       struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+       struct ac97_controller *actrl = adev->ac97_ctrl;
+
+       return actrl->ops->read(adev, reg);
+}
+
+static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
+       .reset = compat_ac97_reset,
+       .warm_reset = compat_ac97_warm_reset,
+       .write = compat_ac97_write,
+       .read = compat_ac97_read,
+};
+
+static struct snd_ac97_bus compat_soc_ac97_bus = {
+       .ops = &compat_snd_ac97_bus_ops,
+};
+
+struct snd_ac97 *compat_alloc_snd_ac97_codec(struct snd_soc_codec *codec)
+{
+       struct snd_ac97 *ac97;
+
+       ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+       if (ac97 == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       ac97->dev = *codec->dev;
+       ac97->private_data = codec->dev;
+       ac97->bus = &compat_soc_ac97_bus;
+       return ac97;
+}
+EXPORT_SYMBOL_GPL(compat_alloc_snd_ac97_codec);
+
+void compat_release_snd_ac97_codec(struct snd_ac97 *ac97)
+{
+       kfree(ac97);
+}
+EXPORT_SYMBOL_GPL(compat_release_snd_ac97_codec);
+
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+       unsigned int id_mask)
+{
+       struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+       struct ac97_controller *actrl = adev->ac97_ctrl;
+
+       if (try_warm) {
+               compat_ac97_warm_reset(ac97);
+               if (ac97_bus_scan_one(actrl, adev->num) == adev->vendor_id)
+                       return 1;
+       }
+
+       compat_ac97_reset(ac97);
+       compat_ac97_warm_reset(ac97);
+       if (ac97_bus_scan_one(actrl, adev->num) == adev->vendor_id)
+               return 0;
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
-- 
2.1.4

Reply via email to