[alsa-devel] [PATCH 0/9] Allow hot-unplugging hd-audio codec driver
Hi,
here is yet another patchset for HD-audio. This time it's about robustifying the dynamic unbind of a codec driver. With these patches, the codec driver can be freely bound / unbound in run time. We had a similar feature via hwdep reconfig sysfs entry, but this is a more generic form.
The patchset is applied on top of the previous one, topic/hda-regmap branch of sound git tree. This patchset itself is found in topic/hda-unbind branch.
Takashi
===
Takashi Iwai (9): ALSA: hda - Split snd_hda_build_pcms() ALSA: hda - Add card field to hda_codec struct ALSA: hda - Remove superfluous memory allocation error messages ALSA: hda - Allocate hda_pcm objects dynamically ALSA: hda - Don't assume non-NULL PCM ops ALSA: core: Re-add snd_device_disconnect() ALSA: hda - Implement unbind more safely ALSA: hda - Build PCMs and controls at codec driver probe ALSA: hda - Drop codec driver module refcounting
include/sound/core.h | 3 +- sound/core/device.c | 43 +++-- sound/core/init.c | 5 +- sound/pci/hda/hda_beep.c | 4 +- sound/pci/hda/hda_bind.c | 33 ++-- sound/pci/hda/hda_codec.c | 363 ++++++++++++++++++++--------------------- sound/pci/hda/hda_codec.h | 27 ++- sound/pci/hda/hda_controller.c | 53 +++--- sound/pci/hda/hda_generic.c | 28 ++-- sound/pci/hda/hda_generic.h | 2 +- sound/pci/hda/hda_hwdep.c | 2 +- sound/pci/hda/hda_intel.c | 15 +- sound/pci/hda/hda_jack.c | 8 +- sound/pci/hda/hda_local.h | 1 + sound/pci/hda/hda_proc.c | 8 +- sound/pci/hda/hda_sysfs.c | 2 +- sound/pci/hda/hda_tegra.c | 10 -- sound/pci/hda/hda_trace.h | 4 +- sound/pci/hda/patch_ca0132.c | 31 ++-- sound/pci/hda/patch_hdmi.c | 36 ++-- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_si3054.c | 11 +- sound/pci/hda/patch_via.c | 14 +- 23 files changed, 349 insertions(+), 356 deletions(-)
snd_hda_build_pcms() does actually three things: let the codec driver build up hda_pcm list, set the PCM default values, and call the attach_pcm bus ops for each hda_pcm instance. The former two are basically independent from the bus implementation, so it'd make the code a bit more readable.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 102 +++++++++++++++++++++++++++------------------- sound/pci/hda/hda_codec.h | 1 + 2 files changed, 60 insertions(+), 43 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 26d45268647d..a6e49e446f8d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4118,71 +4118,87 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) return -EAGAIN; }
-/* - * attach a new PCM stream - */ -static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) +/* call build_pcms ops of the given codec and set up the default parameters */ +int snd_hda_codec_parse_pcms(struct hda_codec *codec) { - struct hda_bus *bus = codec->bus; - struct hda_pcm_stream *info; - int stream, err; + unsigned int pcm; + int err;
- if (snd_BUG_ON(!pcm->name)) - return -EINVAL; - for (stream = 0; stream < 2; stream++) { - info = &pcm->stream[stream]; - if (info->substreams) { + if (codec->num_pcms) + return 0; /* already parsed */ + + if (!codec->patch_ops.build_pcms) + return 0; + + err = codec->patch_ops.build_pcms(codec); + if (err < 0) { + codec_err(codec, "cannot build PCMs for #%d (error %d)\n", + codec->addr, err); + return err; + } + + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + int stream; + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *info = &cpcm->stream[stream]; + + if (!info->substreams) + continue; + if (snd_BUG_ON(!cpcm->name)) + return -EINVAL; err = set_pcm_default_values(codec, info); - if (err < 0) + if (err < 0) { + codec_warn(codec, + "fail to setup default for PCM %s\n", + cpcm->name); return err; + } } } - return bus->ops.attach_pcm(bus, codec, pcm); + + return 0; }
/* assign all PCMs of the given codec */ int snd_hda_codec_build_pcms(struct hda_codec *codec) { + struct hda_bus *bus = codec->bus; unsigned int pcm; - int err; + int dev, err;
- if (!codec->num_pcms) { - if (!codec->patch_ops.build_pcms) - return 0; - err = codec->patch_ops.build_pcms(codec); - if (err < 0) { - codec_err(codec, - "cannot build PCMs for #%d (error %d)\n", - codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "cannot revert codec\n"); - return err; - } - } + if (snd_BUG_ON(!bus->ops.attach_pcm)) + return -EINVAL; + + err = snd_hda_codec_parse_pcms(codec); + if (err < 0) { + snd_hda_codec_reset(codec); + return err; } + + /* attach a new PCM streams */ for (pcm = 0; pcm < codec->num_pcms; pcm++) { struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - int dev;
+ if (cpcm->pcm) + continue; /* already attached */ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) continue; /* no substreams assigned */
- if (!cpcm->pcm) { - dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); - if (dev < 0) - continue; /* no fatal error */ - cpcm->device = dev; - err = snd_hda_attach_pcm(codec, cpcm); - if (err < 0) { - codec_err(codec, - "cannot attach PCM stream %d for codec #%d\n", - dev, codec->addr); - continue; /* no fatal error */ - } + dev = get_empty_pcm_device(bus, cpcm->pcm_type); + if (dev < 0) + continue; /* no fatal error */ + cpcm->device = dev; + err = bus->ops.attach_pcm(bus, codec, cpcm); + if (err < 0) { + codec_err(codec, + "cannot attach PCM stream %d for codec #%d\n", + dev, codec->addr); + continue; /* no fatal error */ } } + return 0; }
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 634d07c2634b..fff3603bfff5 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -490,6 +490,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec); * PCM */ int snd_hda_build_pcms(struct hda_bus *bus); +int snd_hda_codec_parse_pcms(struct hda_codec *codec); int snd_hda_codec_build_pcms(struct hda_codec *codec);
int snd_hda_codec_prepare(struct hda_codec *codec,
Allow the codec object to have an individual card pointer. Not only this simplifies the redirections in many places, also this will allow us to make each codec assigned to a different card object.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_beep.c | 4 ++-- sound/pci/hda/hda_bind.c | 6 +++--- sound/pci/hda/hda_codec.c | 30 +++++++++++++++--------------- sound/pci/hda/hda_codec.h | 5 +++-- sound/pci/hda/hda_controller.c | 2 +- sound/pci/hda/hda_hwdep.c | 2 +- sound/pci/hda/hda_jack.c | 8 ++++---- sound/pci/hda/hda_proc.c | 2 +- sound/pci/hda/hda_sysfs.c | 2 +- sound/pci/hda/hda_trace.h | 4 ++-- sound/pci/hda/patch_ca0132.c | 2 +- sound/pci/hda/patch_hdmi.c | 6 +++--- sound/pci/hda/patch_via.c | 8 ++++---- 13 files changed, 41 insertions(+), 40 deletions(-)
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 45a3d7ae6f56..9054dfab583f 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -160,7 +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->dev.parent = &codec->card->card_dev;
input_dev->id.vendor = codec->vendor_id >> 16; input_dev->id.product = codec->vendor_id & 0xffff; @@ -224,7 +224,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) if (beep == NULL) return -ENOMEM; snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); + "card%d/codec#%d/beep0", codec->card->number, codec->addr); /* enable linear scale */ snd_hda_regmap_write(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, 0x01);
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 8be97ffe10b2..d0d5aead3746 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -293,9 +293,9 @@ int snd_hda_codec_configure(struct hda_codec *codec) }
/* audio codec should override the mixer name */ - if (codec->afg || !*codec->bus->card->mixername) - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), + if (codec->afg || !*codec->card->mixername) + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); return 0;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a6e49e446f8d..9f7d22f4f9f9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1208,9 +1208,8 @@ static void snd_hda_codec_dev_release(struct device *dev) * * Returns 0 if successful, or a negative error code. */ -int snd_hda_codec_new(struct hda_bus *bus, - unsigned int codec_addr, - struct hda_codec **codecp) +int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp) { struct hda_codec *codec; struct device *dev; @@ -1229,7 +1228,7 @@ int snd_hda_codec_new(struct hda_bus *bus, return -EINVAL;
if (bus->caddr_tbl[codec_addr]) { - dev_err(bus->card->dev, + dev_err(card->dev, "address 0x%x is already occupied\n", codec_addr); return -EBUSY; @@ -1237,21 +1236,22 @@ int snd_hda_codec_new(struct hda_bus *bus,
codec = kzalloc(sizeof(*codec), GFP_KERNEL); if (codec == NULL) { - dev_err(bus->card->dev, "can't allocate struct hda_codec\n"); + dev_err(card->dev, "can't allocate struct hda_codec\n"); return -ENOMEM; }
dev = hda_codec_dev(codec); device_initialize(dev); - dev->parent = bus->card->dev; + dev->parent = 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_name(dev, "hdaudioC%dD%d", card->number, codec_addr); dev_set_drvdata(dev, codec); /* for sysfs */ device_enable_async_suspend(dev);
codec->bus = bus; + codec->card = card; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); @@ -1309,7 +1309,7 @@ int snd_hda_codec_new(struct hda_bus *bus,
setup_fg_nodes(codec); if (!codec->afg && !codec->mfg) { - dev_err(bus->card->dev, "no AFG or MFG node found\n"); + dev_err(card->dev, "no AFG or MFG node found\n"); err = -ENODEV; goto error; } @@ -1317,7 +1317,7 @@ int snd_hda_codec_new(struct hda_bus *bus, fg = codec->afg ? codec->afg : codec->mfg; err = read_widget_caps(codec, fg); if (err < 0) { - dev_err(bus->card->dev, "cannot malloc\n"); + dev_err(card->dev, "cannot malloc\n"); goto error; } err = read_pin_defaults(codec); @@ -1346,9 +1346,9 @@ int snd_hda_codec_new(struct hda_bus *bus,
sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id); - snd_component_add(codec->bus->card, component); + snd_component_add(card, component);
- err = snd_device_new(bus->card, SNDRV_DEV_CODEC, codec, &dev_ops); + err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); if (err < 0) goto error;
@@ -1956,7 +1956,7 @@ find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; strcpy(id.name, name); - return snd_ctl_find_id(codec->bus->card, &id); + return snd_ctl_find_id(codec->card, &id); }
/** @@ -2020,7 +2020,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, nid = kctl->id.subdevice & 0xffff; if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) kctl->id.subdevice = 0; - err = snd_ctl_add(codec->bus->card, kctl); + err = snd_ctl_add(codec->card, kctl); if (err < 0) return err; item = snd_array_new(&codec->mixers); @@ -2073,7 +2073,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec) int i; struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, items[i].kctl); + snd_ctl_remove(codec->card, items[i].kctl); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } @@ -2146,7 +2146,7 @@ EXPORT_SYMBOL_GPL(snd_hda_unlock_devices); int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; - struct snd_card *card = bus->card; + struct snd_card *card = codec->card; int i;
if (snd_hda_lock_devices(bus) < 0) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index fff3603bfff5..1fb25cddf819 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -256,6 +256,7 @@ struct hda_pcm { struct hda_codec { struct device dev; struct hda_bus *bus; + struct snd_card *card; unsigned int addr; /* codec addr*/ struct list_head list; /* list point */
@@ -403,8 +404,8 @@ enum { * constructors */ int snd_hda_bus_new(struct snd_card *card, struct hda_bus **busp); -int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - struct hda_codec **codecp); +int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec);
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 1695f0e2bd9d..f50863a5159d 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1898,7 +1898,7 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) for (c = 0; c < max_slots; c++) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(bus, c, &codec); + err = snd_hda_codec_new(bus, bus->card, c, &codec); if (err < 0) continue; codec->jackpoll_interval = get_jackpoll_interval(chip); diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 125f3420fa6a..57df06e76968 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -101,7 +101,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec) int err;
sprintf(hwname, "HDA Codec %d", codec->addr); - err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep); + err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); if (err < 0) return err; codec->hwdep = hwdep; diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 2e933783c83f..897ab7db31e2 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -135,7 +135,7 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_INPUT_JACK /* free jack instances manually when clearing/reconfiguring */ if (!codec->bus->shutdown && jack->jack) - snd_device_free(codec->bus->card, jack->jack); + snd_device_free(codec->card, jack->jack); #endif for (cb = jack->callback; cb; cb = next) { next = cb->next; @@ -340,7 +340,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) if (!jack->kctl || jack->block_report) continue; state = get_jack_plug_state(jack->pin_sense); - snd_kctl_jack_report(codec->bus->card, jack->kctl, state); + snd_kctl_jack_report(codec->card, jack->kctl, state); #ifdef CONFIG_SND_HDA_INPUT_JACK if (jack->jack) snd_jack_report(jack->jack, @@ -412,11 +412,11 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, jack->phantom_jack = !!phantom_jack;
state = snd_hda_jack_detect(codec, nid); - snd_kctl_jack_report(codec->bus->card, kctl, state); + snd_kctl_jack_report(codec->card, kctl, state); #ifdef CONFIG_SND_HDA_INPUT_JACK if (!phantom_jack) { jack->type = get_input_jack_type(codec, nid); - err = snd_jack_new(codec->bus->card, name, jack->type, + err = snd_jack_new(codec->card, name, jack->type, &jack->jack); if (err < 0) return err; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index ce5a6da83419..cc32b878ae2e 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -839,7 +839,7 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) int err;
snprintf(name, sizeof(name), "codec#%d", codec->addr); - err = snd_card_proc_new(codec->bus->card, name, &entry); + err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err;
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index ccc962a1699f..e13c75d67847 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -149,7 +149,7 @@ static int reconfig_codec(struct hda_codec *codec) err = snd_hda_codec_build_controls(codec); if (err < 0) goto error; - err = snd_card_register(codec->bus->card); + err = snd_card_register(codec->card); error: snd_hda_power_down(codec); return err; diff --git a/sound/pci/hda/hda_trace.h b/sound/pci/hda/hda_trace.h index c0e1c7d24dbe..7fedfa862419 100644 --- a/sound/pci/hda/hda_trace.h +++ b/sound/pci/hda/hda_trace.h @@ -23,7 +23,7 @@ DECLARE_EVENT_CLASS(hda_cmd, ),
TP_fast_assign( - __entry->card = (codec)->bus->card->number; + __entry->card = (codec)->card->number; __entry->addr = (codec)->addr; __entry->val = (val); ), @@ -71,7 +71,7 @@ DECLARE_EVENT_CLASS(hda_power, ),
TP_fast_assign( - __entry->card = (codec)->bus->card->number; + __entry->card = (codec)->card->number; __entry->addr = (codec)->addr; ),
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 81991b4134cd..ced3e82d9e23 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4352,7 +4352,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) const struct dsp_image_seg *dsp_os_image; const struct firmware *fw_entry;
- if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0) + if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0) return false;
dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 78f8c2ed86f9..8f40b7d864c0 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -579,7 +579,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) int err;
snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->bus->card, name, &entry); + err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err;
@@ -594,7 +594,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) { if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) { - snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry); + snd_device_free(per_pin->codec->card, per_pin->proc_entry); per_pin->proc_entry = NULL; } } @@ -1622,7 +1622,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) }
if (eld_changed) - snd_ctl_notify(codec->bus->card, + snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); unlock: diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2045f33b1ace..57ad503ff940 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -907,16 +907,16 @@ static int patch_vt1708S(struct hda_codec *codec) if (get_codec_type(codec) == VT1708BCE) { kfree(codec->chip_name); codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); } /* correct names for VT1705 */ if (codec->vendor_id == 0x11064397) { kfree(codec->chip_name); codec->chip_name = kstrdup("VT1705", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); }
The memory allocators should have already given the kernel warning messages, thus we don't have to annoy again.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 16 ++++------------ sound/pci/hda/hda_controller.c | 18 ++++-------------- sound/pci/hda/hda_intel.c | 5 +---- 3 files changed, 9 insertions(+), 30 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9f7d22f4f9f9..42bd7f5e1185 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -794,10 +794,8 @@ int snd_hda_bus_new(struct snd_card *card, *busp = NULL;
bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (bus == NULL) { - dev_err(card->dev, "can't allocate struct hda_bus\n"); + if (!bus) return -ENOMEM; - }
bus->card = card; mutex_init(&bus->cmd_mutex); @@ -1235,10 +1233,8 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, }
codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (codec == NULL) { - dev_err(card->dev, "can't allocate struct hda_codec\n"); + if (!codec) return -ENOMEM; - }
dev = hda_codec_dev(codec); device_initialize(dev); @@ -1316,10 +1312,8 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
fg = codec->afg ? codec->afg : codec->mfg; err = read_widget_caps(codec, fg); - if (err < 0) { - dev_err(card->dev, "cannot malloc\n"); + if (err < 0) goto error; - } err = read_pin_defaults(codec); if (err < 0) goto error; @@ -1380,10 +1374,8 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) kfree(codec->wcaps); fg = codec->afg ? codec->afg : codec->mfg; err = read_widget_caps(codec, fg); - if (err < 0) { - codec_err(codec, "cannot malloc\n"); + if (err < 0) return err; - }
snd_array_free(&codec->init_pins); err = read_pin_defaults(codec); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index f50863a5159d..be02bca6f7e6 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -974,14 +974,9 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, */ static int azx_alloc_cmd_io(struct azx *chip) { - int err; - /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - PAGE_SIZE, &chip->rb); - if (err < 0) - dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n"); - return err; + return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &chip->rb); } EXPORT_SYMBOL_GPL(azx_alloc_cmd_io);
@@ -1472,7 +1467,6 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, int azx_alloc_stream_pages(struct azx *chip) { int i, err; - struct snd_card *card = chip->card;
for (i = 0; i < chip->num_streams; i++) { dsp_lock_init(&chip->azx_dev[i]); @@ -1480,18 +1474,14 @@ int azx_alloc_stream_pages(struct azx *chip) err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, BDL_SIZE, &chip->azx_dev[i].bdl); - if (err < 0) { - dev_err(card->dev, "cannot allocate BDL\n"); + if (err < 0) return -ENOMEM; - } } /* allocate memory for the position buffer */ err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, chip->num_streams * 8, &chip->posbuf); - if (err < 0) { - dev_err(card->dev, "cannot allocate posbuf\n"); + if (err < 0) return -ENOMEM; - }
/* allocate CORB/RIRB */ err = azx_alloc_cmd_io(chip); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e75e8137e296..f7fb1b51d446 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1383,7 +1383,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
hda = kzalloc(sizeof(*hda), GFP_KERNEL); if (!hda) { - dev_err(card->dev, "Cannot allocate hda\n"); pci_disable_device(pci); return -ENOMEM; } @@ -1564,10 +1563,8 @@ static int azx_first_init(struct azx *chip) chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); - if (!chip->azx_dev) { - dev_err(card->dev, "cannot malloc azx_dev\n"); + if (!chip->azx_dev) return -ENOMEM; - }
err = azx_alloc_stream_pages(chip); if (err < 0)
The PCM ops might be set NULL, or cleared to NULL when the driver is unbound. Give a proper NULL check at each place to be more robust.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 9 +++++++-- sound/pci/hda/hda_controller.c | 30 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 6a5fb9bf9441..a8082677373e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4074,7 +4074,11 @@ int snd_hda_codec_prepare(struct hda_codec *codec, { int ret; mutex_lock(&codec->bus->prepare_mutex); - ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream); + if (hinfo->ops.prepare) + ret = hinfo->ops.prepare(hinfo, codec, stream, format, + substream); + else + ret = -ENODEV; if (ret >= 0) purify_inactive_streams(codec); mutex_unlock(&codec->bus->prepare_mutex); @@ -4095,7 +4099,8 @@ void snd_hda_codec_cleanup(struct hda_codec *codec, struct snd_pcm_substream *substream) { mutex_lock(&codec->bus->prepare_mutex); - hinfo->ops.cleanup(hinfo, codec, substream); + if (hinfo->ops.cleanup) + hinfo->ops.cleanup(hinfo, codec, substream); mutex_unlock(&codec->bus->prepare_mutex); } EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index be02bca6f7e6..ad85f9bfaf57 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -416,7 +416,8 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); + if (hinfo->ops.close) + hinfo->ops.close(hinfo, apcm->codec, substream); snd_hda_power_down(apcm->codec); mutex_unlock(&chip->open_mutex); return 0; @@ -808,8 +809,8 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); if (azx_dev == NULL) { - mutex_unlock(&chip->open_mutex); - return -EBUSY; + err = -EBUSY; + goto unlock; } runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; @@ -844,12 +845,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, buff_step); snd_hda_power_up(apcm->codec); - err = hinfo->ops.open(hinfo, apcm->codec, substream); + if (hinfo->ops.open) + err = hinfo->ops.open(hinfo, apcm->codec, substream); + else + err = -ENODEV; if (err < 0) { azx_release_device(azx_dev); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return err; + goto powerdown; } snd_pcm_limit_hw_rates(runtime); /* sanity check */ @@ -858,10 +860,10 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_BUG_ON(!runtime->hw.formats) || snd_BUG_ON(!runtime->hw.rates)) { azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return -EINVAL; + if (hinfo->ops.close) + hinfo->ops.close(hinfo, apcm->codec, substream); + err = -EINVAL; + goto powerdown; }
/* disable LINK_ATIME timestamps for capture streams @@ -880,6 +882,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; + + powerdown: + snd_hda_power_down(apcm->codec); + unlock: + mutex_unlock(&chip->open_mutex); + return err; }
static int azx_pcm_mmap(struct snd_pcm_substream *substream,
Revive snd_device_disconnect() again so that it can be called from the individual driver. This time, HD-audio will need it.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/core.h | 3 ++- sound/core/device.c | 43 +++++++++++++++++++++++++++++++++---------- sound/core/init.c | 5 +---- 3 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/include/sound/core.h b/include/sound/core.h index da5748289968..b12931f513f4 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -278,7 +278,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, void *device_data, struct snd_device_ops *ops); int snd_device_register(struct snd_card *card, void *device_data); int snd_device_register_all(struct snd_card *card); -int snd_device_disconnect_all(struct snd_card *card); +void snd_device_disconnect(struct snd_card *card, void *device_data); +void snd_device_disconnect_all(struct snd_card *card); void snd_device_free(struct snd_card *card, void *device_data); void snd_device_free_all(struct snd_card *card);
diff --git a/sound/core/device.c b/sound/core/device.c index 41bec3075ae5..446dc452654e 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -73,7 +73,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, } EXPORT_SYMBOL(snd_device_new);
-static int __snd_device_disconnect(struct snd_device *dev) +static void __snd_device_disconnect(struct snd_device *dev) { if (dev->state == SNDRV_DEV_REGISTERED) { if (dev->ops->dev_disconnect && @@ -81,7 +81,6 @@ static int __snd_device_disconnect(struct snd_device *dev) dev_err(dev->card->dev, "device disconnect failure\n"); dev->state = SNDRV_DEV_DISCONNECTED; } - return 0; }
static void __snd_device_free(struct snd_device *dev) @@ -109,6 +108,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) }
/** + * snd_device_disconnect - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Return: Zero if successful, or a negative error code on failure or if the + * device not found. + */ +void snd_device_disconnect(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + if (snd_BUG_ON(!card || !device_data)) + return; + dev = look_for_dev(card, device_data); + if (dev) + __snd_device_disconnect(dev); + else + dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n", + device_data, __builtin_return_address(0)); +} +EXPORT_SYMBOL_GPL(snd_device_disconnect); + +/** * snd_device_free - release the device from the card * @card: the card instance * @device_data: the data pointer to release @@ -195,18 +222,14 @@ int snd_device_register_all(struct snd_card *card) * disconnect all the devices on the card. * called from init.c */ -int snd_device_disconnect_all(struct snd_card *card) +void snd_device_disconnect_all(struct snd_card *card) { struct snd_device *dev; - int err = 0;
if (snd_BUG_ON(!card)) - return -ENXIO; - list_for_each_entry_reverse(dev, &card->devices, list) { - if (__snd_device_disconnect(dev) < 0) - err = -ENXIO; - } - return err; + return; + list_for_each_entry_reverse(dev, &card->devices, list) + __snd_device_disconnect(dev); }
/* diff --git a/sound/core/init.c b/sound/core/init.c index 35419054821c..04734e047bfe 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops = int snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; - int err;
if (!card) return -EINVAL; @@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card) #endif
/* notify all devices that we are disconnected */ - err = snd_device_disconnect_all(card); - if (err < 0) - dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); + snd_device_disconnect_all(card);
snd_info_card_disconnect(card); if (card->registered) {
Now we have all pieces ready, and put them into places: - add the hda_pcm refcount to azx_pcm_open() and azx_pcm_close(), - call the most of cleanup code in hda_codec_reset() from the codec driver remove, - call the same code also from the hda_codec object free.
Then the codec driver can be unbound more safely now.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_bind.c | 3 +-- sound/pci/hda/hda_codec.c | 61 ++++++++++++++++++++++-------------------- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_controller.c | 3 +++ sound/pci/hda/hda_local.h | 1 + 5 files changed, 38 insertions(+), 31 deletions(-)
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index d0d5aead3746..114320864cea 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -130,8 +130,7 @@ static int hda_codec_driver_remove(struct device *dev)
if (codec->patch_ops.free) codec->patch_ops.free(codec); - codec->preset = NULL; - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + snd_hda_codec_cleanup_for_unbind(codec); snd_hda_regmap_exit(codec); module_put(dev->driver->owner); return 0; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a8082677373e..a87b247a172a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1174,36 +1174,57 @@ struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
+/* + * codec destructor + */ static void codec_release_pcms(struct hda_codec *codec) { struct hda_pcm *pcm, *n;
list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { list_del_init(&pcm->list); + if (pcm->pcm) + snd_device_disconnect(codec->card, pcm->pcm); snd_hda_codec_pcm_put(pcm); } }
-/* - * codec destructor - */ +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) +{ + cancel_delayed_work_sync(&codec->jackpoll_work); + flush_workqueue(codec->bus->workq); + if (!codec->in_freeing) + snd_hda_ctls_clear(codec); + codec_release_pcms(codec); + snd_hda_detach_beep_device(codec); + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + snd_hda_jack_tbl_clear(codec); + codec->proc_widget_hook = NULL; + codec->spec = NULL; + + /* free only driver_pins so that init_pins + user_pins are restored */ + snd_array_free(&codec->driver_pins); + snd_array_free(&codec->cvt_setups); + snd_array_free(&codec->spdif_out); + snd_array_free(&codec->verbs); + codec->preset = NULL; + codec->slave_dig_outs = NULL; + codec->spdif_status_reset = 0; + snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); + remove_conn_list(codec); +} + static void snd_hda_codec_free(struct hda_codec *codec) { if (!codec) return; - cancel_delayed_work_sync(&codec->jackpoll_work); - codec_release_pcms(codec); + codec->in_freeing = 1; if (device_is_registered(hda_codec_dev(codec))) device_del(hda_codec_dev(codec)); - snd_hda_jack_tbl_clear(codec); free_init_pincfgs(codec); flush_workqueue(codec->bus->workq); list_del(&codec->list); - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); - snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->spdif_out); - remove_conn_list(codec); codec->bus->caddr_tbl[codec->addr] = NULL; clear_bit(codec->addr, &codec->bus->codec_powered); snd_hda_sysfs_clear(codec); @@ -2198,27 +2219,9 @@ int snd_hda_codec_reset(struct hda_codec *codec) return -EBUSY;
/* OK, let it free */ - cancel_delayed_work_sync(&codec->jackpoll_work); - flush_workqueue(bus->workq); - snd_hda_ctls_clear(codec); - codec_release_pcms(codec); - snd_hda_detach_beep_device(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; - codec->spec = NULL; - /* free only driver_pins so that init_pins + user_pins are restored */ - snd_array_free(&codec->driver_pins); - snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->spdif_out); - snd_array_free(&codec->verbs); - codec->preset = NULL; - codec->slave_dig_outs = NULL; - codec->spdif_status_reset = 0; - /* allow device access again */ snd_hda_unlock_devices(bus); return 0; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 5a9b04873655..07001797cece 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -328,6 +328,7 @@ struct hda_codec { #endif
/* misc flags */ + unsigned int in_freeing:1; /* being released */ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each * status change * (e.g. Realtek codecs) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index ad85f9bfaf57..cae50d5ffb81 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -420,6 +420,7 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) hinfo->ops.close(hinfo, apcm->codec, substream); snd_hda_power_down(apcm->codec); mutex_unlock(&chip->open_mutex); + snd_hda_codec_pcm_put(apcm->info); return 0; }
@@ -806,6 +807,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) int err; int buff_step;
+ snd_hda_codec_pcm_get(apcm->info); mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); if (azx_dev == NULL) { @@ -887,6 +889,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_hda_power_down(apcm->codec); unlock: mutex_unlock(&chip->open_mutex); + snd_hda_codec_pcm_put(apcm->info); return err; }
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 0da5796b4690..4aefed9684c2 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -150,6 +150,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) int snd_hda_codec_reset(struct hda_codec *codec); +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
enum { HDA_VMUTE_OFF,
This makes the code flow easier -- instead of the controller driver calling snd_hda_build_pcms() and snd_hda_build_controls() explicitly, the codec driver itself builds PCMs and controls at probe time. Then the controller driver only needs to call snd_card_register().
Also, this allows us the full bind/unbind control, too. Even when a codec driver is bound later, it automatically registers the new PCM and controls by itself.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_bind.c | 22 ++++++++++++---- sound/pci/hda/hda_codec.c | 67 ----------------------------------------------- sound/pci/hda/hda_codec.h | 2 -- sound/pci/hda/hda_intel.c | 10 ------- sound/pci/hda/hda_tegra.c | 10 ------- 5 files changed, 17 insertions(+), 94 deletions(-)
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 114320864cea..ba1adbf0f85f 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -110,16 +110,28 @@ static int hda_codec_driver_probe(struct device *dev) if (err < 0) goto error; err = codec->preset->patch(codec); - if (err < 0) { - module_put(owner); - goto error; + if (err < 0) + goto error_module; + + err = snd_hda_codec_build_pcms(codec); + if (err < 0) + goto error_module; + err = snd_hda_codec_build_controls(codec); + if (err < 0) + goto error_module; + if (codec->card->registered) { + err = snd_card_register(codec->card); + if (err < 0) + goto error_module; }
return 0;
+ error_module: + module_put(owner); + error: - codec->preset = NULL; - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + snd_hda_codec_cleanup_for_unbind(codec); snd_hda_regmap_exit(codec); return err; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a87b247a172a..c78b1603849b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3593,36 +3593,6 @@ const struct dev_pm_ops hda_codec_driver_pm = { NULL) };
-/** - * snd_hda_build_controls - build mixer controls - * @bus: the BUS - * - * Creates mixer controls for each codec included in the bus. - * - * Returns 0 if successful, otherwise a negative error code. - */ -int snd_hda_build_controls(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_controls(codec); - if (err < 0) { - codec_err(codec, - "cannot build controls for #%d (error %d)\n", - codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "cannot revert codec\n"); - return err; - } - } - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_build_controls); - /* * add standard channel maps if not specified */ @@ -4241,43 +4211,6 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) }
/** - * snd_hda_build_pcms - build PCM information - * @bus: the BUS - * - * Create PCM information for each codec included in the bus. - * - * The build_pcms codec patch is requested to create and assign new - * hda_pcm objects. The codec is responsible to call snd_hda_codec_pcm_new() - * and fills the fields. Later they are instantiated by this function. - * - * At least, substreams, channels_min and channels_max must be filled for - * each stream. substreams = 0 indicates that the stream doesn't exist. - * When rates and/or formats are zero, the supported values are queried - * from the given nid. The nid is used also by the default ops.prepare - * and ops.cleanup callbacks. - * - * The driver needs to call ops.open in its open callback. Similarly, - * ops.close is supposed to be called in the close callback. - * ops.prepare should be called in the prepare or hw_params callback - * with the proper parameters for set up. - * ops.cleanup should be called in hw_free for clean up of streams. - * - * This function returns 0 if successful, or a negative error code. - */ -int snd_hda_build_pcms(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_pcms(codec); - if (err < 0) - return err; - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_build_pcms); - -/** * snd_hda_add_new_ctls - create controls from the array * @codec: the HDA codec * @knew: the array of struct snd_kcontrol_new diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 07001797cece..dab1c4ca8351 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -489,13 +489,11 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); /* * Mixer */ -int snd_hda_build_controls(struct hda_bus *bus); int snd_hda_codec_build_controls(struct hda_codec *codec);
/* * PCM */ -int snd_hda_build_pcms(struct hda_bus *bus); int snd_hda_codec_parse_pcms(struct hda_codec *codec); int snd_hda_codec_build_pcms(struct hda_codec *codec);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index f7fb1b51d446..e81461a413b8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1895,16 +1895,6 @@ static int azx_probe_continue(struct azx *chip) goto out_free; }
- /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = snd_hda_build_controls(chip->bus); - if (err < 0) - goto out_free; - err = snd_card_register(chip->card); if (err < 0) goto out_free; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 1359fdd20f02..7586abe91dfb 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -497,16 +497,6 @@ static int hda_tegra_probe(struct platform_device *pdev) if (err < 0) goto out_free;
- /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = snd_hda_build_controls(chip->bus); - if (err < 0) - goto out_free; - err = snd_card_register(chip->card); if (err < 0) goto out_free;
Since we have a full safe control of codec driver bind/unbind, we can lower the safety bar -- the module refcount from the controller driver. This have always annoyed some people because it interferes rmmod in the order of lsmod (due to the reversed dependency).
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_bind.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index ba1adbf0f85f..529aec82d3b2 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -91,7 +91,6 @@ static int codec_refresh_name(struct hda_codec *codec, const char *name) 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)) @@ -101,35 +100,27 @@ static int hda_codec_driver_probe(struct device *dev) if (err < 0) goto error;
- if (!try_module_get(owner)) { - err = -EINVAL; - goto error; - } - err = snd_hda_regmap_init(codec); if (err < 0) goto error; err = codec->preset->patch(codec); if (err < 0) - goto error_module; + goto error;
err = snd_hda_codec_build_pcms(codec); if (err < 0) - goto error_module; + goto error; err = snd_hda_codec_build_controls(codec); if (err < 0) - goto error_module; + goto error; if (codec->card->registered) { err = snd_card_register(codec->card); if (err < 0) - goto error_module; + goto error; }
return 0;
- error_module: - module_put(owner); - error: snd_hda_codec_cleanup_for_unbind(codec); snd_hda_regmap_exit(codec); @@ -144,7 +135,6 @@ static int hda_codec_driver_remove(struct device *dev) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); snd_hda_regmap_exit(codec); - module_put(dev->driver->owner); return 0; }
participants (1)
-
Takashi Iwai