Skip to content

Commit

Permalink
ALSA: hda - Move some codes up to hdac_bus struct
Browse files Browse the repository at this point in the history
A few basic codes for communicating over HD-audio bus are moved to
struct hdac_bus now.  It has only command and get_response ops in
addition to the unsolicited event handling.

Note that the codec-side tracing support is disabled temporarily
during this transition due to the code shuffling.  It will be
re-enabled later once when all pieces are settled down.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Iwai committed Mar 23, 2015
1 parent e3d280f commit d068ebc
Showing 10 changed files with 335 additions and 162 deletions.
61 changes: 61 additions & 0 deletions include/sound/hdaudio.h
Original file line number Diff line number Diff line change
@@ -6,6 +6,11 @@
#define __SOUND_HDAUDIO_H

#include <linux/device.h>
#include <sound/hda_verbs.h>

struct hdac_bus;
struct hdac_device;
struct hdac_driver;

/*
* exported bus type
@@ -18,6 +23,9 @@ extern struct bus_type snd_hda_bus_type;
struct hdac_device {
struct device dev;
int type;
struct hdac_bus *bus;
unsigned int addr; /* codec address */
struct list_head list; /* list point for bus codec_list */
};

/* device/driver type used for matching */
@@ -35,8 +43,61 @@ struct hdac_driver {
struct device_driver driver;
int type;
int (*match)(struct hdac_device *dev, struct hdac_driver *drv);
void (*unsol_event)(struct hdac_device *dev, unsigned int event);
};

#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)

/*
* HD-audio bus base driver
*/
struct hdac_bus_ops {
/* send a single command */
int (*command)(struct hdac_bus *bus, unsigned int cmd);
/* get a response from the last command */
int (*get_response)(struct hdac_bus *bus, unsigned int addr,
unsigned int *res);
};

#define HDA_UNSOL_QUEUE_SIZE 64

struct hdac_bus {
struct device *dev;
const struct hdac_bus_ops *ops;

/* codec linked list */
struct list_head codec_list;
unsigned int num_codecs;

/* link caddr -> codec */
struct hdac_device *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];

/* unsolicited event queue */
u32 unsol_queue[HDA_UNSOL_QUEUE_SIZE * 2]; /* ring buffer */
unsigned int unsol_rp, unsol_wp;
struct work_struct unsol_work;

/* bit flags of powered codecs */
unsigned long codec_powered;

/* flags */
bool sync_write:1; /* sync after verb write */

/* locks */
struct mutex cmd_mutex;
};

int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops);
void snd_hdac_bus_exit(struct hdac_bus *bus);
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res);
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);

int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec);

#endif /* __SOUND_HDAUDIO_H */
2 changes: 1 addition & 1 deletion sound/hda/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
snd-hda-core-objs := hda_bus_type.o
snd-hda-core-objs := hda_bus_type.o hdac_bus.o

obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
181 changes: 181 additions & 0 deletions sound/hda/hdac_bus.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* HD-audio core bus driver
*/

#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/export.h>
#include <sound/hdaudio.h>

static void process_unsol_events(struct work_struct *work);

/**
* snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
bus->ops = ops;
INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, process_unsol_events);
mutex_init(&bus->cmd_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_init);

/**
* snd_hdac_bus_exit - clean up a HD-audio bas bus
* @bus: the pointer to bus object
*/
void snd_hdac_bus_exit(struct hdac_bus *bus)
{
WARN_ON(!list_empty(&bus->codec_list));
cancel_work_sync(&bus->unsol_work);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);

/**
* snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
* @bus: bus object
* @cmd: HD-audio encoded verb
* @res: pointer to store the response, NULL if performing asynchronously
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res)
{
int err;

mutex_lock(&bus->cmd_mutex);
err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
mutex_unlock(&bus->cmd_mutex);
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);

/**
* snd_hdac_bus_exec_verb_unlocked - unlocked version
* @bus: bus object
* @cmd: HD-audio encoded verb
* @res: pointer to store the response, NULL if performing asynchronously
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
unsigned int cmd, unsigned int *res)
{
unsigned int tmp;
int err;

if (cmd == ~0)
return -EINVAL;

if (res)
*res = -1;
else if (bus->sync_write)
res = &tmp;
for (;;) {
err = bus->ops->command(bus, cmd);
if (err != -EAGAIN)
break;
/* process pending verbs */
err = bus->ops->get_response(bus, addr, &tmp);
if (err)
break;
}
if (!err && res)
err = bus->ops->get_response(bus, addr, res);
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);

/**
* snd_hdac_bus_queue_event - add an unsolicited event to queue
* @bus: the BUS
* @res: unsolicited event (lower 32bit of RIRB entry)
* @res_ex: codec addr and flags (upper 32bit or RIRB entry)
*
* Adds the given event to the queue. The events are processed in
* the workqueue asynchronously. Call this function in the interrupt
* hanlder when RIRB receives an unsolicited event.
*/
void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
{
unsigned int wp;

if (!bus)
return;

wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
bus->unsol_wp = wp;

wp <<= 1;
bus->unsol_queue[wp] = res;
bus->unsol_queue[wp + 1] = res_ex;

schedule_work(&bus->unsol_work);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);

/*
* process queued unsolicited events
*/
static void process_unsol_events(struct work_struct *work)
{
struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
struct hdac_device *codec;
struct hdac_driver *drv;
unsigned int rp, caddr, res;

while (bus->unsol_rp != bus->unsol_wp) {
rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
bus->unsol_rp = rp;
rp <<= 1;
res = bus->unsol_queue[rp];
caddr = bus->unsol_queue[rp + 1];
if (!(caddr & (1 << 4))) /* no unsolicited event? */
continue;
codec = bus->caddr_tbl[caddr & 0x0f];
if (!codec || !codec->dev.driver)
continue;
drv = drv_to_hdac_driver(codec->dev.driver);
if (drv->unsol_event)
drv->unsol_event(codec, res);
}
}

int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
{
if (bus->caddr_tbl[codec->addr]) {
dev_err(bus->dev, "address 0x%x is already occupied\n",
codec->addr);
return -EBUSY;
}

list_add_tail(&codec->list, &bus->codec_list);
bus->caddr_tbl[codec->addr] = codec;
set_bit(codec->addr, &bus->codec_powered);
bus->num_codecs++;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);

void snd_hdac_bus_remove_device(struct hdac_bus *bus,
struct hdac_device *codec)
{
WARN_ON(bus != codec->bus);
if (list_empty(&codec->list))
return;
list_del_init(&codec->list);
bus->caddr_tbl[codec->addr] = NULL;
clear_bit(codec->addr, &bus->codec_powered);
bus->num_codecs--;
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);
10 changes: 10 additions & 0 deletions sound/pci/hda/hda_bind.c
Original file line number Diff line number Diff line change
@@ -74,6 +74,15 @@ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
return 0;
}

/* process an unsolicited event */
static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
{
struct hda_codec *codec = container_of(dev, struct hda_codec, core);

if (codec->patch_ops.unsol_event)
codec->patch_ops.unsol_event(codec, ev);
}

/* reset the codec name from the preset */
static int codec_refresh_name(struct hda_codec *codec, const char *name)
{
@@ -163,6 +172,7 @@ int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
drv->core.driver.pm = &hda_codec_driver_pm;
drv->core.type = HDA_DEV_LEGACY;
drv->core.match = hda_codec_match;
drv->core.unsol_event = hda_codec_unsol_event;
return driver_register(&drv->core.driver);
}
EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
Loading

0 comments on commit d068ebc

Please sign in to comment.