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