aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSubhransu S. Prusty <subhransu.s.prusty@intel.com>2016-02-11 21:16:05 -0500
committerMark Brown <broonie@kernel.org>2016-02-15 15:54:03 -0500
commit79f4e922b54771a4d41b1da67caf109ba92ed95a (patch)
treee586ba0d396c09aac2bc17fd833fce0858f68cf7
parent17a42c4500b05a6af4c32eb8e9cfc44bab945d1f (diff)
ASoC: hdac_hdmi: Create widget/route based on nodes enumerated
In skylake, HDMI codec enumerates 3 pins and 3 cvts. Stream can be routed from any cvt to any pin based on the connection list queried from the pin. This patch removes the static modelling of widget/route and creates it dynamically based on the codec widgets enumerated. Mux widgets are added to represent the map between a cvt and pin. Ideally the mux widgets should be created based on the connection list queried from the pin widget. But due to HW behavior, if an external display is not connected on a port, querying the connection list returns zero. So create mux widgets to map all the cvt to all pins. At runtime, playback support on a pin can be verified based on the connection list query. Few function in driver have additional arguments now to support this. Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/hdac_hdmi.c241
1 files changed, 219 insertions, 22 deletions
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index c85deae68064..6fb44c4cc8c7 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -446,46 +446,241 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
446 return err; 446 return err;
447} 447}
448 448
449static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w, 449static int hdac_hdmi_fill_widget_info(struct device *dev,
450 enum snd_soc_dapm_type id, 450 struct snd_soc_dapm_widget *w,
451 const char *wname, const char *stream) 451 enum snd_soc_dapm_type id, void *priv,
452 const char *wname, const char *stream,
453 struct snd_kcontrol_new *wc, int numkc)
452{ 454{
453 w->id = id; 455 w->id = id;
454 w->name = wname; 456 w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
457 if (!w->name)
458 return -ENOMEM;
459
455 w->sname = stream; 460 w->sname = stream;
456 w->reg = SND_SOC_NOPM; 461 w->reg = SND_SOC_NOPM;
457 w->shift = 0; 462 w->shift = 0;
458 w->kcontrol_news = NULL; 463 w->kcontrol_news = wc;
459 w->num_kcontrols = 0; 464 w->num_kcontrols = numkc;
460 w->priv = NULL; 465 w->priv = priv;
466
467 return 0;
461} 468}
462 469
463static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, 470static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
464 const char *sink, const char *control, const char *src) 471 const char *sink, const char *control, const char *src,
472 int (*handler)(struct snd_soc_dapm_widget *src,
473 struct snd_soc_dapm_widget *sink))
465{ 474{
466 route->sink = sink; 475 route->sink = sink;
467 route->source = src; 476 route->source = src;
468 route->control = control; 477 route->control = control;
469 route->connected = NULL; 478 route->connected = handler;
470} 479}
471 480
472static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm, 481/*
473 struct hdac_hdmi_dai_pin_map *dai_map) 482 * Ideally the Mux inputs should be based on the num_muxs enumerated, but
483 * the display driver seem to be programming the connection list for the pin
484 * widget runtime.
485 *
486 * So programming all the possible inputs for the mux, the user has to take
487 * care of selecting the right one and leaving all other inputs selected to
488 * "NONE"
489 */
490static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
491 struct hdac_hdmi_pin *pin,
492 struct snd_soc_dapm_widget *widget,
493 const char *widget_name)
494{
495 struct hdac_hdmi_priv *hdmi = edev->private_data;
496 struct snd_kcontrol_new *kc;
497 struct hdac_hdmi_cvt *cvt;
498 struct soc_enum *se;
499 char kc_name[NAME_SIZE];
500 char mux_items[NAME_SIZE];
501 /* To hold inputs to the Pin mux */
502 char *items[HDA_MAX_CONNECTIONS];
503 int i = 0;
504 int num_items = hdmi->num_cvt + 1;
505
506 kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL);
507 if (!kc)
508 return -ENOMEM;
509
510 se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL);
511 if (!se)
512 return -ENOMEM;
513
514 sprintf(kc_name, "Pin %d Input", pin->nid);
515 kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
516 if (!kc->name)
517 return -ENOMEM;
518
519 kc->private_value = (long)se;
520 kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
521 kc->access = 0;
522 kc->info = snd_soc_info_enum_double;
523 kc->put = snd_soc_dapm_put_enum_double;
524 kc->get = snd_soc_dapm_get_enum_double;
525
526 se->reg = SND_SOC_NOPM;
527
528 /* enum texts: ["NONE", "cvt #", "cvt #", ...] */
529 se->items = num_items;
530 se->mask = roundup_pow_of_two(se->items) - 1;
531
532 sprintf(mux_items, "NONE");
533 items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
534 if (!items[i])
535 return -ENOMEM;
536
537 list_for_each_entry(cvt, &hdmi->cvt_list, head) {
538 i++;
539 sprintf(mux_items, "cvt %d", cvt->nid);
540 items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
541 if (!items[i])
542 return -ENOMEM;
543 }
544
545 se->texts = devm_kmemdup(&edev->hdac.dev, items,
546 (num_items * sizeof(char *)), GFP_KERNEL);
547 if (!se->texts)
548 return -ENOMEM;
549
550 return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
551 snd_soc_dapm_mux, &pin->nid, widget_name,
552 NULL, kc, 1);
553}
554
555/* Add cvt <- input <- mux route map */
556static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
557 struct snd_soc_dapm_widget *widgets,
558 struct snd_soc_dapm_route *route, int rindex)
559{
560 struct hdac_hdmi_priv *hdmi = edev->private_data;
561 const struct snd_kcontrol_new *kc;
562 struct soc_enum *se;
563 int mux_index = hdmi->num_cvt + hdmi->num_pin;
564 int i, j;
565
566 for (i = 0; i < hdmi->num_pin; i++) {
567 kc = widgets[mux_index].kcontrol_news;
568 se = (struct soc_enum *)kc->private_value;
569 for (j = 0; j < hdmi->num_cvt; j++) {
570 hdac_hdmi_fill_route(&route[rindex],
571 widgets[mux_index].name,
572 se->texts[j + 1],
573 widgets[j].name, NULL);
574
575 rindex++;
576 }
577
578 mux_index++;
579 }
580}
581
582/*
583 * Widgets are added in the below sequence
584 * Converter widgets for num converters enumerated
585 * Pin widgets for num pins enumerated
586 * Pin mux widgets to represent connenction list of pin widget
587 *
588 * Total widgets elements = num_cvt + num_pin + num_pin;
589 *
590 * Routes are added as below:
591 * pin mux -> pin (based on num_pins)
592 * cvt -> "Input sel control" -> pin_mux
593 *
594 * Total route elements:
595 * num_pins + (pin_muxes * num_cvt)
596 */
597static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
474{ 598{
475 struct snd_soc_dapm_route route[1]; 599 struct snd_soc_dapm_widget *widgets;
476 struct snd_soc_dapm_widget widgets[2] = { {0} }; 600 struct snd_soc_dapm_route *route;
601 struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
602 struct hdac_hdmi_priv *hdmi = edev->private_data;
603 struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
604 char widget_name[NAME_SIZE];
605 struct hdac_hdmi_cvt *cvt;
606 struct hdac_hdmi_pin *pin;
607 int ret, i = 0, num_routes = 0;
477 608
478 memset(&route, 0, sizeof(route)); 609 if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
610 return -EINVAL;
479 611
480 hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output, 612 widgets = devm_kzalloc(dapm->dev,
481 "hif1 Output", NULL); 613 (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
482 hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in, 614 GFP_KERNEL);
483 "Coverter 1", "hif1");
484 615
485 hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1"); 616 if (!widgets)
617 return -ENOMEM;
618
619 /* DAPM widgets to represent each converter widget */
620 list_for_each_entry(cvt, &hdmi->cvt_list, head) {
621 sprintf(widget_name, "Converter %d", cvt->nid);
622 ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
623 snd_soc_dapm_aif_in, &cvt->nid,
624 widget_name, dai_drv[i].playback.stream_name, NULL, 0);
625 if (ret < 0)
626 return ret;
627 i++;
628 }
629
630 list_for_each_entry(pin, &hdmi->pin_list, head) {
631 sprintf(widget_name, "hif%d Output", pin->nid);
632 ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
633 snd_soc_dapm_output, &pin->nid,
634 widget_name, NULL, NULL, 0);
635 if (ret < 0)
636 return ret;
637 i++;
638 }
639
640 /* DAPM widgets to represent the connection list to pin widget */
641 list_for_each_entry(pin, &hdmi->pin_list, head) {
642 sprintf(widget_name, "Pin %d Mux", pin->nid);
643 ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
644 widget_name);
645 if (ret < 0)
646 return ret;
647 i++;
648
649 /* For cvt to pin_mux mapping */
650 num_routes += hdmi->num_cvt;
651
652 /* For pin_mux to pin mapping */
653 num_routes++;
654 }
655
656 route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
657 GFP_KERNEL);
658 if (!route)
659 return -ENOMEM;
660
661 i = 0;
662 /* Add pin <- NULL <- mux route map */
663 list_for_each_entry(pin, &hdmi->pin_list, head) {
664 int sink_index = i + hdmi->num_cvt;
665 int src_index = sink_index + hdmi->num_pin;
666
667 hdac_hdmi_fill_route(&route[i],
668 widgets[sink_index].name, NULL,
669 widgets[src_index].name, NULL);
670 i++;
671
672 }
673
674 hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
675
676 snd_soc_dapm_new_controls(dapm, widgets,
677 ((2 * hdmi->num_pin) + hdmi->num_cvt));
678
679 snd_soc_dapm_add_routes(dapm, route, num_routes);
680 snd_soc_dapm_new_widgets(dapm->card);
681
682 return 0;
486 683
487 snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
488 snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
489} 684}
490 685
491static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) 686static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
@@ -855,7 +1050,9 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
855 1050
856 edev->scodec = codec; 1051 edev->scodec = codec;
857 1052
858 create_fill_widget_route_map(dapm, &hdmi->dai_map[0]); 1053 ret = create_fill_widget_route_map(dapm);
1054 if (ret < 0)
1055 return ret;
859 1056
860 aops.audio_ptr = edev; 1057 aops.audio_ptr = edev;
861 ret = snd_hdac_i915_register_notifier(&aops); 1058 ret = snd_hdac_i915_register_notifier(&aops);