diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-02-17 15:25:37 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-02-23 09:16:06 +0100 |
commit | d8a766a16ed90c4b3bd7afa6e1417f8d715db507 (patch) | |
tree | 2810742bf71c851726f27e99f9984fe334f97b8d /sound/pci/hda/hda_codec.c | |
parent | 327ef4f02582d01f7eedb291794106823b44a0cf (diff) | |
download | kernel_replicant_linux-d8a766a16ed90c4b3bd7afa6e1417f8d715db507.tar.gz kernel_replicant_linux-d8a766a16ed90c4b3bd7afa6e1417f8d715db507.tar.bz2 kernel_replicant_linux-d8a766a16ed90c4b3bd7afa6e1417f8d715db507.zip |
ALSA: hda - Bind codecs via standard bus
Now we create the standard HD-audio bus (/sys/bus/hdaudio), and bind
the codec driver with the codec device over there. This is the first
step of the whole transition so that the changes to each codec driver
are kept as minimal as possible.
Each codec driver needs to register hda_codec_driver struct containing
the currently existing preset via the new helper macro
module_hda_codec_driver(). The old hda_codec_preset_list is replaced
with this infrastructure. The generic parsers (for HDMI and other)
are also included in the preset with the special IDs to bind
uniquely.
In HD-audio core side, the device binding code is split to
hda_bind.c. It provides the snd_hda_bus_type implementation to match
the codec driver with the given codec vendor ID. It also manages the
module auto-loading by itself like before: when the matching isn't
found, it tries to probe the corresponding codec modules, and finally
falls back to the generic drivers. (The special ID mentioned above is
set at this stage.)
The only visible change to outside is that the hdaudio sysfs entry now
appears in /sys/bus/devices, not as a sound class device.
More works to move the suspend/resume and remove ops will be
(hopefully) done in later patches.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 307 |
1 files changed, 16 insertions, 291 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5e755eb6f19a..61c8174e8013 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -40,69 +40,6 @@ #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); @@ -885,111 +822,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 +1133,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,6 +1140,8 @@ 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 @@ -1335,12 +1155,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 */ 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 +1164,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,10 +1176,7 @@ 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); return 0; } @@ -1373,7 +1186,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 +1198,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 +1214,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 +1242,14 @@ 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 */ codec->bus = bus; codec->addr = codec_addr; @@ -1587,92 +1400,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, @@ -2739,8 +2466,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 +2487,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); |