ASoC: hdac_hdmi: Add machine pin widget for each port
authorJeeja KP <jeeja.kp@intel.com>
Tue, 7 Feb 2017 13:39:55 +0000 (19:09 +0530)
committerMark Brown <broonie@kernel.org>
Thu, 16 Feb 2017 18:56:06 +0000 (18:56 +0000)
Represent each port as machine DAPM pin widget. This helps in
enable/disable pin when monitor is connected/disconnected in case pcm
is rendered to multiple ports.

Create machine pin widgets  and pin switch kcontrol for each port and
report based on the pin status

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Acked-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/hdac_hdmi.h

index 0f2c1e823281b7ca250c2f9f3f4e7d0ece551dab..0a5510a3a8e12adee7fdd10a4bc94ff0fc0b2327 100644 (file)
@@ -95,6 +95,9 @@ struct hdac_hdmi_port {
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
        struct hdac_hdmi_eld eld;
+       const char *jack_pin;
+       struct snd_soc_dapm_context *dapm;
+       const char *output_pin;
 };
 
 struct hdac_hdmi_pcm {
@@ -149,6 +152,11 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
 {
        struct hdac_ext_device *edev = port->pin->edev;
 
+       if (is_connect)
+               snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
+       else
+               snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
+
        if (is_connect) {
                /*
                 * Report Jack connect event when a device is connected
@@ -174,6 +182,8 @@ static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
                if (pcm->jack_event > 0)
                        pcm->jack_event--;
        }
+
+       snd_soc_dapm_sync(port->dapm);
 }
 
 /* MST supported verbs */
@@ -1059,6 +1069,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
                                        SND_SOC_DAPM_POST_PMD);
                        if (ret < 0)
                                return ret;
+                       pin->ports[j].output_pin = widgets[i].name;
                        i++;
                }
        }
@@ -1557,6 +1568,125 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
        return NULL;
 }
 
+/* create jack pin kcontrols */
+static int create_fill_jack_kcontrols(struct snd_soc_card *card,
+                                   struct hdac_ext_device *edev)
+{
+       struct hdac_hdmi_pin *pin;
+       struct snd_kcontrol_new *kc;
+       char kc_name[NAME_SIZE], xname[NAME_SIZE];
+       char *name;
+       int i = 0, j;
+       struct snd_soc_codec *codec = edev->scodec;
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+
+       kc = devm_kcalloc(codec->dev, hdmi->num_ports,
+                               sizeof(*kc), GFP_KERNEL);
+
+       if (!kc)
+               return -ENOMEM;
+
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               for (j = 0; j < pin->num_ports; j++) {
+                       snprintf(xname, sizeof(xname), "hif%d-%d Jack",
+                                               pin->nid, pin->ports[j].id);
+                       name = devm_kstrdup(codec->dev, xname, GFP_KERNEL);
+                       if (!name)
+                               return -ENOMEM;
+                       snprintf(kc_name, sizeof(kc_name), "%s Switch", xname);
+                       kc[i].name = devm_kstrdup(codec->dev, kc_name,
+                                                       GFP_KERNEL);
+                       if (!kc[i].name)
+                               return -ENOMEM;
+
+                       kc[i].private_value = (unsigned long)name;
+                       kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+                       kc[i].access = 0;
+                       kc[i].info = snd_soc_dapm_info_pin_switch;
+                       kc[i].put = snd_soc_dapm_put_pin_switch;
+                       kc[i].get = snd_soc_dapm_get_pin_switch;
+                       i++;
+               }
+       }
+
+       return snd_soc_add_card_controls(card, kc, i);
+}
+
+int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec,
+                       struct snd_soc_dapm_context *dapm)
+{
+       struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pin *pin;
+       struct snd_soc_dapm_widget *widgets;
+       struct snd_soc_dapm_route *route;
+       char w_name[NAME_SIZE];
+       int i = 0, j, ret;
+
+       widgets = devm_kcalloc(dapm->dev, hdmi->num_ports,
+                               sizeof(*widgets), GFP_KERNEL);
+
+       if (!widgets)
+               return -ENOMEM;
+
+       route = devm_kcalloc(dapm->dev, hdmi->num_ports,
+                               sizeof(*route), GFP_KERNEL);
+       if (!route)
+               return -ENOMEM;
+
+       /* create Jack DAPM widget */
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               for (j = 0; j < pin->num_ports; j++) {
+                       snprintf(w_name, sizeof(w_name), "hif%d-%d Jack",
+                                               pin->nid, pin->ports[j].id);
+
+                       ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+                                       snd_soc_dapm_spk, NULL,
+                                       w_name, NULL, NULL, 0, NULL, 0);
+                       if (ret < 0)
+                               return ret;
+
+                       pin->ports[j].jack_pin = widgets[i].name;
+                       pin->ports[j].dapm = dapm;
+
+                       /* add to route from Jack widget to output */
+                       hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin,
+                                       NULL, pin->ports[j].output_pin, NULL);
+
+                       i++;
+               }
+       }
+
+       /* Add Route from Jack widget to the output widget */
+       ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dapm_new_widgets(dapm->card);
+       if (ret < 0)
+               return ret;
+
+       /* Add Jack Pin switch Kcontrol */
+       ret = create_fill_jack_kcontrols(dapm->card, edev);
+
+       if (ret < 0)
+               return ret;
+
+       /* default set the Jack Pin switch to OFF */
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               for (j = 0; j < pin->num_ports; j++)
+                       snd_soc_dapm_disable_pin(pin->ports[j].dapm,
+                                               pin->ports[j].jack_pin);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init);
+
 int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
                                struct snd_soc_jack *jack)
 {
index bf7edb3227d24cabe22384360909c47cffeb7b1b..dfc3a9cf719963a3fdb3bbda7293895609638016 100644 (file)
@@ -4,4 +4,6 @@
 int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm,
                                struct snd_soc_jack *jack);
 
+int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec,
+                       struct snd_soc_dapm_context *dapm);
 #endif /* __HDAC_HDMI_H__ */