blob: 364f64c0e4a313c2a96eb6f5fce90876514b3e49 [file] [log] [blame]
Takashi Iwaid068ebc2015-03-02 23:22:59 +01001/*
2 * HD-audio core bus driver
3 */
4
5#include <linux/init.h>
6#include <linux/device.h>
7#include <linux/module.h>
8#include <linux/export.h>
9#include <sound/hdaudio.h>
10
11static void process_unsol_events(struct work_struct *work);
12
13/**
14 * snd_hdac_bus_init - initialize a HD-audio bas bus
15 * @bus: the pointer to bus object
16 *
17 * Returns 0 if successful, or a negative error code.
18 */
19int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
20 const struct hdac_bus_ops *ops)
21{
22 memset(bus, 0, sizeof(*bus));
23 bus->dev = dev;
24 bus->ops = ops;
25 INIT_LIST_HEAD(&bus->codec_list);
26 INIT_WORK(&bus->unsol_work, process_unsol_events);
27 mutex_init(&bus->cmd_mutex);
28 return 0;
29}
30EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
31
32/**
33 * snd_hdac_bus_exit - clean up a HD-audio bas bus
34 * @bus: the pointer to bus object
35 */
36void snd_hdac_bus_exit(struct hdac_bus *bus)
37{
38 WARN_ON(!list_empty(&bus->codec_list));
39 cancel_work_sync(&bus->unsol_work);
40}
41EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
42
43/**
44 * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
45 * @bus: bus object
46 * @cmd: HD-audio encoded verb
47 * @res: pointer to store the response, NULL if performing asynchronously
48 *
49 * Returns 0 if successful, or a negative error code.
50 */
51int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
52 unsigned int cmd, unsigned int *res)
53{
54 int err;
55
56 mutex_lock(&bus->cmd_mutex);
57 err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
58 mutex_unlock(&bus->cmd_mutex);
59 return err;
60}
61EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);
62
63/**
64 * snd_hdac_bus_exec_verb_unlocked - unlocked version
65 * @bus: bus object
66 * @cmd: HD-audio encoded verb
67 * @res: pointer to store the response, NULL if performing asynchronously
68 *
69 * Returns 0 if successful, or a negative error code.
70 */
71int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
72 unsigned int cmd, unsigned int *res)
73{
74 unsigned int tmp;
75 int err;
76
77 if (cmd == ~0)
78 return -EINVAL;
79
80 if (res)
81 *res = -1;
82 else if (bus->sync_write)
83 res = &tmp;
84 for (;;) {
85 err = bus->ops->command(bus, cmd);
86 if (err != -EAGAIN)
87 break;
88 /* process pending verbs */
89 err = bus->ops->get_response(bus, addr, &tmp);
90 if (err)
91 break;
92 }
93 if (!err && res)
94 err = bus->ops->get_response(bus, addr, res);
95 return err;
96}
97EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
98
99/**
100 * snd_hdac_bus_queue_event - add an unsolicited event to queue
101 * @bus: the BUS
102 * @res: unsolicited event (lower 32bit of RIRB entry)
103 * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
104 *
105 * Adds the given event to the queue. The events are processed in
106 * the workqueue asynchronously. Call this function in the interrupt
107 * hanlder when RIRB receives an unsolicited event.
108 */
109void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
110{
111 unsigned int wp;
112
113 if (!bus)
114 return;
115
116 wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
117 bus->unsol_wp = wp;
118
119 wp <<= 1;
120 bus->unsol_queue[wp] = res;
121 bus->unsol_queue[wp + 1] = res_ex;
122
123 schedule_work(&bus->unsol_work);
124}
125EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
126
127/*
128 * process queued unsolicited events
129 */
130static void process_unsol_events(struct work_struct *work)
131{
132 struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
133 struct hdac_device *codec;
134 struct hdac_driver *drv;
135 unsigned int rp, caddr, res;
136
137 while (bus->unsol_rp != bus->unsol_wp) {
138 rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
139 bus->unsol_rp = rp;
140 rp <<= 1;
141 res = bus->unsol_queue[rp];
142 caddr = bus->unsol_queue[rp + 1];
143 if (!(caddr & (1 << 4))) /* no unsolicited event? */
144 continue;
145 codec = bus->caddr_tbl[caddr & 0x0f];
146 if (!codec || !codec->dev.driver)
147 continue;
148 drv = drv_to_hdac_driver(codec->dev.driver);
149 if (drv->unsol_event)
150 drv->unsol_event(codec, res);
151 }
152}
153
154int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
155{
156 if (bus->caddr_tbl[codec->addr]) {
157 dev_err(bus->dev, "address 0x%x is already occupied\n",
158 codec->addr);
159 return -EBUSY;
160 }
161
162 list_add_tail(&codec->list, &bus->codec_list);
163 bus->caddr_tbl[codec->addr] = codec;
164 set_bit(codec->addr, &bus->codec_powered);
165 bus->num_codecs++;
166 return 0;
167}
168EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
169
170void snd_hdac_bus_remove_device(struct hdac_bus *bus,
171 struct hdac_device *codec)
172{
173 WARN_ON(bus != codec->bus);
174 if (list_empty(&codec->list))
175 return;
176 list_del_init(&codec->list);
177 bus->caddr_tbl[codec->addr] = NULL;
178 clear_bit(codec->addr, &bus->codec_powered);
179 bus->num_codecs--;
180}
181EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);