diff options
Diffstat (limited to 'sound/soc/soc-topology.c')
-rw-r--r-- | sound/soc/soc-topology.c | 197 |
1 files changed, 115 insertions, 82 deletions
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index d096068..69d01cd 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -33,6 +33,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/soc-topology.h> +#include <sound/tlv.h> /* * We make several passes over the data (since it wont necessarily be ordered) @@ -44,12 +45,12 @@ #define SOC_TPLG_PASS_VENDOR 1 #define SOC_TPLG_PASS_MIXER 2 #define SOC_TPLG_PASS_WIDGET 3 -#define SOC_TPLG_PASS_GRAPH 4 -#define SOC_TPLG_PASS_PINS 5 -#define SOC_TPLG_PASS_PCM_DAI 6 +#define SOC_TPLG_PASS_PCM_DAI 4 +#define SOC_TPLG_PASS_GRAPH 5 +#define SOC_TPLG_PASS_PINS 6 #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST -#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PCM_DAI +#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS struct soc_tplg { const struct firmware *fw; @@ -65,10 +66,14 @@ struct soc_tplg { u32 index; /* current block index */ u32 req_index; /* required index, only loaded/free matching blocks */ - /* kcontrol operations */ + /* vendor specific kcontrol operations */ const struct snd_soc_tplg_kcontrol_ops *io_ops; int io_ops_count; + /* vendor specific bytes ext handlers, for TLV bytes controls */ + const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops; + int bytes_ext_ops_count; + /* optional fw loading callbacks to component drivers */ struct snd_soc_tplg_ops *ops; }; @@ -144,7 +149,7 @@ static const struct snd_soc_tplg_kcontrol_ops io_ops[] = { {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe, snd_soc_put_strobe, NULL}, {SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw, - snd_soc_dapm_put_volsw, NULL}, + snd_soc_dapm_put_volsw, snd_soc_info_volsw}, {SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double, snd_soc_dapm_put_enum_double, snd_soc_info_enum_double}, {SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double, @@ -507,38 +512,74 @@ static void remove_pcm_dai(struct snd_soc_component *comp, /* bind a kcontrol to it's IO handlers */ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, struct snd_kcontrol_new *k, - const struct snd_soc_tplg_kcontrol_ops *ops, int num_ops, - const struct snd_soc_tplg_kcontrol_ops *bops, int num_bops) + const struct soc_tplg *tplg) { - int i; + const struct snd_soc_tplg_kcontrol_ops *ops; + const struct snd_soc_tplg_bytes_ext_ops *ext_ops; + int num_ops, i; + + if (hdr->ops.info == SND_SOC_TPLG_CTL_BYTES + && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER + && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE + && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + struct soc_bytes_ext *sbe; + struct snd_soc_tplg_bytes_control *be; + + sbe = (struct soc_bytes_ext *)k->private_value; + be = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); + + /* TLV bytes controls need standard kcontrol info handler, + * TLV callback and extended put/get handlers. + */ + k->info = snd_soc_bytes_info; + k->tlv.c = snd_soc_bytes_tlv_callback; + + ext_ops = tplg->bytes_ext_ops; + num_ops = tplg->bytes_ext_ops_count; + for (i = 0; i < num_ops; i++) { + if (!sbe->put && ext_ops[i].id == be->ext_ops.put) + sbe->put = ext_ops[i].put; + if (!sbe->get && ext_ops[i].id == be->ext_ops.get) + sbe->get = ext_ops[i].get; + } + + if (sbe->put && sbe->get) + return 0; + else + return -EINVAL; + } - /* try and map standard kcontrols handler first */ + /* try and map vendor specific kcontrol handlers first */ + ops = tplg->io_ops; + num_ops = tplg->io_ops_count; for (i = 0; i < num_ops; i++) { - if (ops[i].id == hdr->ops.put) + if (k->put == NULL && ops[i].id == hdr->ops.put) k->put = ops[i].put; - if (ops[i].id == hdr->ops.get) + if (k->get == NULL && ops[i].id == hdr->ops.get) k->get = ops[i].get; - if (ops[i].id == hdr->ops.info) + if (k->info == NULL && ops[i].id == hdr->ops.info) k->info = ops[i].info; } - /* standard handlers found ? */ + /* vendor specific handlers found ? */ if (k->put && k->get && k->info) return 0; - /* none found so try bespoke handlers */ - for (i = 0; i < num_bops; i++) { + /* none found so try standard kcontrol handlers */ + ops = io_ops; + num_ops = ARRAY_SIZE(io_ops); + for (i = 0; i < num_ops; i++) { - if (k->put == NULL && bops[i].id == hdr->ops.put) - k->put = bops[i].put; - if (k->get == NULL && bops[i].id == hdr->ops.get) - k->get = bops[i].get; + if (k->put == NULL && ops[i].id == hdr->ops.put) + k->put = ops[i].put; + if (k->get == NULL && ops[i].id == hdr->ops.get) + k->get = ops[i].get; if (k->info == NULL && ops[i].id == hdr->ops.info) - k->info = bops[i].info; + k->info = ops[i].info; } - /* bespoke handlers found ? */ + /* standard handlers found ? */ if (k->put && k->get && k->info) return 0; @@ -579,29 +620,49 @@ static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, return 0; } + +static int soc_tplg_create_tlv_db_scale(struct soc_tplg *tplg, + struct snd_kcontrol_new *kc, struct snd_soc_tplg_tlv_dbscale *scale) +{ + unsigned int item_len = 2 * sizeof(unsigned int); + unsigned int *p; + + p = kzalloc(item_len + 2 * sizeof(unsigned int), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p[0] = SNDRV_CTL_TLVT_DB_SCALE; + p[1] = item_len; + p[2] = scale->min; + p[3] = (scale->step & TLV_DB_SCALE_MASK) + | (scale->mute ? TLV_DB_SCALE_MUTE : 0); + + kc->tlv.p = (void *)p; + return 0; +} + static int soc_tplg_create_tlv(struct soc_tplg *tplg, - struct snd_kcontrol_new *kc, u32 tlv_size) + struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc) { struct snd_soc_tplg_ctl_tlv *tplg_tlv; - struct snd_ctl_tlv *tlv; - if (tlv_size == 0) + if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) return 0; - tplg_tlv = (struct snd_soc_tplg_ctl_tlv *) tplg->pos; - tplg->pos += tlv_size; - - tlv = kzalloc(sizeof(*tlv) + tlv_size, GFP_KERNEL); - if (tlv == NULL) - return -ENOMEM; - - dev_dbg(tplg->dev, " created TLV type %d size %d bytes\n", - tplg_tlv->numid, tplg_tlv->size); + if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { + tplg_tlv = &tc->tlv; + switch (tplg_tlv->type) { + case SNDRV_CTL_TLVT_DB_SCALE: + return soc_tplg_create_tlv_db_scale(tplg, kc, + &tplg_tlv->scale); - tlv->numid = tplg_tlv->numid; - tlv->length = tplg_tlv->size; - memcpy(tlv->tlv, tplg_tlv + 1, tplg_tlv->size); - kc->tlv.p = (void *)tlv; + /* TODO: add support for other TLV types */ + default: + dev_dbg(tplg->dev, "Unsupported TLV type %d\n", + tplg_tlv->type); + return -EINVAL; + } + } return 0; } @@ -659,8 +720,7 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, INIT_LIST_HEAD(&sbe->dobj.list); /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); if (err) { soc_control_err(tplg, &be->hdr, be->hdr.name); kfree(sbe); @@ -754,8 +814,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); if (err) { soc_control_err(tplg, &mc->hdr, mc->hdr.name); kfree(sm); @@ -773,7 +832,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, } /* create any TLV data */ - soc_tplg_create_tlv(tplg, &kc, mc->hdr.tlv_size); + soc_tplg_create_tlv(tplg, &kc, &mc->hdr); /* register control here */ err = soc_tplg_add_kcontrol(tplg, &kc, @@ -832,12 +891,12 @@ static int soc_tplg_denum_create_values(struct soc_enum *se, if (ec->items > sizeof(*ec->values)) return -EINVAL; - se->dobj.control.dvalues = - kmalloc(ec->items * sizeof(u32), GFP_KERNEL); + se->dobj.control.dvalues = kmemdup(ec->values, + ec->items * sizeof(u32), + GFP_KERNEL); if (!se->dobj.control.dvalues) return -ENOMEM; - memcpy(se->dobj.control.dvalues, ec->values, ec->items * sizeof(u32)); return 0; } @@ -927,8 +986,7 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, } /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); if (err) { soc_control_err(tplg, &ec->hdr, ec->hdr.name); kfree(se); @@ -1070,7 +1128,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( struct snd_soc_tplg_mixer_control *mc; int i, err; - kc = kzalloc(sizeof(*kc) * num_kcontrols, GFP_KERNEL); + kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); if (kc == NULL) return NULL; @@ -1114,8 +1172,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( INIT_LIST_HEAD(&sm->dobj.list); /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], tplg); if (err) { soc_control_err(tplg, &mc->hdr, mc->hdr.name); kfree(sm); @@ -1212,8 +1269,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( } /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); if (err) { soc_control_err(tplg, &ec->hdr, ec->hdr.name); goto err_se; @@ -1251,7 +1307,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( struct snd_kcontrol_new *kc; int i, err; - kc = kzalloc(sizeof(*kc) * count, GFP_KERNEL); + kc = kcalloc(count, sizeof(*kc), GFP_KERNEL); if (!kc) return NULL; @@ -1274,7 +1330,6 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( "ASoC: adding bytes kcontrol %s with access 0x%x\n", be->hdr.name, be->hdr.access); - memset(kc, 0, sizeof(*kc)); kc[i].name = be->hdr.name; kc[i].private_value = (long)sbe; kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -1284,9 +1339,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( INIT_LIST_HEAD(&sbe->dobj.list); /* map standard io handlers and check for external handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], io_ops, - ARRAY_SIZE(io_ops), tplg->io_ops, - tplg->io_ops_count); + err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], tplg); if (err) { soc_control_err(tplg, &be->hdr, be->hdr.name); kfree(sbe); @@ -1351,6 +1404,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, template.reg = w->reg; template.shift = w->shift; template.mask = w->mask; + template.subseq = w->subseq; template.on_val = w->invert ? 0 : 1; template.off_val = w->invert ? 1 : 0; template.ignore_suspend = w->ignore_suspend; @@ -1713,6 +1767,8 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp, tplg.req_index = id; tplg.io_ops = ops->io_ops; tplg.io_ops_count = ops->io_ops_count; + tplg.bytes_ext_ops = ops->bytes_ext_ops; + tplg.bytes_ext_ops_count = ops->bytes_ext_ops_count; return soc_tplg_load(&tplg); } @@ -1734,7 +1790,6 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, u32 index) { struct snd_soc_dapm_widget *w, *next_w; - struct snd_soc_dapm_path *p, *next_p; list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { @@ -1746,31 +1801,9 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, if (w->dobj.index != index && w->dobj.index != SND_SOC_TPLG_INDEX_ALL) continue; - - list_del(&w->list); - - /* - * remove source and sink paths associated to this widget. - * While removing the path, remove reference to it from both - * source and sink widgets so that path is removed only once. - */ - list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { - list_del(&p->list_sink); - list_del(&p->list_source); - list_del(&p->list); - kfree(p); - } - list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { - list_del(&p->list_sink); - list_del(&p->list_source); - list_del(&p->list); - kfree(p); - } /* check and free and dynamic widget kcontrols */ snd_soc_tplg_widget_remove(w); - kfree(w->kcontrols); - kfree(w->name); - kfree(w); + snd_soc_dapm_free_widget(w); } } EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); |