@@ -193,6 +193,35 @@ struct hda_dai_map {
};
#define HDA_MAX_NIDS 16
+#define HDA_MAX_CONNECTIONS 32
+/* amp values */
+#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE 0xb080
+#define AMP_OUT_UNMUTE 0xb000
+
+
+struct hdac_ext_codec_widget;
+
+struct hdac_ext_codec_connection_list {
+ hda_nid_t nid;
+ unsigned int type;
+ struct hdac_ext_codec_widget *input_w;
+};
+
+/**
+ * struct hdac_ext_device - HDAC Ext device
+ */
+struct hdac_ext_codec_widget {
+ struct list_head head;
+ hda_nid_t nid;
+ unsigned int caps;
+ unsigned int type;
+ int num_inputs;
+ struct hdac_ext_codec_connection_list conn_list[HDA_MAX_CONNECTIONS];
+ void *priv; /* Codec specific widget data */
+ void *params; /* Widget specific parameters */
+};
/**
* struct hdac_ext_device - HDAC Ext device
@@ -217,6 +246,8 @@ struct hdac_ext_device {
struct snd_card *card;
void *scodec;
void *private_data;
+
+ struct list_head widget_list;
};
struct hdac_ext_dma_params {
@@ -1,3 +1,4 @@
-snd-hda-ext-core-objs := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o
+snd-hda-ext-core-objs := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o \
+ hdac_ext_codec.o
obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <sound/hdaudio_ext.h>
+#include "hdac_ext_codec.h"
MODULE_DESCRIPTION("HDA extended core");
MODULE_LICENSE("GPL v2");
@@ -128,6 +129,7 @@ static void default_release(struct device *dev)
snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev));
}
+
/**
* snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device
* @ebus: hdac extended bus to attach to
@@ -159,6 +161,9 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
hdev->type = HDA_DEV_ASOC;
hdev->dev.release = default_release;
+ INIT_LIST_HEAD(&edev->widget_list);
+ snd_hdac_ext_parse_widgets(edev);
+
ret = snd_hdac_device_register(hdev);
if (ret) {
dev_err(bus->dev, "failed to register hdac device\n");
@@ -178,6 +183,7 @@ void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev)
{
struct hdac_ext_device *edev = to_ehdac_device(hdev);
+ snd_hdac_ext_codec_cleanup(edev);
snd_hdac_device_exit(hdev);
kfree(edev);
}
new file mode 100644
@@ -0,0 +1,156 @@
+/*
+ * hdac_codec.c - HDA codec library
+ *
+ * Copyright (C) 2016 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include "../../hda/local.h"
+#include "hdac_ext_codec.h"
+#include <sound/hda_verbs.h>
+
+static int hdac_generic_query_connlist(struct hdac_device *hdac,
+ struct hdac_ext_codec_widget *wid)
+{
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ unsigned int caps;
+ int i;
+
+ if (!(get_wcaps(hdac, wid->nid) & AC_WCAP_CONN_LIST)) {
+ dev_dbg(&hdac->dev,
+ "HDAC: wid %d wcaps %#x doesn't support connection list\n",
+ wid->nid, get_wcaps(hdac, wid->nid));
+
+ return 0;
+ }
+
+ wid->num_inputs = snd_hdac_get_connections(hdac, wid->nid,
+ mux_nids, HDA_MAX_CONNECTIONS);
+
+ if (wid->num_inputs == 0) {
+ dev_warn(&hdac->dev, "No connections found for wid: %d\n",
+ wid->nid);
+ return 0;
+ }
+
+ for (i = 0; i < wid->num_inputs; i++) {
+ wid->conn_list[i].nid = mux_nids[i];
+ caps = get_wcaps(hdac, mux_nids[i]);
+ wid->conn_list[i].type = get_wcaps_type(caps);
+ }
+
+ dev_dbg(&hdac->dev, "num_inputs %d for wid: %d\n",
+ wid->num_inputs, wid->nid);
+
+ return wid->num_inputs;
+}
+
+static int hdac_codec_add_widget(struct hdac_ext_device *edev, hda_nid_t nid,
+ unsigned int type, unsigned int caps)
+{
+ struct hdac_device *codec = &edev->hdac;
+ struct hdac_ext_codec_widget *widget;
+ unsigned int *cfg;
+
+ widget = kzalloc(sizeof(*widget), GFP_KERNEL);
+ if (!widget)
+ return -ENOMEM;
+
+ widget->nid = nid;
+ widget->type = type;
+ widget->caps = caps;
+ list_add_tail(&widget->head, &edev->widget_list);
+
+ if (type == AC_WID_PIN) {
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ *cfg = snd_hdac_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ widget->params = cfg;
+ }
+
+ return hdac_generic_query_connlist(codec, widget);
+}
+
+int snd_hdac_ext_parse_widgets(struct hdac_ext_device *edev)
+{
+ struct hdac_device *hdac = &edev->hdac;
+ hda_nid_t nid;
+ int num_nodes, i;
+ struct hdac_ext_codec_widget *wid;
+ struct hdac_ext_codec_widget *tmp;
+ int ret = 0;
+
+ num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
+ if (!nid || num_nodes <= 0) {
+ dev_err(&hdac->dev, "HDAC: failed to get afg sub nodes\n");
+ return -EINVAL;
+ }
+ hdac->num_nodes = num_nodes;
+ hdac->start_nid = nid;
+
+ for (i = 0; i < hdac->num_nodes; i++, nid++) {
+ unsigned int caps;
+ unsigned int type;
+
+ caps = get_wcaps(hdac, nid);
+ type = get_wcaps_type(caps);
+
+ ret = hdac_codec_add_widget(edev, nid, type, caps);
+ if (ret < 0)
+ goto fail_add_widget;
+
+ }
+
+ hdac->end_nid = nid;
+
+ /* Cache input connection to a widget */
+ list_for_each_entry(wid, &edev->widget_list, head) {
+ if (!wid->num_inputs)
+ continue;
+
+ for (i = 0; i < wid->num_inputs; i++) {
+ list_for_each_entry(tmp, &edev->widget_list, head) {
+ if (wid->conn_list[i].nid == tmp->nid) {
+ wid->conn_list[i].input_w = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+
+fail_add_widget:
+ snd_hdac_ext_codec_cleanup(edev);
+ return ret;
+}
+
+void snd_hdac_ext_codec_cleanup(struct hdac_ext_device *edev)
+{
+ struct hdac_ext_codec_widget *wid, *tmp;
+
+ list_for_each_entry_safe(wid, tmp, &edev->widget_list, head) {
+ kfree(wid->params);
+ list_del(&wid->head);
+ kfree(wid);
+ }
+}
new file mode 100644
@@ -0,0 +1,26 @@
+/*
+ * hdac_ext_codec.h - HDA ext codec helpers
+ *
+ * Copyright (C) 2016 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __HDAC_EXT_CODEC_H__
+#define __HDAC_EXT_CODEC_H__
+
+int snd_hdac_ext_parse_widgets(struct hdac_ext_device *hdac);
+void snd_hdac_ext_codec_cleanup(struct hdac_ext_device *hdac);
+
+#endif /* __HDAC_EXT_CODEC_H__ */