summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-03-16 14:48:16 +0100
committerTakashi Iwai <tiwai@suse.de>2015-03-16 14:48:16 +0100
commit8f88f0256f2e8afd83177b3554992009acb98996 (patch)
tree2cfbc07c64edda0db4afa67b5ed2d6c9a6c78537
parentMerge branch 'for-linus' into for-next (diff)
parentALSA: hda - Clear pcm pointer assigned to hda_pcm at device removal (diff)
downloadlinux-8f88f0256f2e8afd83177b3554992009acb98996.tar.xz
linux-8f88f0256f2e8afd83177b3554992009acb98996.zip
Merge branch 'topic/hda-bus' into for-next
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_beep.c2
-rw-r--r--sound/pci/hda/hda_bind.c321
-rw-r--r--sound/pci/hda/hda_codec.c731
-rw-r--r--sound/pci/hda/hda_codec.h108
-rw-r--r--sound/pci/hda/hda_controller.c62
-rw-r--r--sound/pci/hda/hda_controller.h4
-rw-r--r--sound/pci/hda/hda_generic.c18
-rw-r--r--sound/pci/hda/hda_hwdep.c3
-rw-r--r--sound/pci/hda/hda_intel.c33
-rw-r--r--sound/pci/hda/hda_local.h41
-rw-r--r--sound/pci/hda/hda_tegra.c23
-rw-r--r--sound/pci/hda/hda_trace.h24
-rw-r--r--sound/pci/hda/patch_analog.c16
-rw-r--r--sound/pci/hda/patch_ca0110.c16
-rw-r--r--sound/pci/hda/patch_ca0132.c16
-rw-r--r--sound/pci/hda/patch_cirrus.c16
-rw-r--r--sound/pci/hda/patch_cmedia.c16
-rw-r--r--sound/pci/hda/patch_conexant.c16
-rw-r--r--sound/pci/hda/patch_hdmi.c27
-rw-r--r--sound/pci/hda/patch_realtek.c16
-rw-r--r--sound/pci/hda/patch_si3054.c16
-rw-r--r--sound/pci/hda/patch_sigmatel.c20
-rw-r--r--sound/pci/hda/patch_via.c16
24 files changed, 572 insertions, 991 deletions
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 194f30935e77..96caaebfc19d 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -4,7 +4,7 @@ snd-hda-tegra-objs := hda_tegra.o
# for haswell power well
snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
-snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
+snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 1e7de08e77cb..e98438e95e79 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -160,6 +160,7 @@ static int snd_hda_do_attach(struct hda_beep *beep)
input_dev->name = "HDA Digital PCBeep";
input_dev->phys = beep->phys;
input_dev->id.bustype = BUS_PCI;
+ input_dev->dev.parent = &codec->bus->card->card_dev;
input_dev->id.vendor = codec->vendor_id >> 16;
input_dev->id.product = codec->vendor_id & 0xffff;
@@ -168,7 +169,6 @@ static int snd_hda_do_attach(struct hda_beep *beep)
input_dev->evbit[0] = BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = snd_hda_beep_event;
- input_dev->dev.parent = &codec->dev;
input_set_drvdata(input_dev, beep);
beep->dev = input_dev;
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
new file mode 100644
index 000000000000..ce2dd7b0dc07
--- /dev/null
+++ b/sound/pci/hda/hda_bind.c
@@ -0,0 +1,321 @@
+/*
+ * HD-audio codec driver binding
+ * Copyright (c) Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/* codec vendor labels */
+struct hda_vendor_id {
+ unsigned int id;
+ const char *name;
+};
+
+static struct hda_vendor_id hda_vendor_ids[] = {
+ { 0x1002, "ATI" },
+ { 0x1013, "Cirrus Logic" },
+ { 0x1057, "Motorola" },
+ { 0x1095, "Silicon Image" },
+ { 0x10de, "Nvidia" },
+ { 0x10ec, "Realtek" },
+ { 0x1102, "Creative" },
+ { 0x1106, "VIA" },
+ { 0x111d, "IDT" },
+ { 0x11c1, "LSI" },
+ { 0x11d4, "Analog Devices" },
+ { 0x13f6, "C-Media" },
+ { 0x14f1, "Conexant" },
+ { 0x17e8, "Chrontel" },
+ { 0x1854, "LG" },
+ { 0x1aec, "Wolfson Microelectronics" },
+ { 0x1af4, "QEMU" },
+ { 0x434d, "C-Media" },
+ { 0x8086, "Intel" },
+ { 0x8384, "SigmaTel" },
+ {} /* terminator */
+};
+
+/*
+ * find a matching codec preset
+ */
+static int hda_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, dev);
+ struct hda_codec_driver *driver =
+ container_of(drv, struct hda_codec_driver, driver);
+ const struct hda_codec_preset *preset;
+ /* check probe_id instead of vendor_id if set */
+ u32 id = codec->probe_id ? codec->probe_id : codec->vendor_id;
+
+ for (preset = driver->preset; preset->id; preset++) {
+ u32 mask = preset->mask;
+
+ if (preset->afg && preset->afg != codec->afg)
+ continue;
+ if (preset->mfg && preset->mfg != codec->mfg)
+ continue;
+ if (!mask)
+ mask = ~0;
+ if (preset->id == (id & mask) &&
+ (!preset->rev || preset->rev == codec->revision_id)) {
+ codec->preset = preset;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* reset the codec name from the preset */
+static int codec_refresh_name(struct hda_codec *codec, const char *name)
+{
+ char tmp[16];
+
+ kfree(codec->chip_name);
+ if (!name) {
+ sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
+ name = tmp;
+ }
+ codec->chip_name = kstrdup(name, GFP_KERNEL);
+ return codec->chip_name ? 0 : -ENOMEM;
+}
+
+static int hda_codec_driver_probe(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct module *owner = dev->driver->owner;
+ int err;
+
+ if (WARN_ON(!codec->preset))
+ return -EINVAL;
+
+ err = codec_refresh_name(codec, codec->preset->name);
+ if (err < 0)
+ goto error;
+
+ if (!try_module_get(owner)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ err = codec->preset->patch(codec);
+ if (err < 0) {
+ module_put(owner);
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ codec->preset = NULL;
+ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+ return err;
+}
+
+static int hda_codec_driver_remove(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+ codec->preset = NULL;
+ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+ module_put(dev->driver->owner);
+ return 0;
+}
+
+int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
+ struct module *owner)
+{
+ drv->driver.name = name;
+ drv->driver.owner = owner;
+ drv->driver.bus = &snd_hda_bus_type;
+ drv->driver.probe = hda_codec_driver_probe;
+ drv->driver.remove = hda_codec_driver_remove;
+ drv->driver.pm = &hda_codec_driver_pm;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
+
+void hda_codec_driver_unregister(struct hda_codec_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(hda_codec_driver_unregister);
+
+static inline bool codec_probed(struct hda_codec *codec)
+{
+ return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
+}
+
+/* try to auto-load and bind the codec module */
+static void codec_bind_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+ request_module("snd-hda-codec-id:%08x", codec->vendor_id);
+ if (codec_probed(codec))
+ return;
+ request_module("snd-hda-codec-id:%04x*",
+ (codec->vendor_id >> 16) & 0xffff);
+ if (codec_probed(codec))
+ return;
+#endif
+}
+
+/* store the codec vendor name */
+static int get_codec_vendor_name(struct hda_codec *codec)
+{
+ const struct hda_vendor_id *c;
+ const char *vendor = NULL;
+ u16 vendor_id = codec->vendor_id >> 16;
+ char tmp[16];
+
+ for (c = hda_vendor_ids; c->id; c++) {
+ if (c->id == vendor_id) {
+ vendor = c->name;
+ break;
+ }
+ }
+ if (!vendor) {
+ sprintf(tmp, "Generic %04x", vendor_id);
+ vendor = tmp;
+ }
+ codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
+ if (!codec->vendor_name)
+ return -ENOMEM;
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
+static bool is_likely_hdmi_codec(struct hda_codec *codec)
+{
+ hda_nid_t nid = codec->start_nid;
+ int i;
+
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ switch (get_wcaps_type(wcaps)) {
+ case AC_WID_AUD_IN:
+ return false; /* HDMI parser supports only HDMI out */
+ case AC_WID_AUD_OUT:
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+#else
+/* no HDMI codec parser support */
+#define is_likely_hdmi_codec(codec) false
+#endif /* CONFIG_SND_HDA_CODEC_HDMI */
+
+static int codec_bind_generic(struct hda_codec *codec)
+{
+ if (codec->probe_id)
+ return -ENODEV;
+
+ if (is_likely_hdmi_codec(codec)) {
+ codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
+#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
+ request_module("snd-hda-codec-hdmi");
+#endif
+ if (codec_probed(codec))
+ return 0;
+ }
+
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+ request_module("snd-hda-codec-generic");
+#endif
+ if (codec_probed(codec))
+ return 0;
+ return -ENODEV;
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
+#define is_generic_config(codec) \
+ (codec->modelname && !strcmp(codec->modelname, "generic"))
+#else
+#define is_generic_config(codec) 0
+#endif
+
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+ int err;
+
+ if (!codec->vendor_name) {
+ err = get_codec_vendor_name(codec);
+ if (err < 0)
+ return err;
+ }
+
+ if (is_generic_config(codec))
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ else
+ codec->probe_id = 0;
+
+ err = device_add(hda_codec_dev(codec));
+ if (err < 0)
+ return err;
+
+ if (!codec->preset)
+ codec_bind_module(codec);
+ if (!codec->preset) {
+ err = codec_bind_generic(codec);
+ if (err < 0) {
+ codec_err(codec, "Unable to bind the codec\n");
+ goto error;
+ }
+ }
+
+ /* audio codec should override the mixer name */
+ if (codec->afg || !*codec->bus->card->mixername)
+ snprintf(codec->bus->card->mixername,
+ sizeof(codec->bus->card->mixername),
+ "%s %s", codec->vendor_name, codec->chip_name);
+ return 0;
+
+ error:
+ device_del(hda_codec_dev(codec));
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
+
+/*
+ * bus registration
+ */
+struct bus_type snd_hda_bus_type = {
+ .name = "hdaudio",
+ .match = hda_bus_match,
+};
+
+static int __init hda_codec_init(void)
+{
+ return bus_register(&snd_hda_bus_type);
+}
+
+static void __exit hda_codec_exit(void)
+{
+ bus_unregister(&snd_hda_bus_type);
+}
+
+module_init(hda_codec_init);
+module_exit(hda_codec_exit);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 5e755eb6f19a..db86b446743c 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -26,6 +26,8 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/async.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <sound/core.h>
#include "hda_codec.h"
#include <sound/asoundef.h>
@@ -40,92 +42,13 @@
#define CREATE_TRACE_POINTS
#include "hda_trace.h"
-/*
- * vendor / preset table
- */
-
-struct hda_vendor_id {
- unsigned int id;
- const char *name;
-};
-
-/* codec vendor labels */
-static struct hda_vendor_id hda_vendor_ids[] = {
- { 0x1002, "ATI" },
- { 0x1013, "Cirrus Logic" },
- { 0x1057, "Motorola" },
- { 0x1095, "Silicon Image" },
- { 0x10de, "Nvidia" },
- { 0x10ec, "Realtek" },
- { 0x1102, "Creative" },
- { 0x1106, "VIA" },
- { 0x111d, "IDT" },
- { 0x11c1, "LSI" },
- { 0x11d4, "Analog Devices" },
- { 0x13f6, "C-Media" },
- { 0x14f1, "Conexant" },
- { 0x17e8, "Chrontel" },
- { 0x1854, "LG" },
- { 0x1aec, "Wolfson Microelectronics" },
- { 0x1af4, "QEMU" },
- { 0x434d, "C-Media" },
- { 0x8086, "Intel" },
- { 0x8384, "SigmaTel" },
- {} /* terminator */
-};
-
-static DEFINE_MUTEX(preset_mutex);
-static LIST_HEAD(hda_preset_tables);
-
-/**
- * snd_hda_add_codec_preset - Add a codec preset to the chain
- * @preset: codec preset table to add
- */
-int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
-{
- mutex_lock(&preset_mutex);
- list_add_tail(&preset->list, &hda_preset_tables);
- mutex_unlock(&preset_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset);
-
-/**
- * snd_hda_delete_codec_preset - Delete a codec preset from the chain
- * @preset: codec preset table to delete
- */
-int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
-{
- mutex_lock(&preset_mutex);
- list_del(&preset->list);
- mutex_unlock(&preset_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_delete_codec_preset);
-
#ifdef CONFIG_PM
-#define codec_in_pm(codec) ((codec)->in_pm)
-static void hda_power_work(struct work_struct *work);
-static void hda_keep_power_on(struct hda_codec *codec);
-#define hda_codec_is_power_on(codec) ((codec)->power_on)
-
-static void hda_call_pm_notify(struct hda_codec *codec, bool power_up)
-{
- struct hda_bus *bus = codec->bus;
-
- if ((power_up && codec->pm_up_notified) ||
- (!power_up && !codec->pm_up_notified))
- return;
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus, power_up);
- codec->pm_up_notified = power_up;
-}
-
+#define codec_in_pm(codec) atomic_read(&(codec)->in_pm)
+#define hda_codec_is_power_on(codec) \
+ (!pm_runtime_suspended(hda_codec_dev(codec)))
#else
#define codec_in_pm(codec) 0
-static inline void hda_keep_power_on(struct hda_codec *codec) {}
#define hda_codec_is_power_on(codec) 1
-#define hda_call_pm_notify(codec, state) {}
#endif
/**
@@ -885,111 +808,6 @@ int snd_hda_bus_new(struct snd_card *card,
}
EXPORT_SYMBOL_GPL(snd_hda_bus_new);
-#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
-#define is_generic_config(codec) \
- (codec->modelname && !strcmp(codec->modelname, "generic"))
-#else
-#define is_generic_config(codec) 0
-#endif
-
-#ifdef MODULE
-#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */
-#else
-#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */
-#endif
-
-/*
- * find a matching codec preset
- */
-static const struct hda_codec_preset *
-find_codec_preset(struct hda_codec *codec)
-{
- struct hda_codec_preset_list *tbl;
- const struct hda_codec_preset *preset;
- unsigned int mod_requested = 0;
-
- again:
- mutex_lock(&preset_mutex);
- list_for_each_entry(tbl, &hda_preset_tables, list) {
- if (!try_module_get(tbl->owner)) {
- codec_err(codec, "cannot module_get\n");
- continue;
- }
- for (preset = tbl->preset; preset->id; preset++) {
- u32 mask = preset->mask;
- if (preset->afg && preset->afg != codec->afg)
- continue;
- if (preset->mfg && preset->mfg != codec->mfg)
- continue;
- if (!mask)
- mask = ~0;
- if (preset->id == (codec->vendor_id & mask) &&
- (!preset->rev ||
- preset->rev == codec->revision_id)) {
- mutex_unlock(&preset_mutex);
- codec->owner = tbl->owner;
- return preset;
- }
- }
- module_put(tbl->owner);
- }
- mutex_unlock(&preset_mutex);
-
- if (mod_requested < HDA_MODREQ_MAX_COUNT) {
- if (!mod_requested)
- request_module("snd-hda-codec-id:%08x",
- codec->vendor_id);
- else
- request_module("snd-hda-codec-id:%04x*",
- (codec->vendor_id >> 16) & 0xffff);
- mod_requested++;
- goto again;
- }
- return NULL;
-}
-
-/*
- * get_codec_name - store the codec name
- */
-static int get_codec_name(struct hda_codec *codec)
-{
- const struct hda_vendor_id *c;
- const char *vendor = NULL;
- u16 vendor_id = codec->vendor_id >> 16;
- char tmp[16];
-
- if (codec->vendor_name)
- goto get_chip_name;
-
- for (c = hda_vendor_ids; c->id; c++) {
- if (c->id == vendor_id) {
- vendor = c->name;
- break;
- }
- }
- if (!vendor) {
- sprintf(tmp, "Generic %04x", vendor_id);
- vendor = tmp;
- }
- codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
- if (!codec->vendor_name)
- return -ENOMEM;
-
- get_chip_name:
- if (codec->chip_name)
- return 0;
-
- if (codec->preset && codec->preset->name)
- codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
- else {
- sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
- codec->chip_name = kstrdup(tmp, GFP_KERNEL);
- }
- if (!codec->chip_name)
- return -ENOMEM;
- return 0;
-}
-
/*
* look for an AFG and MFG nodes
*/
@@ -1301,20 +1119,6 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
}
/*
- * Dynamic symbol binding for the codec parsers
- */
-
-#define load_parser(codec, sym) \
- ((codec)->parser = (int (*)(struct hda_codec *))symbol_request(sym))
-
-static void unload_parser(struct hda_codec *codec)
-{
- if (codec->parser)
- symbol_put_addr(codec->parser);
- codec->parser = NULL;
-}
-
-/*
* codec destructor
*/
static void snd_hda_codec_free(struct hda_codec *codec)
@@ -1322,12 +1126,11 @@ static void snd_hda_codec_free(struct hda_codec *codec)
if (!codec)
return;
cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (device_is_registered(hda_codec_dev(codec)))
+ device_del(hda_codec_dev(codec));
snd_hda_jack_tbl_clear(codec);
free_init_pincfgs(codec);
-#ifdef CONFIG_PM
- cancel_delayed_work(&codec->power_work);
flush_workqueue(codec->bus->workq);
-#endif
list_del(&codec->list);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
@@ -1335,12 +1138,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
snd_array_free(&codec->spdif_out);
remove_conn_list(codec);
codec->bus->caddr_tbl[codec->addr] = NULL;
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
- hda_call_pm_notify(codec, false); /* cancel leftover refcounts */
+ clear_bit(codec->addr, &codec->bus->codec_powered);
snd_hda_sysfs_clear(codec);
- unload_parser(codec);
- module_put(codec->owner);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
kfree(codec->vendor_name);
@@ -1348,7 +1147,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
kfree(codec->modelname);
kfree(codec->wcaps);
codec->bus->num_codecs--;
- put_device(&codec->dev);
+ put_device(hda_codec_dev(codec));
}
static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
@@ -1360,11 +1159,12 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
static int snd_hda_codec_dev_register(struct snd_device *device)
{
struct hda_codec *codec = device->device_data;
- int err = device_add(&codec->dev);
- if (err < 0)
- return err;
snd_hda_register_beep_device(codec);
+ if (device_is_registered(hda_codec_dev(codec)))
+ pm_runtime_enable(hda_codec_dev(codec));
+ /* it was powered up in snd_hda_codec_new(), now all done */
+ snd_hda_power_down(codec);
return 0;
}
@@ -1373,7 +1173,6 @@ static int snd_hda_codec_dev_disconnect(struct snd_device *device)
struct hda_codec *codec = device->device_data;
snd_hda_detach_beep_device(codec);
- device_del(&codec->dev);
return 0;
}
@@ -1386,7 +1185,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
/* just free the container */
static void snd_hda_codec_dev_release(struct device *dev)
{
- kfree(container_of(dev, struct hda_codec, dev));
+ kfree(dev_to_hda_codec(dev));
}
/**
@@ -1402,6 +1201,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
struct hda_codec **codecp)
{
struct hda_codec *codec;
+ struct device *dev;
char component[31];
hda_nid_t fg;
int err;
@@ -1429,14 +1229,15 @@ int snd_hda_codec_new(struct hda_bus *bus,
return -ENOMEM;
}
- device_initialize(&codec->dev);
- codec->dev.parent = &bus->card->card_dev;
- codec->dev.class = sound_class;
- codec->dev.release = snd_hda_codec_dev_release;
- codec->dev.groups = snd_hda_dev_attr_groups;
- dev_set_name(&codec->dev, "hdaudioC%dD%d", bus->card->number,
- codec_addr);
- dev_set_drvdata(&codec->dev, codec); /* for sysfs */
+ dev = hda_codec_dev(codec);
+ device_initialize(dev);
+ dev->parent = bus->card->dev;
+ dev->bus = &snd_hda_bus_type;
+ dev->release = snd_hda_codec_dev_release;
+ dev->groups = snd_hda_dev_attr_groups;
+ dev_set_name(dev, "hdaudioC%dD%d", bus->card->number, codec_addr);
+ dev_set_drvdata(dev, codec); /* for sysfs */
+ device_enable_async_suspend(dev);
codec->bus = bus;
codec->addr = codec_addr;
@@ -1460,13 +1261,13 @@ int snd_hda_codec_new(struct hda_bus *bus,
codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
#ifdef CONFIG_PM
- spin_lock_init(&codec->power_lock);
- INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
/* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
- * the caller has to power down appropriatley after initialization
- * phase.
+ * it's powered down later in snd_hda_codec_dev_register().
*/
- hda_keep_power_on(codec);
+ set_bit(codec->addr, &bus->codec_powered);
+ pm_runtime_set_active(hda_codec_dev(codec));
+ pm_runtime_get_noresume(hda_codec_dev(codec));
+ codec->power_jiffies = jiffies;
#endif
snd_hda_sysfs_init(codec);
@@ -1526,11 +1327,6 @@ int snd_hda_codec_new(struct hda_bus *bus,
#endif
codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
AC_PWRST_EPSS);
-#ifdef CONFIG_PM
- if (!codec->d3_stop_clk || !codec->epss)
- bus->power_keep_link_on = 1;
-#endif
-
/* power-up all before initialization */
hda_set_power_state(codec, AC_PWRST_D0);
@@ -1587,92 +1383,6 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec)
}
EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets);
-
-#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
-/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
-static bool is_likely_hdmi_codec(struct hda_codec *codec)
-{
- hda_nid_t nid = codec->start_nid;
- int i;
-
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- switch (get_wcaps_type(wcaps)) {
- case AC_WID_AUD_IN:
- return false; /* HDMI parser supports only HDMI out */
- case AC_WID_AUD_OUT:
- if (!(wcaps & AC_WCAP_DIGITAL))
- return false;
- break;
- }
- }
- return true;
-}
-#else
-/* no HDMI codec parser support */
-#define is_likely_hdmi_codec(codec) false
-#endif /* CONFIG_SND_HDA_CODEC_HDMI */
-
-/**
- * snd_hda_codec_configure - (Re-)configure the HD-audio codec
- * @codec: the HDA codec
- *
- * Start parsing of the given codec tree and (re-)initialize the whole
- * patch instance.
- *
- * Returns 0 if successful or a negative error code.
- */
-int snd_hda_codec_configure(struct hda_codec *codec)
-{
- int (*patch)(struct hda_codec *) = NULL;
- int err;
-
- codec->preset = find_codec_preset(codec);
- if (!codec->vendor_name || !codec->chip_name) {
- err = get_codec_name(codec);
- if (err < 0)
- return err;
- }
-
- if (!is_generic_config(codec) && codec->preset)
- patch = codec->preset->patch;
- if (!patch) {
- unload_parser(codec); /* to be sure */
- if (is_likely_hdmi_codec(codec)) {
-#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
- patch = load_parser(codec, snd_hda_parse_hdmi_codec);
-#elif IS_BUILTIN(CONFIG_SND_HDA_CODEC_HDMI)
- patch = snd_hda_parse_hdmi_codec;
-#endif
- }
- if (!patch) {
-#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
- patch = load_parser(codec, snd_hda_parse_generic_codec);
-#elif IS_BUILTIN(CONFIG_SND_HDA_GENERIC)
- patch = snd_hda_parse_generic_codec;
-#endif
- }
- if (!patch) {
- codec_err(codec, "No codec parser is available\n");
- return -ENODEV;
- }
- }
-
- err = patch(codec);
- if (err < 0) {
- unload_parser(codec);
- return err;
- }
-
- /* audio codec should override the mixer name */
- if (codec->afg || !*codec->bus->card->mixername)
- snprintf(codec->bus->card->mixername,
- sizeof(codec->bus->card->mixername),
- "%s %s", codec->vendor_name, codec->chip_name);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
-
/* update the stream-id if changed */
static void update_pcm_stream_id(struct hda_codec *codec,
struct hda_cvt_setup *p, hda_nid_t nid,
@@ -2725,10 +2435,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
/* OK, let it free */
cancel_delayed_work_sync(&codec->jackpoll_work);
-#ifdef CONFIG_PM
- cancel_delayed_work_sync(&codec->power_work);
flush_workqueue(bus->workq);
-#endif
snd_hda_ctls_clear(codec);
/* release PCMs */
for (i = 0; i < codec->num_pcms; i++) {
@@ -2739,8 +2446,9 @@ int snd_hda_codec_reset(struct hda_codec *codec)
}
}
snd_hda_detach_beep_device(codec);
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
+ if (device_is_registered(hda_codec_dev(codec)))
+ device_del(hda_codec_dev(codec));
+
memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
snd_hda_jack_tbl_clear(codec);
codec->proc_widget_hook = NULL;
@@ -2759,9 +2467,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
codec->preset = NULL;
codec->slave_dig_outs = NULL;
codec->spdif_status_reset = 0;
- unload_parser(codec);
- module_put(codec->owner);
- codec->owner = NULL;
/* allow device access again */
snd_hda_unlock_devices(bus);
@@ -4167,31 +3872,40 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
#endif
#ifdef CONFIG_PM
+/* update the power on/off account with the current jiffies */
+static void update_power_acct(struct hda_codec *codec, bool on)
+{
+ unsigned long delta = jiffies - codec->power_jiffies;
+
+ if (on)
+ codec->power_on_acct += delta;
+ else
+ codec->power_off_acct += delta;
+ codec->power_jiffies += delta;
+}
+
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+ update_power_acct(codec, hda_codec_is_power_on(codec));
+}
+
/*
* call suspend and power-down; used both from PM and power-save
* this function returns the power state in the end
*/
-static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
+static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
{
unsigned int state;
- codec->in_pm = 1;
+ atomic_inc(&codec->in_pm);
if (codec->patch_ops.suspend)
codec->patch_ops.suspend(codec);
hda_cleanup_all_streams(codec);
state = hda_set_power_state(codec, AC_PWRST_D3);
- /* Cancel delayed work if we aren't currently running from it. */
- if (!in_wq)
- cancel_delayed_work_sync(&codec->power_work);
- spin_lock(&codec->power_lock);
- snd_hda_update_power_acct(codec);
trace_hda_power_down(codec);
- codec->power_on = 0;
- codec->power_transition = 0;
- codec->power_jiffies = jiffies;
- spin_unlock(&codec->power_lock);
- codec->in_pm = 0;
+ update_power_acct(codec, true);
+ atomic_dec(&codec->in_pm);
return state;
}
@@ -4216,14 +3930,13 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
*/
static void hda_call_codec_resume(struct hda_codec *codec)
{
- codec->in_pm = 1;
+ atomic_inc(&codec->in_pm);
+ trace_hda_power_up(codec);
hda_mark_cmd_cache_dirty(codec);
- /* set as if powered on for avoiding re-entering the resume
- * in the resume / power-save sequence
- */
- hda_keep_power_on(codec);
+ codec->power_jiffies = jiffies;
+
hda_set_power_state(codec, AC_PWRST_D0);
restore_shutup_pins(codec);
hda_exec_init_verbs(codec);
@@ -4241,12 +3954,42 @@ static void hda_call_codec_resume(struct hda_codec *codec)
hda_jackpoll_work(&codec->jackpoll_work.work);
else
snd_hda_jack_report_sync(codec);
+ atomic_dec(&codec->in_pm);
+}
+
+static int hda_codec_runtime_suspend(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ unsigned int state;
+ int i;
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ for (i = 0; i < codec->num_pcms; i++)
+ snd_pcm_suspend_all(codec->pcm_info[i].pcm);
+ state = hda_call_codec_suspend(codec);
+ if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK))
+ clear_bit(codec->addr, &codec->bus->codec_powered);
+ return 0;
+}
+
+static int hda_codec_runtime_resume(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
- codec->in_pm = 0;
- snd_hda_power_down(codec); /* flag down before returning */
+ set_bit(codec->addr, &codec->bus->codec_powered);
+ hda_call_codec_resume(codec);
+ pm_runtime_mark_last_busy(dev);
+ return 0;
}
#endif /* CONFIG_PM */
+/* referred in hda_bind.c */
+const struct dev_pm_ops hda_codec_driver_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
+ NULL)
+};
/**
* snd_hda_build_controls - build mixer controls
@@ -4984,127 +4727,70 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
#ifdef CONFIG_PM
-static void hda_power_work(struct work_struct *work)
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the usage counter and resume the device if not done yet.
+ */
+void snd_hda_power_up(struct hda_codec *codec)
{
- struct hda_codec *codec =
- container_of(work, struct hda_codec, power_work.work);
- struct hda_bus *bus = codec->bus;
- unsigned int state;
+ struct device *dev = hda_codec_dev(codec);
- spin_lock(&codec->power_lock);
- if (codec->power_transition > 0) { /* during power-up sequence? */
- spin_unlock(&codec->power_lock);
- return;
- }
- if (!codec->power_on || codec->power_count) {
- codec->power_transition = 0;
- spin_unlock(&codec->power_lock);
+ if (codec_in_pm(codec))
return;
- }
- spin_unlock(&codec->power_lock);
-
- state = hda_call_codec_suspend(codec, true);
- if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK))
- hda_call_pm_notify(codec, false);
-}
-
-static void hda_keep_power_on(struct hda_codec *codec)
-{
- spin_lock(&codec->power_lock);
- codec->power_count++;
- codec->power_on = 1;
- codec->power_jiffies = jiffies;
- spin_unlock(&codec->power_lock);
- hda_call_pm_notify(codec, true);
+ pm_runtime_get_sync(dev);
}
+EXPORT_SYMBOL_GPL(snd_hda_power_up);
-/* update the power on/off account with the current jiffies */
-void snd_hda_update_power_acct(struct hda_codec *codec)
-{
- unsigned long delta = jiffies - codec->power_jiffies;
- if (codec->power_on)
- codec->power_on_acct += delta;
- else
- codec->power_off_acct += delta;
- codec->power_jiffies += delta;
-}
-
-/* Transition to powered up, if wait_power_down then wait for a pending
- * transition to D3 to complete. A pending D3 transition is indicated
- * with power_transition == -1. */
-/* call this with codec->power_lock held! */
-static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the usage counter and schedules the autosuspend if none used.
+ */
+void snd_hda_power_down(struct hda_codec *codec)
{
- /* Return if power_on or transitioning to power_on, unless currently
- * powering down. */
- if ((codec->power_on || codec->power_transition > 0) &&
- !(wait_power_down && codec->power_transition < 0))
- return;
- spin_unlock(&codec->power_lock);
-
- cancel_delayed_work_sync(&codec->power_work);
+ struct device *dev = hda_codec_dev(codec);
- spin_lock(&codec->power_lock);
- /* If the power down delayed work was cancelled above before starting,
- * then there is no need to go through power up here.
- */
- if (codec->power_on) {
- if (codec->power_transition < 0)
- codec->power_transition = 0;
+ if (codec_in_pm(codec))
return;
- }
-
- trace_hda_power_up(codec);
- snd_hda_update_power_acct(codec);
- codec->power_on = 1;
- codec->power_jiffies = jiffies;
- codec->power_transition = 1; /* avoid reentrance */
- spin_unlock(&codec->power_lock);
-
- hda_call_codec_resume(codec);
-
- spin_lock(&codec->power_lock);
- codec->power_transition = 0;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
}
+EXPORT_SYMBOL_GPL(snd_hda_power_down);
-#define power_save(codec) \
- ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
-/* Transition to powered down */
-static void __snd_hda_power_down(struct hda_codec *codec)
+static void codec_set_power_save(struct hda_codec *codec, int delay)
{
- if (!codec->power_on || codec->power_count || codec->power_transition)
- return;
+ struct device *dev = hda_codec_dev(codec);
- if (power_save(codec)) {
- codec->power_transition = -1; /* avoid reentrance */
- queue_delayed_work(codec->bus->workq, &codec->power_work,
- msecs_to_jiffies(power_save(codec) * 1000));
+ if (delay > 0) {
+ pm_runtime_set_autosuspend_delay(dev, delay);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_allow(dev);
+ if (!pm_runtime_suspended(dev))
+ pm_runtime_mark_last_busy(dev);
+ } else {
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_forbid(dev);
}
}
/**
- * snd_hda_power_save - Power-up/down/sync the codec
- * @codec: HD-audio codec
- * @delta: the counter delta to change
- * @d3wait: sync for D3 transition complete
+ * snd_hda_set_power_save - reprogram autosuspend for the given delay
+ * @bus: HD-audio bus
+ * @delay: autosuspend delay in msec, 0 = off
*
- * Change the power-up counter via @delta, and power up or down the hardware
- * appropriately. For the power-down, queue to the delayed action.
- * Passing zero to @delta means to synchronize the power state.
+ * Synchronize the runtime PM autosuspend state from the power_save option.
*/
-void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait)
+void snd_hda_set_power_save(struct hda_bus *bus, int delay)
{
- spin_lock(&codec->power_lock);
- codec->power_count += delta;
- trace_hda_power_count(codec);
- if (delta > 0)
- __snd_hda_power_up(codec, d3wait);
- else
- __snd_hda_power_down(codec);
- spin_unlock(&codec->power_lock);
+ struct hda_codec *c;
+
+ list_for_each_entry(c, &bus->codec_list, list)
+ codec_set_power_save(c, delay);
}
-EXPORT_SYMBOL_GPL(snd_hda_power_save);
+EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
/**
* snd_hda_check_amp_list_power - Check the amp list and update the power
@@ -5158,88 +4844,6 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
#endif
/*
- * Channel mode helper
- */
-
-/**
- * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
- * @codec: the HDA codec
- * @uinfo: pointer to get/store the data
- * @chmode: channel mode array
- * @num_chmodes: channel mode array size
- */
-int snd_hda_ch_mode_info(struct hda_codec *codec,
- struct snd_ctl_elem_info *uinfo,
- const struct hda_channel_mode *chmode,
- int num_chmodes)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_chmodes;
- if (uinfo->value.enumerated.item >= num_chmodes)
- uinfo->value.enumerated.item = num_chmodes - 1;
- sprintf(uinfo->value.enumerated.name, "%dch",
- chmode[uinfo->value.enumerated.item].channels);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info);
-
-/**
- * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
- * @codec: the HDA codec
- * @ucontrol: pointer to get/store the data
- * @chmode: channel mode array
- * @num_chmodes: channel mode array size
- * @max_channels: max number of channels
- */
-int snd_hda_ch_mode_get(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int max_channels)
-{
- int i;
-
- for (i = 0; i < num_chmodes; i++) {
- if (max_channels == chmode[i].channels) {
- ucontrol->value.enumerated.item[0] = i;
- break;
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get);
-
-/**
- * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
- * @codec: the HDA codec
- * @ucontrol: pointer to get/store the data
- * @chmode: channel mode array
- * @num_chmodes: channel mode array size
- * @max_channelsp: pointer to store the max channels
- */
-int snd_hda_ch_mode_put(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int *max_channelsp)
-{
- unsigned int mode;
-
- mode = ucontrol->value.enumerated.item[0];
- if (mode >= num_chmodes)
- return -EINVAL;
- if (*max_channelsp == chmode[mode].channels)
- return 0;
- /* change the current channel setting */
- *max_channelsp = chmode[mode].channels;
- if (chmode[mode].sequence)
- snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
- return 1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put);
-
-/*
* input MUX helper
*/
@@ -5780,77 +5384,26 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
-
-#ifdef CONFIG_PM
-/*
- * power management
- */
-
-
-static void hda_async_suspend(void *data, async_cookie_t cookie)
-{
- hda_call_codec_suspend(data, false);
-}
-
-static void hda_async_resume(void *data, async_cookie_t cookie)
-{
- hda_call_codec_resume(data);
-}
-
/**
- * snd_hda_suspend - suspend the codecs
- * @bus: the HDA bus
- *
- * Returns 0 if successful.
+ * snd_hda_bus_reset - Reset the bus
+ * @bus: HD-audio bus
*/
-int snd_hda_suspend(struct hda_bus *bus)
+void snd_hda_bus_reset(struct hda_bus *bus)
{
struct hda_codec *codec;
- ASYNC_DOMAIN_EXCLUSIVE(domain);
list_for_each_entry(codec, &bus->codec_list, list) {
+ /* FIXME: maybe a better way needed for forced reset */
cancel_delayed_work_sync(&codec->jackpoll_work);
+#ifdef CONFIG_PM
if (hda_codec_is_power_on(codec)) {
- if (bus->num_codecs > 1)
- async_schedule_domain(hda_async_suspend, codec,
- &domain);
- else
- hda_call_codec_suspend(codec, false);
- }
- }
-
- if (bus->num_codecs > 1)
- async_synchronize_full_domain(&domain);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_suspend);
-
-/**
- * snd_hda_resume - resume the codecs
- * @bus: the HDA bus
- *
- * Returns 0 if successful.
- */
-int snd_hda_resume(struct hda_bus *bus)
-{
- struct hda_codec *codec;
- ASYNC_DOMAIN_EXCLUSIVE(domain);
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (bus->num_codecs > 1)
- async_schedule_domain(hda_async_resume, codec, &domain);
- else
+ hda_call_codec_suspend(codec);
hda_call_codec_resume(codec);
+ }
+#endif
}
-
- if (bus->num_codecs > 1)
- async_synchronize_full_domain(&domain);
-
- return 0;
}
-EXPORT_SYMBOL_GPL(snd_hda_resume);
-#endif /* CONFIG_PM */
+EXPORT_SYMBOL_GPL(snd_hda_bus_reset);
/*
* generic arrays
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 96421a3b32cd..457fc589eb46 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -83,10 +83,6 @@ struct hda_bus_ops {
struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
-#ifdef CONFIG_PM
- /* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_bus *bus, bool power_up);
-#endif
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* prepare DSP transfer */
int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
@@ -122,7 +118,6 @@ struct hda_bus {
void *private_data;
struct pci_dev *pci;
const char *modelname;
- int *power_save;
struct hda_bus_ops ops;
/* codec linked list */
@@ -151,10 +146,10 @@ struct hda_bus {
unsigned int rirb_error:1; /* error in codec communication */
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
- unsigned int power_keep_link_on:1; /* don't power off HDA link */
unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
int primary_dig_out_type; /* primary digital out PCM type */
+ unsigned long codec_powered; /* bit flags of powered codecs */
};
/*
@@ -174,15 +169,22 @@ struct hda_codec_preset {
int (*patch)(struct hda_codec *codec);
};
-struct hda_codec_preset_list {
+#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101
+#define HDA_CODEC_ID_GENERIC 0x00000201
+
+struct hda_codec_driver {
+ struct device_driver driver;
const struct hda_codec_preset *preset;
- struct module *owner;
- struct list_head list;
};
-/* initial hook */
-int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
-int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
+int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
+ struct module *owner);
+#define hda_codec_driver_register(drv) \
+ __hda_codec_driver_register(drv, KBUILD_MODNAME, THIS_MODULE)
+void hda_codec_driver_unregister(struct hda_codec_driver *drv);
+#define module_hda_codec_driver(drv) \
+ module_driver(drv, hda_codec_driver_register, \
+ hda_codec_driver_unregister)
/* ops set by the preset patch */
struct hda_codec_ops {
@@ -286,11 +288,10 @@ struct hda_codec {
u32 vendor_id;
u32 subsystem_id;
u32 revision_id;
+ u32 probe_id; /* overridden id for probing */
/* detected preset */
const struct hda_codec_preset *preset;
- struct module *owner;
- int (*parser)(struct hda_codec *codec);
const char *vendor_name; /* codec vendor name */
const char *chip_name; /* codec chip name */
const char *modelname; /* model name for preset */
@@ -366,17 +367,11 @@ struct hda_codec {
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
#ifdef CONFIG_PM
- unsigned int power_on :1; /* current (global) power-state */
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
- unsigned int pm_up_notified:1; /* PM notified to controller */
- unsigned int in_pm:1; /* suspend/resume being performed */
- int power_transition; /* power-state in transition */
- int power_count; /* current (global) power refcount */
- struct delayed_work power_work; /* delayed task for powerdown */
+ atomic_t in_pm; /* suspend/resume being performed */
unsigned long power_on_acct;
unsigned long power_off_acct;
unsigned long power_jiffies;
- spinlock_t power_lock;
#endif
/* filter the requested power state per nid */
@@ -408,6 +403,11 @@ struct hda_codec {
struct snd_array verbs;
};
+#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, dev)
+#define hda_codec_dev(_dev) (&(_dev)->dev)
+
+extern struct bus_type snd_hda_bus_type;
+
/* direction */
enum {
HDA_INPUT, HDA_OUTPUT
@@ -556,14 +556,12 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus);
+void snd_hda_bus_reset(struct hda_bus *bus);
/*
* power management
*/
-#ifdef CONFIG_PM
-int snd_hda_suspend(struct hda_bus *bus);
-int snd_hda_resume(struct hda_bus *bus);
-#endif
+extern const struct dev_pm_ops hda_codec_driver_pm;
static inline
int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
@@ -586,64 +584,16 @@ const char *snd_hda_get_jack_location(u32 cfg);
* power saving
*/
#ifdef CONFIG_PM
-void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait);
+void snd_hda_power_up(struct hda_codec *codec);
+void snd_hda_power_down(struct hda_codec *codec);
+void snd_hda_set_power_save(struct hda_bus *bus, int delay);
void snd_hda_update_power_acct(struct hda_codec *codec);
#else
-static inline void snd_hda_power_save(struct hda_codec *codec, int delta,
- bool d3wait) {}
+static inline void snd_hda_power_up(struct hda_codec *codec) {}
+static inline void snd_hda_power_down(struct hda_codec *codec) {}
+static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {}
#endif
-/**
- * snd_hda_power_up - Power-up the codec
- * @codec: HD-audio codec
- *
- * Increment the power-up counter and power up the hardware really when
- * not turned on yet.
- */
-static inline void snd_hda_power_up(struct hda_codec *codec)
-{
- snd_hda_power_save(codec, 1, false);
-}
-
-/**
- * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending
- * D3 transition to complete. This differs from snd_hda_power_up() when
- * power_transition == -1. snd_hda_power_up sees this case as a nop,
- * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers
- * back up.
- * @codec: HD-audio codec
- *
- * Cancel any power down operation hapenning on the work queue, then power up.
- */
-static inline void snd_hda_power_up_d3wait(struct hda_codec *codec)
-{
- snd_hda_power_save(codec, 1, true);
-}
-
-/**
- * snd_hda_power_down - Power-down the codec
- * @codec: HD-audio codec
- *
- * Decrement the power-up counter and schedules the power-off work if
- * the counter rearches to zero.
- */
-static inline void snd_hda_power_down(struct hda_codec *codec)
-{
- snd_hda_power_save(codec, -1, false);
-}
-
-/**
- * snd_hda_power_sync - Synchronize the power-save status
- * @codec: HD-audio codec
- *
- * Synchronize the actual power state with the power account;
- * called when power_save parameter is changed
- */
-static inline void snd_hda_power_sync(struct hda_codec *codec)
-{
- snd_hda_power_save(codec, 0, false);
-}
-
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/*
* patch firmware
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 2e73a59f3f71..a438e8540763 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -258,11 +258,18 @@ static void azx_timecounter_init(struct snd_pcm_substream *substream,
tc->cycle_last = last;
}
+static inline struct hda_pcm_stream *
+to_hda_pcm_stream(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ return &apcm->info->stream[substream->stream];
+}
+
static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
u64 nsec)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
u64 codec_frames, codec_nsecs;
if (!hinfo->ops.get_delay)
@@ -398,7 +405,7 @@ static int azx_setup_periods(struct azx *chip,
static int azx_pcm_close(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev = get_azx_dev(substream);
unsigned long flags;
@@ -440,7 +447,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx_dev *azx_dev = get_azx_dev(substream);
struct azx *chip = apcm->chip;
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
int err;
/* reset BDL address */
@@ -467,7 +474,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int bufsize, period_bytes, format_val, stream_tag;
int err;
@@ -707,7 +714,7 @@ unsigned int azx_get_position(struct azx *chip,
if (substream->runtime) {
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[stream];
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
if (chip->get_delay[stream])
delay += chip->get_delay[stream](chip, azx_dev, pos);
@@ -790,7 +797,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
static int azx_pcm_open(struct snd_pcm_substream *substream)
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev;
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -836,7 +843,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
buff_step);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
buff_step);
- snd_hda_power_up_d3wait(apcm->codec);
+ snd_hda_power_up(apcm->codec);
err = hinfo->ops.open(hinfo, apcm->codec, substream);
if (err < 0) {
azx_release_device(azx_dev);
@@ -904,6 +911,7 @@ static void azx_pcm_free(struct snd_pcm *pcm)
struct azx_pcm *apcm = pcm->private_data;
if (apcm) {
list_del(&apcm->list);
+ apcm->info->pcm = NULL;
kfree(apcm);
}
}
@@ -940,6 +948,7 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
apcm->chip = chip;
apcm->pcm = pcm;
apcm->codec = codec;
+ apcm->info = cpcm;
pcm->private_data = apcm;
pcm->private_free = azx_pcm_free;
if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
@@ -947,7 +956,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
list_add_tail(&apcm->list, &chip->pcm_list);
cpcm->pcm = pcm;
for (s = 0; s < 2; s++) {
- apcm->hinfo[s] = &cpcm->stream[s];
if (cpcm->stream[s].substreams)
snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
}
@@ -958,9 +966,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
chip->card->dev,
size, MAX_PREALLOC_SIZE);
- /* link to codec */
- for (s = 0; s < 2; s++)
- pcm->streams[s].dev.parent = &codec->dev;
return 0;
}
@@ -1778,34 +1783,11 @@ static void azx_bus_reset(struct hda_bus *bus)
bus->in_reset = 1;
azx_stop_chip(chip);
azx_init_chip(chip, true);
-#ifdef CONFIG_PM
- if (chip->initialized) {
- struct azx_pcm *p;
- list_for_each_entry(p, &chip->pcm_list, list)
- snd_pcm_suspend_all(p->pcm);
- snd_hda_suspend(chip->bus);
- snd_hda_resume(chip->bus);
- }
-#endif
+ if (chip->initialized)
+ snd_hda_bus_reset(chip->bus);
bus->in_reset = 0;
}
-#ifdef CONFIG_PM
-/* power-up/down the controller */
-static void azx_power_notify(struct hda_bus *bus, bool power_up)
-{
- struct azx *chip = bus->private_data;
-
- if (!azx_has_pm_runtime(chip))
- return;
-
- if (power_up)
- pm_runtime_get_sync(chip->card->dev);
- else
- pm_runtime_put_sync(chip->card->dev);
-}
-#endif
-
static int get_jackpoll_interval(struct azx *chip)
{
int i;
@@ -1832,9 +1814,6 @@ static struct hda_bus_ops bus_ops = {
.get_response = azx_get_response,
.attach_pcm = azx_attach_pcm_stream,
.bus_reset = azx_bus_reset,
-#ifdef CONFIG_PM
- .pm_notify = azx_power_notify,
-#endif
#ifdef CONFIG_SND_HDA_DSP_LOADER
.load_dsp_prepare = azx_load_dsp_prepare,
.load_dsp_trigger = azx_load_dsp_trigger,
@@ -1843,7 +1822,7 @@ static struct hda_bus_ops bus_ops = {
};
/* HD-audio bus initialization */
-int azx_bus_create(struct azx *chip, const char *model, int *power_save_to)
+int azx_bus_create(struct azx *chip, const char *model)
{
struct hda_bus *bus;
int err;
@@ -1857,9 +1836,6 @@ int azx_bus_create(struct azx *chip, const char *model, int *power_save_to)
bus->pci = chip->pci;
bus->modelname = model;
bus->ops = bus_ops;
-#ifdef CONFIG_PM
- bus->power_save = power_save_to;
-#endif
if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index 0d09aa6b49ac..94c1a4719f7f 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -283,7 +283,7 @@ struct azx_pcm {
struct azx *chip;
struct snd_pcm *pcm;
struct hda_codec *codec;
- struct hda_pcm_stream *hinfo[2];
+ struct hda_pcm *info;
struct list_head list;
};
@@ -432,7 +432,7 @@ void azx_enter_link_reset(struct azx *chip);
irqreturn_t azx_interrupt(int irq, void *dev_id);
/* Codec interface */
-int azx_bus_create(struct azx *chip, const char *model, int *power_save_to);
+int azx_bus_create(struct azx *chip, const char *model);
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
int azx_codec_configure(struct azx *chip);
int azx_init_stream(struct azx *chip);
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 8ec5289f8e05..43ad51c672d6 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -5524,13 +5524,11 @@ static const struct hda_codec_ops generic_patch_ops = {
#endif
};
-/**
+/*
* snd_hda_parse_generic_codec - Generic codec parser
* @codec: the HDA codec
- *
- * This should be called from the HDA codec core.
*/
-int snd_hda_parse_generic_codec(struct hda_codec *codec)
+static int snd_hda_parse_generic_codec(struct hda_codec *codec)
{
struct hda_gen_spec *spec;
int err;
@@ -5556,7 +5554,17 @@ error:
snd_hda_gen_free(codec);
return err;
}
-EXPORT_SYMBOL_GPL(snd_hda_parse_generic_codec);
+
+static const struct hda_codec_preset snd_hda_preset_generic[] = {
+ { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+ {} /* terminator */
+};
+
+static struct hda_codec_driver generic_driver = {
+ .preset = snd_hda_preset_generic,
+};
+
+module_hda_codec_driver(generic_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic HD-audio codec parser");
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 11b5a42b4ec8..125f3420fa6a 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -116,9 +116,6 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
- /* link to codec */
- hwdep->dev.parent = &codec->dev;
-
/* for sysfs */
hwdep->dev.groups = snd_hda_dev_attr_groups;
dev_set_drvdata(&hwdep->dev, codec);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 4614cb1f9582..13529715bc55 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -173,7 +173,6 @@ static struct kernel_param_ops param_ops_xint = {
#define param_check_xint param_check_int
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-static int *power_save_addr = &power_save;
module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
"(in second, 0 = disable).");
@@ -186,7 +185,7 @@ static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
#else
-static int *power_save_addr;
+#define power_save 0
#endif /* CONFIG_PM */
static int align_buffer_size = -1;
@@ -740,7 +739,6 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
{
struct hda_intel *hda;
struct azx *chip;
- struct hda_codec *c;
int prev = power_save;
int ret = param_set_int(val, kp);
@@ -752,8 +750,7 @@ static int param_set_xint(const char *val, const struct kernel_param *kp)
chip = &hda->chip;
if (!chip->bus || chip->disabled)
continue;
- list_for_each_entry(c, &chip->bus->codec_list, list)
- snd_hda_power_sync(c);
+ snd_hda_set_power_save(chip->bus, power_save * 1000);
}
mutex_unlock(&card_list_lock);
return 0;
@@ -772,7 +769,6 @@ static int azx_suspend(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hda_intel *hda;
- struct azx_pcm *p;
if (!card)
return 0;
@@ -784,10 +780,6 @@ static int azx_suspend(struct device *dev)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
azx_clear_irq_pending(chip);
- list_for_each_entry(p, &chip->pcm_list, list)
- snd_pcm_suspend_all(p->pcm);
- if (chip->initialized)
- snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
azx_enter_link_reset(chip);
if (chip->irq >= 0) {
@@ -830,7 +822,6 @@ static int azx_resume(struct device *dev)
azx_init_chip(chip, true);
- snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
@@ -927,7 +918,8 @@ static int azx_runtime_idle(struct device *dev)
if (chip->disabled || hda->init_failed)
return 0;
- if (!power_save_controller || !azx_has_pm_runtime(chip))
+ if (!power_save_controller || !azx_has_pm_runtime(chip) ||
+ chip->bus->codec_powered)
return -EBUSY;
return 0;
@@ -1612,19 +1604,6 @@ static int azx_first_init(struct azx *chip)
return 0;
}
-static void power_down_all_codecs(struct azx *chip)
-{
-#ifdef CONFIG_PM
- /* The codecs were powered up in snd_hda_codec_new().
- * Now all initialization done, so turn them down if possible
- */
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- snd_hda_power_down(codec);
- }
-#endif
-}
-
#ifdef CONFIG_SND_HDA_PATCH_LOADER
/* callback from request_firmware_nowait() */
static void azx_firmware_cb(const struct firmware *fw, void *context)
@@ -1893,7 +1872,7 @@ static int azx_probe_continue(struct azx *chip)
#endif
/* create codec instances */
- err = azx_bus_create(chip, model[dev], power_save_addr);
+ err = azx_bus_create(chip, model[dev]);
if (err < 0)
goto out_free;
@@ -1934,9 +1913,9 @@ static int azx_probe_continue(struct azx *chip)
goto out_free;
chip->running = 1;
- power_down_all_codecs(chip);
azx_notifier_register(chip);
azx_add_card_list(chip);
+ snd_hda_set_power_save(chip->bus, power_save * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
pm_runtime_put_noidle(&pci->dev);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 49c08a7278c4..8588813163e3 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -273,29 +273,6 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
int index, int *type_index_ret);
/*
- * Channel mode helper
- */
-struct hda_channel_mode {
- int channels;
- const struct hda_verb *sequence;
-};
-
-int snd_hda_ch_mode_info(struct hda_codec *codec,
- struct snd_ctl_elem_info *uinfo,
- const struct hda_channel_mode *chmode,
- int num_chmodes);
-int snd_hda_ch_mode_get(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int max_channels);
-int snd_hda_ch_mode_put(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int *max_channelsp);
-
-/*
* Multi-channel / digital-out PCM helper
*/
@@ -351,12 +328,6 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout);
/*
- * generic codec parser
- */
-int snd_hda_parse_generic_codec(struct hda_codec *codec);
-int snd_hda_parse_hdmi_codec(struct hda_codec *codec);
-
-/*
* generic proc interface
*/
#ifdef CONFIG_PROC_FS
@@ -783,9 +754,13 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
/*
*/
-#define codec_err(codec, fmt, args...) dev_err(&(codec)->dev, fmt, ##args)
-#define codec_warn(codec, fmt, args...) dev_warn(&(codec)->dev, fmt, ##args)
-#define codec_info(codec, fmt, args...) dev_info(&(codec)->dev, fmt, ##args)
-#define codec_dbg(codec, fmt, args...) dev_dbg(&(codec)->dev, fmt, ##args)
+#define codec_err(codec, fmt, args...) \
+ dev_err(hda_codec_dev(codec), fmt, ##args)
+#define codec_warn(codec, fmt, args...) \
+ dev_warn(hda_codec_dev(codec), fmt, ##args)
+#define codec_info(codec, fmt, args...) \
+ dev_info(hda_codec_dev(codec), fmt, ##args)
+#define codec_dbg(codec, fmt, args...) \
+ dev_dbg(hda_codec_dev(codec), fmt, ##args)
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 1bd7a9e04046..1359fdd20f02 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -81,7 +81,7 @@ module_param(power_save, bint, 0644);
MODULE_PARM_DESC(power_save,
"Automatic power-saving timeout (in seconds, 0 = disable).");
#else
-static int power_save = 0;
+#define power_save 0
#endif
/*
@@ -249,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
- struct azx_pcm *p;
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- list_for_each_entry(p, &chip->pcm_list, list)
- snd_pcm_suspend_all(p->pcm);
- if (chip->initialized)
- snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
azx_enter_link_reset(chip);
@@ -277,7 +272,6 @@ static int hda_tegra_resume(struct device *dev)
azx_init_chip(chip, 1);
- snd_hda_resume(chip->bus);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
@@ -343,17 +337,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
return 0;
}
-/*
- * The codecs were powered up in snd_hda_codec_new().
- * Now all initialization done, so turn them down if possible
- */
-static void power_down_all_codecs(struct azx *chip)
-{
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list)
- snd_hda_power_down(codec);
-}
-
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
{
struct snd_card *card = chip->card;
@@ -502,7 +485,7 @@ static int hda_tegra_probe(struct platform_device *pdev)
goto out_free;
/* create codec instances */
- err = azx_bus_create(chip, NULL, &power_save);
+ err = azx_bus_create(chip, NULL);
if (err < 0)
goto out_free;
@@ -529,8 +512,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
goto out_free;
chip->running = 1;
- power_down_all_codecs(chip);
azx_notifier_register(chip);
+ snd_hda_set_power_save(chip->bus, power_save * 1000);
return 0;
diff --git a/sound/pci/hda/hda_trace.h b/sound/pci/hda/hda_trace.h
index 3a1c63161eb1..c0e1c7d24dbe 100644
--- a/sound/pci/hda/hda_trace.h
+++ b/sound/pci/hda/hda_trace.h
@@ -87,30 +87,6 @@ DEFINE_EVENT(hda_power, hda_power_up,
TP_PROTO(struct hda_codec *codec),
TP_ARGS(codec)
);
-
-TRACE_EVENT(hda_power_count,
- TP_PROTO(struct hda_codec *codec),
- TP_ARGS(codec),
- TP_STRUCT__entry(
- __field( unsigned int, card )
- __field( unsigned int, addr )
- __field( int, power_count )
- __field( int, power_on )
- __field( int, power_transition )
- ),
-
- TP_fast_assign(
- __entry->card = (codec)->bus->card->number;
- __entry->addr = (codec)->addr;
- __entry->power_count = (codec)->power_count;
- __entry->power_on = (codec)->power_on;
- __entry->power_transition = (codec)->power_transition;
- ),
-
- TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d",
- __entry->card, __entry->addr, __entry->power_count,
- __entry->power_on, __entry->power_transition)
-);
#endif /* CONFIG_PM */
TRACE_EVENT(hda_unsol_event,
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index d285904cdb64..af4c7be86c27 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1194,20 +1194,8 @@ MODULE_ALIAS("snd-hda-codec-id:11d4*");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Analog Devices HD-audio codec");
-static struct hda_codec_preset_list analog_list = {
+static struct hda_codec_driver analog_driver = {
.preset = snd_hda_preset_analog,
- .owner = THIS_MODULE,
};
-static int __init patch_analog_init(void)
-{
- return snd_hda_add_codec_preset(&analog_list);
-}
-
-static void __exit patch_analog_exit(void)
-{
- snd_hda_delete_codec_preset(&analog_list);
-}
-
-module_init(patch_analog_init)
-module_exit(patch_analog_exit)
+module_hda_codec_driver(analog_driver);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index 5e65999e0d8e..447302695195 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -98,20 +98,8 @@ MODULE_ALIAS("snd-hda-codec-id:1102000d");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
-static struct hda_codec_preset_list ca0110_list = {
+static struct hda_codec_driver ca0110_driver = {
.preset = snd_hda_preset_ca0110,
- .owner = THIS_MODULE,
};
-static int __init patch_ca0110_init(void)
-{
- return snd_hda_add_codec_preset(&ca0110_list);
-}
-
-static void __exit patch_ca0110_exit(void)
-{
- snd_hda_delete_codec_preset(&ca0110_list);
-}
-
-module_init(patch_ca0110_init)
-module_exit(patch_ca0110_exit)
+module_hda_codec_driver(ca0110_driver);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index e0383eea9880..81991b4134cd 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -4702,20 +4702,8 @@ MODULE_ALIAS("snd-hda-codec-id:11020011");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Creative Sound Core3D codec");
-static struct hda_codec_preset_list ca0132_list = {
+static struct hda_codec_driver ca0132_driver = {
.preset = snd_hda_preset_ca0132,
- .owner = THIS_MODULE,
};
-static int __init patch_ca0132_init(void)
-{
- return snd_hda_add_codec_preset(&ca0132_list);
-}
-
-static void __exit patch_ca0132_exit(void)
-{
- snd_hda_delete_codec_preset(&ca0132_list);
-}
-
-module_init(patch_ca0132_init)
-module_exit(patch_ca0132_exit)
+module_hda_codec_driver(ca0132_driver);
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index dd2b3d92071f..50e9dd675579 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -1221,20 +1221,8 @@ MODULE_ALIAS("snd-hda-codec-id:10134213");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
-static struct hda_codec_preset_list cirrus_list = {
+static struct hda_codec_driver cirrus_driver = {
.preset = snd_hda_preset_cirrus,
- .owner = THIS_MODULE,
};
-static int __init patch_cirrus_init(void)
-{
- return snd_hda_add_codec_preset(&cirrus_list);
-}
-
-static void __exit patch_cirrus_exit(void)
-{
- snd_hda_delete_codec_preset(&cirrus_list);
-}
-
-module_init(patch_cirrus_init)
-module_exit(patch_cirrus_exit)
+module_hda_codec_driver(cirrus_driver);
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index c895a8f21192..617d9012e78a 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -137,20 +137,8 @@ MODULE_ALIAS("snd-hda-codec-id:434d4980");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("C-Media HD-audio codec");
-static struct hda_codec_preset_list cmedia_list = {
+static struct hda_codec_driver cmedia_driver = {
.preset = snd_hda_preset_cmedia,
- .owner = THIS_MODULE,
};
-static int __init patch_cmedia_init(void)
-{
- return snd_hda_add_codec_preset(&cmedia_list);
-}
-
-static void __exit patch_cmedia_exit(void)
-{
- snd_hda_delete_codec_preset(&cmedia_list);
-}
-
-module_init(patch_cmedia_init)
-module_exit(patch_cmedia_exit)
+module_hda_codec_driver(cmedia_driver);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index da67ea8645a6..5aa466a13e43 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -1018,20 +1018,8 @@ MODULE_ALIAS("snd-hda-codec-id:14f151d7");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
-static struct hda_codec_preset_list conexant_list = {
+static struct hda_codec_driver conexant_driver = {
.preset = snd_hda_preset_conexant,
- .owner = THIS_MODULE,
};
-static int __init patch_conexant_init(void)
-{
- return snd_hda_add_codec_preset(&conexant_list);
-}
-
-static void __exit patch_conexant_exit(void)
-{
- snd_hda_delete_codec_preset(&conexant_list);
-}
-
-module_init(patch_conexant_init)
-module_exit(patch_conexant_exit)
+module_hda_codec_driver(conexant_driver);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index b422e406a9cb..f1812aabd63e 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3301,15 +3301,6 @@ static int patch_via_hdmi(struct hda_codec *codec)
}
/*
- * called from hda_codec.c for generic HDMI support
- */
-int snd_hda_parse_hdmi_codec(struct hda_codec *codec)
-{
- return patch_generic_hdmi(codec);
-}
-EXPORT_SYMBOL_GPL(snd_hda_parse_hdmi_codec);
-
-/*
* patch entries
*/
static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
@@ -3373,6 +3364,8 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+/* special ID for generic HDMI */
+{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
{} /* terminator */
};
@@ -3442,20 +3435,8 @@ MODULE_ALIAS("snd-hda-codec-intelhdmi");
MODULE_ALIAS("snd-hda-codec-nvhdmi");
MODULE_ALIAS("snd-hda-codec-atihdmi");
-static struct hda_codec_preset_list intel_list = {
+static struct hda_codec_driver hdmi_driver = {
.preset = snd_hda_preset_hdmi,
- .owner = THIS_MODULE,
};
-static int __init patch_hdmi_init(void)
-{
- return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_hdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_hdmi_init)
-module_exit(patch_hdmi_exit)
+module_hda_codec_driver(hdmi_driver);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 526398a4a442..0ae1f5b7639b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6521,20 +6521,8 @@ MODULE_ALIAS("snd-hda-codec-id:10ec*");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Realtek HD-audio codec");
-static struct hda_codec_preset_list realtek_list = {
+static struct hda_codec_driver realtek_driver = {
.preset = snd_hda_preset_realtek,
- .owner = THIS_MODULE,
};
-static int __init patch_realtek_init(void)
-{
- return snd_hda_add_codec_preset(&realtek_list);
-}
-
-static void __exit patch_realtek_exit(void)
-{
- snd_hda_delete_codec_preset(&realtek_list);
-}
-
-module_init(patch_realtek_init)
-module_exit(patch_realtek_exit)
+module_hda_codec_driver(realtek_driver);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 3208ad69583e..38a477333321 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -319,20 +319,8 @@ MODULE_ALIAS("snd-hda-codec-id:18540018");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
-static struct hda_codec_preset_list si3054_list = {
+static struct hda_codec_driver si3054_driver = {
.preset = snd_hda_preset_si3054,
- .owner = THIS_MODULE,
};
-static int __init patch_si3054_init(void)
-{
- return snd_hda_add_codec_preset(&si3054_list);
-}
-
-static void __exit patch_si3054_exit(void)
-{
- snd_hda_delete_codec_preset(&si3054_list);
-}
-
-module_init(patch_si3054_init)
-module_exit(patch_si3054_exit)
+module_hda_codec_driver(si3054_driver);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 87eff3173ce9..2956a6ba6bf0 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -2132,8 +2132,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+#ifdef CONFIG_PM
/* resetting controller clears GPIO, so we need to keep on */
- codec->bus->power_keep_link_on = 1;
+ codec->d3_stop_clk = 0;
+#endif
}
}
@@ -5091,20 +5093,8 @@ MODULE_ALIAS("snd-hda-codec-id:111d*");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
-static struct hda_codec_preset_list sigmatel_list = {
+static struct hda_codec_driver sigmatel_driver = {
.preset = snd_hda_preset_sigmatel,
- .owner = THIS_MODULE,
};
-static int __init patch_sigmatel_init(void)
-{
- return snd_hda_add_codec_preset(&sigmatel_list);
-}
-
-static void __exit patch_sigmatel_exit(void)
-{
- snd_hda_delete_codec_preset(&sigmatel_list);
-}
-
-module_init(patch_sigmatel_init)
-module_exit(patch_sigmatel_exit)
+module_hda_codec_driver(sigmatel_driver);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 3de6d3d779c9..2045f33b1ace 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1884,23 +1884,11 @@ static const struct hda_codec_preset snd_hda_preset_via[] = {
MODULE_ALIAS("snd-hda-codec-id:1106*");
-static struct hda_codec_preset_list via_list = {
+static struct hda_codec_driver via_driver = {
.preset = snd_hda_preset_via,
- .owner = THIS_MODULE,
};
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VIA HD-audio codec");
-static int __init patch_via_init(void)
-{
- return snd_hda_add_codec_preset(&via_list);
-}
-
-static void __exit patch_via_exit(void)
-{
- snd_hda_delete_codec_preset(&via_list);
-}
-
-module_init(patch_via_init)
-module_exit(patch_via_exit)
+module_hda_codec_driver(via_driver);