162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HD-audio controller helpers 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <sound/core.h> 1062306a36Sopenharmony_ci#include <sound/hdaudio.h> 1162306a36Sopenharmony_ci#include <sound/hda_register.h> 1262306a36Sopenharmony_ci#include "local.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* clear CORB read pointer properly */ 1562306a36Sopenharmony_cistatic void azx_clear_corbrp(struct hdac_bus *bus) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci int timeout; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci for (timeout = 1000; timeout > 0; timeout--) { 2062306a36Sopenharmony_ci if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST) 2162306a36Sopenharmony_ci break; 2262306a36Sopenharmony_ci udelay(1); 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci if (timeout <= 0) 2562306a36Sopenharmony_ci dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n", 2662306a36Sopenharmony_ci snd_hdac_chip_readw(bus, CORBRP)); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci snd_hdac_chip_writew(bus, CORBRP, 0); 2962306a36Sopenharmony_ci for (timeout = 1000; timeout > 0; timeout--) { 3062306a36Sopenharmony_ci if (snd_hdac_chip_readw(bus, CORBRP) == 0) 3162306a36Sopenharmony_ci break; 3262306a36Sopenharmony_ci udelay(1); 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci if (timeout <= 0) 3562306a36Sopenharmony_ci dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n", 3662306a36Sopenharmony_ci snd_hdac_chip_readw(bus, CORBRP)); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers 4162306a36Sopenharmony_ci * @bus: HD-audio core bus 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_civoid snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci WARN_ON_ONCE(!bus->rb.area); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 4862306a36Sopenharmony_ci /* CORB set up */ 4962306a36Sopenharmony_ci bus->corb.addr = bus->rb.addr; 5062306a36Sopenharmony_ci bus->corb.buf = (__le32 *)bus->rb.area; 5162306a36Sopenharmony_ci snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr); 5262306a36Sopenharmony_ci snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr)); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* set the corb size to 256 entries (ULI requires explicitly) */ 5562306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, CORBSIZE, 0x02); 5662306a36Sopenharmony_ci /* set the corb write pointer to 0 */ 5762306a36Sopenharmony_ci snd_hdac_chip_writew(bus, CORBWP, 0); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* reset the corb hw read pointer */ 6062306a36Sopenharmony_ci snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST); 6162306a36Sopenharmony_ci if (!bus->corbrp_self_clear) 6262306a36Sopenharmony_ci azx_clear_corbrp(bus); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* enable corb dma */ 6562306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* RIRB set up */ 6862306a36Sopenharmony_ci bus->rirb.addr = bus->rb.addr + 2048; 6962306a36Sopenharmony_ci bus->rirb.buf = (__le32 *)(bus->rb.area + 2048); 7062306a36Sopenharmony_ci bus->rirb.wp = bus->rirb.rp = 0; 7162306a36Sopenharmony_ci memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds)); 7262306a36Sopenharmony_ci snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr); 7362306a36Sopenharmony_ci snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* set the rirb size to 256 entries (ULI requires explicitly) */ 7662306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02); 7762306a36Sopenharmony_ci /* reset the rirb hw write pointer */ 7862306a36Sopenharmony_ci snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST); 7962306a36Sopenharmony_ci /* set N=1, get RIRB response interrupt for new entry */ 8062306a36Sopenharmony_ci snd_hdac_chip_writew(bus, RINTCNT, 1); 8162306a36Sopenharmony_ci /* enable rirb dma and response irq */ 8262306a36Sopenharmony_ci if (bus->not_use_interrupts) 8362306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN); 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); 8662306a36Sopenharmony_ci /* Accept unsolicited responses */ 8762306a36Sopenharmony_ci snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); 8862306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* wait for cmd dmas till they are stopped */ 9362306a36Sopenharmony_cistatic void hdac_wait_for_cmd_dmas(struct hdac_bus *bus) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long timeout; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 9862306a36Sopenharmony_ci while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN) 9962306a36Sopenharmony_ci && time_before(jiffies, timeout)) 10062306a36Sopenharmony_ci udelay(10); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 10362306a36Sopenharmony_ci while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN) 10462306a36Sopenharmony_ci && time_before(jiffies, timeout)) 10562306a36Sopenharmony_ci udelay(10); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers 11062306a36Sopenharmony_ci * @bus: HD-audio core bus 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_civoid snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 11562306a36Sopenharmony_ci /* disable ringbuffer DMAs */ 11662306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBCTL, 0); 11762306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, CORBCTL, 0); 11862306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci hdac_wait_for_cmd_dmas(bus); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 12362306a36Sopenharmony_ci /* disable unsolicited responses */ 12462306a36Sopenharmony_ci snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0); 12562306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic unsigned int azx_command_addr(u32 cmd) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci unsigned int addr = cmd >> 28; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (snd_BUG_ON(addr >= HDA_MAX_CODECS)) 13462306a36Sopenharmony_ci addr = 0; 13562306a36Sopenharmony_ci return addr; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * snd_hdac_bus_send_cmd - send a command verb via CORB 14062306a36Sopenharmony_ci * @bus: HD-audio core bus 14162306a36Sopenharmony_ci * @val: encoded verb value to send 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Returns zero for success or a negative error code. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ciint snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci unsigned int addr = azx_command_addr(val); 14862306a36Sopenharmony_ci unsigned int wp, rp; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci bus->last_cmd[azx_command_addr(val)] = val; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* add command to corb */ 15562306a36Sopenharmony_ci wp = snd_hdac_chip_readw(bus, CORBWP); 15662306a36Sopenharmony_ci if (wp == 0xffff) { 15762306a36Sopenharmony_ci /* something wrong, controller likely turned to D3 */ 15862306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 15962306a36Sopenharmony_ci return -EIO; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci wp++; 16262306a36Sopenharmony_ci wp %= AZX_MAX_CORB_ENTRIES; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rp = snd_hdac_chip_readw(bus, CORBRP); 16562306a36Sopenharmony_ci if (wp == rp) { 16662306a36Sopenharmony_ci /* oops, it's full */ 16762306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 16862306a36Sopenharmony_ci return -EAGAIN; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci bus->rirb.cmds[addr]++; 17262306a36Sopenharmony_ci bus->corb.buf[wp] = cpu_to_le32(val); 17362306a36Sopenharmony_ci snd_hdac_chip_writew(bus, CORBWP, wp); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci#define AZX_RIRB_EX_UNSOL_EV (1<<4) 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/** 18462306a36Sopenharmony_ci * snd_hdac_bus_update_rirb - retrieve RIRB entries 18562306a36Sopenharmony_ci * @bus: HD-audio core bus 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * Usually called from interrupt handler. 18862306a36Sopenharmony_ci * The caller needs bus->reg_lock spinlock before calling this. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_civoid snd_hdac_bus_update_rirb(struct hdac_bus *bus) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci unsigned int rp, wp; 19362306a36Sopenharmony_ci unsigned int addr; 19462306a36Sopenharmony_ci u32 res, res_ex; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci wp = snd_hdac_chip_readw(bus, RIRBWP); 19762306a36Sopenharmony_ci if (wp == 0xffff) { 19862306a36Sopenharmony_ci /* something wrong, controller likely turned to D3 */ 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (wp == bus->rirb.wp) 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci bus->rirb.wp = wp; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci while (bus->rirb.rp != wp) { 20762306a36Sopenharmony_ci bus->rirb.rp++; 20862306a36Sopenharmony_ci bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */ 21162306a36Sopenharmony_ci res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]); 21262306a36Sopenharmony_ci res = le32_to_cpu(bus->rirb.buf[rp]); 21362306a36Sopenharmony_ci addr = res_ex & 0xf; 21462306a36Sopenharmony_ci if (addr >= HDA_MAX_CODECS) { 21562306a36Sopenharmony_ci dev_err(bus->dev, 21662306a36Sopenharmony_ci "spurious response %#x:%#x, rp = %d, wp = %d", 21762306a36Sopenharmony_ci res, res_ex, bus->rirb.rp, wp); 21862306a36Sopenharmony_ci snd_BUG(); 21962306a36Sopenharmony_ci } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) 22062306a36Sopenharmony_ci snd_hdac_bus_queue_event(bus, res, res_ex); 22162306a36Sopenharmony_ci else if (bus->rirb.cmds[addr]) { 22262306a36Sopenharmony_ci bus->rirb.res[addr] = res; 22362306a36Sopenharmony_ci bus->rirb.cmds[addr]--; 22462306a36Sopenharmony_ci if (!bus->rirb.cmds[addr] && 22562306a36Sopenharmony_ci waitqueue_active(&bus->rirb_wq)) 22662306a36Sopenharmony_ci wake_up(&bus->rirb_wq); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci dev_err_ratelimited(bus->dev, 22962306a36Sopenharmony_ci "spurious response %#x:%#x, last cmd=%#08x\n", 23062306a36Sopenharmony_ci res, res_ex, bus->last_cmd[addr]); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/** 23762306a36Sopenharmony_ci * snd_hdac_bus_get_response - receive a response via RIRB 23862306a36Sopenharmony_ci * @bus: HD-audio core bus 23962306a36Sopenharmony_ci * @addr: codec address 24062306a36Sopenharmony_ci * @res: pointer to store the value, NULL when not needed 24162306a36Sopenharmony_ci * 24262306a36Sopenharmony_ci * Returns zero if a value is read, or a negative error code. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ciint snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, 24562306a36Sopenharmony_ci unsigned int *res) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci unsigned long timeout; 24862306a36Sopenharmony_ci unsigned long loopcounter; 24962306a36Sopenharmony_ci wait_queue_entry_t wait; 25062306a36Sopenharmony_ci bool warned = false; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci init_wait_entry(&wait, 0); 25362306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1000); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (loopcounter = 0;; loopcounter++) { 25662306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 25762306a36Sopenharmony_ci if (!bus->polling_mode) 25862306a36Sopenharmony_ci prepare_to_wait(&bus->rirb_wq, &wait, 25962306a36Sopenharmony_ci TASK_UNINTERRUPTIBLE); 26062306a36Sopenharmony_ci if (bus->polling_mode) 26162306a36Sopenharmony_ci snd_hdac_bus_update_rirb(bus); 26262306a36Sopenharmony_ci if (!bus->rirb.cmds[addr]) { 26362306a36Sopenharmony_ci if (res) 26462306a36Sopenharmony_ci *res = bus->rirb.res[addr]; /* the last value */ 26562306a36Sopenharmony_ci if (!bus->polling_mode) 26662306a36Sopenharmony_ci finish_wait(&bus->rirb_wq, &wait); 26762306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 27162306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci#define LOOP_COUNT_MAX 3000 27462306a36Sopenharmony_ci if (!bus->polling_mode) { 27562306a36Sopenharmony_ci schedule_timeout(msecs_to_jiffies(2)); 27662306a36Sopenharmony_ci } else if (bus->needs_damn_long_delay || 27762306a36Sopenharmony_ci loopcounter > LOOP_COUNT_MAX) { 27862306a36Sopenharmony_ci if (loopcounter > LOOP_COUNT_MAX && !warned) { 27962306a36Sopenharmony_ci dev_dbg_ratelimited(bus->dev, 28062306a36Sopenharmony_ci "too slow response, last cmd=%#08x\n", 28162306a36Sopenharmony_ci bus->last_cmd[addr]); 28262306a36Sopenharmony_ci warned = true; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci msleep(2); /* temporary workaround */ 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci udelay(10); 28762306a36Sopenharmony_ci cond_resched(); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (!bus->polling_mode) 29262306a36Sopenharmony_ci finish_wait(&bus->rirb_wq, &wait); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return -EIO; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci#define HDAC_MAX_CAPS 10 29962306a36Sopenharmony_ci/** 30062306a36Sopenharmony_ci * snd_hdac_bus_parse_capabilities - parse capability structure 30162306a36Sopenharmony_ci * @bus: the pointer to bus object 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * Returns 0 if successful, or a negative error code. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ciint snd_hdac_bus_parse_capabilities(struct hdac_bus *bus) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci unsigned int cur_cap; 30862306a36Sopenharmony_ci unsigned int offset; 30962306a36Sopenharmony_ci unsigned int counter = 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci offset = snd_hdac_chip_readw(bus, LLCH); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Lets walk the linked capabilities list */ 31462306a36Sopenharmony_ci do { 31562306a36Sopenharmony_ci cur_cap = _snd_hdac_chip_readl(bus, offset); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci dev_dbg(bus->dev, "Capability version: 0x%x\n", 31862306a36Sopenharmony_ci (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dev_dbg(bus->dev, "HDA capability ID: 0x%x\n", 32162306a36Sopenharmony_ci (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (cur_cap == -1) { 32462306a36Sopenharmony_ci dev_dbg(bus->dev, "Invalid capability reg read\n"); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) { 32962306a36Sopenharmony_ci case AZX_ML_CAP_ID: 33062306a36Sopenharmony_ci dev_dbg(bus->dev, "Found ML capability\n"); 33162306a36Sopenharmony_ci bus->mlcap = bus->remap_addr + offset; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci case AZX_GTS_CAP_ID: 33562306a36Sopenharmony_ci dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset); 33662306a36Sopenharmony_ci bus->gtscap = bus->remap_addr + offset; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci case AZX_PP_CAP_ID: 34062306a36Sopenharmony_ci /* PP capability found, the Audio DSP is present */ 34162306a36Sopenharmony_ci dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset); 34262306a36Sopenharmony_ci bus->ppcap = bus->remap_addr + offset; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci case AZX_SPB_CAP_ID: 34662306a36Sopenharmony_ci /* SPIB capability found, handler function */ 34762306a36Sopenharmony_ci dev_dbg(bus->dev, "Found SPB capability\n"); 34862306a36Sopenharmony_ci bus->spbcap = bus->remap_addr + offset; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci case AZX_DRSM_CAP_ID: 35262306a36Sopenharmony_ci /* DMA resume capability found, handler function */ 35362306a36Sopenharmony_ci dev_dbg(bus->dev, "Found DRSM capability\n"); 35462306a36Sopenharmony_ci bus->drsmcap = bus->remap_addr + offset; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci default: 35862306a36Sopenharmony_ci dev_err(bus->dev, "Unknown capability %d\n", cur_cap); 35962306a36Sopenharmony_ci cur_cap = 0; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci counter++; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (counter > HDAC_MAX_CAPS) { 36662306a36Sopenharmony_ci dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n"); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* read the offset of next capability */ 37162306a36Sopenharmony_ci offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci } while (offset); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* 38062306a36Sopenharmony_ci * Lowlevel interface 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/** 38462306a36Sopenharmony_ci * snd_hdac_bus_enter_link_reset - enter link reset 38562306a36Sopenharmony_ci * @bus: HD-audio core bus 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Enter to the link reset state. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_civoid snd_hdac_bus_enter_link_reset(struct hdac_bus *bus) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci unsigned long timeout; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* reset controller */ 39462306a36Sopenharmony_ci snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 39762306a36Sopenharmony_ci while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) && 39862306a36Sopenharmony_ci time_before(jiffies, timeout)) 39962306a36Sopenharmony_ci usleep_range(500, 1000); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/** 40462306a36Sopenharmony_ci * snd_hdac_bus_exit_link_reset - exit link reset 40562306a36Sopenharmony_ci * @bus: HD-audio core bus 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * Exit from the link reset state. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_civoid snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci unsigned long timeout; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(100); 41662306a36Sopenharmony_ci while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) 41762306a36Sopenharmony_ci usleep_range(500, 1000); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* reset codec link */ 42262306a36Sopenharmony_ciint snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci if (!full_reset) 42562306a36Sopenharmony_ci goto skip_reset; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* clear STATESTS if not in reset */ 42862306a36Sopenharmony_ci if (snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) 42962306a36Sopenharmony_ci snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* reset controller */ 43262306a36Sopenharmony_ci snd_hdac_bus_enter_link_reset(bus); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* delay for >= 100us for codec PLL to settle per spec 43562306a36Sopenharmony_ci * Rev 0.9 section 5.5.1 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci usleep_range(500, 1000); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Bring controller out of reset */ 44062306a36Sopenharmony_ci snd_hdac_bus_exit_link_reset(bus); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Brent Chartrand said to wait >= 540us for codecs to initialize */ 44362306a36Sopenharmony_ci usleep_range(1000, 1200); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci skip_reset: 44662306a36Sopenharmony_ci /* check to see if controller is ready */ 44762306a36Sopenharmony_ci if (!snd_hdac_chip_readb(bus, GCTL)) { 44862306a36Sopenharmony_ci dev_dbg(bus->dev, "controller not ready!\n"); 44962306a36Sopenharmony_ci return -EBUSY; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* detect codecs */ 45362306a36Sopenharmony_ci if (!bus->codec_mask) { 45462306a36Sopenharmony_ci bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); 45562306a36Sopenharmony_ci dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/* enable interrupts */ 46362306a36Sopenharmony_cistatic void azx_int_enable(struct hdac_bus *bus) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci /* enable controller CIE and GIE */ 46662306a36Sopenharmony_ci snd_hdac_chip_updatel(bus, INTCTL, 46762306a36Sopenharmony_ci AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, 46862306a36Sopenharmony_ci AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* disable interrupts */ 47262306a36Sopenharmony_cistatic void azx_int_disable(struct hdac_bus *bus) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct hdac_stream *azx_dev; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* disable interrupts in stream descriptor */ 47762306a36Sopenharmony_ci list_for_each_entry(azx_dev, &bus->stream_list, list) 47862306a36Sopenharmony_ci snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* disable SIE for all streams & disable controller CIE and GIE */ 48162306a36Sopenharmony_ci snd_hdac_chip_writel(bus, INTCTL, 0); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* clear interrupts */ 48562306a36Sopenharmony_cistatic void azx_int_clear(struct hdac_bus *bus) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct hdac_stream *azx_dev; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* clear stream status */ 49062306a36Sopenharmony_ci list_for_each_entry(azx_dev, &bus->stream_list, list) 49162306a36Sopenharmony_ci snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* clear STATESTS */ 49462306a36Sopenharmony_ci snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* clear rirb status */ 49762306a36Sopenharmony_ci snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* clear int status */ 50062306a36Sopenharmony_ci snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * snd_hdac_bus_init_chip - reset and start the controller registers 50562306a36Sopenharmony_ci * @bus: HD-audio core bus 50662306a36Sopenharmony_ci * @full_reset: Do full reset 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_cibool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci if (bus->chip_init) 51162306a36Sopenharmony_ci return false; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* reset controller */ 51462306a36Sopenharmony_ci snd_hdac_bus_reset_link(bus, full_reset); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* clear interrupts */ 51762306a36Sopenharmony_ci azx_int_clear(bus); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* initialize the codec command I/O */ 52062306a36Sopenharmony_ci snd_hdac_bus_init_cmd_io(bus); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* enable interrupts after CORB/RIRB buffers are initialized above */ 52362306a36Sopenharmony_ci azx_int_enable(bus); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* program the position buffer */ 52662306a36Sopenharmony_ci if (bus->use_posbuf && bus->posbuf.addr) { 52762306a36Sopenharmony_ci snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr); 52862306a36Sopenharmony_ci snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr)); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci bus->chip_init = true; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return true; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/** 53862306a36Sopenharmony_ci * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os 53962306a36Sopenharmony_ci * @bus: HD-audio core bus 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_civoid snd_hdac_bus_stop_chip(struct hdac_bus *bus) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci if (!bus->chip_init) 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* disable interrupts */ 54762306a36Sopenharmony_ci azx_int_disable(bus); 54862306a36Sopenharmony_ci azx_int_clear(bus); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* disable CORB/RIRB */ 55162306a36Sopenharmony_ci snd_hdac_bus_stop_cmd_io(bus); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* disable position buffer */ 55462306a36Sopenharmony_ci if (bus->posbuf.addr) { 55562306a36Sopenharmony_ci snd_hdac_chip_writel(bus, DPLBASE, 0); 55662306a36Sopenharmony_ci snd_hdac_chip_writel(bus, DPUBASE, 0); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci bus->chip_init = false; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/** 56462306a36Sopenharmony_ci * snd_hdac_bus_handle_stream_irq - interrupt handler for streams 56562306a36Sopenharmony_ci * @bus: HD-audio core bus 56662306a36Sopenharmony_ci * @status: INTSTS register value 56762306a36Sopenharmony_ci * @ack: callback to be called for woken streams 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * Returns the bits of handled streams, or zero if no stream is handled. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ciint snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, 57262306a36Sopenharmony_ci void (*ack)(struct hdac_bus *, 57362306a36Sopenharmony_ci struct hdac_stream *)) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct hdac_stream *azx_dev; 57662306a36Sopenharmony_ci u8 sd_status; 57762306a36Sopenharmony_ci int handled = 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci list_for_each_entry(azx_dev, &bus->stream_list, list) { 58062306a36Sopenharmony_ci if (status & azx_dev->sd_int_sta_mask) { 58162306a36Sopenharmony_ci sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); 58262306a36Sopenharmony_ci snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); 58362306a36Sopenharmony_ci handled |= 1 << azx_dev->index; 58462306a36Sopenharmony_ci if ((!azx_dev->substream && !azx_dev->cstream) || 58562306a36Sopenharmony_ci !azx_dev->running || !(sd_status & SD_INT_COMPLETE)) 58662306a36Sopenharmony_ci continue; 58762306a36Sopenharmony_ci if (ack) 58862306a36Sopenharmony_ci ack(bus, azx_dev); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci return handled; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/** 59662306a36Sopenharmony_ci * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers 59762306a36Sopenharmony_ci * @bus: HD-audio core bus 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * Call this after assigning the all streams. 60062306a36Sopenharmony_ci * Returns zero for success, or a negative error code. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ciint snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct hdac_stream *s; 60562306a36Sopenharmony_ci int num_streams = 0; 60662306a36Sopenharmony_ci int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV; 60762306a36Sopenharmony_ci int err; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 61062306a36Sopenharmony_ci /* allocate memory for the BDL for each stream */ 61162306a36Sopenharmony_ci err = snd_dma_alloc_pages(dma_type, bus->dev, 61262306a36Sopenharmony_ci BDL_SIZE, &s->bdl); 61362306a36Sopenharmony_ci num_streams++; 61462306a36Sopenharmony_ci if (err < 0) 61562306a36Sopenharmony_ci return -ENOMEM; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (WARN_ON(!num_streams)) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci /* allocate memory for the position buffer */ 62162306a36Sopenharmony_ci err = snd_dma_alloc_pages(dma_type, bus->dev, 62262306a36Sopenharmony_ci num_streams * 8, &bus->posbuf); 62362306a36Sopenharmony_ci if (err < 0) 62462306a36Sopenharmony_ci return -ENOMEM; 62562306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) 62662306a36Sopenharmony_ci s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* single page (at least 4096 bytes) must suffice for both ringbuffes */ 62962306a36Sopenharmony_ci return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/** 63462306a36Sopenharmony_ci * snd_hdac_bus_free_stream_pages - release BDL and other buffers 63562306a36Sopenharmony_ci * @bus: HD-audio core bus 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_civoid snd_hdac_bus_free_stream_pages(struct hdac_bus *bus) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct hdac_stream *s; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci list_for_each_entry(s, &bus->stream_list, list) { 64262306a36Sopenharmony_ci if (s->bdl.area) 64362306a36Sopenharmony_ci snd_dma_free_pages(&s->bdl); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (bus->rb.area) 64762306a36Sopenharmony_ci snd_dma_free_pages(&bus->rb); 64862306a36Sopenharmony_ci if (bus->posbuf.area) 64962306a36Sopenharmony_ci snd_dma_free_pages(&bus->posbuf); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/** 65462306a36Sopenharmony_ci * snd_hdac_bus_link_power - power up/down codec link 65562306a36Sopenharmony_ci * @codec: HD-audio device 65662306a36Sopenharmony_ci * @enable: whether to power-up the link 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_civoid snd_hdac_bus_link_power(struct hdac_device *codec, bool enable) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci if (enable) 66162306a36Sopenharmony_ci set_bit(codec->addr, &codec->bus->codec_powered); 66262306a36Sopenharmony_ci else 66362306a36Sopenharmony_ci clear_bit(codec->addr, &codec->bus->codec_powered); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_bus_link_power); 666