summaryrefslogtreecommitdiff
path: root/sound/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/hda')
-rw-r--r--sound/hda/Kconfig7
-rw-r--r--sound/hda/Makefile1
-rw-r--r--sound/hda/ext/hdac_ext_bus.c80
-rw-r--r--sound/hda/ext/hdac_ext_controller.c64
-rw-r--r--sound/hda/ext/hdac_ext_stream.c104
-rw-r--r--sound/hda/hdac_component.c335
-rw-r--r--sound/hda/hdac_i915.c335
7 files changed, 470 insertions, 456 deletions
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 3129546398d0..2d90e11b3eaa 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -5,11 +5,12 @@ config SND_HDA_CORE
config SND_HDA_DSP_LOADER
bool
+config SND_HDA_COMPONENT
+ bool
+
config SND_HDA_I915
bool
- default y
- depends on DRM_I915
- depends on SND_HDA_CORE
+ select SND_HDA_COMPONENT
config SND_HDA_EXT_CORE
tristate
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index e4e726f2ce98..2160202e2dc1 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -6,6 +6,7 @@ snd-hda-core-objs += trace.o
CFLAGS_trace.o := -I$(src)
# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += hdac_component.o
snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o
obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c
index 0daf31383084..9c37d9af3023 100644
--- a/sound/hda/ext/hdac_ext_bus.c
+++ b/sound/hda/ext/hdac_ext_bus.c
@@ -87,9 +87,10 @@ static const struct hdac_io_ops hdac_ext_default_io = {
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
+int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
- const struct hdac_io_ops *io_ops)
+ const struct hdac_io_ops *io_ops,
+ const struct hdac_ext_bus_ops *ext_ops)
{
int ret;
static int idx;
@@ -98,15 +99,16 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
if (io_ops == NULL)
io_ops = &hdac_ext_default_io;
- ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops);
+ ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
if (ret < 0)
return ret;
- INIT_LIST_HEAD(&ebus->hlink_list);
- ebus->idx = idx++;
+ bus->ext_ops = ext_ops;
+ INIT_LIST_HEAD(&bus->hlink_list);
+ bus->idx = idx++;
- mutex_init(&ebus->lock);
- ebus->cmd_dma_state = true;
+ mutex_init(&bus->lock);
+ bus->cmd_dma_state = true;
return 0;
}
@@ -116,10 +118,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
* snd_hdac_ext_bus_exit - clean up a HD-audio extended bus
* @ebus: the pointer to extended bus object
*/
-void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus)
+void snd_hdac_ext_bus_exit(struct hdac_bus *bus)
{
- snd_hdac_bus_exit(&ebus->bus);
- WARN_ON(!list_empty(&ebus->hlink_list));
+ snd_hdac_bus_exit(bus);
+ WARN_ON(!list_empty(&bus->hlink_list));
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
@@ -135,21 +137,15 @@ static void default_release(struct device *dev)
*
* Returns zero for success or a negative error code.
*/
-int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
+int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
+ struct hdac_device *hdev)
{
- struct hdac_ext_device *edev;
- struct hdac_device *hdev = NULL;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
char name[15];
int ret;
- edev = kzalloc(sizeof(*edev), GFP_KERNEL);
- if (!edev)
- return -ENOMEM;
- hdev = &edev->hdev;
- edev->ebus = ebus;
+ hdev->bus = bus;
- snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);
+ snprintf(name, sizeof(name), "ehdaudio%dD%d", bus->idx, addr);
ret = snd_hdac_device_init(hdev, bus, name, addr);
if (ret < 0) {
@@ -176,10 +172,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init);
*/
void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev)
{
- struct hdac_ext_device *edev = to_ehdac_device(hdev);
-
snd_hdac_device_exit(hdev);
- kfree(edev);
+ kfree(hdev);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit);
@@ -188,14 +182,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit);
*
* @ebus: HD-audio extended bus
*/
-void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus)
+void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus)
{
struct hdac_device *codec, *__codec;
/*
* we need to remove all the codec devices objects created in the
* snd_hdac_ext_bus_device_init
*/
- list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) {
+ list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) {
snd_hdac_device_unregister(codec);
put_device(&codec->dev);
}
@@ -204,35 +198,31 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove);
#define dev_to_hdac(dev) (container_of((dev), \
struct hdac_device, dev))
-static inline struct hdac_ext_driver *get_edrv(struct device *dev)
+static inline struct hdac_driver *get_hdrv(struct device *dev)
{
struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
- struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv);
-
- return edrv;
+ return hdrv;
}
-static inline struct hdac_ext_device *get_edev(struct device *dev)
+static inline struct hdac_device *get_hdev(struct device *dev)
{
struct hdac_device *hdev = dev_to_hdac_dev(dev);
- struct hdac_ext_device *edev = to_ehdac_device(hdev);
-
- return edev;
+ return hdev;
}
static int hda_ext_drv_probe(struct device *dev)
{
- return (get_edrv(dev))->probe(get_edev(dev));
+ return (get_hdrv(dev))->probe(get_hdev(dev));
}
static int hdac_ext_drv_remove(struct device *dev)
{
- return (get_edrv(dev))->remove(get_edev(dev));
+ return (get_hdrv(dev))->remove(get_hdev(dev));
}
static void hdac_ext_drv_shutdown(struct device *dev)
{
- return (get_edrv(dev))->shutdown(get_edev(dev));
+ return (get_hdrv(dev))->shutdown(get_hdev(dev));
}
/**
@@ -240,20 +230,20 @@ static void hdac_ext_drv_shutdown(struct device *dev)
*
* @drv: ext hda driver structure
*/
-int snd_hda_ext_driver_register(struct hdac_ext_driver *drv)
+int snd_hda_ext_driver_register(struct hdac_driver *drv)
{
- drv->hdac.type = HDA_DEV_ASOC;
- drv->hdac.driver.bus = &snd_hda_bus_type;
+ drv->type = HDA_DEV_ASOC;
+ drv->driver.bus = &snd_hda_bus_type;
/* we use default match */
if (drv->probe)
- drv->hdac.driver.probe = hda_ext_drv_probe;
+ drv->driver.probe = hda_ext_drv_probe;
if (drv->remove)
- drv->hdac.driver.remove = hdac_ext_drv_remove;
+ drv->driver.remove = hdac_ext_drv_remove;
if (drv->shutdown)
- drv->hdac.driver.shutdown = hdac_ext_drv_shutdown;
+ drv->driver.shutdown = hdac_ext_drv_shutdown;
- return driver_register(&drv->hdac.driver);
+ return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
@@ -262,8 +252,8 @@ EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
*
* @drv: ext hda driver structure
*/
-void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv)
+void snd_hda_ext_driver_unregister(struct hdac_driver *drv)
{
- driver_unregister(&drv->hdac.driver);
+ driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister);
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 84f3b8168716..5bc4a1d587d4 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -39,9 +39,8 @@
* @ebus: HD-audio extended core bus
* @enable: flag to turn on/off the capability
*/
-void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
+void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable)
{
- struct hdac_bus *bus = &ebus->bus;
if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL");
@@ -60,9 +59,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
* @ebus: HD-audio extended core bus
* @enable: flag to enable/disable interrupt
*/
-void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
+void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable)
{
- struct hdac_bus *bus = &ebus->bus;
if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL\n");
@@ -89,12 +87,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
* in hlink_list of extended hdac bus
* Note: this will be freed on bus exit by driver
*/
-int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
+int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
{
int idx;
u32 link_count;
struct hdac_ext_link *hlink;
- struct hdac_bus *bus = &ebus->bus;
link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
@@ -114,7 +111,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
/* since link in On, update the ref */
hlink->ref_count = 1;
- list_add_tail(&hlink->list, &ebus->hlink_list);
+ list_add_tail(&hlink->list, &bus->hlink_list);
}
return 0;
@@ -127,12 +124,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
* @ebus: HD-audio ext core bus
*/
-void snd_hdac_link_free_all(struct hdac_ext_bus *ebus)
+void snd_hdac_link_free_all(struct hdac_bus *bus)
{
struct hdac_ext_link *l;
- while (!list_empty(&ebus->hlink_list)) {
- l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list);
+ while (!list_empty(&bus->hlink_list)) {
+ l = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
list_del(&l->list);
kfree(l);
}
@@ -144,7 +141,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_link_free_all);
* @ebus: HD-audio extended core bus
* @codec_name: codec name
*/
-struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus,
+struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus,
const char *codec_name)
{
int i;
@@ -153,10 +150,10 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus,
if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
return NULL;
- if (ebus->idx != bus_idx)
+ if (bus->idx != bus_idx)
return NULL;
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
for (i = 0; i < HDA_MAX_CODECS; i++) {
if (hlink->lsdiid & (0x1 << addr))
return hlink;
@@ -219,12 +216,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
* snd_hdac_ext_bus_link_power_up_all -power up all hda link
* @ebus: HD-audio extended bus
*/
-int snd_hdac_ext_bus_link_power_up_all(struct hdac_ext_bus *ebus)
+int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus)
{
struct hdac_ext_link *hlink = NULL;
int ret;
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
snd_hdac_updatel(hlink->ml_addr,
AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA);
ret = check_hdac_link_power_active(hlink, true);
@@ -240,12 +237,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
* snd_hdac_ext_bus_link_power_down_all -power down all hda link
* @ebus: HD-audio extended bus
*/
-int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
+int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus)
{
struct hdac_ext_link *hlink = NULL;
int ret;
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0);
ret = check_hdac_link_power_active(hlink, false);
if (ret < 0)
@@ -256,39 +253,48 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
-int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
+int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
struct hdac_ext_link *link)
{
int ret = 0;
- mutex_lock(&ebus->lock);
+ mutex_lock(&bus->lock);
/*
* if we move from 0 to 1, count will be 1 so power up this link
* as well, also check the dma status and trigger that
*/
if (++link->ref_count == 1) {
- if (!ebus->cmd_dma_state) {
- snd_hdac_bus_init_cmd_io(&ebus->bus);
- ebus->cmd_dma_state = true;
+ if (!bus->cmd_dma_state) {
+ snd_hdac_bus_init_cmd_io(bus);
+ bus->cmd_dma_state = true;
}
ret = snd_hdac_ext_bus_link_power_up(link);
+
+ /*
+ * wait for 521usec for codec to report status
+ * HDA spec section 4.3 - Codec Discovery
+ */
+ udelay(521);
+ bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ snd_hdac_chip_writew(bus, STATESTS, bus->codec_mask);
}
- mutex_unlock(&ebus->lock);
+ mutex_unlock(&bus->lock);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
-int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
+int snd_hdac_ext_bus_link_put(struct hdac_bus *bus,
struct hdac_ext_link *link)
{
int ret = 0;
struct hdac_ext_link *hlink;
bool link_up = false;
- mutex_lock(&ebus->lock);
+ mutex_lock(&bus->lock);
/*
* if we move from 1 to 0, count will be 0
@@ -301,7 +307,7 @@ int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
* now check if all links are off, if so turn off
* cmd dma as well
*/
- list_for_each_entry(hlink, &ebus->hlink_list, list) {
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
if (hlink->ref_count) {
link_up = true;
break;
@@ -309,12 +315,12 @@ int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
}
if (!link_up) {
- snd_hdac_bus_stop_cmd_io(&ebus->bus);
- ebus->cmd_dma_state = false;
+ snd_hdac_bus_stop_cmd_io(bus);
+ bus->cmd_dma_state = false;
}
}
- mutex_unlock(&ebus->lock);
+ mutex_unlock(&bus->lock);
return ret;
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index c96d7a7a36af..1bd27576db98 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -25,7 +25,7 @@
/**
* snd_hdac_ext_stream_init - initialize each stream (aka device)
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @stream: HD-audio ext core stream object to initialize
* @idx: stream index number
* @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
@@ -34,18 +34,16 @@
* initialize the stream, if ppcap is enabled then init those and then
* invoke hdac stream initialization routine
*/
-void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
+void snd_hdac_ext_stream_init(struct hdac_bus *bus,
struct hdac_ext_stream *stream,
int idx, int direction, int tag)
{
- struct hdac_bus *bus = &ebus->bus;
-
if (bus->ppcap) {
stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
AZX_PPHC_INTERVAL * idx;
stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
- AZX_PPLC_MULTI * ebus->num_streams +
+ AZX_PPLC_MULTI * bus->num_streams +
AZX_PPLC_INTERVAL * idx;
}
@@ -71,12 +69,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
/**
* snd_hdac_ext_stream_init_all - create and initialize the stream objects
* for an extended hda bus
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @start_idx: start index for streams
* @num_stream: number of streams to initialize
* @dir: direction of streams
*/
-int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx,
+int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
int num_stream, int dir)
{
int stream_tag = 0;
@@ -88,7 +86,7 @@ int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx,
if (!stream)
return -ENOMEM;
tag = ++stream_tag;
- snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag);
+ snd_hdac_ext_stream_init(bus, stream, idx, dir, tag);
idx++;
}
@@ -100,17 +98,16 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
/**
* snd_hdac_stream_free_all - free hdac extended stream objects
*
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
*/
-void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus)
+void snd_hdac_stream_free_all(struct hdac_bus *bus)
{
struct hdac_stream *s, *_s;
struct hdac_ext_stream *stream;
- struct hdac_bus *bus = ebus_to_hbus(ebus);
list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
stream = stream_to_hdac_ext_stream(s);
- snd_hdac_ext_stream_decouple(ebus, stream, false);
+ snd_hdac_ext_stream_decouple(bus, stream, false);
list_del(&s->list);
kfree(stream);
}
@@ -119,15 +116,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
/**
* snd_hdac_ext_stream_decouple - decouple the hdac stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @stream: HD-audio ext core stream object to initialize
* @decouple: flag to decouple
*/
-void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
+void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
struct hdac_ext_stream *stream, bool decouple)
{
struct hdac_stream *hstream = &stream->hstream;
- struct hdac_bus *bus = &ebus->bus;
u32 val;
int mask = AZX_PPCTL_PROCEN(hstream->index);
@@ -251,19 +247,18 @@ void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
static struct hdac_ext_stream *
-hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
+hdac_ext_link_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *res = NULL;
struct hdac_stream *stream = NULL;
- struct hdac_bus *hbus = &ebus->bus;
- if (!hbus->ppcap) {
- dev_err(hbus->dev, "stream type not supported\n");
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
return NULL;
}
- list_for_each_entry(stream, &hbus->stream_list, list) {
+ list_for_each_entry(stream, &bus->stream_list, list) {
struct hdac_ext_stream *hstream = container_of(stream,
struct hdac_ext_stream,
hstream);
@@ -277,34 +272,33 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
}
if (!hstream->link_locked) {
- snd_hdac_ext_stream_decouple(ebus, hstream, true);
+ snd_hdac_ext_stream_decouple(bus, hstream, true);
res = hstream;
break;
}
}
if (res) {
- spin_lock_irq(&hbus->reg_lock);
+ spin_lock_irq(&bus->reg_lock);
res->link_locked = 1;
res->link_substream = substream;
- spin_unlock_irq(&hbus->reg_lock);
+ spin_unlock_irq(&bus->reg_lock);
}
return res;
}
static struct hdac_ext_stream *
-hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
+hdac_ext_host_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream)
{
struct hdac_ext_stream *res = NULL;
struct hdac_stream *stream = NULL;
- struct hdac_bus *hbus = &ebus->bus;
- if (!hbus->ppcap) {
- dev_err(hbus->dev, "stream type not supported\n");
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
return NULL;
}
- list_for_each_entry(stream, &hbus->stream_list, list) {
+ list_for_each_entry(stream, &bus->stream_list, list) {
struct hdac_ext_stream *hstream = container_of(stream,
struct hdac_ext_stream,
hstream);
@@ -313,17 +307,17 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
if (!stream->opened) {
if (!hstream->decoupled)
- snd_hdac_ext_stream_decouple(ebus, hstream, true);
+ snd_hdac_ext_stream_decouple(bus, hstream, true);
res = hstream;
break;
}
}
if (res) {
- spin_lock_irq(&hbus->reg_lock);
+ spin_lock_irq(&bus->reg_lock);
res->hstream.opened = 1;
res->hstream.running = 0;
res->hstream.substream = substream;
- spin_unlock_irq(&hbus->reg_lock);
+ spin_unlock_irq(&bus->reg_lock);
}
return res;
@@ -331,7 +325,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
/**
* snd_hdac_ext_stream_assign - assign a stream for the PCM
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @substream: PCM substream to assign
* @type: type of stream (coupled, host or link stream)
*
@@ -346,27 +340,26 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
* the same stream object when it's used beforehand. when a stream is
* decoupled, it becomes a host stream and link stream.
*/
-struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus,
+struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
struct snd_pcm_substream *substream,
int type)
{
struct hdac_ext_stream *hstream = NULL;
struct hdac_stream *stream = NULL;
- struct hdac_bus *hbus = &ebus->bus;
switch (type) {
case HDAC_EXT_STREAM_TYPE_COUPLED:
- stream = snd_hdac_stream_assign(hbus, substream);
+ stream = snd_hdac_stream_assign(bus, substream);
if (stream)
hstream = container_of(stream,
struct hdac_ext_stream, hstream);
return hstream;
case HDAC_EXT_STREAM_TYPE_HOST:
- return hdac_ext_host_stream_assign(ebus, substream);
+ return hdac_ext_host_stream_assign(bus, substream);
case HDAC_EXT_STREAM_TYPE_LINK:
- return hdac_ext_link_stream_assign(ebus, substream);
+ return hdac_ext_link_stream_assign(bus, substream);
default:
return NULL;
@@ -384,7 +377,6 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
{
struct hdac_bus *bus = stream->hstream.bus;
- struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
switch (type) {
case HDAC_EXT_STREAM_TYPE_COUPLED:
@@ -393,13 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
case HDAC_EXT_STREAM_TYPE_HOST:
if (stream->decoupled && !stream->link_locked)
- snd_hdac_ext_stream_decouple(ebus, stream, false);
+ snd_hdac_ext_stream_decouple(bus, stream, false);
snd_hdac_stream_release(&stream->hstream);
break;
case HDAC_EXT_STREAM_TYPE_LINK:
if (stream->decoupled && !stream->hstream.opened)
- snd_hdac_ext_stream_decouple(ebus, stream, false);
+ snd_hdac_ext_stream_decouple(bus, stream, false);
spin_lock_irq(&bus->reg_lock);
stream->link_locked = 0;
stream->link_substream = NULL;
@@ -415,16 +407,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
/**
* snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @enable: flag to enable/disable SPIB
* @index: stream index for which SPIB need to be enabled
*/
-void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
+void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus,
bool enable, int index)
{
u32 mask = 0;
u32 register_mask = 0;
- struct hdac_bus *bus = &ebus->bus;
if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL\n");
@@ -446,14 +437,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
/**
* snd_hdac_ext_stream_set_spib - sets the spib value of a stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @stream: hdac_ext_stream
* @value: spib value to set
*/
-int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
+int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus,
struct hdac_ext_stream *stream, u32 value)
{
- struct hdac_bus *bus = &ebus->bus;
if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL\n");
@@ -468,15 +458,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
/**
* snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @stream: hdac_ext_stream
*
* Return maxfifo for the stream
*/
-int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
+int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus,
struct hdac_ext_stream *stream)
{
- struct hdac_bus *bus = &ebus->bus;
if (!bus->spbcap) {
dev_err(bus->dev, "Address of SPB capability is NULL\n");
@@ -490,11 +479,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
/**
* snd_hdac_ext_stop_streams - stop all stream if running
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
*/
-void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus)
+void snd_hdac_ext_stop_streams(struct hdac_bus *bus)
{
- struct hdac_bus *bus = ebus_to_hbus(ebus);
struct hdac_stream *stream;
if (bus->chip_init) {
@@ -507,16 +495,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
/**
* snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @enable: flag to enable/disable DRSM
* @index: stream index for which DRSM need to be enabled
*/
-void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
+void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus,
bool enable, int index)
{
u32 mask = 0;
u32 register_mask = 0;
- struct hdac_bus *bus = &ebus->bus;
if (!bus->drsmcap) {
dev_err(bus->dev, "Address of DRSM capability is NULL\n");
@@ -538,14 +525,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
/**
* snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @stream: hdac_ext_stream
* @value: dpib value to set
*/
-int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
+int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus,
struct hdac_ext_stream *stream, u32 value)
{
- struct hdac_bus *bus = &ebus->bus;
if (!bus->drsmcap) {
dev_err(bus->dev, "Address of DRSM capability is NULL\n");
@@ -560,7 +546,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr);
/**
* snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream
- * @ebus: HD-audio ext core bus
+ * @bus: HD-audio core bus
* @stream: hdac_ext_stream
* @value: lpib value to set
*/
diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c
new file mode 100644
index 000000000000..6e46a9c73aed
--- /dev/null
+++ b/sound/hda/hdac_component.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+// hdac_component.c - routines for sync between HD-A core and DRM driver
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/component.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_component.h>
+#include <sound/hda_register.h>
+
+static void hdac_acomp_release(struct device *dev, void *res)
+{
+}
+
+static struct drm_audio_component *hdac_get_acomp(struct device *dev)
+{
+ return devres_find(dev, hdac_acomp_release, NULL, NULL);
+}
+
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ if (!acomp->ops->codec_wake_override)
+ return 0;
+
+ dev_dbg(bus->dev, "%s codec wakeup\n",
+ enable ? "enable" : "disable");
+
+ acomp->ops->codec_wake_override(acomp->dev, enable);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
+
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @enable: power up or down
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function manages a refcount and calls the get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ dev_dbg(bus->dev, "display power %s\n",
+ enable ? "enable" : "disable");
+
+ if (enable) {
+ if (!bus->drm_power_refcount++) {
+ if (acomp->ops->get_power)
+ acomp->ops->get_power(acomp->dev);
+ snd_hdac_set_codec_wakeup(bus, true);
+ snd_hdac_set_codec_wakeup(bus, false);
+ }
+ } else {
+ WARN_ON(!bus->drm_power_refcount);
+ if (!--bus->drm_power_refcount)
+ if (acomp->ops->put_power)
+ acomp->ops->put_power(acomp->dev);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_display_power);
+
+/**
+ * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @rate: the sample rate to set
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets N/CTS value based on the given sample rate.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
+ int dev_id, int rate)
+{
+ struct hdac_bus *bus = codec->bus;
+ struct drm_audio_component *acomp = bus->audio_component;
+ int port, pipe;
+
+ if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
+ return -ENODEV;
+ port = nid;
+ if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+ port = acomp->audio_ops->pin2port(codec, nid);
+ if (port < 0)
+ return -EINVAL;
+ }
+ pipe = dev_id;
+ return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
+
+/**
+ * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @audio_enabled: the pointer to store the current audio state
+ * @buffer: the buffer pointer to store ELD bytes
+ * @max_bytes: the max bytes to be stored on @buffer
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function queries the current state of the audio on the given
+ * digital port and fetches the ELD bytes onto the given buffer.
+ * It returns the number of bytes for the total ELD data, zero for
+ * invalid ELD, or a negative error code.
+ *
+ * The return size is the total bytes required for the whole ELD bytes,
+ * thus it may be over @max_bytes. If it's over @max_bytes, it implies
+ * that only a part of ELD bytes have been fetched.
+ */
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
+ bool *audio_enabled, char *buffer, int max_bytes)
+{
+ struct hdac_bus *bus = codec->bus;
+ struct drm_audio_component *acomp = bus->audio_component;
+ int port, pipe;
+
+ if (!acomp || !acomp->ops || !acomp->ops->get_eld)
+ return -ENODEV;
+
+ port = nid;
+ if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+ port = acomp->audio_ops->pin2port(codec, nid);
+ if (port < 0)
+ return -EINVAL;
+ }
+ pipe = dev_id;
+ return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
+ buffer, max_bytes);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
+
+static int hdac_component_master_bind(struct device *dev)
+{
+ struct drm_audio_component *acomp = hdac_get_acomp(dev);
+ int ret;
+
+ if (WARN_ON(!acomp))
+ return -EINVAL;
+
+ ret = component_bind_all(dev, acomp);
+ if (ret < 0)
+ return ret;
+
+ if (WARN_ON(!(acomp->dev && acomp->ops))) {
+ ret = -EINVAL;
+ goto out_unbind;
+ }
+
+ /* pin the module to avoid dynamic unbinding, but only if given */
+ if (!try_module_get(acomp->ops->owner)) {
+ ret = -ENODEV;
+ goto out_unbind;
+ }
+
+ if (acomp->audio_ops && acomp->audio_ops->master_bind) {
+ ret = acomp->audio_ops->master_bind(dev, acomp);
+ if (ret < 0)
+ goto module_put;
+ }
+
+ return 0;
+
+ module_put:
+ module_put(acomp->ops->owner);
+out_unbind:
+ component_unbind_all(dev, acomp);
+
+ return ret;
+}
+
+static void hdac_component_master_unbind(struct device *dev)
+{
+ struct drm_audio_component *acomp = hdac_get_acomp(dev);
+
+ if (acomp->audio_ops && acomp->audio_ops->master_unbind)
+ acomp->audio_ops->master_unbind(dev, acomp);
+ module_put(acomp->ops->owner);
+ component_unbind_all(dev, acomp);
+ WARN_ON(acomp->ops || acomp->dev);
+}
+
+static const struct component_master_ops hdac_component_master_ops = {
+ .bind = hdac_component_master_bind,
+ .unbind = hdac_component_master_unbind,
+};
+
+/**
+ * snd_hdac_acomp_register_notifier - Register audio component ops
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets the given ops to be called by the graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
+ const struct drm_audio_component_audio_ops *aops)
+{
+ if (!bus->audio_component)
+ return -ENODEV;
+
+ bus->audio_component->audio_ops = aops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
+
+/**
+ * snd_hdac_acomp_init - Initialize audio component
+ * @bus: HDA core bus
+ * @match_master: match function for finding components
+ * @extra_size: Extra bytes to allocate
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with graphics driver.
+ *
+ * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
+ * binding with the DRM component. Each caller needs to sync via master_bind
+ * audio_ops.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_init(struct hdac_bus *bus,
+ const struct drm_audio_component_audio_ops *aops,
+ int (*match_master)(struct device *, void *),
+ size_t extra_size)
+{
+ struct component_match *match = NULL;
+ struct device *dev = bus->dev;
+ struct drm_audio_component *acomp;
+ int ret;
+
+ if (WARN_ON(hdac_get_acomp(dev)))
+ return -EBUSY;
+
+ acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
+ GFP_KERNEL);
+ if (!acomp)
+ return -ENOMEM;
+ acomp->audio_ops = aops;
+ bus->audio_component = acomp;
+ devres_add(dev, acomp);
+
+ component_match_add(dev, &match, match_master, bus);
+ ret = component_master_add_with_match(dev, &hdac_component_master_ops,
+ match);
+ if (ret < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ bus->audio_component = NULL;
+ devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+ dev_info(dev, "failed to add audio component master (%d)\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
+
+/**
+ * snd_hdac_acomp_exit - Finalize audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function releases the audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_exit(struct hdac_bus *bus)
+{
+ struct device *dev = bus->dev;
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp)
+ return 0;
+
+ WARN_ON(bus->drm_power_refcount);
+ if (bus->drm_power_refcount > 0 && acomp->ops)
+ acomp->ops->put_power(acomp->dev);
+
+ component_master_del(dev, &hdac_component_master_ops);
+
+ bus->audio_component = NULL;
+ devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index cbe818eda336..8f2aa8bc1185 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -15,89 +15,11 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/component.h>
-#include <drm/i915_component.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
#include <sound/hda_i915.h>
#include <sound/hda_register.h>
-static struct i915_audio_component *hdac_acomp;
-
-/**
- * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
- * @bus: HDA core bus
- * @enable: enable or disable the wakeup
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function should be called during the chip reset, also called at
- * resume for updating STATESTS register read.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
-{
- struct i915_audio_component *acomp = bus->audio_component;
-
- if (!acomp || !acomp->ops)
- return -ENODEV;
-
- if (!acomp->ops->codec_wake_override) {
- dev_warn(bus->dev,
- "Invalid codec wake callback\n");
- return 0;
- }
-
- dev_dbg(bus->dev, "%s codec wakeup\n",
- enable ? "enable" : "disable");
-
- acomp->ops->codec_wake_override(acomp->dev, enable);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
-
-/**
- * snd_hdac_display_power - Power up / down the power refcount
- * @bus: HDA core bus
- * @enable: power up or down
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function manages a refcount and calls the i915 get_power() and
- * put_power() ops accordingly, toggling the codec wakeup, too.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
-{
- struct i915_audio_component *acomp = bus->audio_component;
-
- if (!acomp || !acomp->ops)
- return -ENODEV;
-
- dev_dbg(bus->dev, "display power %s\n",
- enable ? "enable" : "disable");
-
- if (enable) {
- if (!bus->i915_power_refcount++) {
- acomp->ops->get_power(acomp->dev);
- snd_hdac_set_codec_wakeup(bus, true);
- snd_hdac_set_codec_wakeup(bus, false);
- }
- } else {
- WARN_ON(!bus->i915_power_refcount);
- if (!--bus->i915_power_refcount)
- acomp->ops->put_power(acomp->dev);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_display_power);
-
#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \
((pci)->device == 0x0c0c) || \
((pci)->device == 0x0d0c) || \
@@ -119,7 +41,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_display_power);
*/
void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
{
- struct i915_audio_component *acomp = bus->audio_component;
+ struct drm_audio_component *acomp = bus->audio_component;
struct pci_dev *pci = to_pci_dev(bus->dev);
int cdclk_freq;
unsigned int bclk_m, bclk_n;
@@ -158,181 +80,11 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
}
EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
-/* There is a fixed mapping between audio pin node and display port.
- * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
- * Pin Widget 5 - PORT B (port = 1 in i915 driver)
- * Pin Widget 6 - PORT C (port = 2 in i915 driver)
- * Pin Widget 7 - PORT D (port = 3 in i915 driver)
- *
- * on VLV, ILK:
- * Pin Widget 4 - PORT B (port = 1 in i915 driver)
- * Pin Widget 5 - PORT C (port = 2 in i915 driver)
- * Pin Widget 6 - PORT D (port = 3 in i915 driver)
- */
-static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
+static int i915_component_master_match(struct device *dev, void *data)
{
- int base_nid;
-
- switch (codec->vendor_id) {
- case 0x80860054: /* ILK */
- case 0x80862804: /* ILK */
- case 0x80862882: /* VLV */
- base_nid = 3;
- break;
- default:
- base_nid = 4;
- break;
- }
-
- if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
- return -1;
- return pin_nid - base_nid;
-}
-
-/**
- * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
- * @codec: HDA codec
- * @nid: the pin widget NID
- * @dev_id: device identifier
- * @rate: the sample rate to set
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function sets N/CTS value based on the given sample rate.
- * Returns zero for success, or a negative error code.
- */
-int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
- int dev_id, int rate)
-{
- struct hdac_bus *bus = codec->bus;
- struct i915_audio_component *acomp = bus->audio_component;
- int port, pipe;
-
- if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
- return -ENODEV;
- port = pin2port(codec, nid);
- if (port < 0)
- return -EINVAL;
- pipe = dev_id;
- return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
-
-/**
- * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
- * @codec: HDA codec
- * @nid: the pin widget NID
- * @dev_id: device identifier
- * @audio_enabled: the pointer to store the current audio state
- * @buffer: the buffer pointer to store ELD bytes
- * @max_bytes: the max bytes to be stored on @buffer
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function queries the current state of the audio on the given
- * digital port and fetches the ELD bytes onto the given buffer.
- * It returns the number of bytes for the total ELD data, zero for
- * invalid ELD, or a negative error code.
- *
- * The return size is the total bytes required for the whole ELD bytes,
- * thus it may be over @max_bytes. If it's over @max_bytes, it implies
- * that only a part of ELD bytes have been fetched.
- */
-int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
- bool *audio_enabled, char *buffer, int max_bytes)
-{
- struct hdac_bus *bus = codec->bus;
- struct i915_audio_component *acomp = bus->audio_component;
- int port, pipe;
-
- if (!acomp || !acomp->ops || !acomp->ops->get_eld)
- return -ENODEV;
-
- port = pin2port(codec, nid);
- if (port < 0)
- return -EINVAL;
-
- pipe = dev_id;
- return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
- buffer, max_bytes);
-}
-EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
-
-static int hdac_component_master_bind(struct device *dev)
-{
- struct i915_audio_component *acomp = hdac_acomp;
- int ret;
-
- ret = component_bind_all(dev, acomp);
- if (ret < 0)
- return ret;
-
- if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power &&
- acomp->ops->put_power && acomp->ops->get_cdclk_freq))) {
- ret = -EINVAL;
- goto out_unbind;
- }
-
- /*
- * Atm, we don't support dynamic unbinding initiated by the child
- * component, so pin its containing module until we unbind.
- */
- if (!try_module_get(acomp->ops->owner)) {
- ret = -ENODEV;
- goto out_unbind;
- }
-
- return 0;
-
-out_unbind:
- component_unbind_all(dev, acomp);
-
- return ret;
-}
-
-static void hdac_component_master_unbind(struct device *dev)
-{
- struct i915_audio_component *acomp = hdac_acomp;
-
- module_put(acomp->ops->owner);
- component_unbind_all(dev, acomp);
- WARN_ON(acomp->ops || acomp->dev);
-}
-
-static const struct component_master_ops hdac_component_master_ops = {
- .bind = hdac_component_master_bind,
- .unbind = hdac_component_master_unbind,
-};
-
-static int hdac_component_master_match(struct device *dev, void *data)
-{
- /* i915 is the only supported component */
return !strcmp(dev->driver->name, "i915");
}
-/**
- * snd_hdac_i915_register_notifier - Register i915 audio component ops
- * @aops: i915 audio component ops
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function sets the given ops to be called by the i915 graphics driver.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
-{
- if (!hdac_acomp)
- return -ENODEV;
-
- hdac_acomp->audio_ops = aops;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
-
/* check whether intel graphics is present */
static bool i915_gfx_present(void)
{
@@ -359,83 +111,26 @@ static bool i915_gfx_present(void)
*/
int snd_hdac_i915_init(struct hdac_bus *bus)
{
- struct component_match *match = NULL;
- struct device *dev = bus->dev;
- struct i915_audio_component *acomp;
- int ret;
-
- if (WARN_ON(hdac_acomp))
- return -EBUSY;
+ struct drm_audio_component *acomp;
+ int err;
if (!i915_gfx_present())
return -ENODEV;
- acomp = kzalloc(sizeof(*acomp), GFP_KERNEL);
+ err = snd_hdac_acomp_init(bus, NULL,
+ i915_component_master_match,
+ sizeof(struct i915_audio_component) - sizeof(*acomp));
+ if (err < 0)
+ return err;
+ acomp = bus->audio_component;
if (!acomp)
- return -ENOMEM;
- bus->audio_component = acomp;
- hdac_acomp = acomp;
-
- component_match_add(dev, &match, hdac_component_master_match, bus);
- ret = component_master_add_with_match(dev, &hdac_component_master_ops,
- match);
- if (ret < 0)
- goto out_err;
-
- /*
- * Atm, we don't support deferring the component binding, so make sure
- * i915 is loaded and that the binding successfully completes.
- */
- request_module("i915");
-
+ return -ENODEV;
+ if (!acomp->ops)
+ request_module("i915");
if (!acomp->ops) {
- ret = -ENODEV;
- goto out_master_del;
+ snd_hdac_acomp_exit(bus);
+ return -ENODEV;
}
- dev_dbg(dev, "bound to i915 component master\n");
-
return 0;
-out_master_del:
- component_master_del(dev, &hdac_component_master_ops);
-out_err:
- kfree(acomp);
- bus->audio_component = NULL;
- hdac_acomp = NULL;
- dev_info(dev, "failed to add i915 component master (%d)\n", ret);
-
- return ret;
}
EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
-
-/**
- * snd_hdac_i915_exit - Finalize i915 audio component
- * @bus: HDA core bus
- *
- * This function is supposed to be used only by a HD-audio controller
- * driver that needs the interaction with i915 graphics.
- *
- * This function releases the i915 audio component that has been used.
- *
- * Returns zero for success or a negative error code.
- */
-int snd_hdac_i915_exit(struct hdac_bus *bus)
-{
- struct device *dev = bus->dev;
- struct i915_audio_component *acomp = bus->audio_component;
-
- if (!acomp)
- return 0;
-
- WARN_ON(bus->i915_power_refcount);
- if (bus->i915_power_refcount > 0 && acomp->ops)
- acomp->ops->put_power(acomp->dev);
-
- component_master_del(dev, &hdac_component_master_ops);
-
- kfree(acomp);
- bus->audio_component = NULL;
- hdac_acomp = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_i915_exit);