root/sound/hda/hdac_bus.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_hdac_bus_init
  2. snd_hdac_bus_exit
  3. snd_hdac_bus_exec_verb
  4. snd_hdac_bus_exec_verb_unlocked
  5. snd_hdac_bus_queue_event
  6. snd_hdac_bus_process_unsol_events
  7. snd_hdac_bus_add_device
  8. snd_hdac_bus_remove_device
  9. snd_hdac_aligned_read
  10. snd_hdac_aligned_write

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * HD-audio core bus driver
   4  */
   5 
   6 #include <linux/init.h>
   7 #include <linux/io.h>
   8 #include <linux/device.h>
   9 #include <linux/module.h>
  10 #include <linux/export.h>
  11 #include <sound/hdaudio.h>
  12 #include "local.h"
  13 #include "trace.h"
  14 
  15 static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
  16 
  17 static const struct hdac_bus_ops default_ops = {
  18         .command = snd_hdac_bus_send_cmd,
  19         .get_response = snd_hdac_bus_get_response,
  20 };
  21 
  22 /**
  23  * snd_hdac_bus_init - initialize a HD-audio bas bus
  24  * @bus: the pointer to bus object
  25  * @ops: bus verb operators
  26  *
  27  * Returns 0 if successful, or a negative error code.
  28  */
  29 int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
  30                       const struct hdac_bus_ops *ops)
  31 {
  32         memset(bus, 0, sizeof(*bus));
  33         bus->dev = dev;
  34         if (ops)
  35                 bus->ops = ops;
  36         else
  37                 bus->ops = &default_ops;
  38         bus->dma_type = SNDRV_DMA_TYPE_DEV;
  39         INIT_LIST_HEAD(&bus->stream_list);
  40         INIT_LIST_HEAD(&bus->codec_list);
  41         INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
  42         spin_lock_init(&bus->reg_lock);
  43         mutex_init(&bus->cmd_mutex);
  44         mutex_init(&bus->lock);
  45         INIT_LIST_HEAD(&bus->hlink_list);
  46         bus->irq = -1;
  47         return 0;
  48 }
  49 EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
  50 
  51 /**
  52  * snd_hdac_bus_exit - clean up a HD-audio bas bus
  53  * @bus: the pointer to bus object
  54  */
  55 void snd_hdac_bus_exit(struct hdac_bus *bus)
  56 {
  57         WARN_ON(!list_empty(&bus->stream_list));
  58         WARN_ON(!list_empty(&bus->codec_list));
  59         cancel_work_sync(&bus->unsol_work);
  60 }
  61 EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
  62 
  63 /**
  64  * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
  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  */
  71 int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
  72                            unsigned int cmd, unsigned int *res)
  73 {
  74         int err;
  75 
  76         mutex_lock(&bus->cmd_mutex);
  77         err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
  78         mutex_unlock(&bus->cmd_mutex);
  79         return err;
  80 }
  81 EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb);
  82 
  83 /**
  84  * snd_hdac_bus_exec_verb_unlocked - unlocked version
  85  * @bus: bus object
  86  * @cmd: HD-audio encoded verb
  87  * @res: pointer to store the response, NULL if performing asynchronously
  88  *
  89  * Returns 0 if successful, or a negative error code.
  90  */
  91 int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
  92                                     unsigned int cmd, unsigned int *res)
  93 {
  94         unsigned int tmp;
  95         int err;
  96 
  97         if (cmd == ~0)
  98                 return -EINVAL;
  99 
 100         if (res)
 101                 *res = -1;
 102         else if (bus->sync_write)
 103                 res = &tmp;
 104         for (;;) {
 105                 trace_hda_send_cmd(bus, cmd);
 106                 err = bus->ops->command(bus, cmd);
 107                 if (err != -EAGAIN)
 108                         break;
 109                 /* process pending verbs */
 110                 err = bus->ops->get_response(bus, addr, &tmp);
 111                 if (err)
 112                         break;
 113         }
 114         if (!err && res) {
 115                 err = bus->ops->get_response(bus, addr, res);
 116                 trace_hda_get_response(bus, addr, *res);
 117         }
 118         return err;
 119 }
 120 EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
 121 
 122 /**
 123  * snd_hdac_bus_queue_event - add an unsolicited event to queue
 124  * @bus: the BUS
 125  * @res: unsolicited event (lower 32bit of RIRB entry)
 126  * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
 127  *
 128  * Adds the given event to the queue.  The events are processed in
 129  * the workqueue asynchronously.  Call this function in the interrupt
 130  * hanlder when RIRB receives an unsolicited event.
 131  */
 132 void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
 133 {
 134         unsigned int wp;
 135 
 136         if (!bus)
 137                 return;
 138 
 139         trace_hda_unsol_event(bus, res, res_ex);
 140         wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
 141         bus->unsol_wp = wp;
 142 
 143         wp <<= 1;
 144         bus->unsol_queue[wp] = res;
 145         bus->unsol_queue[wp + 1] = res_ex;
 146 
 147         schedule_work(&bus->unsol_work);
 148 }
 149 EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event);
 150 
 151 /*
 152  * process queued unsolicited events
 153  */
 154 static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
 155 {
 156         struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
 157         struct hdac_device *codec;
 158         struct hdac_driver *drv;
 159         unsigned int rp, caddr, res;
 160 
 161         while (bus->unsol_rp != bus->unsol_wp) {
 162                 rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
 163                 bus->unsol_rp = rp;
 164                 rp <<= 1;
 165                 res = bus->unsol_queue[rp];
 166                 caddr = bus->unsol_queue[rp + 1];
 167                 if (!(caddr & (1 << 4))) /* no unsolicited event? */
 168                         continue;
 169                 codec = bus->caddr_tbl[caddr & 0x0f];
 170                 if (!codec || !codec->dev.driver)
 171                         continue;
 172                 drv = drv_to_hdac_driver(codec->dev.driver);
 173                 if (drv->unsol_event)
 174                         drv->unsol_event(codec, res);
 175         }
 176 }
 177 
 178 /**
 179  * snd_hdac_bus_add_device - Add a codec to bus
 180  * @bus: HDA core bus
 181  * @codec: HDA core device to add
 182  *
 183  * Adds the given codec to the list in the bus.  The caddr_tbl array
 184  * and codec_powered bits are updated, as well.
 185  * Returns zero if success, or a negative error code.
 186  */
 187 int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
 188 {
 189         if (bus->caddr_tbl[codec->addr]) {
 190                 dev_err(bus->dev, "address 0x%x is already occupied\n",
 191                         codec->addr);
 192                 return -EBUSY;
 193         }
 194 
 195         list_add_tail(&codec->list, &bus->codec_list);
 196         bus->caddr_tbl[codec->addr] = codec;
 197         set_bit(codec->addr, &bus->codec_powered);
 198         bus->num_codecs++;
 199         return 0;
 200 }
 201 
 202 /**
 203  * snd_hdac_bus_remove_device - Remove a codec from bus
 204  * @bus: HDA core bus
 205  * @codec: HDA core device to remove
 206  */
 207 void snd_hdac_bus_remove_device(struct hdac_bus *bus,
 208                                 struct hdac_device *codec)
 209 {
 210         WARN_ON(bus != codec->bus);
 211         if (list_empty(&codec->list))
 212                 return;
 213         list_del_init(&codec->list);
 214         bus->caddr_tbl[codec->addr] = NULL;
 215         clear_bit(codec->addr, &bus->codec_powered);
 216         bus->num_codecs--;
 217         flush_work(&bus->unsol_work);
 218 }
 219 
 220 #ifdef CONFIG_SND_HDA_ALIGNED_MMIO
 221 /* Helpers for aligned read/write of mmio space, for Tegra */
 222 unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
 223 {
 224         void __iomem *aligned_addr =
 225                 (void __iomem *)((unsigned long)(addr) & ~0x3);
 226         unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
 227         unsigned int v;
 228 
 229         v = readl(aligned_addr);
 230         return (v >> shift) & mask;
 231 }
 232 EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
 233 
 234 void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
 235                             unsigned int mask)
 236 {
 237         void __iomem *aligned_addr =
 238                 (void __iomem *)((unsigned long)(addr) & ~0x3);
 239         unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
 240         unsigned int v;
 241 
 242         v = readl(aligned_addr);
 243         v &= ~(mask << shift);
 244         v |= val << shift;
 245         writel(v, aligned_addr);
 246 }
 247 EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
 248 #endif /* CONFIG_SND_HDA_ALIGNED_MMIO */

/* [<][>][^][v][top][bottom][index][help] */