ALSA: hda - Allocate hda_pcm objects dynamically

So far, the hda_codec object kept the hda_pcm list in an array, and
the codec driver was expected to assign the array.  However, this
makes the object life cycle management harder, because the assigned
array is freed at the codec driver detach while it might be still
accessed by the opened streams.

In this patch, we allocate each hda_pcm object dynamically and manage
it as a linked list.  Each object has a kref refcount, and both the
codec driver binder and the PCM open/close touches it, so that the
object won't be freed while in use.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2015-02-27 17:43:19 +01:00
parent f4de8fe6cf
commit bbbc7e8502
10 changed files with 145 additions and 103 deletions

View File

@ -1116,6 +1116,60 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
return p; return p;
} }
/*
* PCM device
*/
static void release_pcm(struct kref *kref)
{
struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
if (pcm->pcm)
snd_device_free(pcm->codec->card, pcm->pcm);
clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
kfree(pcm->name);
kfree(pcm);
}
void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
{
kref_put(&pcm->kref, release_pcm);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
const char *fmt, ...)
{
struct hda_pcm *pcm;
va_list args;
va_start(args, fmt);
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (!pcm)
return NULL;
pcm->codec = codec;
kref_init(&pcm->kref);
pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
if (!pcm->name) {
kfree(pcm);
return NULL;
}
list_add_tail(&pcm->list, &codec->pcm_list_head);
return pcm;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
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);
snd_hda_codec_pcm_put(pcm);
}
}
/* /*
* codec destructor * codec destructor
*/ */
@ -1124,6 +1178,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
if (!codec) if (!codec)
return; return;
cancel_delayed_work_sync(&codec->jackpoll_work); cancel_delayed_work_sync(&codec->jackpoll_work);
codec_release_pcms(codec);
if (device_is_registered(hda_codec_dev(codec))) if (device_is_registered(hda_codec_dev(codec)))
device_del(hda_codec_dev(codec)); device_del(hda_codec_dev(codec));
snd_hda_jack_tbl_clear(codec); snd_hda_jack_tbl_clear(codec);
@ -1251,6 +1306,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
INIT_LIST_HEAD(&codec->conn_list); INIT_LIST_HEAD(&codec->conn_list);
INIT_LIST_HEAD(&codec->pcm_list_head);
INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
codec->depop_delay = -1; codec->depop_delay = -1;
@ -2370,9 +2426,8 @@ int snd_hda_lock_devices(struct hda_bus *bus)
goto err_clear; goto err_clear;
list_for_each_entry(codec, &bus->codec_list, list) { list_for_each_entry(codec, &bus->codec_list, list) {
int pcm; struct hda_pcm *cpcm;
for (pcm = 0; pcm < codec->num_pcms; pcm++) { list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
if (!cpcm->pcm) if (!cpcm->pcm)
continue; continue;
if (cpcm->pcm->streams[0].substream_opened || if (cpcm->pcm->streams[0].substream_opened ||
@ -2419,8 +2474,6 @@ EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
int snd_hda_codec_reset(struct hda_codec *codec) int snd_hda_codec_reset(struct hda_codec *codec)
{ {
struct hda_bus *bus = codec->bus; struct hda_bus *bus = codec->bus;
struct snd_card *card = codec->card;
int i;
if (snd_hda_lock_devices(bus) < 0) if (snd_hda_lock_devices(bus) < 0)
return -EBUSY; return -EBUSY;
@ -2429,14 +2482,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
cancel_delayed_work_sync(&codec->jackpoll_work); cancel_delayed_work_sync(&codec->jackpoll_work);
flush_workqueue(bus->workq); flush_workqueue(bus->workq);
snd_hda_ctls_clear(codec); snd_hda_ctls_clear(codec);
/* release PCMs */ codec_release_pcms(codec);
for (i = 0; i < codec->num_pcms; i++) {
if (codec->pcm_info[i].pcm) {
snd_device_free(card, codec->pcm_info[i].pcm);
clear_bit(codec->pcm_info[i].device,
bus->pcm_dev_bits);
}
}
snd_hda_detach_beep_device(codec); snd_hda_detach_beep_device(codec);
if (device_is_registered(hda_codec_dev(codec))) if (device_is_registered(hda_codec_dev(codec)))
device_del(hda_codec_dev(codec)); device_del(hda_codec_dev(codec));
@ -2454,8 +2500,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
snd_array_free(&codec->cvt_setups); snd_array_free(&codec->cvt_setups);
snd_array_free(&codec->spdif_out); snd_array_free(&codec->spdif_out);
snd_array_free(&codec->verbs); snd_array_free(&codec->verbs);
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL; codec->preset = NULL;
codec->slave_dig_outs = NULL; codec->slave_dig_outs = NULL;
codec->spdif_status_reset = 0; codec->spdif_status_reset = 0;
@ -3952,12 +3996,12 @@ static void hda_call_codec_resume(struct hda_codec *codec)
static int hda_codec_runtime_suspend(struct device *dev) static int hda_codec_runtime_suspend(struct device *dev)
{ {
struct hda_codec *codec = dev_to_hda_codec(dev); struct hda_codec *codec = dev_to_hda_codec(dev);
struct hda_pcm *pcm;
unsigned int state; unsigned int state;
int i;
cancel_delayed_work_sync(&codec->jackpoll_work); cancel_delayed_work_sync(&codec->jackpoll_work);
for (i = 0; i < codec->num_pcms; i++) list_for_each_entry(pcm, &codec->pcm_list_head, list)
snd_pcm_suspend_all(codec->pcm_info[i].pcm); snd_pcm_suspend_all(pcm->pcm);
state = hda_call_codec_suspend(codec); state = hda_call_codec_suspend(codec);
if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK)) if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK))
clear_bit(codec->addr, &codec->bus->codec_powered); clear_bit(codec->addr, &codec->bus->codec_powered);
@ -4018,22 +4062,21 @@ EXPORT_SYMBOL_GPL(snd_hda_build_controls);
*/ */
static int add_std_chmaps(struct hda_codec *codec) static int add_std_chmaps(struct hda_codec *codec)
{ {
int i, str, err; struct hda_pcm *pcm;
int str, err;
for (i = 0; i < codec->num_pcms; i++) { list_for_each_entry(pcm, &codec->pcm_list_head, list) {
for (str = 0; str < 2; str++) { for (str = 0; str < 2; str++) {
struct snd_pcm *pcm = codec->pcm_info[i].pcm; struct hda_pcm_stream *hinfo = &pcm->stream[str];
struct hda_pcm_stream *hinfo =
&codec->pcm_info[i].stream[str];
struct snd_pcm_chmap *chmap; struct snd_pcm_chmap *chmap;
const struct snd_pcm_chmap_elem *elem; const struct snd_pcm_chmap_elem *elem;
if (codec->pcm_info[i].own_chmap) if (pcm->own_chmap)
continue; continue;
if (!pcm || !hinfo->substreams) if (!pcm || !hinfo->substreams)
continue; continue;
elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
err = snd_pcm_add_chmap_ctls(pcm, str, elem, err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
hinfo->channels_max, hinfo->channels_max,
0, &chmap); 0, &chmap);
if (err < 0) if (err < 0)
@ -4564,10 +4607,10 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
/* call build_pcms ops of the given codec and set up the default parameters */ /* call build_pcms ops of the given codec and set up the default parameters */
int snd_hda_codec_parse_pcms(struct hda_codec *codec) int snd_hda_codec_parse_pcms(struct hda_codec *codec)
{ {
unsigned int pcm; struct hda_pcm *cpcm;
int err; int err;
if (codec->num_pcms) if (!list_empty(&codec->pcm_list_head))
return 0; /* already parsed */ return 0; /* already parsed */
if (!codec->patch_ops.build_pcms) if (!codec->patch_ops.build_pcms)
@ -4580,8 +4623,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
return err; return err;
} }
for (pcm = 0; pcm < codec->num_pcms; pcm++) { list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
int stream; int stream;
for (stream = 0; stream < 2; stream++) { for (stream = 0; stream < 2; stream++) {
@ -4589,8 +4631,6 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
if (!info->substreams) if (!info->substreams)
continue; continue;
if (snd_BUG_ON(!cpcm->name))
return -EINVAL;
err = set_pcm_default_values(codec, info); err = set_pcm_default_values(codec, info);
if (err < 0) { if (err < 0) {
codec_warn(codec, codec_warn(codec,
@ -4608,7 +4648,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
int snd_hda_codec_build_pcms(struct hda_codec *codec) int snd_hda_codec_build_pcms(struct hda_codec *codec)
{ {
struct hda_bus *bus = codec->bus; struct hda_bus *bus = codec->bus;
unsigned int pcm; struct hda_pcm *cpcm;
int dev, err; int dev, err;
if (snd_BUG_ON(!bus->ops.attach_pcm)) if (snd_BUG_ON(!bus->ops.attach_pcm))
@ -4621,9 +4661,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
} }
/* attach a new PCM streams */ /* attach a new PCM streams */
for (pcm = 0; pcm < codec->num_pcms; pcm++) { list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
if (cpcm->pcm) if (cpcm->pcm)
continue; /* already attached */ continue; /* already attached */
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
@ -4651,11 +4689,9 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
* *
* Create PCM information for each codec included in the bus. * Create PCM information for each codec included in the bus.
* *
* The build_pcms codec patch is requested to set up codec->num_pcms and * The build_pcms codec patch is requested to create and assign new
* codec->pcm_info properly. The array is referred by the top-level driver * hda_pcm objects. The codec is responsible to call snd_hda_codec_pcm_new()
* to create its PCM instances. * and fills the fields. Later they are instantiated by this function.
* The allocated codec->pcm_info should be released in codec->patch_ops.free
* callback.
* *
* At least, substreams, channels_min and channels_max must be filled for * At least, substreams, channels_min and channels_max must be filled for
* each stream. substreams = 0 indicates that the stream doesn't exist. * each stream. substreams = 0 indicates that the stream doesn't exist.

View File

@ -21,6 +21,7 @@
#ifndef __SOUND_HDA_CODEC_H #ifndef __SOUND_HDA_CODEC_H
#define __SOUND_HDA_CODEC_H #define __SOUND_HDA_CODEC_H
#include <linux/kref.h>
#include <sound/info.h> #include <sound/info.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/pcm.h> #include <sound/pcm.h>
@ -268,6 +269,10 @@ struct hda_pcm {
int device; /* device number to assign */ int device; /* device number to assign */
struct snd_pcm *pcm; /* assigned PCM instance */ struct snd_pcm *pcm; /* assigned PCM instance */
bool own_chmap; /* codec driver provides own channel maps */ bool own_chmap; /* codec driver provides own channel maps */
/* private: */
struct hda_codec *codec;
struct kref kref;
struct list_head list;
}; };
/* codec information */ /* codec information */
@ -301,8 +306,7 @@ struct hda_codec {
struct hda_codec_ops patch_ops; struct hda_codec_ops patch_ops;
/* PCM to create, set by patch_ops.build_pcms callback */ /* PCM to create, set by patch_ops.build_pcms callback */
unsigned int num_pcms; struct list_head pcm_list_head;
struct hda_pcm *pcm_info;
/* codec specific info */ /* codec specific info */
void *spec; void *spec;
@ -521,6 +525,16 @@ int snd_hda_build_pcms(struct hda_bus *bus);
int snd_hda_codec_parse_pcms(struct hda_codec *codec); int snd_hda_codec_parse_pcms(struct hda_codec *codec);
int snd_hda_codec_build_pcms(struct hda_codec *codec); int snd_hda_codec_build_pcms(struct hda_codec *codec);
__printf(2, 3)
struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
const char *fmt, ...);
static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
{
kref_get(&pcm->kref);
}
void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
int snd_hda_codec_prepare(struct hda_codec *codec, int snd_hda_codec_prepare(struct hda_codec *codec,
struct hda_pcm_stream *hinfo, struct hda_pcm_stream *hinfo,
unsigned int stream, unsigned int stream,

View File

@ -4644,7 +4644,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
err = snd_hda_create_dig_out_ctls(codec, err = snd_hda_create_dig_out_ctls(codec,
spec->multiout.dig_out_nid, spec->multiout.dig_out_nid,
spec->multiout.dig_out_nid, spec->multiout.dig_out_nid,
spec->pcm_rec[1].pcm_type); spec->pcm_rec[1]->pcm_type);
if (err < 0) if (err < 0)
return err; return err;
if (!spec->no_analog) { if (!spec->no_analog) {
@ -5115,20 +5115,20 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
int snd_hda_gen_build_pcms(struct hda_codec *codec) int snd_hda_gen_build_pcms(struct hda_codec *codec)
{ {
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec; struct hda_pcm *info;
const struct hda_pcm_stream *p; const struct hda_pcm_stream *p;
bool have_multi_adcs; bool have_multi_adcs;
codec->num_pcms = 1;
codec->pcm_info = info;
if (spec->no_analog) if (spec->no_analog)
goto skip_analog; goto skip_analog;
fill_pcm_stream_name(spec->stream_name_analog, fill_pcm_stream_name(spec->stream_name_analog,
sizeof(spec->stream_name_analog), sizeof(spec->stream_name_analog),
" Analog", codec->chip_name); " Analog", codec->chip_name);
info->name = spec->stream_name_analog; info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
if (!info)
return -ENOMEM;
spec->pcm_rec[0] = info;
if (spec->multiout.num_dacs > 0) { if (spec->multiout.num_dacs > 0) {
p = spec->stream_analog_playback; p = spec->stream_analog_playback;
@ -5161,10 +5161,12 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
fill_pcm_stream_name(spec->stream_name_digital, fill_pcm_stream_name(spec->stream_name_digital,
sizeof(spec->stream_name_digital), sizeof(spec->stream_name_digital),
" Digital", codec->chip_name); " Digital", codec->chip_name);
codec->num_pcms = 2; info = snd_hda_codec_pcm_new(codec, "%s",
spec->stream_name_digital);
if (!info)
return -ENOMEM;
codec->slave_dig_outs = spec->multiout.slave_dig_outs; codec->slave_dig_outs = spec->multiout.slave_dig_outs;
info = spec->pcm_rec + 1; spec->pcm_rec[1] = info;
info->name = spec->stream_name_digital;
if (spec->dig_out_type) if (spec->dig_out_type)
info->pcm_type = spec->dig_out_type; info->pcm_type = spec->dig_out_type;
else else
@ -5198,9 +5200,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
fill_pcm_stream_name(spec->stream_name_alt_analog, fill_pcm_stream_name(spec->stream_name_alt_analog,
sizeof(spec->stream_name_alt_analog), sizeof(spec->stream_name_alt_analog),
" Alt Analog", codec->chip_name); " Alt Analog", codec->chip_name);
codec->num_pcms = 3; info = snd_hda_codec_pcm_new(codec, "%s",
info = spec->pcm_rec + 2; spec->stream_name_alt_analog);
info->name = spec->stream_name_alt_analog; if (!info)
return -ENOMEM;
spec->pcm_rec[2] = info;
if (spec->alt_dac_nid) { if (spec->alt_dac_nid) {
p = spec->stream_analog_alt_playback; p = spec->stream_analog_alt_playback;
if (!p) if (!p)

View File

@ -144,7 +144,7 @@ struct hda_gen_spec {
int const_channel_count; /* channel count for all */ int const_channel_count; /* channel count for all */
/* PCM information */ /* PCM information */
struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */
/* dynamic controls, init_verbs and input_mux */ /* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg; struct auto_pin_cfg autocfg;

View File

@ -99,10 +99,10 @@ static void print_nid_array(struct snd_info_buffer *buffer,
static void print_nid_pcms(struct snd_info_buffer *buffer, static void print_nid_pcms(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid) struct hda_codec *codec, hda_nid_t nid)
{ {
int pcm, type; int type;
struct hda_pcm *cpcm; struct hda_pcm *cpcm;
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
cpcm = &codec->pcm_info[pcm]; list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
for (type = 0; type < 2; type++) { for (type = 0; type < 2; type++) {
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
continue; continue;

View File

@ -719,7 +719,6 @@ struct ca0132_spec {
unsigned int num_inputs; unsigned int num_inputs;
hda_nid_t shared_mic_nid; hda_nid_t shared_mic_nid;
hda_nid_t shared_out_nid; hda_nid_t shared_out_nid;
struct hda_pcm pcm_rec[5]; /* PCM information */
/* chip access */ /* chip access */
struct mutex chipio_mutex; /* chip access mutex */ struct mutex chipio_mutex; /* chip access mutex */
@ -4036,12 +4035,11 @@ static struct hda_pcm_stream ca0132_pcm_digital_capture = {
static int ca0132_build_pcms(struct hda_codec *codec) static int ca0132_build_pcms(struct hda_codec *codec)
{ {
struct ca0132_spec *spec = codec->spec; struct ca0132_spec *spec = codec->spec;
struct hda_pcm *info = spec->pcm_rec; struct hda_pcm *info;
codec->pcm_info = info; info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
codec->num_pcms = 0; if (!info)
return -ENOMEM;
info->name = "CA0132 Analog";
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
@ -4049,27 +4047,27 @@ static int ca0132_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
codec->num_pcms++;
info++; info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
info->name = "CA0132 Analog Mic-In2"; if (!info)
return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
codec->num_pcms++;
info++; info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
info->name = "CA0132 What U Hear"; if (!info)
return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2]; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
codec->num_pcms++;
if (!spec->dig_out && !spec->dig_in) if (!spec->dig_out && !spec->dig_in)
return 0; return 0;
info++; info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
info->name = "CA0132 Digital"; if (!info)
return -ENOMEM;
info->pcm_type = HDA_PCM_TYPE_SPDIF; info->pcm_type = HDA_PCM_TYPE_SPDIF;
if (spec->dig_out) { if (spec->dig_out) {
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
@ -4081,7 +4079,6 @@ static int ca0132_build_pcms(struct hda_codec *codec)
ca0132_pcm_digital_capture; ca0132_pcm_digital_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
} }
codec->num_pcms++;
return 0; return 0;
} }

View File

@ -86,7 +86,6 @@ struct hdmi_spec_per_pin {
bool non_pcm; bool non_pcm;
bool chmap_set; /* channel-map override by ALSA API? */ bool chmap_set; /* channel-map override by ALSA API? */
unsigned char chmap[8]; /* ALSA API channel-map */ unsigned char chmap[8]; /* ALSA API channel-map */
char pcm_name[8]; /* filled in build_pcm callbacks */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
struct snd_info_entry *proc_entry; struct snd_info_entry *proc_entry;
#endif #endif
@ -132,7 +131,7 @@ struct hdmi_spec {
int num_pins; int num_pins;
struct snd_array pins; /* struct hdmi_spec_per_pin */ struct snd_array pins; /* struct hdmi_spec_per_pin */
struct snd_array pcm_rec; /* struct hda_pcm */ struct hda_pcm *pcm_rec[16];
unsigned int channels_max; /* max over all cvts */ unsigned int channels_max; /* max over all cvts */
struct hdmi_eld temp_eld; struct hdmi_eld temp_eld;
@ -355,8 +354,7 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
#define get_cvt(spec, idx) \ #define get_cvt(spec, idx) \
((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
#define get_pcm_rec(spec, idx) \ #define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx))
static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
{ {
@ -2056,11 +2054,10 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
struct hdmi_spec_per_pin *per_pin; struct hdmi_spec_per_pin *per_pin;
per_pin = get_pin(spec, pin_idx); per_pin = get_pin(spec, pin_idx);
sprintf(per_pin->pcm_name, "HDMI %d", pin_idx); info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
info = snd_array_new(&spec->pcm_rec);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
info->name = per_pin->pcm_name; spec->pcm_rec[pin_idx] = info;
info->pcm_type = HDA_PCM_TYPE_HDMI; info->pcm_type = HDA_PCM_TYPE_HDMI;
info->own_chmap = true; info->own_chmap = true;
@ -2070,9 +2067,6 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
/* other pstr fields are set in open */ /* other pstr fields are set in open */
} }
codec->num_pcms = spec->num_pins;
codec->pcm_info = spec->pcm_rec.list;
return 0; return 0;
} }
@ -2125,13 +2119,15 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
/* add channel maps */ /* add channel maps */
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hda_pcm *pcm;
struct snd_pcm_chmap *chmap; struct snd_pcm_chmap *chmap;
struct snd_kcontrol *kctl; struct snd_kcontrol *kctl;
int i; int i;
if (!codec->pcm_info[pin_idx].pcm) pcm = spec->pcm_rec[pin_idx];
if (!pcm || !pcm->pcm)
break; break;
err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, err = snd_pcm_add_chmap_ctls(pcm->pcm,
SNDRV_PCM_STREAM_PLAYBACK, SNDRV_PCM_STREAM_PLAYBACK,
NULL, 0, pin_idx, &chmap); NULL, 0, pin_idx, &chmap);
if (err < 0) if (err < 0)
@ -2186,14 +2182,12 @@ static void hdmi_array_init(struct hdmi_spec *spec, int nums)
{ {
snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums);
} }
static void hdmi_array_free(struct hdmi_spec *spec) static void hdmi_array_free(struct hdmi_spec *spec)
{ {
snd_array_free(&spec->pins); snd_array_free(&spec->pins);
snd_array_free(&spec->cvts); snd_array_free(&spec->cvts);
snd_array_free(&spec->pcm_rec);
} }
static void generic_hdmi_free(struct hda_codec *codec) static void generic_hdmi_free(struct hda_codec *codec)
@ -2381,11 +2375,10 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
chans = get_wcaps(codec, per_cvt->cvt_nid); chans = get_wcaps(codec, per_cvt->cvt_nid);
chans = get_wcaps_channels(chans); chans = get_wcaps_channels(chans);
info = snd_array_new(&spec->pcm_rec); info = snd_hda_codec_pcm_new(codec, "HDMI 0");
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
info->name = get_pin(spec, 0)->pcm_name; spec->pcm_rec[0] = info;
sprintf(info->name, "HDMI 0");
info->pcm_type = HDA_PCM_TYPE_HDMI; info->pcm_type = HDA_PCM_TYPE_HDMI;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
*pstr = spec->pcm_playback; *pstr = spec->pcm_playback;
@ -2393,9 +2386,6 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
if (pstr->channels_max <= 2 && chans && chans <= 16) if (pstr->channels_max <= 2 && chans && chans <= 16)
pstr->channels_max = chans; pstr->channels_max = chans;
codec->num_pcms = 1;
codec->pcm_info = info;
return 0; return 0;
} }

View File

@ -5843,7 +5843,7 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec,
{ {
if (action == HDA_FIXUP_ACT_BUILD) { if (action == HDA_FIXUP_ACT_BUILD) {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps; spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps;
} }
} }

View File

@ -83,7 +83,6 @@
struct si3054_spec { struct si3054_spec {
unsigned international; unsigned international;
struct hda_pcm pcm;
}; };
@ -199,11 +198,11 @@ static const struct hda_pcm_stream si3054_pcm = {
static int si3054_build_pcms(struct hda_codec *codec) static int si3054_build_pcms(struct hda_codec *codec)
{ {
struct si3054_spec *spec = codec->spec; struct hda_pcm *info;
struct hda_pcm *info = &spec->pcm;
codec->num_pcms = 1; info = snd_hda_codec_pcm_new(codec, "Si3054 Modem");
codec->pcm_info = info; if (!info)
info->name = "Si3054 Modem"; return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg;

View File

@ -683,8 +683,10 @@ static int vt1708_build_pcms(struct hda_codec *codec)
* 24bit samples are used. Until any workaround is found, * 24bit samples are used. Until any workaround is found,
* disable the 24bit format, so far. * disable the 24bit format, so far.
*/ */
for (i = 0; i < codec->num_pcms; i++) { for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
struct hda_pcm *info = &spec->gen.pcm_rec[i]; struct hda_pcm *info = spec->gen.pcm_rec[i];
if (!info)
continue;
if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
info->pcm_type != HDA_PCM_TYPE_AUDIO) info->pcm_type != HDA_PCM_TYPE_AUDIO)
continue; continue;