From patchwork Wed Mar 25 06:01:39 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vinod Koul X-Patchwork-Id: 6088381 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id D7F89BF90F for ; Wed, 25 Mar 2015 06:07:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 1F791202F2 for ; Wed, 25 Mar 2015 06:07:22 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id A630420212 for ; Wed, 25 Mar 2015 06:07:18 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id C3F392651FF; Wed, 25 Mar 2015 07:07:17 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 19FD4261ADE; Wed, 25 Mar 2015 07:05:58 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 38FCD2616EF; Wed, 25 Mar 2015 07:05:56 +0100 (CET) Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by alsa0.perex.cz (Postfix) with ESMTP id 7FBE32617B6 for ; Wed, 25 Mar 2015 07:05:50 +0100 (CET) Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga101.fm.intel.com with ESMTP; 24 Mar 2015 23:05:50 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.11,463,1422950400"; d="scan'208";a="472257807" Received: from vkoul-udesk3.iind.intel.com ([10.223.84.65]) by FMSMGA003.fm.intel.com with ESMTP; 24 Mar 2015 23:05:47 -0700 From: Vinod Koul To: alsa-devel@alsa-project.org Date: Wed, 25 Mar 2015 11:31:39 +0530 Message-Id: <1427263299-14707-4-git-send-email-vinod.koul@intel.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1427263299-14707-1-git-send-email-vinod.koul@intel.com> References: <1427263299-14707-1-git-send-email-vinod.koul@intel.com> Cc: tiwai@suse.de, jeeja.kp@intel.com, Vinod Koul , lgirdwood@gmail.com Subject: [alsa-devel] [RFC v2 3/3] ALSA: hda: add hda controller to hda X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP From: Jeeja KP This adds hdac_controller into hda core. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul --- include/sound/hdaudio.h | 215 ++++++++ sound/hda/Makefile | 2 +- sound/hda/hdac_controller.c | 1284 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1500 insertions(+), 1 deletion(-) create mode 100644 sound/hda/hdac_controller.c diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 675614dc2b88..2843471efc7a 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -5,8 +5,11 @@ #ifndef __SOUND_HDAUDIO_H #define __SOUND_HDAUDIO_H +#include +#include #include #include +#include /* codec node id */ typedef u16 hda_nid_t; @@ -15,6 +18,7 @@ struct hdac_bus; struct hdac_device; struct hdac_driver; struct hdac_widget_tree; +struct hda; /* * exported bus type @@ -125,6 +129,8 @@ struct hdac_bus_ops { /* get a response from the last command */ int (*get_response)(struct hdac_bus *bus, unsigned int addr, unsigned int *res); + /* reset bus for retry verb */ + void (*bus_reset)(struct hdac_bus *bus); }; #define HDA_UNSOL_QUEUE_SIZE 64 @@ -144,6 +150,7 @@ struct hdac_bus { u32 unsol_queue[HDA_UNSOL_QUEUE_SIZE * 2]; /* ring buffer */ unsigned int unsol_rp, unsol_wp; struct work_struct unsol_work; + struct workqueue_struct *workq; /* common workqueue for codecs */ /* bit flags of powered codecs */ unsigned long codec_powered; @@ -153,6 +160,17 @@ struct hdac_bus { /* locks */ struct mutex cmd_mutex; + + void *private_data; + + /* msic op flags */ + unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ + + /*status for controller */ + unsigned int rirb_error:1; /* error in codec communication */ + unsigned int response_reset:1; /* controller was reset */ + unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ + unsigned int in_reset:1; /* during reset operation */ }; int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, @@ -178,4 +196,201 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) clear_bit(codec->addr, &codec->bus->codec_powered); } +/* + * HD-audio contoller base device + */ +struct hda_device { + struct snd_dma_buffer bdl; /* BDL buffer */ + u32 *posbuf; /* position buffer pointer */ + + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int period_bytes; /* size of the period in bytes */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int fifo_size; /* FIFO size */ + unsigned long start_wallclk; /* start + minimum wallclk */ + unsigned long period_wallclk; /* wallclk for period */ + + void __iomem *sd_addr; /* stream descriptor pointer */ + + u32 sd_int_sta_mask; /* stream int status mask */ + + /* pcm support */ + struct snd_pcm_substream *substream; /* assigned substream, + * set in PCM open + */ + unsigned int format_val; /* format value to be set in the + * controller and the codec + */ + unsigned char stream_tag; /* assigned stream */ + unsigned char index; /* stream index */ + int assigned_key; /* last device# key assigned to */ + + unsigned int opened:1; + unsigned int running:1; + unsigned int irq_pending:1; + unsigned int prepared:1; + unsigned int locked:1; + unsigned int wc_marked:1; + unsigned int no_period_wakeup:1; + + struct timecounter tc; + struct cyclecounter cc; + + int delay_negative_threshold; + + /* Allows dsp load to have sole access to the playback stream. */ + struct mutex dsp_mutex; +}; + +/* CORB/RIRB */ +struct hda_rb { + u32 *buf; /* CORB/RIRB buffer + * Each CORB entry is 4byte, RIRB is 8byte + */ + dma_addr_t addr; /* physical address of CORB/RIRB buffer */ + + /* for RIRB */ + unsigned short rp, wp; /* read/write pointers */ + int cmds[HDA_MAX_CODECS]; /* number of pending requests */ + u32 res[HDA_MAX_CODECS]; /* last read value */ +}; + +/* Functions to read/write to hda registers. */ +/* FIXME: should we name this something else ??? */ +struct hda_ops { + void (*reg_writel)(u32 value, u32 __iomem *addr); + u32 (*reg_readl)(u32 __iomem *addr); + void (*reg_writew)(u16 value, u16 __iomem *addr); + u16 (*reg_readw)(u16 __iomem *addr); + void (*reg_writeb)(u8 value, u8 __iomem *addr); + u8 (*reg_readb)(u8 __iomem *addr); + /* Disable msi if supported, PCI only */ + int (*disable_msi_reset_irq)(struct hda *); + /* Allocation ops */ + int (*dma_alloc_pages)(struct hda *chip, + int type, + size_t size, + struct snd_dma_buffer *buf); + void (*dma_free_pages)(struct hda *chip, struct snd_dma_buffer *buf); + int (*substream_alloc_pages)(struct hda *chip, + struct snd_pcm_substream *substream, + size_t size); + int (*substream_free_pages)(struct hda *chip, + struct snd_pcm_substream *substream); + void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream, + struct vm_area_struct *area); + /* Check if current position is acceptable */ + int (*position_check)(struct hda *chip, struct hda_device *hda_dev); +}; + +typedef unsigned int (*hda_get_pos_callback_t)(struct hda *, struct hda_device *); +typedef int (*hda_get_delay_callback_t)(struct hda *, struct hda_device *, + unsigned int pos); + +struct hda { + struct pci_dev *pci; /* FIXME: should we remove PCI assumption, right now its true for us always */ + struct device *dev; + int dev_index; + + /* chip type specific */ + int driver_type; + unsigned int driver_caps; + int playback_streams; + int playback_index_offset; + int capture_streams; + int capture_index_offset; + int num_streams; + + /* Register interaction. */ + const struct hda_ops *ops; + + /* position adjustment callbacks */ + hda_get_pos_callback_t get_position[2]; + hda_get_delay_callback_t get_delay[2]; + + /* pci resources */ + unsigned long addr; + void __iomem *remap_addr; + int irq; + + /* locks */ + spinlock_t reg_lock; + struct mutex open_mutex; /* Prevents concurrent open/close operations */ + struct completion probe_wait; + + /* streams (x num_streams) */ + struct hda_device *hda_dev; + + /* HD codec */ + unsigned short codec_mask; + int codec_probe_mask; /* copied from probe_mask option */ + struct hdac_bus *bus; + unsigned int beep_mode; + + /* CORB/RIRB */ + struct hda_rb corb; + struct hda_rb rirb; + + /* CORB/RIRB and position buffers */ + struct snd_dma_buffer rb; + struct snd_dma_buffer posbuf; + + /* flags */ + const int *bdl_pos_adj; + int poll_count; + unsigned int running:1; + unsigned int initialized:1; + unsigned int single_cmd:1; + unsigned int polling_mode:1; + unsigned int msi:1; + unsigned int probing:1; /* codec probing phase */ + unsigned int snoop:1; + unsigned int align_buffer_size:1; + unsigned int region_requested:1; + unsigned int disabled:1; /* disabled by VGA-switcher */ + + /* for debugging */ + unsigned int last_cmd[HDA_MAX_CODECS]; + + /* reboot notifier (for mysterious hangup problem at power-down) */ + struct notifier_block reboot_notifier; + + struct hda_device saved_hda_dev; /* FIXME: check if we need this */ +}; + +/* Allocation functions. */ +int hda_alloc_stream_pages(struct hda *chip); +void hda_free_stream_pages(struct hda *chip); + +/* pcm helper functions */ +int hda_setup_periods(struct hda *chip, + struct snd_pcm_substream *substream, + struct hda_device *dev); +void hda_reset_device(struct hda *chip, struct hda_device *hda_dev); +int hda_set_device_params(struct hda *chip, struct snd_pcm_substream *substream, + unsigned int format_val); +void hda_set_pcm_constrains(struct hda *chip, struct snd_pcm_runtime *runtime); + +/* PCM setup */ +static inline struct hda_device *get_hda_dev(struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} +unsigned int hda_get_position(struct hda *chip, struct hda_device *hda_dev, + int codec_delay); +unsigned int hda_get_pos_lpib(struct hda *chip, struct hda_device *hda_dev); +unsigned int hda_get_pos_posbuf(struct hda *chip, struct hda_device *hda_dev); + +/* Stream control. */ +void hda_stream_stop(struct hda *chip, struct hda_device *hda_dev); + +/* Allocation functions. */ +int hda_alloc_stream_pages(struct hda *chip); +void hda_free_stream_pages(struct hda *chip); + +/* Low level azx interface */ +void hda_init_chip(struct hda *chip, bool full_reset); +void hda_stop_chip(struct hda *chip); +void hda_enter_link_reset(struct hda *chip); + #endif /* __SOUND_HDAUDIO_H */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index eec5da03b41f..836c5f34b4dd 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,4 +1,4 @@ -snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o hdac_controller.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c new file mode 100644 index 000000000000..3b14f7be43da --- /dev/null +++ b/sound/hda/hdac_controller.c @@ -0,0 +1,1284 @@ +/* + * + * Implementation of Common HDA driver funcitons for Intel HD Audio. + * + * Copyright (c) 2014 Intel Corporation + * Copyright(c) 2004 Intel Corporation. All rights reserved. + * + * Copyright (c) 2004 Takashi Iwai + * PeiSen Hou + * + * Modified by: KP Jeeja + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DSP lock helpers */ +#ifdef CONFIG_SND_HDA_DSP_LOADER +#define dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex) +#define dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex) +#define dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex) +#define dsp_is_locked(dev) ((dev)->locked) +#else +#define dsp_lock_init(dev) do {} while (0) +#define dsp_lock(dev) do {} while (0) +#define dsp_unlock(dev) do {} while (0) +#define dsp_is_locked(dev) 0 +#endif + +/* + * AZX stream operations. + */ + +/* start a stream */ +void hda_stream_start(struct hda *chip, struct hda_device *azx_dev) +{ + /* enable SIE */ + azx_writel(chip, INTCTL, + azx_readl(chip, INTCTL) | (1 << azx_dev->index)); + /* set DMA start and interrupt mask */ + azx_sd_writeb(chip, azx_dev, SD_CTL, + azx_sd_readb(chip, azx_dev, SD_CTL) | + SD_CTL_DMA_START | SD_INT_MASK); +} +EXPORT_SYMBOL_GPL(hda_stream_start); +/* stop DMA */ +static void hda_stream_clear(struct hda *chip, struct hda_device *azx_dev) +{ + azx_sd_writeb(chip, azx_dev, SD_CTL, + azx_sd_readb(chip, azx_dev, SD_CTL) & + ~(SD_CTL_DMA_START | SD_INT_MASK)); + azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ +} + +/* stop a stream */ +void hda_stream_stop(struct hda *chip, struct hda_device *azx_dev) +{ + hda_stream_clear(chip, azx_dev); + /* disable SIE */ + azx_writel(chip, INTCTL, + azx_readl(chip, INTCTL) & ~(1 << azx_dev->index)); +} +EXPORT_SYMBOL_GPL(hda_stream_stop); + +/* reset stream */ +void hda_stream_reset(struct hda *chip, struct hda_device *azx_dev) +{ + unsigned char val; + int timeout; + + hda_stream_clear(chip, azx_dev); + + azx_sd_writeb(chip, azx_dev, SD_CTL, + azx_sd_readb(chip, azx_dev, SD_CTL) | + SD_CTL_STREAM_RESET); + udelay(3); + timeout = 300; + while (!((val = azx_sd_readb(chip, azx_dev, SD_CTL)) & + SD_CTL_STREAM_RESET) && --timeout) + ; + val &= ~SD_CTL_STREAM_RESET; + azx_sd_writeb(chip, azx_dev, SD_CTL, val); + udelay(3); + + timeout = 300; + /* waiting for hardware to report that the stream is out of reset */ + while (((val = azx_sd_readb(chip, azx_dev, SD_CTL)) & + SD_CTL_STREAM_RESET) && --timeout) + ; + + /* reset first position - may not be synced with hw at this time */ + *azx_dev->posbuf = 0; +} +EXPORT_SYMBOL_GPL(hda_stream_reset); + +/* + * set up the SD for streaming + */ +int hda_setup_controller(struct hda *chip, struct hda_device *azx_dev) +{ + unsigned int val; + /* make sure the run bit is zero for SD */ + hda_stream_clear(chip, azx_dev); + /* program the stream_tag */ + val = azx_sd_readl(chip, azx_dev, SD_CTL); + val = (val & ~SD_CTL_STREAM_TAG_MASK) | + (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); + azx_sd_writel(chip, azx_dev, SD_CTL, val); + + /* program the length of samples in cyclic buffer */ + azx_sd_writel(chip, azx_dev, SD_CBL, azx_dev->bufsize); + + /* program the stream format */ + /* this value needs to be the same as the one programmed */ + azx_sd_writew(chip, azx_dev, SD_FORMAT, azx_dev->format_val); + + /* program the stream LVI (last valid index) of the BDL */ + azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->frags - 1); + + /* program the BDL address */ + /* lower BDL address */ + azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + /* upper BDL address */ + azx_sd_writel(chip, azx_dev, SD_BDLPU, + upper_32_bits(azx_dev->bdl.addr)); + + /* enable the position buffer */ + if (chip->get_position[0] != hda_get_pos_lpib || + chip->get_position[1] != hda_get_pos_lpib) { + if (!(azx_readl(chip, DPLBASE) & AZX_DPLBASE_ENABLE)) + azx_writel(chip, DPLBASE, + (u32)chip->posbuf.addr | AZX_DPLBASE_ENABLE); + } + + /* set the interrupt enable bits in the descriptor control register */ + azx_sd_writel(chip, azx_dev, SD_CTL, + azx_sd_readl(chip, azx_dev, SD_CTL) | SD_INT_MASK); + + return 0; +} +EXPORT_SYMBOL_GPL(hda_setup_controller); + +/* assign a stream for the PCM */ +struct hda_device * +hda_assign_device(struct hda *chip, struct snd_pcm_substream *substream) +{ + int dev, i, nums; + struct hda_device *res = NULL; + /* make a non-zero unique key for the substream */ + int key = (substream->pcm->device << 16) | (substream->number << 2) | + (substream->stream + 1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev = chip->playback_index_offset; + nums = chip->playback_streams; + } else { + dev = chip->capture_index_offset; + nums = chip->capture_streams; + } + for (i = 0; i < nums; i++, dev++) { + struct hda_device *azx_dev = &chip->hda_dev[dev]; + + dsp_lock(azx_dev); + if (!azx_dev->opened && !dsp_is_locked(azx_dev)) { + if (azx_dev->assigned_key == key) { + azx_dev->opened = 1; + azx_dev->assigned_key = key; + dsp_unlock(azx_dev); + return azx_dev; + } + if (!res || + (chip->driver_caps & AZX_DCAPS_REVERSE_ASSIGN)) + res = azx_dev; + } + dsp_unlock(azx_dev); + } + if (res) { + dsp_lock(res); + res->opened = 1; + res->assigned_key = key; + dsp_unlock(res); + } + return res; +} +EXPORT_SYMBOL_GPL(hda_assign_device); + +/* release the assigned stream */ +void hda_release_device(struct hda_device *hda_dev) +{ + hda_dev->opened = 0; +} +EXPORT_SYMBOL_GPL(hda_release_device); + +/* + * set up a BDL entry + */ +int hda_setup_bdle(struct hda *chip, + struct snd_dma_buffer *dmab, + struct hda_device *azx_dev, u32 **bdlp, + int ofs, int size, int with_ioc) +{ + u32 *bdl = *bdlp; + + while (size > 0) { + dma_addr_t addr; + int chunk; + + if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) + return -EINVAL; + + addr = snd_sgbuf_get_addr(dmab, ofs); + dev_dbg(chip->dev, "buffer address=%#llx\n", (u64)addr); + /* program the address field of the BDL entry */ + bdl[0] = cpu_to_le32((u32)addr); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + /* program the size field of the BDL entry */ + chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); + /* one BDLE cannot cross 4K boundary on CTHDA chips */ + if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) { + u32 remain = 0x1000 - (ofs & 0xfff); + + if (chunk > remain) + chunk = remain; + } + bdl[2] = cpu_to_le32(chunk); + /* program the IOC to enable interrupt + * only when the whole fragment is processed + */ + size -= chunk; + bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + bdl += 4; + azx_dev->frags++; + ofs += chunk; + } + *bdlp = bdl; + dev_dbg(chip->dev, "bdl: 0x%p, ofs:0x%x\n", bdl, ofs); + return ofs; +} +EXPORT_SYMBOL_GPL(hda_setup_bdle); + +/* + * set up BDL entries + */ +int hda_setup_periods(struct hda *chip, + struct snd_pcm_substream *substream, + struct hda_device *azx_dev) +{ + u32 *bdl; + int i, ofs, periods, period_bytes; + int pos_adj = 0; + + /* reset BDL address */ + azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); + azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); + + period_bytes = azx_dev->period_bytes; + periods = azx_dev->bufsize / period_bytes; + + /* program the initial BDL entries */ + bdl = (u32 *)azx_dev->bdl.area; + ofs = 0; + azx_dev->frags = 0; + + if (chip->bdl_pos_adj) + pos_adj = chip->bdl_pos_adj[chip->dev_index]; + if (!azx_dev->no_period_wakeup && pos_adj > 0) { + struct snd_pcm_runtime *runtime = substream->runtime; + int pos_align = pos_adj; + + pos_adj = (pos_adj * runtime->rate + 47999) / 48000; + if (!pos_adj) + pos_adj = pos_align; + else + pos_adj = ((pos_adj + pos_align - 1) / pos_align) * + pos_align; + pos_adj = frames_to_bytes(runtime, pos_adj); + if (pos_adj >= period_bytes) { + dev_warn(chip->dev, "Too big adjustment %d\n", + pos_adj); + pos_adj = 0; + } else { + ofs = hda_setup_bdle(chip, snd_pcm_get_dma_buf(substream), + azx_dev, + &bdl, ofs, pos_adj, true); + if (ofs < 0) + goto error; + } + } else + pos_adj = 0; + + for (i = 0; i < periods; i++) { + if (i == periods - 1 && pos_adj) + ofs = hda_setup_bdle(chip, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes - pos_adj, 0); + else + ofs = hda_setup_bdle(chip, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes, + !azx_dev->no_period_wakeup); + if (ofs < 0) + goto error; + } + return 0; + + error: + dev_err(chip->dev, "Too many BDL entries: buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(hda_setup_periods); + +unsigned int hda_get_pos_lpib(struct hda *chip, struct hda_device *azx_dev) +{ + return azx_sd_readl(chip, azx_dev, SD_LPIB); +} +EXPORT_SYMBOL_GPL(hda_get_pos_lpib); + +unsigned int hda_get_pos_posbuf(struct hda *chip, struct hda_device *azx_dev) +{ + return le32_to_cpu(*azx_dev->posbuf); +} +EXPORT_SYMBOL_GPL(hda_get_pos_posbuf); + +unsigned int hda_get_position(struct hda *chip, + struct hda_device *azx_dev, int codec_delay) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + unsigned int pos; + int stream = substream->stream; + int delay = 0; + + if (chip->get_position[stream]) + pos = chip->get_position[stream](chip, azx_dev); + else /* use the position buffer as default */ + pos = hda_get_pos_posbuf(chip, azx_dev); + + if (pos >= azx_dev->bufsize) + pos = 0; + + if (substream->runtime) { + if (chip->get_delay[stream]) + delay += chip->get_delay[stream](chip, azx_dev, pos); + delay += codec_delay; + substream->runtime->delay = delay; + } + + return pos; +} +EXPORT_SYMBOL_GPL(hda_get_position); + +void hda_reset_device(struct hda *chip, struct hda_device *azx_dev) +{ + azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); + azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); + azx_sd_writel(chip, azx_dev, SD_CTL, 0); + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; +} +EXPORT_SYMBOL_GPL(hda_reset_device); + +int hda_set_device_params(struct hda *chip, struct snd_pcm_substream *substream, + unsigned int format_val) +{ + + unsigned int bufsize, period_bytes, stream_tag; + struct hda_device *azx_dev = get_hda_dev(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + + dev_dbg(chip->dev, "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", + bufsize, format_val); + + if (bufsize != azx_dev->bufsize || + period_bytes != azx_dev->period_bytes || + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + azx_dev->bufsize = bufsize; + azx_dev->period_bytes = period_bytes; + azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; + err = hda_setup_periods(chip, substream, azx_dev); + if (err < 0) + return err; + } + + /* when LPIB delay correction gives a small negative value, + * we ignore it; currently set the threshold statically to + * 64 frames + */ + if (runtime->period_size > 64) + azx_dev->delay_negative_threshold = -frames_to_bytes(runtime, 64); + else + azx_dev->delay_negative_threshold = 0; + + /* wallclk has 24Mhz clock source */ + azx_dev->period_wallclk = (((runtime->period_size * 24000) / + runtime->rate) * 1000); + hda_setup_controller(chip, azx_dev); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + azx_dev->fifo_size = + azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1; + else + azx_dev->fifo_size = 0; + + stream_tag = azx_dev->stream_tag; + return 0; +} +EXPORT_SYMBOL_GPL(hda_set_device_params); + +void hda_set_pcm_constrains(struct hda *chip, struct snd_pcm_runtime *runtime) +{ + int buff_step; + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + /* avoid wrap-around with wall-clock */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + 20, + 178000000); + + if (chip->align_buffer_size) + /* constrain buffer sizes to be multiple of 128 + bytes. This is more efficient in terms of memory + access but isn't required by the HDA spec and + prevents users from specifying exact period/buffer + sizes. For example for 44.1kHz, a period size set + to 20ms will be rounded to 19.59ms. */ + buff_step = 128; + else + /* Don't enforce steps on buffer sizes, still need to + be multiple of 4 bytes (HDA spec). Tested on Intel + HDA controllers, may not work on all devices where + option needs to be disabled */ + buff_step = 4; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + buff_step); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + buff_step); + +} +EXPORT_SYMBOL_GPL(hda_set_pcm_constrains); + +/* + * CORB / RIRB interface + */ +static int hda_alloc_cmd_io(struct hda *chip) +{ + int err; + + /* single page (at least 4096 bytes) must suffice for both ringbuffes */ + err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &chip->rb); + if (err < 0) + dev_err(chip->dev, "cannot allocate CORB/RIRB\n"); + return err; +} +EXPORT_SYMBOL_GPL(hda_alloc_cmd_io); + +static void hda_init_cmd_io(struct hda *chip) +{ + int timeout; + + spin_lock_irq(&chip->reg_lock); + /* CORB set up */ + chip->corb.addr = chip->rb.addr; + chip->corb.buf = (u32 *)chip->rb.area; + azx_writel(chip, CORBLBASE, (u32)chip->corb.addr); + azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr)); + + /* set the corb size to 256 entries (ULI requires explicitly) */ + azx_writeb(chip, CORBSIZE, 0x02); + /* set the corb write pointer to 0 */ + azx_writew(chip, CORBWP, 0); + + /* reset the corb hw read pointer */ + azx_writew(chip, CORBRP, AZX_CORBRP_RST); + if (!(chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)) { + for (timeout = 1000; timeout > 0; timeout--) { + if ((azx_readw(chip, CORBRP) & AZX_CORBRP_RST) == AZX_CORBRP_RST) + break; + udelay(1); + } + if (timeout <= 0) + dev_err(chip->dev, "CORB reset timeout#1, CORBRP = %d\n", + azx_readw(chip, CORBRP)); + + azx_writew(chip, CORBRP, 0); + for (timeout = 1000; timeout > 0; timeout--) { + if (azx_readw(chip, CORBRP) == 0) + break; + udelay(1); + } + if (timeout <= 0) + dev_err(chip->dev, "CORB reset timeout#2, CORBRP = %d\n", + azx_readw(chip, CORBRP)); + } + + /* enable corb dma */ + azx_writeb(chip, CORBCTL, AZX_CORBCTL_RUN); + + /* RIRB set up */ + chip->rirb.addr = chip->rb.addr + 2048; + chip->rirb.buf = (u32 *)(chip->rb.area + 2048); + chip->rirb.wp = chip->rirb.rp = 0; + memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds)); + azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); + azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); + + /* set the rirb size to 256 entries (ULI requires explicitly) */ + azx_writeb(chip, RIRBSIZE, 0x02); + /* reset the rirb hw write pointer */ + azx_writew(chip, RIRBWP, AZX_RIRBWP_RST); + /* set N=1, get RIRB response interrupt for new entry */ + if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) + azx_writew(chip, RINTCNT, 0xc0); + else + azx_writew(chip, RINTCNT, 1); + /* enable rirb dma and response irq */ + azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); + spin_unlock_irq(&chip->reg_lock); +} +EXPORT_SYMBOL_GPL(hda_init_cmd_io); + +static void hda_free_cmd_io(struct hda *chip) +{ + spin_lock_irq(&chip->reg_lock); + /* disable ringbuffer DMAs */ + azx_writeb(chip, RIRBCTL, 0); + azx_writeb(chip, CORBCTL, 0); + spin_unlock_irq(&chip->reg_lock); +} +EXPORT_SYMBOL_GPL(hda_free_cmd_io); + +static unsigned int hda_command_addr(u32 cmd) +{ + unsigned int addr = cmd >> 28; + + if (addr >= HDA_MAX_CODECS) { + snd_BUG(); + addr = 0; + } + + return addr; +} + +/* send a command */ +static int hda_corb_send_cmd(struct hdac_bus *bus, u32 val) +{ + struct hda *chip = bus->private_data; + unsigned int addr = hda_command_addr(val); + unsigned int wp, rp; + + spin_lock_irq(&chip->reg_lock); + + /* add command to corb */ + wp = azx_readw(chip, CORBWP); + if (wp == 0xffff) { + /* something wrong, controller likely turned to D3 */ + spin_unlock_irq(&chip->reg_lock); + return -EIO; + } + wp++; + wp %= AZX_MAX_CORB_ENTRIES; + + rp = azx_readw(chip, CORBRP); + if (wp == rp) { + /* oops, it's full */ + spin_unlock_irq(&chip->reg_lock); + return -EAGAIN; + } + + chip->rirb.cmds[addr]++; + chip->corb.buf[wp] = cpu_to_le32(val); + azx_writew(chip, CORBWP, wp); + + spin_unlock_irq(&chip->reg_lock); + + return 0; +} + +#define AZX_RIRB_EX_UNSOL_EV (1<<4) + +/** + * hda_queue_unsol_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. + * + * Returns 0 if successful, or a negative error code. + */ +int hda_queue_unsol_event(struct hdac_bus *bus, u32 res, u32 res_ex) +{ + unsigned int wp; + + if (!bus) + return 0; + + wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE; + + wp <<= 1; + bus->unsol_queue[wp] = res; + bus->unsol_queue[wp + 1] = res_ex; + + queue_work(bus->workq, &bus->unsol_work); + + return 0; +} +EXPORT_SYMBOL_GPL(hda_queue_unsol_event); + +/* retrieve RIRB entry - called from interrupt handler */ +static void hda_update_rirb(struct hda *chip) +{ + unsigned int rp, wp; + unsigned int addr; + u32 res, res_ex; + + wp = azx_readw(chip, RIRBWP); + if (wp == 0xffff) { + /* something wrong, controller likely turned to D3 */ + return; + } + + if (wp == chip->rirb.wp) + return; + chip->rirb.wp = wp; + + while (chip->rirb.rp != wp) { + chip->rirb.rp++; + chip->rirb.rp %= AZX_MAX_RIRB_ENTRIES; + + rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ + res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); + res = le32_to_cpu(chip->rirb.buf[rp]); + addr = res_ex & 0xf; + if ((addr >= HDA_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) { + dev_err(chip->dev, "spurious response %#x:%#x, rp = %d, wp = %d", + res, res_ex, + chip->rirb.rp, wp); + snd_BUG(); + } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) + hda_queue_unsol_event(chip->bus, res, res_ex); + else if (chip->rirb.cmds[addr]) { + chip->rirb.res[addr] = res; + smp_wmb(); + chip->rirb.cmds[addr]--; + } else if (printk_ratelimit()) { + dev_err(chip->dev, "spurious response %#x:%#x, last cmd=%#08x\n", + res, res_ex, + chip->last_cmd[addr]); + } + } +} + +/* receive a response */ +static unsigned int hda_rirb_get_response(struct hdac_bus *bus, + unsigned int addr) +{ + struct hda *chip = bus->private_data; + unsigned long timeout; + unsigned long loopcounter; + int do_poll = 0; + + again: + timeout = jiffies + msecs_to_jiffies(1000); + + for (loopcounter = 0;; loopcounter++) { + if (chip->polling_mode || do_poll) { + spin_lock_irq(&chip->reg_lock); + hda_update_rirb(chip); + spin_unlock_irq(&chip->reg_lock); + } + if (!chip->rirb.cmds[addr]) { + smp_rmb(); + bus->rirb_error = 0; + + if (!do_poll) + chip->poll_count = 0; + return chip->rirb.res[addr]; /* the last value */ + } + if (time_after(jiffies, timeout)) + break; + else { + udelay(10); + cond_resched(); + } + } + + if (!bus->no_response_fallback) + return -1; + + if (!chip->polling_mode && chip->poll_count < 2) { + dev_dbg(chip->dev, + "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n", + chip->last_cmd[addr]); + do_poll = 1; + chip->poll_count++; + goto again; + } + + + if (!chip->polling_mode) { + dev_warn(chip->dev, + "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n", + chip->last_cmd[addr]); + chip->polling_mode = 1; + goto again; + } + + if (chip->msi) { + dev_warn(chip->dev, + "No response from codec, disabling MSI: last cmd=0x%08x\n", + chip->last_cmd[addr]); + if (chip->ops->disable_msi_reset_irq(chip) && + chip->ops->disable_msi_reset_irq(chip) < 0) { + bus->rirb_error = 1; + return -1; + } + goto again; + } + + if (chip->probing) { + /* If this critical timeout happens during the codec probing + * phase, this is likely an access to a non-existing codec + * slot. Better to return an error and reset the system. + */ + return -1; + } + + /* a fatal communication error; need either to reset or to fallback + * to the single_cmd mode + */ + bus->rirb_error = 1; + if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { + bus->response_reset = 1; + return -1; /* give a chance to retry */ + } + + dev_err(chip->dev, + "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n", + chip->last_cmd[addr]); + chip->single_cmd = 1; + bus->response_reset = 0; + /* release CORB/RIRB */ + hda_free_cmd_io(chip); + /* disable unsolicited responses */ + azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL); + return -1; +} + +/* + * Use the single immediate command instead of CORB/RIRB for simplicity + * + * Note: according to Intel, this is not preferred use. The command was + * intended for the BIOS only, and may get confused with unsolicited + * responses. So, we shouldn't use it for normal operation from the + * driver. + * I left the codes, however, for debugging/testing purposes. + */ + +/* receive a response */ +static int hda_single_wait_for_response(struct hda *chip, unsigned int addr) +{ + int timeout = 50; + + while (timeout--) { + /* check IRV busy bit */ + if (azx_readw(chip, IRS) & AZX_IRS_VALID) { + /* reuse rirb.res as the response return value */ + chip->rirb.res[addr] = azx_readl(chip, IR); + return 0; + } + udelay(1); + } + if (printk_ratelimit()) + dev_dbg(chip->dev, "get_response timeout: IRS=0x%x\n", + azx_readw(chip, IRS)); + chip->rirb.res[addr] = -1; + return -EIO; +} + +/* send a command */ +static int hda_single_send_cmd(struct hdac_bus *bus, u32 val) +{ + struct hda *chip = bus->private_data; + unsigned int addr = hda_command_addr(val); + int timeout = 50; + + bus->rirb_error = 0; + while (timeout--) { + /* check ICB busy bit */ + if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) { + /* Clear IRV valid bit */ + azx_writew(chip, IRS, azx_readw(chip, IRS) | + AZX_IRS_VALID); + azx_writel(chip, IC, val); + azx_writew(chip, IRS, azx_readw(chip, IRS) | + AZX_IRS_BUSY); + return hda_single_wait_for_response(chip, addr); + } + udelay(1); + } + if (printk_ratelimit()) + dev_dbg(chip->dev, + "send_cmd timeout: IRS=0x%x, val=0x%x\n", + azx_readw(chip, IRS), val); + return -EIO; +} + +/* receive a response */ +static unsigned int hda_single_get_response(struct hdac_bus *bus, + unsigned int addr) +{ + struct hda *chip = bus->private_data; + + return chip->rirb.res[addr]; +} + +/* + * The below are the main callbacks from hda_codec. + * + * They are just the skeleton to call sub-callbacks according to the + * current setting of chip->single_cmd. + */ + +/* send a command */ +int hda_send_cmd(struct hdac_bus *bus, unsigned int val) +{ + struct hda *chip = bus->private_data; + + if (chip->disabled) + return 0; + chip->last_cmd[hda_command_addr(val)] = val; + if (chip->single_cmd) + return hda_single_send_cmd(bus, val); + else + return hda_corb_send_cmd(bus, val); +} +EXPORT_SYMBOL_GPL(hda_send_cmd); + +/* get a response */ +unsigned int hda_get_response(struct hdac_bus *bus, + unsigned int addr) +{ + struct hda *chip = bus->private_data; + + if (chip->disabled) + return 0; + if (chip->single_cmd) + return hda_single_get_response(bus, addr); + else + return hda_rirb_get_response(bus, addr); +} +EXPORT_SYMBOL_GPL(hda_get_response); + +void hda_bus_reset(struct hdac_bus *bus) +{ + struct hda *chip = bus->private_data; + + bus->in_reset = 1; + hda_stop_chip(chip); + hda_init_chip(chip, true); + bus->in_reset = 0; +} +EXPORT_SYMBOL_GPL(hda_bus_reset); + +int hda_alloc_stream_pages(struct hda *chip) +{ + int i, err; + + for (i = 0; i < chip->num_streams; i++) { + dsp_lock_init(&chip->hda_dev[i]); + /* allocate memory for the BDL for each stream */ + err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + BDL_SIZE, + &chip->hda_dev[i].bdl); + if (err < 0) { + dev_err(chip->dev, "cannot allocate BDL\n"); + return -ENOMEM; + } + } + /* allocate memory for the position buffer */ + err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + chip->num_streams * 8, &chip->posbuf); + if (err < 0) { + dev_err(chip->dev, "cannot allocate posbuf\n"); + return -ENOMEM; + } + + /* allocate CORB/RIRB */ + err = hda_alloc_cmd_io(chip); + if (err < 0) + return err; + return 0; +} +EXPORT_SYMBOL_GPL(hda_alloc_stream_pages); + +void hda_free_stream_pages(struct hda *chip) +{ + int i; + + if (chip->hda_dev) { + for (i = 0; i < chip->num_streams; i++) + if (chip->hda_dev[i].bdl.area) + chip->ops->dma_free_pages( + chip, &chip->hda_dev[i].bdl); + } + if (chip->rb.area) + chip->ops->dma_free_pages(chip, &chip->rb); + if (chip->posbuf.area) + chip->ops->dma_free_pages(chip, &chip->posbuf); +} +EXPORT_SYMBOL_GPL(hda_free_stream_pages); + +/* + * Lowlevel interface + */ + +/* enter link reset */ +void hda_enter_link_reset(struct hda *chip) +{ + unsigned long timeout; + + /* reset controller */ + azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_RESET); + + timeout = jiffies + msecs_to_jiffies(100); + while ((azx_readb(chip, GCTL) & AZX_GCTL_RESET) && + time_before(jiffies, timeout)) + usleep_range(500, 1000); +} +EXPORT_SYMBOL_GPL(hda_enter_link_reset); + +/* exit link reset */ +void hda_exit_link_reset(struct hda *chip) +{ + unsigned long timeout; + + azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | AZX_GCTL_RESET); + + timeout = jiffies + msecs_to_jiffies(100); + while (!azx_readb(chip, GCTL) && + time_before(jiffies, timeout)) + usleep_range(500, 1000); +} +EXPORT_SYMBOL_GPL(hda_exit_link_reset); + +/* reset codec link */ +static int hda_reset(struct hda *chip, bool full_reset) +{ + if (!full_reset) + goto __skip; + + /* clear STATESTS */ + azx_writew(chip, STATESTS, STATESTS_INT_MASK); + + /* reset controller */ + hda_enter_link_reset(chip); + + /* delay for >= 100us for codec PLL to settle per spec + * Rev 0.9 section 5.5.1 + */ + usleep_range(500, 1000); + + /* Bring controller out of reset */ + hda_exit_link_reset(chip); + + /* Brent Chartrand said to wait >= 540us for codecs to initialize */ + usleep_range(1000, 1200); + +__skip: + /* check to see if controller is ready */ + if (!azx_readb(chip, GCTL)) { + dev_dbg(chip->dev, "azx_reset: controller not ready!\n"); + return -EBUSY; + } + + /* Accept unsolicited responses */ + if (!chip->single_cmd) + azx_writel(chip, GCTL, azx_readl(chip, GCTL) | + AZX_GCTL_UNSOL); + + /* detect codecs */ + if (!chip->codec_mask) { + chip->codec_mask = azx_readw(chip, STATESTS); + dev_dbg(chip->dev, "codec_mask = 0x%x\n", + chip->codec_mask); + } + + return 0; +} + +/* enable interrupts */ +static void hda_int_enable(struct hda *chip) +{ + /* enable controller CIE and GIE */ + azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) | + AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); +} + +/* disable interrupts */ +static void hda_int_disable(struct hda *chip) +{ + int i; + + /* disable interrupts in stream descriptor */ + for (i = 0; i < chip->num_streams; i++) { + struct hda_device *azx_dev = &chip->hda_dev[i]; + + azx_sd_writeb(chip, azx_dev, SD_CTL, + azx_sd_readb(chip, azx_dev, SD_CTL) & + ~SD_INT_MASK); + } + + /* disable SIE for all streams */ + azx_writeb(chip, INTCTL, 0); + + /* disable controller CIE and GIE */ + azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) & + ~(AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN)); +} + +/* clear interrupts */ +static void hda_int_clear(struct hda *chip) +{ + int i; + + /* clear stream status */ + for (i = 0; i < chip->num_streams; i++) { + struct hda_device *azx_dev = &chip->hda_dev[i]; + + azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); + } + + /* clear STATESTS */ + azx_writew(chip, STATESTS, STATESTS_INT_MASK); + + /* clear rirb status */ + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + + /* clear int status */ + azx_writel(chip, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); +} + +/* + * reset and start the controller registers + */ +void hda_init_chip(struct hda *chip, bool full_reset) +{ + if (chip->initialized) + return; + + /* reset controller */ + hda_reset(chip, full_reset); + + /* initialize interrupts */ + hda_int_clear(chip); + hda_int_enable(chip); + + /* initialize the codec command I/O */ + if (!chip->single_cmd) + hda_init_cmd_io(chip); + + /* program the position buffer */ + azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); + azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr)); + + chip->initialized = 1; +} +EXPORT_SYMBOL_GPL(hda_init_chip); + +void hda_stop_chip(struct hda *chip) +{ + if (!chip->initialized) + return; + + /* disable interrupts */ + hda_int_disable(chip); + hda_int_clear(chip); + + /* disable CORB/RIRB */ + hda_free_cmd_io(chip); + + /* disable position buffer */ + azx_writel(chip, DPLBASE, 0); + azx_writel(chip, DPUBASE, 0); + + chip->initialized = 0; +} +EXPORT_SYMBOL_GPL(hda_stop_chip); + +/* + * interrupt handler + */ +irqreturn_t hda_interrupt(int irq, void *dev_id) +{ + struct hda *chip = dev_id; + u32 status; + +#ifdef CONFIG_PM_RUNTIME + if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME) + if (!pm_runtime_active(chip->dev)) + return IRQ_NONE; +#endif + + spin_lock(&chip->reg_lock); + + if (chip->disabled) { + spin_unlock(&chip->reg_lock); + return IRQ_NONE; + } + + status = azx_readl(chip, INTSTS); + if (status == 0 || status == 0xffffffff) { + spin_unlock(&chip->reg_lock); + return IRQ_NONE; + } + spin_unlock(&chip->reg_lock); + + return IRQ_WAKE_THREAD; +} +EXPORT_SYMBOL_GPL(hda_interrupt); + + +irqreturn_t hda_threaded_handler(int irq, void *dev_id) +{ + struct hda *chip = dev_id; + struct hda_device *azx_dev; + u32 status; + u8 sd_status; + int i; + unsigned long cookie; + + status = azx_readl(chip, INTSTS); + spin_lock_irqsave(&chip->reg_lock, cookie); + for (i = 0; i < chip->num_streams; i++) { + azx_dev = &chip->hda_dev[i]; + if (status & azx_dev->sd_int_sta_mask) { + sd_status = azx_sd_readb(chip, azx_dev, SD_STS); + azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); + if (!azx_dev->substream || !azx_dev->running || + !(sd_status & SD_INT_COMPLETE)) + continue; + /* check whether this IRQ is really acceptable */ + if (!chip->ops->position_check || + chip->ops->position_check(chip, azx_dev)) { + spin_unlock_irqrestore(&chip->reg_lock, cookie); + snd_pcm_period_elapsed(azx_dev->substream); + spin_lock_irqsave(&chip->reg_lock, cookie); + } + } + } + + /* clear rirb int */ + status = azx_readb(chip, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) { + if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY) + udelay(80); + hda_update_rirb(chip); + } + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + } + + spin_unlock_irqrestore(&chip->reg_lock, cookie); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(hda_threaded_handler); + +static bool is_input_stream(struct hda *chip, unsigned char index) +{ + return (index >= chip->capture_index_offset && + index < chip->capture_index_offset + chip->capture_streams); +} + +/* initialize SD streams */ +int hda_init_stream(struct hda *chip) +{ + int i; + int in_stream_tag = 0; + int out_stream_tag = 0; + + /* initialize each stream (aka device) + * assign the starting bdl address to each stream (device) + * and initialize + */ + for (i = 0; i < chip->num_streams; i++) { + struct hda_device *azx_dev = &chip->hda_dev[i]; + + azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8); + /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ + azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); + /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ + azx_dev->sd_int_sta_mask = 1 << i; + azx_dev->index = i; + + /* stream tag must be unique throughout + * the stream direction group, + * valid values 1...15 + * use separate stream tag if the flag + * AZX_DCAPS_SEPARATE_STREAM_TAG is used + */ + if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) + azx_dev->stream_tag = + is_input_stream(chip, i) ? + ++in_stream_tag : + ++out_stream_tag; + else + azx_dev->stream_tag = i + 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hda_init_stream); + +void hda_timecounter_init(struct snd_pcm_substream *substream, + bool force, cycle_t last, void *read_fn) +{ + struct hda_device *azx_dev = get_hda_dev(substream); + struct timecounter *tc = &azx_dev->tc; + struct cyclecounter *cc = &azx_dev->cc; + u64 nsec; + + cc->read = read_fn; + cc->mask = CLOCKSOURCE_MASK(32); + + /* + * Converting from 24 MHz to ns means applying a 125/3 factor. + * To avoid any saturation issues in intermediate operations, + * the 125 factor is applied first. The division is applied + * last after reading the timecounter value. + * Applying the 1/3 factor as part of the multiplication + * requires at least 20 bits for a decent precision, however + * overflows occur after about 4 hours or less, not a option. + */ + + cc->mult = 125; /* saturation after 195 years */ + cc->shift = 0; + + nsec = 0; /* audio time is elapsed time since trigger */ + timecounter_init(tc, cc, nsec); + if (force) + /* + * force timecounter to use predefined value, + * used for synchronized starts + */ + tc->cycle_last = last; +} +EXPORT_SYMBOL_GPL(hda_timecounter_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Common HDA driver funcitons");