From: Takashi Iwai Date: Thu, 27 Nov 2008 13:17:01 +0000 (+0100) Subject: ALSA: hda - Fix PCM reconfigure X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=529bd6c4a63f8468fd66f63fdc22d7070439b3cd;p=linux-beck.git ALSA: hda - Fix PCM reconfigure The reconfiguration of PCM affected all PCM streams on the bus, but this this should be done rather only for the target codec. This patch does the following: - introduce bitmap indicating the PCM device usages on a hda_bus - refactor the PCM build functions - fix __devinit prefix in some fucntions - add a proper ifdef around HDA-reconfig-specific functions Signed-off-by: Takashi Iwai --- diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 052a898a63ba..1cb85b73e19b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1206,6 +1206,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl) return 0; } +#ifdef CONFIG_SND_HDA_RECONFIG /* Clear all controls assigned to the given codec */ void snd_hda_ctls_clear(struct hda_codec *codec) { @@ -1227,9 +1228,12 @@ void snd_hda_codec_reset(struct hda_codec *codec) snd_hda_ctls_clear(codec); /* relase PCMs */ for (i = 0; i < codec->num_pcms; i++) { - if (codec->pcm_info[i].pcm) + if (codec->pcm_info[i].pcm) { snd_device_free(codec->bus->card, codec->pcm_info[i].pcm); + clear_bit(codec->pcm_info[i].device, + codec->bus->pcm_dev_bits); + } } if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1240,6 +1244,7 @@ void snd_hda_codec_reset(struct hda_codec *codec) codec->pcm_info = NULL; codec->preset = NULL; } +#endif /* CONFIG_SND_HDA_RECONFIG */ /* create a virtual master control and add slaves */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, @@ -2432,11 +2437,59 @@ static int set_pcm_default_values(struct hda_codec *codec, return 0; } +/* + * get the empty PCM device number to assign + */ +static int get_empty_pcm_device(struct hda_bus *bus, int type) +{ + static const char *dev_name[HDA_PCM_NTYPES] = { + "Audio", "SPDIF", "HDMI", "Modem" + }; + /* starting device index for each PCM type */ + static int dev_idx[HDA_PCM_NTYPES] = { + [HDA_PCM_TYPE_AUDIO] = 0, + [HDA_PCM_TYPE_SPDIF] = 1, + [HDA_PCM_TYPE_HDMI] = 3, + [HDA_PCM_TYPE_MODEM] = 6 + }; + /* normal audio device indices; not linear to keep compatibility */ + static int audio_idx[4] = { 0, 2, 4, 5 }; + int i, dev; + + switch (type) { + case HDA_PCM_TYPE_AUDIO: + for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { + dev = audio_idx[i]; + if (!test_bit(dev, bus->pcm_dev_bits)) + break; + } + if (i >= ARRAY_SIZE(audio_idx)) { + snd_printk(KERN_WARNING "Too many audio devices\n"); + return -EAGAIN; + } + break; + case HDA_PCM_TYPE_SPDIF: + case HDA_PCM_TYPE_HDMI: + case HDA_PCM_TYPE_MODEM: + dev = dev_idx[type]; + if (test_bit(dev, bus->pcm_dev_bits)) { + snd_printk(KERN_WARNING "%s already defined\n", + dev_name[type]); + return -EAGAIN; + } + break; + default: + snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); + return -EINVAL; + } + set_bit(dev, bus->pcm_dev_bits); + return dev; +} + /* * attach a new PCM stream */ -static int __devinit -snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) +static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) { struct hda_bus *bus = codec->bus; struct hda_pcm_stream *info; @@ -2455,6 +2508,39 @@ snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) return bus->ops.attach_pcm(bus, codec, pcm); } +/* assign all PCMs of the given codec */ +int snd_hda_codec_build_pcms(struct hda_codec *codec) +{ + unsigned int pcm; + int err; + + if (!codec->num_pcms) { + if (!codec->patch_ops.build_pcms) + return 0; + err = codec->patch_ops.build_pcms(codec); + if (err < 0) + return err; + } + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + int dev; + + if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) + return 0; /* no substreams assigned */ + + if (!cpcm->pcm) { + dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); + if (dev < 0) + return 0; + cpcm->device = dev; + err = snd_hda_attach_pcm(codec, cpcm); + if (err < 0) + return err; + } + } + return 0; +} + /** * snd_hda_build_pcms - build PCM information * @bus: the BUS @@ -2481,76 +2567,14 @@ snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) * * This function returns 0 if successfull, or a negative error code. */ -int snd_hda_build_pcms(struct hda_bus *bus) +int __devinit snd_hda_build_pcms(struct hda_bus *bus) { - static const char *dev_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" - }; - /* starting device index for each PCM type */ - static int dev_idx[HDA_PCM_NTYPES] = { - [HDA_PCM_TYPE_AUDIO] = 0, - [HDA_PCM_TYPE_SPDIF] = 1, - [HDA_PCM_TYPE_HDMI] = 3, - [HDA_PCM_TYPE_MODEM] = 6 - }; - /* normal audio device indices; not linear to keep compatibility */ - static int audio_idx[4] = { 0, 2, 4, 5 }; struct hda_codec *codec; - int num_devs[HDA_PCM_NTYPES]; - memset(num_devs, 0, sizeof(num_devs)); list_for_each_entry(codec, &bus->codec_list, list) { - unsigned int pcm; - int err; - if (!codec->num_pcms) { - if (!codec->patch_ops.build_pcms) - continue; - err = codec->patch_ops.build_pcms(codec); - if (err < 0) - return err; - } - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - int type = cpcm->pcm_type; - int dev; - - if (!cpcm->stream[0].substreams && - !cpcm->stream[1].substreams) - continue; /* no substreams assigned */ - - switch (type) { - case HDA_PCM_TYPE_AUDIO: - if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { - snd_printk(KERN_WARNING - "Too many audio devices\n"); - continue; - } - dev = audio_idx[num_devs[type]]; - break; - case HDA_PCM_TYPE_SPDIF: - case HDA_PCM_TYPE_HDMI: - case HDA_PCM_TYPE_MODEM: - if (num_devs[type]) { - snd_printk(KERN_WARNING - "%s already defined\n", - dev_name[type]); - continue; - } - dev = dev_idx[type]; - break; - default: - snd_printk(KERN_WARNING - "Invalid PCM type %d\n", type); - continue; - } - num_devs[type]++; - if (!cpcm->pcm) { - cpcm->device = dev; - err = snd_hda_attach_pcm(codec, cpcm); - if (err < 0) - return err; - } - } + int err = snd_hda_codec_build_pcms(codec); + if (err < 0) + return err; } return 0; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 4034625b5997..9fe0b67bb1e4 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -617,6 +617,9 @@ struct hda_bus { struct snd_info_entry *proc; + /* assigned PCMs */ + DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); + /* misc op flags */ unsigned int needs_damn_long_delay :1; unsigned int shutdown :1; /* being unloaded */ @@ -846,6 +849,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_build_pcms(struct hda_codec *codec); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format); diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 5868bbc131cd..173af489322f 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -168,7 +168,7 @@ static int reconfig_codec(struct hda_codec *codec) if (err < 0) return err; /* rebuild PCMs */ - err = snd_hda_build_pcms(codec->bus); + err = snd_hda_codec_build_pcms(codec); if (err < 0) return err; /* rebuild mixers */