Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
8ab418d3
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
8ab418d3
编写于
4月 27, 2015
作者:
T
Takashi Iwai
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'topic/hda' into for-4.2
上级
782e50e0
0dd76f36
变更
19
展开全部
隐藏空白更改
内联
并排
Showing
19 changed file
with
2457 addition
and
1928 deletion
+2457
-1928
include/sound/hda_register.h
include/sound/hda_register.h
+152
-0
include/sound/hdaudio.h
include/sound/hdaudio.h
+283
-3
sound/hda/Kconfig
sound/hda/Kconfig
+3
-0
sound/hda/Makefile
sound/hda/Makefile
+1
-1
sound/hda/hdac_bus.c
sound/hda/hdac_bus.c
+18
-2
sound/hda/hdac_controller.c
sound/hda/hdac_controller.c
+507
-0
sound/hda/hdac_device.c
sound/hda/hdac_device.c
+300
-0
sound/hda/hdac_stream.c
sound/hda/hdac_stream.c
+686
-0
sound/pci/hda/Kconfig
sound/pci/hda/Kconfig
+0
-3
sound/pci/hda/Makefile
sound/pci/hda/Makefile
+1
-2
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.c
+10
-411
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_codec.h
+22
-58
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_controller.c
+270
-1060
sound/pci/hda/hda_controller.h
sound/pci/hda/hda_controller.h
+28
-242
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_intel.c
+131
-103
sound/pci/hda/hda_intel.h
sound/pci/hda/hda_intel.h
+1
-0
sound/pci/hda/hda_intel_trace.h
sound/pci/hda/hda_intel_trace.h
+2
-2
sound/pci/hda/hda_tegra.c
sound/pci/hda/hda_tegra.c
+40
-36
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_ca0132.c
+2
-5
未找到文件。
include/sound/hda_register.h
0 → 100644
浏览文件 @
8ab418d3
/*
* HD-audio controller (Azalia) registers and helpers
*
* For traditional reasons, we still use azx_ prefix here
*/
#ifndef __SOUND_HDA_REGISTER_H
#define __SOUND_HDA_REGISTER_H
#include <linux/io.h>
#include <sound/hdaudio.h>
#define AZX_REG_GCAP 0x00
#define AZX_GCAP_64OK (1 << 0)
/* 64bit address support */
#define AZX_GCAP_NSDO (3 << 1)
/* # of serial data out signals */
#define AZX_GCAP_BSS (31 << 3)
/* # of bidirectional streams */
#define AZX_GCAP_ISS (15 << 8)
/* # of input streams */
#define AZX_GCAP_OSS (15 << 12)
/* # of output streams */
#define AZX_REG_VMIN 0x02
#define AZX_REG_VMAJ 0x03
#define AZX_REG_OUTPAY 0x04
#define AZX_REG_INPAY 0x06
#define AZX_REG_GCTL 0x08
#define AZX_GCTL_RESET (1 << 0)
/* controller reset */
#define AZX_GCTL_FCNTRL (1 << 1)
/* flush control */
#define AZX_GCTL_UNSOL (1 << 8)
/* accept unsol. response enable */
#define AZX_REG_WAKEEN 0x0c
#define AZX_REG_STATESTS 0x0e
#define AZX_REG_GSTS 0x10
#define AZX_GSTS_FSTS (1 << 1)
/* flush status */
#define AZX_REG_INTCTL 0x20
#define AZX_REG_INTSTS 0x24
#define AZX_REG_WALLCLK 0x30
/* 24Mhz source */
#define AZX_REG_OLD_SSYNC 0x34
/* SSYNC for old ICH */
#define AZX_REG_SSYNC 0x38
#define AZX_REG_CORBLBASE 0x40
#define AZX_REG_CORBUBASE 0x44
#define AZX_REG_CORBWP 0x48
#define AZX_REG_CORBRP 0x4a
#define AZX_CORBRP_RST (1 << 15)
/* read pointer reset */
#define AZX_REG_CORBCTL 0x4c
#define AZX_CORBCTL_RUN (1 << 1)
/* enable DMA */
#define AZX_CORBCTL_CMEIE (1 << 0)
/* enable memory error irq */
#define AZX_REG_CORBSTS 0x4d
#define AZX_CORBSTS_CMEI (1 << 0)
/* memory error indication */
#define AZX_REG_CORBSIZE 0x4e
#define AZX_REG_RIRBLBASE 0x50
#define AZX_REG_RIRBUBASE 0x54
#define AZX_REG_RIRBWP 0x58
#define AZX_RIRBWP_RST (1 << 15)
/* write pointer reset */
#define AZX_REG_RINTCNT 0x5a
#define AZX_REG_RIRBCTL 0x5c
#define AZX_RBCTL_IRQ_EN (1 << 0)
/* enable IRQ */
#define AZX_RBCTL_DMA_EN (1 << 1)
/* enable DMA */
#define AZX_RBCTL_OVERRUN_EN (1 << 2)
/* enable overrun irq */
#define AZX_REG_RIRBSTS 0x5d
#define AZX_RBSTS_IRQ (1 << 0)
/* response irq */
#define AZX_RBSTS_OVERRUN (1 << 2)
/* overrun irq */
#define AZX_REG_RIRBSIZE 0x5e
#define AZX_REG_IC 0x60
#define AZX_REG_IR 0x64
#define AZX_REG_IRS 0x68
#define AZX_IRS_VALID (1<<1)
#define AZX_IRS_BUSY (1<<0)
#define AZX_REG_DPLBASE 0x70
#define AZX_REG_DPUBASE 0x74
#define AZX_DPLBASE_ENABLE 0x1
/* Enable position buffer */
/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
enum
{
SDI0
,
SDI1
,
SDI2
,
SDI3
,
SDO0
,
SDO1
,
SDO2
,
SDO3
};
/* stream register offsets from stream base */
#define AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08
#define AZX_REG_SD_LVI 0x0c
#define AZX_REG_SD_FIFOW 0x0e
#define AZX_REG_SD_FIFOSIZE 0x10
#define AZX_REG_SD_FORMAT 0x12
#define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE 4096
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define STATESTS_INT_MASK ((1 << HDA_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01
/* stream reset bit */
#define SD_CTL_DMA_START 0x02
/* stream DMA start bit */
#define SD_CTL_STRIPE (3 << 16)
/* stripe control */
#define SD_CTL_TRAFFIC_PRIO (1 << 18)
/* traffic priority */
#define SD_CTL_DIR (1 << 19)
/* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
/* SD_CTL and SD_STS */
#define SD_INT_DESC_ERR 0x10
/* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08
/* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04
/* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE)
/* SD_STS */
#define SD_STS_FIFO_READY 0x20
/* FIFO ready */
/* INTCTL and INTSTS */
#define AZX_INT_ALL_STREAM 0xff
/* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000
/* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000
/* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define AZX_MAX_CORB_ENTRIES 256
#define AZX_MAX_RIRB_ENTRIES 256
/*
* helpers to read the stream position
*/
static
inline
unsigned
int
snd_hdac_stream_get_pos_lpib
(
struct
hdac_stream
*
stream
)
{
return
snd_hdac_stream_readl
(
stream
,
SD_LPIB
);
}
static
inline
unsigned
int
snd_hdac_stream_get_pos_posbuf
(
struct
hdac_stream
*
stream
)
{
return
le32_to_cpu
(
*
stream
->
posbuf
);
}
#endif
/* __SOUND_HDA_REGISTER_H */
include/sound/hdaudio.h
浏览文件 @
8ab418d3
...
...
@@ -6,12 +6,17 @@
#define __SOUND_HDAUDIO_H
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/timecounter.h>
#include <sound/core.h>
#include <sound/memalloc.h>
#include <sound/hda_verbs.h>
/* codec node id */
typedef
u16
hda_nid_t
;
struct
hdac_bus
;
struct
hdac_stream
;
struct
hdac_device
;
struct
hdac_driver
;
struct
hdac_widget_tree
;
...
...
@@ -85,6 +90,7 @@ struct hdac_device {
enum
{
HDA_DEV_CORE
,
HDA_DEV_LEGACY
,
HDA_DEV_ASOC
,
};
/* direction */
...
...
@@ -118,6 +124,15 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t
*
conn_list
,
int
max_conns
);
int
snd_hdac_get_sub_nodes
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
,
hda_nid_t
*
start_id
);
unsigned
int
snd_hdac_calc_stream_format
(
unsigned
int
rate
,
unsigned
int
channels
,
unsigned
int
format
,
unsigned
int
maxbps
,
unsigned
short
spdif_ctls
);
int
snd_hdac_query_supported_pcm
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
,
u32
*
ratesp
,
u64
*
formatsp
,
unsigned
int
*
bpsp
);
bool
snd_hdac_is_supported_format
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
,
unsigned
int
format
);
/**
* snd_hdac_read_parm - read a codec parameter
...
...
@@ -161,7 +176,7 @@ struct hdac_driver {
#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)
/*
*
HD-audio bus base driver
*
Bus verb operators
*/
struct
hdac_bus_ops
{
/* send a single command */
...
...
@@ -171,11 +186,55 @@ struct hdac_bus_ops {
unsigned
int
*
res
);
};
/*
* Lowlevel I/O operators
*/
struct
hdac_io_ops
{
/* mapped register accesses */
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
);
/* Allocation ops */
int
(
*
dma_alloc_pages
)(
struct
hdac_bus
*
bus
,
int
type
,
size_t
size
,
struct
snd_dma_buffer
*
buf
);
void
(
*
dma_free_pages
)(
struct
hdac_bus
*
bus
,
struct
snd_dma_buffer
*
buf
);
};
#define HDA_UNSOL_QUEUE_SIZE 64
#define HDA_MAX_CODECS 8
/* limit by controller side */
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
/*
* CORB/RIRB
*
* Each CORB entry is 4byte, RIRB is 8byte
*/
struct
hdac_rb
{
__le32
*
buf
;
/* virtual address of CORB/RIRB buffer */
dma_addr_t
addr
;
/* physical address of CORB/RIRB buffer */
unsigned
short
rp
,
wp
;
/* RIRB read/write pointers */
int
cmds
[
HDA_MAX_CODECS
];
/* number of pending requests */
u32
res
[
HDA_MAX_CODECS
];
/* last read value */
};
/*
* HD-audio bus base driver
*/
struct
hdac_bus
{
struct
device
*
dev
;
const
struct
hdac_bus_ops
*
ops
;
const
struct
hdac_io_ops
*
io_ops
;
/* h/w resources */
unsigned
long
addr
;
void
__iomem
*
remap_addr
;
int
irq
;
/* codec linked list */
struct
list_head
codec_list
;
...
...
@@ -189,18 +248,45 @@ struct hdac_bus {
unsigned
int
unsol_rp
,
unsol_wp
;
struct
work_struct
unsol_work
;
/* bit flags of detected codecs */
unsigned
long
codec_mask
;
/* bit flags of powered codecs */
unsigned
long
codec_powered
;
/* flags */
/* CORB/RIRB */
struct
hdac_rb
corb
;
struct
hdac_rb
rirb
;
unsigned
int
last_cmd
[
HDA_MAX_CODECS
];
/* last sent command */
/* CORB/RIRB and position buffers */
struct
snd_dma_buffer
rb
;
struct
snd_dma_buffer
posbuf
;
/* hdac_stream linked list */
struct
list_head
stream_list
;
/* operation state */
bool
chip_init
:
1
;
/* h/w initialized */
/* behavior flags */
bool
sync_write
:
1
;
/* sync after verb write */
bool
use_posbuf
:
1
;
/* use position buffer */
bool
snoop
:
1
;
/* enable snooping */
bool
align_bdle_4k
:
1
;
/* BDLE align 4K boundary */
bool
reverse_assign
:
1
;
/* assign devices in reverse order */
bool
corbrp_self_clear
:
1
;
/* CORBRP clears itself after reset */
int
bdl_pos_adj
;
/* BDL position adjustment */
/* locks */
spinlock_t
reg_lock
;
struct
mutex
cmd_mutex
;
};
int
snd_hdac_bus_init
(
struct
hdac_bus
*
bus
,
struct
device
*
dev
,
const
struct
hdac_bus_ops
*
ops
);
const
struct
hdac_bus_ops
*
ops
,
const
struct
hdac_io_ops
*
io_ops
);
void
snd_hdac_bus_exit
(
struct
hdac_bus
*
bus
);
int
snd_hdac_bus_exec_verb
(
struct
hdac_bus
*
bus
,
unsigned
int
addr
,
unsigned
int
cmd
,
unsigned
int
*
res
);
...
...
@@ -222,6 +308,200 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
clear_bit
(
codec
->
addr
,
&
codec
->
bus
->
codec_powered
);
}
int
snd_hdac_bus_send_cmd
(
struct
hdac_bus
*
bus
,
unsigned
int
val
);
int
snd_hdac_bus_get_response
(
struct
hdac_bus
*
bus
,
unsigned
int
addr
,
unsigned
int
*
res
);
bool
snd_hdac_bus_init_chip
(
struct
hdac_bus
*
bus
,
bool
full_reset
);
void
snd_hdac_bus_stop_chip
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_init_cmd_io
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_stop_cmd_io
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_enter_link_reset
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_exit_link_reset
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_update_rirb
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_handle_stream_irq
(
struct
hdac_bus
*
bus
,
unsigned
int
status
,
void
(
*
ack
)(
struct
hdac_bus
*
,
struct
hdac_stream
*
));
int
snd_hdac_bus_alloc_stream_pages
(
struct
hdac_bus
*
bus
);
void
snd_hdac_bus_free_stream_pages
(
struct
hdac_bus
*
bus
);
/*
* macros for easy use
*/
#define _snd_hdac_chip_write(type, chip, reg, value) \
((chip)->io_ops->reg_write ## type(value, (chip)->remap_addr + (reg)))
#define _snd_hdac_chip_read(type, chip, reg) \
((chip)->io_ops->reg_read ## type((chip)->remap_addr + (reg)))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_writel(chip, reg, value) \
_snd_hdac_chip_write(l, chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_writew(chip, reg, value) \
_snd_hdac_chip_write(w, chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_writeb(chip, reg, value) \
_snd_hdac_chip_write(b, chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_readl(chip, reg) \
_snd_hdac_chip_read(l, chip, AZX_REG_ ## reg)
#define snd_hdac_chip_readw(chip, reg) \
_snd_hdac_chip_read(w, chip, AZX_REG_ ## reg)
#define snd_hdac_chip_readb(chip, reg) \
_snd_hdac_chip_read(b, chip, AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_updatel(chip, reg, mask, val) \
snd_hdac_chip_writel(chip, reg, \
(snd_hdac_chip_readl(chip, reg) & ~(mask)) | (val))
#define snd_hdac_chip_updatew(chip, reg, mask, val) \
snd_hdac_chip_writew(chip, reg, \
(snd_hdac_chip_readw(chip, reg) & ~(mask)) | (val))
#define snd_hdac_chip_updateb(chip, reg, mask, val) \
snd_hdac_chip_writeb(chip, reg, \
(snd_hdac_chip_readb(chip, reg) & ~(mask)) | (val))
/*
* HD-audio stream
*/
struct
hdac_stream
{
struct
hdac_bus
*
bus
;
struct
snd_dma_buffer
bdl
;
/* BDL buffer */
__le32
*
posbuf
;
/* position buffer pointer */
int
direction
;
/* playback / capture (SNDRV_PCM_STREAM_*) */
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 */
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 */
bool
opened
:
1
;
bool
running
:
1
;
bool
prepared
:
1
;
bool
no_period_wakeup
:
1
;
bool
locked
:
1
;
/* timestamp */
unsigned
long
start_wallclk
;
/* start + minimum wallclk */
unsigned
long
period_wallclk
;
/* wallclk for period */
struct
timecounter
tc
;
struct
cyclecounter
cc
;
int
delay_negative_threshold
;
struct
list_head
list
;
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* DSP access mutex */
struct
mutex
dsp_mutex
;
#endif
};
void
snd_hdac_stream_init
(
struct
hdac_bus
*
bus
,
struct
hdac_stream
*
azx_dev
,
int
idx
,
int
direction
,
int
tag
);
struct
hdac_stream
*
snd_hdac_stream_assign
(
struct
hdac_bus
*
bus
,
struct
snd_pcm_substream
*
substream
);
void
snd_hdac_stream_release
(
struct
hdac_stream
*
azx_dev
);
int
snd_hdac_stream_setup
(
struct
hdac_stream
*
azx_dev
);
void
snd_hdac_stream_cleanup
(
struct
hdac_stream
*
azx_dev
);
int
snd_hdac_stream_setup_periods
(
struct
hdac_stream
*
azx_dev
);
int
snd_hdac_stream_set_params
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
format_val
);
void
snd_hdac_stream_start
(
struct
hdac_stream
*
azx_dev
,
bool
fresh_start
);
void
snd_hdac_stream_clear
(
struct
hdac_stream
*
azx_dev
);
void
snd_hdac_stream_stop
(
struct
hdac_stream
*
azx_dev
);
void
snd_hdac_stream_reset
(
struct
hdac_stream
*
azx_dev
);
void
snd_hdac_stream_sync_trigger
(
struct
hdac_stream
*
azx_dev
,
bool
set
,
unsigned
int
streams
,
unsigned
int
reg
);
void
snd_hdac_stream_sync
(
struct
hdac_stream
*
azx_dev
,
bool
start
,
unsigned
int
streams
);
void
snd_hdac_stream_timecounter_init
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
streams
);
/*
* macros for easy use
*/
#define _snd_hdac_stream_write(type, dev, reg, value) \
((dev)->bus->io_ops->reg_write ## type(value, (dev)->sd_addr + (reg)))
#define _snd_hdac_stream_read(type, dev, reg) \
((dev)->bus->io_ops->reg_read ## type((dev)->sd_addr + (reg)))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_writel(dev, reg, value) \
_snd_hdac_stream_write(l, dev, AZX_REG_ ## reg, value)
#define snd_hdac_stream_writew(dev, reg, value) \
_snd_hdac_stream_write(w, dev, AZX_REG_ ## reg, value)
#define snd_hdac_stream_writeb(dev, reg, value) \
_snd_hdac_stream_write(b, dev, AZX_REG_ ## reg, value)
#define snd_hdac_stream_readl(dev, reg) \
_snd_hdac_stream_read(l, dev, AZX_REG_ ## reg)
#define snd_hdac_stream_readw(dev, reg) \
_snd_hdac_stream_read(w, dev, AZX_REG_ ## reg)
#define snd_hdac_stream_readb(dev, reg) \
_snd_hdac_stream_read(b, dev, AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
snd_hdac_stream_writel(dev, reg, \
(snd_hdac_stream_readl(dev, reg) & \
~(mask)) | (val))
#define snd_hdac_stream_updatew(dev, reg, mask, val) \
snd_hdac_stream_writew(dev, reg, \
(snd_hdac_stream_readw(dev, reg) & \
~(mask)) | (val))
#define snd_hdac_stream_updateb(dev, reg, mask, val) \
snd_hdac_stream_writeb(dev, reg, \
(snd_hdac_stream_readb(dev, reg) & \
~(mask)) | (val))
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* DSP lock helpers */
#define snd_hdac_dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex)
#define snd_hdac_dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex)
#define snd_hdac_dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex)
#define snd_hdac_stream_is_locked(dev) ((dev)->locked)
/* DSP loader helpers */
int
snd_hdac_dsp_prepare
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
format
,
unsigned
int
byte_size
,
struct
snd_dma_buffer
*
bufp
);
void
snd_hdac_dsp_trigger
(
struct
hdac_stream
*
azx_dev
,
bool
start
);
void
snd_hdac_dsp_cleanup
(
struct
hdac_stream
*
azx_dev
,
struct
snd_dma_buffer
*
dmab
);
#else
/* CONFIG_SND_HDA_DSP_LOADER */
#define snd_hdac_dsp_lock_init(dev) do {} while (0)
#define snd_hdac_dsp_lock(dev) do {} while (0)
#define snd_hdac_dsp_unlock(dev) do {} while (0)
#define snd_hdac_stream_is_locked(dev) 0
static
inline
int
snd_hdac_dsp_prepare
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
format
,
unsigned
int
byte_size
,
struct
snd_dma_buffer
*
bufp
)
{
return
0
;
}
static
inline
void
snd_hdac_dsp_trigger
(
struct
hdac_stream
*
azx_dev
,
bool
start
)
{
}
static
inline
void
snd_hdac_dsp_cleanup
(
struct
hdac_stream
*
azx_dev
,
struct
snd_dma_buffer
*
dmab
)
{
}
#endif
/* CONFIG_SND_HDA_DSP_LOADER */
/*
* generic array helpers
*/
...
...
sound/hda/Kconfig
浏览文件 @
8ab418d3
config SND_HDA_CORE
tristate
select REGMAP
config SND_HDA_DSP_LOADER
bool
sound/hda/Makefile
浏览文件 @
8ab418d3
snd-hda-core-objs
:=
hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o
\
hdac_regmap.o array.o
hdac_regmap.o
hdac_controller.o hdac_stream.o
array.o
snd-hda-core-objs
+=
trace.o
CFLAGS_trace.o
:=
-I
$(src)
...
...
sound/hda/hdac_bus.c
浏览文件 @
8ab418d3
...
...
@@ -11,21 +11,36 @@
static
void
process_unsol_events
(
struct
work_struct
*
work
);
static
const
struct
hdac_bus_ops
default_ops
=
{
.
command
=
snd_hdac_bus_send_cmd
,
.
get_response
=
snd_hdac_bus_get_response
,
};
/**
* snd_hdac_bus_init - initialize a HD-audio bas bus
* @bus: the pointer to bus object
* @ops: bus verb operators
* @io_ops: lowlevel I/O operators
*
* Returns 0 if successful, or a negative error code.
*/
int
snd_hdac_bus_init
(
struct
hdac_bus
*
bus
,
struct
device
*
dev
,
const
struct
hdac_bus_ops
*
ops
)
const
struct
hdac_bus_ops
*
ops
,
const
struct
hdac_io_ops
*
io_ops
)
{
memset
(
bus
,
0
,
sizeof
(
*
bus
));
bus
->
dev
=
dev
;
bus
->
ops
=
ops
;
if
(
ops
)
bus
->
ops
=
ops
;
else
bus
->
ops
=
&
default_ops
;
bus
->
io_ops
=
io_ops
;
INIT_LIST_HEAD
(
&
bus
->
stream_list
);
INIT_LIST_HEAD
(
&
bus
->
codec_list
);
INIT_WORK
(
&
bus
->
unsol_work
,
process_unsol_events
);
spin_lock_init
(
&
bus
->
reg_lock
);
mutex_init
(
&
bus
->
cmd_mutex
);
bus
->
irq
=
-
1
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_init
);
...
...
@@ -36,6 +51,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
*/
void
snd_hdac_bus_exit
(
struct
hdac_bus
*
bus
)
{
WARN_ON
(
!
list_empty
(
&
bus
->
stream_list
));
WARN_ON
(
!
list_empty
(
&
bus
->
codec_list
));
cancel_work_sync
(
&
bus
->
unsol_work
);
}
...
...
sound/hda/hdac_controller.c
0 → 100644
浏览文件 @
8ab418d3
/*
* HD-audio controller helpers
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <sound/core.h>
#include <sound/hdaudio.h>
#include <sound/hda_register.h>
/* clear CORB read pointer properly */
static
void
azx_clear_corbrp
(
struct
hdac_bus
*
bus
)
{
int
timeout
;
for
(
timeout
=
1000
;
timeout
>
0
;
timeout
--
)
{
if
(
snd_hdac_chip_readw
(
bus
,
CORBRP
)
&
AZX_CORBRP_RST
)
break
;
udelay
(
1
);
}
if
(
timeout
<=
0
)
dev_err
(
bus
->
dev
,
"CORB reset timeout#1, CORBRP = %d
\n
"
,
snd_hdac_chip_readw
(
bus
,
CORBRP
));
snd_hdac_chip_writew
(
bus
,
CORBRP
,
0
);
for
(
timeout
=
1000
;
timeout
>
0
;
timeout
--
)
{
if
(
snd_hdac_chip_readw
(
bus
,
CORBRP
)
==
0
)
break
;
udelay
(
1
);
}
if
(
timeout
<=
0
)
dev_err
(
bus
->
dev
,
"CORB reset timeout#2, CORBRP = %d
\n
"
,
snd_hdac_chip_readw
(
bus
,
CORBRP
));
}
/**
* snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers
* @bus: HD-audio core bus
*/
void
snd_hdac_bus_init_cmd_io
(
struct
hdac_bus
*
bus
)
{
spin_lock_irq
(
&
bus
->
reg_lock
);
/* CORB set up */
bus
->
corb
.
addr
=
bus
->
rb
.
addr
;
bus
->
corb
.
buf
=
(
__le32
*
)
bus
->
rb
.
area
;
snd_hdac_chip_writel
(
bus
,
CORBLBASE
,
(
u32
)
bus
->
corb
.
addr
);
snd_hdac_chip_writel
(
bus
,
CORBUBASE
,
upper_32_bits
(
bus
->
corb
.
addr
));
/* set the corb size to 256 entries (ULI requires explicitly) */
snd_hdac_chip_writeb
(
bus
,
CORBSIZE
,
0x02
);
/* set the corb write pointer to 0 */
snd_hdac_chip_writew
(
bus
,
CORBWP
,
0
);
/* reset the corb hw read pointer */
snd_hdac_chip_writew
(
bus
,
CORBRP
,
AZX_CORBRP_RST
);
if
(
!
bus
->
corbrp_self_clear
)
azx_clear_corbrp
(
bus
);
/* enable corb dma */
snd_hdac_chip_writeb
(
bus
,
CORBCTL
,
AZX_CORBCTL_RUN
);
/* RIRB set up */
bus
->
rirb
.
addr
=
bus
->
rb
.
addr
+
2048
;
bus
->
rirb
.
buf
=
(
__le32
*
)(
bus
->
rb
.
area
+
2048
);
bus
->
rirb
.
wp
=
bus
->
rirb
.
rp
=
0
;
memset
(
bus
->
rirb
.
cmds
,
0
,
sizeof
(
bus
->
rirb
.
cmds
));
snd_hdac_chip_writel
(
bus
,
RIRBLBASE
,
(
u32
)
bus
->
rirb
.
addr
);
snd_hdac_chip_writel
(
bus
,
RIRBUBASE
,
upper_32_bits
(
bus
->
rirb
.
addr
));
/* set the rirb size to 256 entries (ULI requires explicitly) */
snd_hdac_chip_writeb
(
bus
,
RIRBSIZE
,
0x02
);
/* reset the rirb hw write pointer */
snd_hdac_chip_writew
(
bus
,
RIRBWP
,
AZX_RIRBWP_RST
);
/* set N=1, get RIRB response interrupt for new entry */
snd_hdac_chip_writew
(
bus
,
RINTCNT
,
1
);
/* enable rirb dma and response irq */
snd_hdac_chip_writeb
(
bus
,
RIRBCTL
,
AZX_RBCTL_DMA_EN
|
AZX_RBCTL_IRQ_EN
);
spin_unlock_irq
(
&
bus
->
reg_lock
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_init_cmd_io
);
/**
* snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
* @bus: HD-audio core bus
*/
void
snd_hdac_bus_stop_cmd_io
(
struct
hdac_bus
*
bus
)
{
spin_lock_irq
(
&
bus
->
reg_lock
);
/* disable ringbuffer DMAs */
snd_hdac_chip_writeb
(
bus
,
RIRBCTL
,
0
);
snd_hdac_chip_writeb
(
bus
,
CORBCTL
,
0
);
/* disable unsolicited responses */
snd_hdac_chip_updatel
(
bus
,
GCTL
,
AZX_GCTL_UNSOL
,
0
);
spin_unlock_irq
(
&
bus
->
reg_lock
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_stop_cmd_io
);
static
unsigned
int
azx_command_addr
(
u32
cmd
)
{
unsigned
int
addr
=
cmd
>>
28
;
if
(
snd_BUG_ON
(
addr
>=
HDA_MAX_CODECS
))
addr
=
0
;
return
addr
;
}
/**
* snd_hdac_bus_send_cmd - send a command verb via CORB
* @bus: HD-audio core bus
* @val: encoded verb value to send
*
* Returns zero for success or a negative error code.
*/
int
snd_hdac_bus_send_cmd
(
struct
hdac_bus
*
bus
,
unsigned
int
val
)
{
unsigned
int
addr
=
azx_command_addr
(
val
);
unsigned
int
wp
,
rp
;
spin_lock_irq
(
&
bus
->
reg_lock
);
bus
->
last_cmd
[
azx_command_addr
(
val
)]
=
val
;
/* add command to corb */
wp
=
snd_hdac_chip_readw
(
bus
,
CORBWP
);
if
(
wp
==
0xffff
)
{
/* something wrong, controller likely turned to D3 */
spin_unlock_irq
(
&
bus
->
reg_lock
);
return
-
EIO
;
}
wp
++
;
wp
%=
AZX_MAX_CORB_ENTRIES
;
rp
=
snd_hdac_chip_readw
(
bus
,
CORBRP
);
if
(
wp
==
rp
)
{
/* oops, it's full */
spin_unlock_irq
(
&
bus
->
reg_lock
);
return
-
EAGAIN
;
}
bus
->
rirb
.
cmds
[
addr
]
++
;
bus
->
corb
.
buf
[
wp
]
=
cpu_to_le32
(
val
);
snd_hdac_chip_writew
(
bus
,
CORBWP
,
wp
);
spin_unlock_irq
(
&
bus
->
reg_lock
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_send_cmd
);
#define AZX_RIRB_EX_UNSOL_EV (1<<4)
/**
* snd_hdac_bus_update_rirb - retrieve RIRB entries
* @bus: HD-audio core bus
*
* Usually called from interrupt handler.
*/
void
snd_hdac_bus_update_rirb
(
struct
hdac_bus
*
bus
)
{
unsigned
int
rp
,
wp
;
unsigned
int
addr
;
u32
res
,
res_ex
;
wp
=
snd_hdac_chip_readw
(
bus
,
RIRBWP
);
if
(
wp
==
0xffff
)
{
/* something wrong, controller likely turned to D3 */
return
;
}
if
(
wp
==
bus
->
rirb
.
wp
)
return
;
bus
->
rirb
.
wp
=
wp
;
while
(
bus
->
rirb
.
rp
!=
wp
)
{
bus
->
rirb
.
rp
++
;
bus
->
rirb
.
rp
%=
AZX_MAX_RIRB_ENTRIES
;
rp
=
bus
->
rirb
.
rp
<<
1
;
/* an RIRB entry is 8-bytes */
res_ex
=
le32_to_cpu
(
bus
->
rirb
.
buf
[
rp
+
1
]);
res
=
le32_to_cpu
(
bus
->
rirb
.
buf
[
rp
]);
addr
=
res_ex
&
0xf
;
if
(
addr
>=
HDA_MAX_CODECS
)
{
dev_err
(
bus
->
dev
,
"spurious response %#x:%#x, rp = %d, wp = %d"
,
res
,
res_ex
,
bus
->
rirb
.
rp
,
wp
);
snd_BUG
();
}
else
if
(
res_ex
&
AZX_RIRB_EX_UNSOL_EV
)
snd_hdac_bus_queue_event
(
bus
,
res
,
res_ex
);
else
if
(
bus
->
rirb
.
cmds
[
addr
])
{
bus
->
rirb
.
res
[
addr
]
=
res
;
bus
->
rirb
.
cmds
[
addr
]
--
;
}
else
{
dev_err_ratelimited
(
bus
->
dev
,
"spurious response %#x:%#x, last cmd=%#08x
\n
"
,
res
,
res_ex
,
bus
->
last_cmd
[
addr
]);
}
}
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_update_rirb
);
/**
* snd_hdac_bus_get_response - receive a response via RIRB
* @bus: HD-audio core bus
* @addr: codec address
* @res: pointer to store the value, NULL when not needed
*
* Returns zero if a value is read, or a negative error code.
*/
int
snd_hdac_bus_get_response
(
struct
hdac_bus
*
bus
,
unsigned
int
addr
,
unsigned
int
*
res
)
{
unsigned
long
timeout
;
unsigned
long
loopcounter
;
timeout
=
jiffies
+
msecs_to_jiffies
(
1000
);
for
(
loopcounter
=
0
;;
loopcounter
++
)
{
spin_lock_irq
(
&
bus
->
reg_lock
);
if
(
!
bus
->
rirb
.
cmds
[
addr
])
{
if
(
res
)
*
res
=
bus
->
rirb
.
res
[
addr
];
/* the last value */
spin_unlock_irq
(
&
bus
->
reg_lock
);
return
0
;
}
spin_unlock_irq
(
&
bus
->
reg_lock
);
if
(
time_after
(
jiffies
,
timeout
))
break
;
if
(
loopcounter
>
3000
)
msleep
(
2
);
/* temporary workaround */
else
{
udelay
(
10
);
cond_resched
();
}
}
return
-
EIO
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_get_response
);
/*
* Lowlevel interface
*/
/**
* snd_hdac_bus_enter_link_reset - enter link reset
* @bus: HD-audio core bus
*
* Enter to the link reset state.
*/
void
snd_hdac_bus_enter_link_reset
(
struct
hdac_bus
*
bus
)
{
unsigned
long
timeout
;
/* reset controller */
snd_hdac_chip_updatel
(
bus
,
GCTL
,
AZX_GCTL_RESET
,
0
);
timeout
=
jiffies
+
msecs_to_jiffies
(
100
);
while
((
snd_hdac_chip_readb
(
bus
,
GCTL
)
&
AZX_GCTL_RESET
)
&&
time_before
(
jiffies
,
timeout
))
usleep_range
(
500
,
1000
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_enter_link_reset
);
/**
* snd_hdac_bus_exit_link_reset - exit link reset
* @bus: HD-audio core bus
*
* Exit from the link reset state.
*/
void
snd_hdac_bus_exit_link_reset
(
struct
hdac_bus
*
bus
)
{
unsigned
long
timeout
;
snd_hdac_chip_updateb
(
bus
,
GCTL
,
0
,
AZX_GCTL_RESET
);
timeout
=
jiffies
+
msecs_to_jiffies
(
100
);
while
(
!
snd_hdac_chip_readb
(
bus
,
GCTL
)
&&
time_before
(
jiffies
,
timeout
))
usleep_range
(
500
,
1000
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_exit_link_reset
);
/* reset codec link */
static
int
azx_reset
(
struct
hdac_bus
*
bus
,
bool
full_reset
)
{
if
(
!
full_reset
)
goto
skip_reset
;
/* clear STATESTS */
snd_hdac_chip_writew
(
bus
,
STATESTS
,
STATESTS_INT_MASK
);
/* reset controller */
snd_hdac_bus_enter_link_reset
(
bus
);
/* 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 */
snd_hdac_bus_exit_link_reset
(
bus
);
/* Brent Chartrand said to wait >= 540us for codecs to initialize */
usleep_range
(
1000
,
1200
);
skip_reset:
/* check to see if controller is ready */
if
(
!
snd_hdac_chip_readb
(
bus
,
GCTL
))
{
dev_dbg
(
bus
->
dev
,
"azx_reset: controller not ready!
\n
"
);
return
-
EBUSY
;
}
/* Accept unsolicited responses */
snd_hdac_chip_updatel
(
bus
,
GCTL
,
0
,
AZX_GCTL_UNSOL
);
/* detect codecs */
if
(
!
bus
->
codec_mask
)
{
bus
->
codec_mask
=
snd_hdac_chip_readw
(
bus
,
STATESTS
);
dev_dbg
(
bus
->
dev
,
"codec_mask = 0x%lx
\n
"
,
bus
->
codec_mask
);
}
return
0
;
}
/* enable interrupts */
static
void
azx_int_enable
(
struct
hdac_bus
*
bus
)
{
/* enable controller CIE and GIE */
snd_hdac_chip_updatel
(
bus
,
INTCTL
,
0
,
AZX_INT_CTRL_EN
|
AZX_INT_GLOBAL_EN
);
}
/* disable interrupts */
static
void
azx_int_disable
(
struct
hdac_bus
*
bus
)
{
struct
hdac_stream
*
azx_dev
;
/* disable interrupts in stream descriptor */
list_for_each_entry
(
azx_dev
,
&
bus
->
stream_list
,
list
)
snd_hdac_stream_updateb
(
azx_dev
,
SD_CTL
,
SD_INT_MASK
,
0
);
/* disable SIE for all streams */
snd_hdac_chip_writeb
(
bus
,
INTCTL
,
0
);
/* disable controller CIE and GIE */
snd_hdac_chip_updatel
(
bus
,
INTCTL
,
AZX_INT_CTRL_EN
|
AZX_INT_GLOBAL_EN
,
0
);
}
/* clear interrupts */
static
void
azx_int_clear
(
struct
hdac_bus
*
bus
)
{
struct
hdac_stream
*
azx_dev
;
/* clear stream status */
list_for_each_entry
(
azx_dev
,
&
bus
->
stream_list
,
list
)
snd_hdac_stream_writeb
(
azx_dev
,
SD_STS
,
SD_INT_MASK
);
/* clear STATESTS */
snd_hdac_chip_writew
(
bus
,
STATESTS
,
STATESTS_INT_MASK
);
/* clear rirb status */
snd_hdac_chip_writeb
(
bus
,
RIRBSTS
,
RIRB_INT_MASK
);
/* clear int status */
snd_hdac_chip_writel
(
bus
,
INTSTS
,
AZX_INT_CTRL_EN
|
AZX_INT_ALL_STREAM
);
}
/**
* snd_hdac_bus_init_chip - reset and start the controller registers
* @bus: HD-audio core bus
* @full_reset: Do full reset
*/
bool
snd_hdac_bus_init_chip
(
struct
hdac_bus
*
bus
,
bool
full_reset
)
{
if
(
bus
->
chip_init
)
return
false
;
/* reset controller */
azx_reset
(
bus
,
full_reset
);
/* initialize interrupts */
azx_int_clear
(
bus
);
azx_int_enable
(
bus
);
/* initialize the codec command I/O */
snd_hdac_bus_init_cmd_io
(
bus
);
/* program the position buffer */
if
(
bus
->
use_posbuf
&&
bus
->
posbuf
.
addr
)
{
snd_hdac_chip_writel
(
bus
,
DPLBASE
,
(
u32
)
bus
->
posbuf
.
addr
);
snd_hdac_chip_writel
(
bus
,
DPUBASE
,
upper_32_bits
(
bus
->
posbuf
.
addr
));
}
bus
->
chip_init
=
true
;
return
true
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_init_chip
);
/**
* snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os
* @bus: HD-audio core bus
*/
void
snd_hdac_bus_stop_chip
(
struct
hdac_bus
*
bus
)
{
if
(
!
bus
->
chip_init
)
return
;
/* disable interrupts */
azx_int_disable
(
bus
);
azx_int_clear
(
bus
);
/* disable CORB/RIRB */
snd_hdac_bus_stop_cmd_io
(
bus
);
/* disable position buffer */
if
(
bus
->
posbuf
.
addr
)
{
snd_hdac_chip_writel
(
bus
,
DPLBASE
,
0
);
snd_hdac_chip_writel
(
bus
,
DPUBASE
,
0
);
}
bus
->
chip_init
=
false
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_stop_chip
);
/**
* snd_hdac_bus_handle_stream_irq - interrupt handler for streams
* @bus: HD-audio core bus
* @status: INTSTS register value
* @ask: callback to be called for woken streams
*/
void
snd_hdac_bus_handle_stream_irq
(
struct
hdac_bus
*
bus
,
unsigned
int
status
,
void
(
*
ack
)(
struct
hdac_bus
*
,
struct
hdac_stream
*
))
{
struct
hdac_stream
*
azx_dev
;
u8
sd_status
;
list_for_each_entry
(
azx_dev
,
&
bus
->
stream_list
,
list
)
{
if
(
status
&
azx_dev
->
sd_int_sta_mask
)
{
sd_status
=
snd_hdac_stream_readb
(
azx_dev
,
SD_STS
);
snd_hdac_stream_writeb
(
azx_dev
,
SD_STS
,
SD_INT_MASK
);
if
(
!
azx_dev
->
substream
||
!
azx_dev
->
running
||
!
(
sd_status
&
SD_INT_COMPLETE
))
continue
;
if
(
ack
)
ack
(
bus
,
azx_dev
);
}
}
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_handle_stream_irq
);
/**
* snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
* @bus: HD-audio core bus
*
* Call this after assigning the all streams.
* Returns zero for success, or a negative error code.
*/
int
snd_hdac_bus_alloc_stream_pages
(
struct
hdac_bus
*
bus
)
{
struct
hdac_stream
*
s
;
int
num_streams
=
0
;
int
err
;
list_for_each_entry
(
s
,
&
bus
->
stream_list
,
list
)
{
/* allocate memory for the BDL for each stream */
err
=
bus
->
io_ops
->
dma_alloc_pages
(
bus
,
SNDRV_DMA_TYPE_DEV
,
BDL_SIZE
,
&
s
->
bdl
);
num_streams
++
;
if
(
err
<
0
)
return
-
ENOMEM
;
}
if
(
WARN_ON
(
!
num_streams
))
return
-
EINVAL
;
/* allocate memory for the position buffer */
err
=
bus
->
io_ops
->
dma_alloc_pages
(
bus
,
SNDRV_DMA_TYPE_DEV
,
num_streams
*
8
,
&
bus
->
posbuf
);
if
(
err
<
0
)
return
-
ENOMEM
;
list_for_each_entry
(
s
,
&
bus
->
stream_list
,
list
)
s
->
posbuf
=
(
__le32
*
)(
bus
->
posbuf
.
area
+
s
->
index
*
8
);
/* single page (at least 4096 bytes) must suffice for both ringbuffes */
return
bus
->
io_ops
->
dma_alloc_pages
(
bus
,
SNDRV_DMA_TYPE_DEV
,
PAGE_SIZE
,
&
bus
->
rb
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_alloc_stream_pages
);
/**
* snd_hdac_bus_free_stream_pages - release BDL and other buffers
* @bus: HD-audio core bus
*/
void
snd_hdac_bus_free_stream_pages
(
struct
hdac_bus
*
bus
)
{
struct
hdac_stream
*
s
;
list_for_each_entry
(
s
,
&
bus
->
stream_list
,
list
)
{
if
(
s
->
bdl
.
area
)
bus
->
io_ops
->
dma_free_pages
(
bus
,
&
s
->
bdl
);
}
if
(
bus
->
rb
.
area
)
bus
->
io_ops
->
dma_free_pages
(
bus
,
&
bus
->
rb
);
if
(
bus
->
posbuf
.
area
)
bus
->
io_ops
->
dma_free_pages
(
bus
,
&
bus
->
posbuf
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_bus_free_stream_pages
);
sound/hda/hdac_device.c
浏览文件 @
8ab418d3
...
...
@@ -10,6 +10,7 @@
#include <linux/pm_runtime.h>
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
#include <sound/pcm.h>
#include "local.h"
static
void
setup_fg_nodes
(
struct
hdac_device
*
codec
);
...
...
@@ -597,3 +598,302 @@ static int get_codec_vendor_name(struct hdac_device *codec)
codec
->
vendor_name
=
kasprintf
(
GFP_KERNEL
,
"Generic %04x"
,
vendor_id
);
return
codec
->
vendor_name
?
0
:
-
ENOMEM
;
}
/*
* stream formats
*/
struct
hda_rate_tbl
{
unsigned
int
hz
;
unsigned
int
alsa_bits
;
unsigned
int
hda_fmt
;
};
/* rate = base * mult / div */
#define HDA_RATE(base, mult, div) \
(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
(((div) - 1) << AC_FMT_DIV_SHIFT))
static
struct
hda_rate_tbl
rate_bits
[]
=
{
/* rate in Hz, ALSA rate bitmask, HDA format value */
/* autodetected value used in snd_hda_query_supported_pcm */
{
8000
,
SNDRV_PCM_RATE_8000
,
HDA_RATE
(
48
,
1
,
6
)
},
{
11025
,
SNDRV_PCM_RATE_11025
,
HDA_RATE
(
44
,
1
,
4
)
},
{
16000
,
SNDRV_PCM_RATE_16000
,
HDA_RATE
(
48
,
1
,
3
)
},
{
22050
,
SNDRV_PCM_RATE_22050
,
HDA_RATE
(
44
,
1
,
2
)
},
{
32000
,
SNDRV_PCM_RATE_32000
,
HDA_RATE
(
48
,
2
,
3
)
},
{
44100
,
SNDRV_PCM_RATE_44100
,
HDA_RATE
(
44
,
1
,
1
)
},
{
48000
,
SNDRV_PCM_RATE_48000
,
HDA_RATE
(
48
,
1
,
1
)
},
{
88200
,
SNDRV_PCM_RATE_88200
,
HDA_RATE
(
44
,
2
,
1
)
},
{
96000
,
SNDRV_PCM_RATE_96000
,
HDA_RATE
(
48
,
2
,
1
)
},
{
176400
,
SNDRV_PCM_RATE_176400
,
HDA_RATE
(
44
,
4
,
1
)
},
{
192000
,
SNDRV_PCM_RATE_192000
,
HDA_RATE
(
48
,
4
,
1
)
},
#define AC_PAR_PCM_RATE_BITS 11
/* up to bits 10, 384kHZ isn't supported properly */
/* not autodetected value */
{
9600
,
SNDRV_PCM_RATE_KNOT
,
HDA_RATE
(
48
,
1
,
5
)
},
{
0
}
/* terminator */
};
/**
* snd_hdac_calc_stream_format - calculate the format bitset
* @rate: the sample rate
* @channels: the number of channels
* @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
* @maxbps: the max. bps
* @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
*
* Calculate the format bitset from the given rate, channels and th PCM format.
*
* Return zero if invalid.
*/
unsigned
int
snd_hdac_calc_stream_format
(
unsigned
int
rate
,
unsigned
int
channels
,
unsigned
int
format
,
unsigned
int
maxbps
,
unsigned
short
spdif_ctls
)
{
int
i
;
unsigned
int
val
=
0
;
for
(
i
=
0
;
rate_bits
[
i
].
hz
;
i
++
)
if
(
rate_bits
[
i
].
hz
==
rate
)
{
val
=
rate_bits
[
i
].
hda_fmt
;
break
;
}
if
(
!
rate_bits
[
i
].
hz
)
return
0
;
if
(
channels
==
0
||
channels
>
8
)
return
0
;
val
|=
channels
-
1
;
switch
(
snd_pcm_format_width
(
format
))
{
case
8
:
val
|=
AC_FMT_BITS_8
;
break
;
case
16
:
val
|=
AC_FMT_BITS_16
;
break
;
case
20
:
case
24
:
case
32
:
if
(
maxbps
>=
32
||
format
==
SNDRV_PCM_FORMAT_FLOAT_LE
)
val
|=
AC_FMT_BITS_32
;
else
if
(
maxbps
>=
24
)
val
|=
AC_FMT_BITS_24
;
else
val
|=
AC_FMT_BITS_20
;
break
;
default:
return
0
;
}
if
(
spdif_ctls
&
AC_DIG1_NONAUDIO
)
val
|=
AC_FMT_TYPE_NON_PCM
;
return
val
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_calc_stream_format
);
static
unsigned
int
query_pcm_param
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
)
{
unsigned
int
val
=
0
;
if
(
nid
!=
codec
->
afg
&&
(
get_wcaps
(
codec
,
nid
)
&
AC_WCAP_FORMAT_OVRD
))
val
=
snd_hdac_read_parm
(
codec
,
nid
,
AC_PAR_PCM
);
if
(
!
val
||
val
==
-
1
)
val
=
snd_hdac_read_parm
(
codec
,
codec
->
afg
,
AC_PAR_PCM
);
if
(
!
val
||
val
==
-
1
)
return
0
;
return
val
;
}
static
unsigned
int
query_stream_param
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
)
{
unsigned
int
streams
=
snd_hdac_read_parm
(
codec
,
nid
,
AC_PAR_STREAM
);
if
(
!
streams
||
streams
==
-
1
)
streams
=
snd_hdac_read_parm
(
codec
,
codec
->
afg
,
AC_PAR_STREAM
);
if
(
!
streams
||
streams
==
-
1
)
return
0
;
return
streams
;
}
/**
* snd_hdac_query_supported_pcm - query the supported PCM rates and formats
* @codec: the codec object
* @nid: NID to query
* @ratesp: the pointer to store the detected rate bitflags
* @formatsp: the pointer to store the detected formats
* @bpsp: the pointer to store the detected format widths
*
* Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
* or @bsps argument is ignored.
*
* Returns 0 if successful, otherwise a negative error code.
*/
int
snd_hdac_query_supported_pcm
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
,
u32
*
ratesp
,
u64
*
formatsp
,
unsigned
int
*
bpsp
)
{
unsigned
int
i
,
val
,
wcaps
;
wcaps
=
get_wcaps
(
codec
,
nid
);
val
=
query_pcm_param
(
codec
,
nid
);
if
(
ratesp
)
{
u32
rates
=
0
;
for
(
i
=
0
;
i
<
AC_PAR_PCM_RATE_BITS
;
i
++
)
{
if
(
val
&
(
1
<<
i
))
rates
|=
rate_bits
[
i
].
alsa_bits
;
}
if
(
rates
==
0
)
{
dev_err
(
&
codec
->
dev
,
"rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)
\n
"
,
nid
,
val
,
(
wcaps
&
AC_WCAP_FORMAT_OVRD
)
?
1
:
0
);
return
-
EIO
;
}
*
ratesp
=
rates
;
}
if
(
formatsp
||
bpsp
)
{
u64
formats
=
0
;
unsigned
int
streams
,
bps
;
streams
=
query_stream_param
(
codec
,
nid
);
if
(
!
streams
)
return
-
EIO
;
bps
=
0
;
if
(
streams
&
AC_SUPFMT_PCM
)
{
if
(
val
&
AC_SUPPCM_BITS_8
)
{
formats
|=
SNDRV_PCM_FMTBIT_U8
;
bps
=
8
;
}
if
(
val
&
AC_SUPPCM_BITS_16
)
{
formats
|=
SNDRV_PCM_FMTBIT_S16_LE
;
bps
=
16
;
}
if
(
wcaps
&
AC_WCAP_DIGITAL
)
{
if
(
val
&
AC_SUPPCM_BITS_32
)
formats
|=
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
;
if
(
val
&
(
AC_SUPPCM_BITS_20
|
AC_SUPPCM_BITS_24
))
formats
|=
SNDRV_PCM_FMTBIT_S32_LE
;
if
(
val
&
AC_SUPPCM_BITS_24
)
bps
=
24
;
else
if
(
val
&
AC_SUPPCM_BITS_20
)
bps
=
20
;
}
else
if
(
val
&
(
AC_SUPPCM_BITS_20
|
AC_SUPPCM_BITS_24
|
AC_SUPPCM_BITS_32
))
{
formats
|=
SNDRV_PCM_FMTBIT_S32_LE
;
if
(
val
&
AC_SUPPCM_BITS_32
)
bps
=
32
;
else
if
(
val
&
AC_SUPPCM_BITS_24
)
bps
=
24
;
else
if
(
val
&
AC_SUPPCM_BITS_20
)
bps
=
20
;
}
}
#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
if (streams & AC_SUPFMT_FLOAT32) {
formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
if (!bps)
bps = 32;
}
#endif
if
(
streams
==
AC_SUPFMT_AC3
)
{
/* should be exclusive */
/* temporary hack: we have still no proper support
* for the direct AC3 stream...
*/
formats
|=
SNDRV_PCM_FMTBIT_U8
;
bps
=
8
;
}
if
(
formats
==
0
)
{
dev_err
(
&
codec
->
dev
,
"formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)
\n
"
,
nid
,
val
,
(
wcaps
&
AC_WCAP_FORMAT_OVRD
)
?
1
:
0
,
streams
);
return
-
EIO
;
}
if
(
formatsp
)
*
formatsp
=
formats
;
if
(
bpsp
)
*
bpsp
=
bps
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_query_supported_pcm
);
/**
* snd_hdac_is_supported_format - Check the validity of the format
* @codec: the codec object
* @nid: NID to check
* @format: the HD-audio format value to check
*
* Check whether the given node supports the format value.
*
* Returns true if supported, false if not.
*/
bool
snd_hdac_is_supported_format
(
struct
hdac_device
*
codec
,
hda_nid_t
nid
,
unsigned
int
format
)
{
int
i
;
unsigned
int
val
=
0
,
rate
,
stream
;
val
=
query_pcm_param
(
codec
,
nid
);
if
(
!
val
)
return
false
;
rate
=
format
&
0xff00
;
for
(
i
=
0
;
i
<
AC_PAR_PCM_RATE_BITS
;
i
++
)
if
(
rate_bits
[
i
].
hda_fmt
==
rate
)
{
if
(
val
&
(
1
<<
i
))
break
;
return
false
;
}
if
(
i
>=
AC_PAR_PCM_RATE_BITS
)
return
false
;
stream
=
query_stream_param
(
codec
,
nid
);
if
(
!
stream
)
return
false
;
if
(
stream
&
AC_SUPFMT_PCM
)
{
switch
(
format
&
0xf0
)
{
case
0x00
:
if
(
!
(
val
&
AC_SUPPCM_BITS_8
))
return
false
;
break
;
case
0x10
:
if
(
!
(
val
&
AC_SUPPCM_BITS_16
))
return
false
;
break
;
case
0x20
:
if
(
!
(
val
&
AC_SUPPCM_BITS_20
))
return
false
;
break
;
case
0x30
:
if
(
!
(
val
&
AC_SUPPCM_BITS_24
))
return
false
;
break
;
case
0x40
:
if
(
!
(
val
&
AC_SUPPCM_BITS_32
))
return
false
;
break
;
default:
return
false
;
}
}
else
{
/* FIXME: check for float32 and AC3? */
}
return
true
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_is_supported_format
);
sound/hda/hdac_stream.c
0 → 100644
浏览文件 @
8ab418d3
/*
* HD-audio stream operations
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clocksource.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/hdaudio.h>
#include <sound/hda_register.h>
/**
* snd_hdac_stream_init - initialize each stream (aka device)
* @bus: HD-audio core bus
* @azx_dev: HD-audio core stream object to initialize
* @idx: stream index number
* @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
* @tag: the tag id to assign
*
* Assign the starting bdl address to each stream (device) and initialize.
*/
void
snd_hdac_stream_init
(
struct
hdac_bus
*
bus
,
struct
hdac_stream
*
azx_dev
,
int
idx
,
int
direction
,
int
tag
)
{
azx_dev
->
bus
=
bus
;
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
azx_dev
->
sd_addr
=
bus
->
remap_addr
+
(
0x20
*
idx
+
0x80
);
/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
azx_dev
->
sd_int_sta_mask
=
1
<<
idx
;
azx_dev
->
index
=
idx
;
azx_dev
->
direction
=
direction
;
azx_dev
->
stream_tag
=
tag
;
snd_hdac_dsp_lock_init
(
azx_dev
);
list_add_tail
(
&
azx_dev
->
list
,
&
bus
->
stream_list
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_init
);
/**
* snd_hdac_stream_start - start a stream
* @azx_dev: HD-audio core stream to start
* @fresh_start: false = wallclock timestamp relative to period wallclock
*
* Start a stream, set start_wallclk and set the running flag.
*/
void
snd_hdac_stream_start
(
struct
hdac_stream
*
azx_dev
,
bool
fresh_start
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
azx_dev
->
start_wallclk
=
snd_hdac_chip_readl
(
bus
,
WALLCLK
);
if
(
!
fresh_start
)
azx_dev
->
start_wallclk
-=
azx_dev
->
period_wallclk
;
/* enable SIE */
snd_hdac_chip_updatel
(
bus
,
INTCTL
,
0
,
1
<<
azx_dev
->
index
);
/* set DMA start and interrupt mask */
snd_hdac_stream_updateb
(
azx_dev
,
SD_CTL
,
0
,
SD_CTL_DMA_START
|
SD_INT_MASK
);
azx_dev
->
running
=
true
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_start
);
/**
* snd_hdac_stream_clear - stop a stream DMA
* @azx_dev: HD-audio core stream to stop
*/
void
snd_hdac_stream_clear
(
struct
hdac_stream
*
azx_dev
)
{
snd_hdac_stream_updateb
(
azx_dev
,
SD_CTL
,
SD_CTL_DMA_START
|
SD_INT_MASK
,
0
);
snd_hdac_stream_writeb
(
azx_dev
,
SD_STS
,
SD_INT_MASK
);
/* to be sure */
azx_dev
->
running
=
false
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_clear
);
/**
* snd_hdac_stream_stop - stop a stream
* @azx_dev: HD-audio core stream to stop
*
* Stop a stream DMA and disable stream interrupt
*/
void
snd_hdac_stream_stop
(
struct
hdac_stream
*
azx_dev
)
{
snd_hdac_stream_clear
(
azx_dev
);
/* disable SIE */
snd_hdac_chip_updatel
(
azx_dev
->
bus
,
INTCTL
,
1
<<
azx_dev
->
index
,
0
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_stop
);
/**
* snd_hdac_stream_reset - reset a stream
* @azx_dev: HD-audio core stream to reset
*/
void
snd_hdac_stream_reset
(
struct
hdac_stream
*
azx_dev
)
{
unsigned
char
val
;
int
timeout
;
snd_hdac_stream_clear
(
azx_dev
);
snd_hdac_stream_updateb
(
azx_dev
,
SD_CTL
,
0
,
SD_CTL_STREAM_RESET
);
udelay
(
3
);
timeout
=
300
;
do
{
val
=
snd_hdac_stream_readb
(
azx_dev
,
SD_CTL
)
&
SD_CTL_STREAM_RESET
;
if
(
val
)
break
;
}
while
(
--
timeout
);
val
&=
~
SD_CTL_STREAM_RESET
;
snd_hdac_stream_writeb
(
azx_dev
,
SD_CTL
,
val
);
udelay
(
3
);
timeout
=
300
;
/* waiting for hardware to report that the stream is out of reset */
do
{
val
=
snd_hdac_stream_readb
(
azx_dev
,
SD_CTL
)
&
SD_CTL_STREAM_RESET
;
if
(
!
val
)
break
;
}
while
(
--
timeout
);
/* reset first position - may not be synced with hw at this time */
if
(
azx_dev
->
posbuf
)
*
azx_dev
->
posbuf
=
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_reset
);
/**
* snd_hdac_stream_setup - set up the SD for streaming
* @azx_dev: HD-audio core stream to set up
*/
int
snd_hdac_stream_setup
(
struct
hdac_stream
*
azx_dev
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
struct
snd_pcm_runtime
*
runtime
=
azx_dev
->
substream
->
runtime
;
unsigned
int
val
;
/* make sure the run bit is zero for SD */
snd_hdac_stream_clear
(
azx_dev
);
/* program the stream_tag */
val
=
snd_hdac_stream_readl
(
azx_dev
,
SD_CTL
);
val
=
(
val
&
~
SD_CTL_STREAM_TAG_MASK
)
|
(
azx_dev
->
stream_tag
<<
SD_CTL_STREAM_TAG_SHIFT
);
if
(
!
bus
->
snoop
)
val
|=
SD_CTL_TRAFFIC_PRIO
;
snd_hdac_stream_writel
(
azx_dev
,
SD_CTL
,
val
);
/* program the length of samples in cyclic buffer */
snd_hdac_stream_writel
(
azx_dev
,
SD_CBL
,
azx_dev
->
bufsize
);
/* program the stream format */
/* this value needs to be the same as the one programmed */
snd_hdac_stream_writew
(
azx_dev
,
SD_FORMAT
,
azx_dev
->
format_val
);
/* program the stream LVI (last valid index) of the BDL */
snd_hdac_stream_writew
(
azx_dev
,
SD_LVI
,
azx_dev
->
frags
-
1
);
/* program the BDL address */
/* lower BDL address */
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPL
,
(
u32
)
azx_dev
->
bdl
.
addr
);
/* upper BDL address */
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPU
,
upper_32_bits
(
azx_dev
->
bdl
.
addr
));
/* enable the position buffer */
if
(
bus
->
use_posbuf
&&
bus
->
posbuf
.
addr
)
{
if
(
!
(
snd_hdac_chip_readl
(
bus
,
DPLBASE
)
&
AZX_DPLBASE_ENABLE
))
snd_hdac_chip_writel
(
bus
,
DPLBASE
,
(
u32
)
bus
->
posbuf
.
addr
|
AZX_DPLBASE_ENABLE
);
}
/* set the interrupt enable bits in the descriptor control register */
snd_hdac_stream_updatel
(
azx_dev
,
SD_CTL
,
0
,
SD_INT_MASK
);
if
(
azx_dev
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
)
azx_dev
->
fifo_size
=
snd_hdac_stream_readw
(
azx_dev
,
SD_FIFOSIZE
)
+
1
;
else
azx_dev
->
fifo_size
=
0
;
/* 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
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_setup
);
/**
* snd_hdac_stream_cleanup - cleanup a stream
* @azx_dev: HD-audio core stream to clean up
*/
void
snd_hdac_stream_cleanup
(
struct
hdac_stream
*
azx_dev
)
{
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPL
,
0
);
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPU
,
0
);
snd_hdac_stream_writel
(
azx_dev
,
SD_CTL
,
0
);
azx_dev
->
bufsize
=
0
;
azx_dev
->
period_bytes
=
0
;
azx_dev
->
format_val
=
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_cleanup
);
/**
* snd_hdac_stream_assign - assign a stream for the PCM
* @bus: HD-audio core bus
* @substream: PCM substream to assign
*
* Look for an unused stream for the given PCM substream, assign it
* and return the stream object. If no stream is free, returns NULL.
* The function tries to keep using the same stream object when it's used
* beforehand. Also, when bus->reverse_assign flag is set, the last free
* or matching entry is returned. This is needed for some strange codecs.
*/
struct
hdac_stream
*
snd_hdac_stream_assign
(
struct
hdac_bus
*
bus
,
struct
snd_pcm_substream
*
substream
)
{
struct
hdac_stream
*
azx_dev
;
struct
hdac_stream
*
res
=
NULL
;
/* make a non-zero unique key for the substream */
int
key
=
(
substream
->
pcm
->
device
<<
16
)
|
(
substream
->
number
<<
2
)
|
(
substream
->
stream
+
1
);
list_for_each_entry
(
azx_dev
,
&
bus
->
stream_list
,
list
)
{
if
(
azx_dev
->
direction
!=
substream
->
stream
)
continue
;
if
(
azx_dev
->
opened
)
continue
;
if
(
azx_dev
->
assigned_key
==
key
)
{
res
=
azx_dev
;
break
;
}
if
(
!
res
||
bus
->
reverse_assign
)
res
=
azx_dev
;
}
if
(
res
)
{
spin_lock_irq
(
&
bus
->
reg_lock
);
res
->
opened
=
1
;
res
->
running
=
0
;
res
->
assigned_key
=
key
;
res
->
substream
=
substream
;
spin_unlock_irq
(
&
bus
->
reg_lock
);
}
return
res
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_assign
);
/**
* snd_hdac_stream_release - release the assigned stream
* @azx_dev: HD-audio core stream to release
*
* Release the stream that has been assigned by snd_hdac_stream_assign().
*/
void
snd_hdac_stream_release
(
struct
hdac_stream
*
azx_dev
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
spin_lock_irq
(
&
bus
->
reg_lock
);
azx_dev
->
opened
=
0
;
azx_dev
->
running
=
0
;
azx_dev
->
substream
=
NULL
;
spin_unlock_irq
(
&
bus
->
reg_lock
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_release
);
/*
* set up a BDL entry
*/
static
int
setup_bdle
(
struct
hdac_bus
*
bus
,
struct
snd_dma_buffer
*
dmab
,
struct
hdac_stream
*
azx_dev
,
__le32
**
bdlp
,
int
ofs
,
int
size
,
int
with_ioc
)
{
__le32
*
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
);
/* 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
(
bus
->
align_bdle_4k
)
{
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
;
return
ofs
;
}
/**
* snd_hdac_stream_setup_periods - set up BDL entries
* @azx_dev: HD-audio core stream to set up
*
* Set up the buffer descriptor table of the given stream based on the
* period and buffer sizes of the assigned PCM substream.
*/
int
snd_hdac_stream_setup_periods
(
struct
hdac_stream
*
azx_dev
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
struct
snd_pcm_substream
*
substream
=
azx_dev
->
substream
;
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
__le32
*
bdl
;
int
i
,
ofs
,
periods
,
period_bytes
;
int
pos_adj
,
pos_align
;
/* reset BDL address */
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPL
,
0
);
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPU
,
0
);
period_bytes
=
azx_dev
->
period_bytes
;
periods
=
azx_dev
->
bufsize
/
period_bytes
;
/* program the initial BDL entries */
bdl
=
(
__le32
*
)
azx_dev
->
bdl
.
area
;
ofs
=
0
;
azx_dev
->
frags
=
0
;
pos_adj
=
bus
->
bdl_pos_adj
;
if
(
!
azx_dev
->
no_period_wakeup
&&
pos_adj
>
0
)
{
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
(
bus
->
dev
,
"Too big adjustment %d
\n
"
,
pos_adj
);
pos_adj
=
0
;
}
else
{
ofs
=
setup_bdle
(
bus
,
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
=
setup_bdle
(
bus
,
snd_pcm_get_dma_buf
(
substream
),
azx_dev
,
&
bdl
,
ofs
,
period_bytes
-
pos_adj
,
0
);
else
ofs
=
setup_bdle
(
bus
,
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
(
bus
->
dev
,
"Too many BDL entries: buffer=%d, period=%d
\n
"
,
azx_dev
->
bufsize
,
period_bytes
);
return
-
EINVAL
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_setup_periods
);
/* snd_hdac_stream_set_params - set stream parameters
* @azx_dev: HD-audio core stream for which parameters are to be set
* @format_val: format value parameter
*
* Setup the HD-audio core stream parameters from substream of the stream
* and passed format value
*/
int
snd_hdac_stream_set_params
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
format_val
)
{
unsigned
int
bufsize
,
period_bytes
;
struct
snd_pcm_substream
*
substream
=
azx_dev
->
substream
;
struct
snd_pcm_runtime
*
runtime
;
int
err
;
if
(
!
substream
)
return
-
EINVAL
;
runtime
=
substream
->
runtime
;
bufsize
=
snd_pcm_lib_buffer_bytes
(
substream
);
period_bytes
=
snd_pcm_lib_period_bytes
(
substream
);
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
=
snd_hdac_stream_setup_periods
(
azx_dev
);
if
(
err
<
0
)
return
err
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_set_params
);
static
cycle_t
azx_cc_read
(
const
struct
cyclecounter
*
cc
)
{
struct
hdac_stream
*
azx_dev
=
container_of
(
cc
,
struct
hdac_stream
,
cc
);
return
snd_hdac_chip_readl
(
azx_dev
->
bus
,
WALLCLK
);
}
static
void
azx_timecounter_init
(
struct
hdac_stream
*
azx_dev
,
bool
force
,
cycle_t
last
)
{
struct
timecounter
*
tc
=
&
azx_dev
->
tc
;
struct
cyclecounter
*
cc
=
&
azx_dev
->
cc
;
u64
nsec
;
cc
->
read
=
azx_cc_read
;
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
;
}
}
/**
* snd_hdac_stream_timecounter_init - initialize time counter
* @azx_dev: HD-audio core stream (master stream)
* @streams: bit flags of streams to set up
*
* Initializes the time counter of streams marked by the bit flags (each
* bit corresponds to the stream index).
* The trigger timestamp of PCM substream assigned to the given stream is
* updated accordingly, too.
*/
void
snd_hdac_stream_timecounter_init
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
streams
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
struct
snd_pcm_runtime
*
runtime
=
azx_dev
->
substream
->
runtime
;
struct
hdac_stream
*
s
;
bool
inited
=
false
;
cycle_t
cycle_last
=
0
;
int
i
=
0
;
list_for_each_entry
(
s
,
&
bus
->
stream_list
,
list
)
{
if
(
streams
&
(
1
<<
i
))
{
azx_timecounter_init
(
s
,
inited
,
cycle_last
);
if
(
!
inited
)
{
inited
=
true
;
cycle_last
=
s
->
tc
.
cycle_last
;
}
}
i
++
;
}
snd_pcm_gettime
(
runtime
,
&
runtime
->
trigger_tstamp
);
runtime
->
trigger_tstamp_latched
=
true
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_timecounter_init
);
/**
* snd_hdac_stream_sync_trigger - turn on/off stream sync register
* @azx_dev: HD-audio core stream (master stream)
* @streams: bit flags of streams to sync
*/
void
snd_hdac_stream_sync_trigger
(
struct
hdac_stream
*
azx_dev
,
bool
set
,
unsigned
int
streams
,
unsigned
int
reg
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
unsigned
int
val
;
if
(
!
reg
)
reg
=
AZX_REG_SSYNC
;
val
=
_snd_hdac_chip_read
(
l
,
bus
,
reg
);
if
(
set
)
val
|=
streams
;
else
val
&=
~
streams
;
_snd_hdac_chip_write
(
l
,
bus
,
reg
,
val
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_sync_trigger
);
/**
* snd_hdac_stream_sync - sync with start/strop trigger operation
* @azx_dev: HD-audio core stream (master stream)
* @start: true = start, false = stop
* @streams: bit flags of streams to sync
*
* For @start = true, wait until all FIFOs get ready.
* For @start = false, wait until all RUN bits are cleared.
*/
void
snd_hdac_stream_sync
(
struct
hdac_stream
*
azx_dev
,
bool
start
,
unsigned
int
streams
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
int
i
,
nwait
,
timeout
;
struct
hdac_stream
*
s
;
for
(
timeout
=
5000
;
timeout
;
timeout
--
)
{
nwait
=
0
;
i
=
0
;
list_for_each_entry
(
s
,
&
bus
->
stream_list
,
list
)
{
if
(
streams
&
(
1
<<
i
))
{
if
(
start
)
{
/* check FIFO gets ready */
if
(
!
(
snd_hdac_stream_readb
(
s
,
SD_STS
)
&
SD_STS_FIFO_READY
))
nwait
++
;
}
else
{
/* check RUN bit is cleared */
if
(
snd_hdac_stream_readb
(
s
,
SD_CTL
)
&
SD_CTL_DMA_START
)
nwait
++
;
}
}
i
++
;
}
if
(
!
nwait
)
break
;
cpu_relax
();
}
}
EXPORT_SYMBOL_GPL
(
snd_hdac_stream_sync
);
#ifdef CONFIG_SND_HDA_DSP_LOADER
/**
* snd_hdac_dsp_prepare - prepare for DSP loading
* @azx_dev: HD-audio core stream used for DSP loading
* @format: HD-audio stream format
* @byte_size: data chunk byte size
* @bufp: allocated buffer
*
* Allocate the buffer for the given size and set up the given stream for
* DSP loading. Returns the stream tag (>= 0), or a negative error code.
*/
int
snd_hdac_dsp_prepare
(
struct
hdac_stream
*
azx_dev
,
unsigned
int
format
,
unsigned
int
byte_size
,
struct
snd_dma_buffer
*
bufp
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
u32
*
bdl
;
int
err
;
snd_hdac_dsp_lock
(
azx_dev
);
spin_lock_irq
(
&
bus
->
reg_lock
);
if
(
azx_dev
->
running
||
azx_dev
->
locked
)
{
spin_unlock_irq
(
&
bus
->
reg_lock
);
err
=
-
EBUSY
;
goto
unlock
;
}
azx_dev
->
locked
=
true
;
spin_unlock_irq
(
&
bus
->
reg_lock
);
err
=
bus
->
io_ops
->
dma_alloc_pages
(
bus
,
SNDRV_DMA_TYPE_DEV_SG
,
byte_size
,
bufp
);
if
(
err
<
0
)
goto
err_alloc
;
azx_dev
->
bufsize
=
byte_size
;
azx_dev
->
period_bytes
=
byte_size
;
azx_dev
->
format_val
=
format
;
snd_hdac_stream_reset
(
azx_dev
);
/* reset BDL address */
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPL
,
0
);
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPU
,
0
);
azx_dev
->
frags
=
0
;
bdl
=
(
u32
*
)
azx_dev
->
bdl
.
area
;
err
=
setup_bdle
(
bus
,
bufp
,
azx_dev
,
&
bdl
,
0
,
byte_size
,
0
);
if
(
err
<
0
)
goto
error
;
snd_hdac_stream_setup
(
azx_dev
);
snd_hdac_dsp_unlock
(
azx_dev
);
return
azx_dev
->
stream_tag
;
error:
bus
->
io_ops
->
dma_free_pages
(
bus
,
bufp
);
err_alloc:
spin_lock_irq
(
&
bus
->
reg_lock
);
azx_dev
->
locked
=
false
;
spin_unlock_irq
(
&
bus
->
reg_lock
);
unlock:
snd_hdac_dsp_unlock
(
azx_dev
);
return
err
;
}
EXPORT_SYMBOL_GPL
(
snd_hdac_dsp_prepare
);
/**
* snd_hdac_dsp_trigger - start / stop DSP loading
* @azx_dev: HD-audio core stream used for DSP loading
* @start: trigger start or stop
*/
void
snd_hdac_dsp_trigger
(
struct
hdac_stream
*
azx_dev
,
bool
start
)
{
if
(
start
)
snd_hdac_stream_start
(
azx_dev
,
true
);
else
snd_hdac_stream_stop
(
azx_dev
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_dsp_trigger
);
/**
* snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
* @azx_dev: HD-audio core stream used for DSP loading
* @dmab: buffer used by DSP loading
*/
void
snd_hdac_dsp_cleanup
(
struct
hdac_stream
*
azx_dev
,
struct
snd_dma_buffer
*
dmab
)
{
struct
hdac_bus
*
bus
=
azx_dev
->
bus
;
if
(
!
dmab
->
area
||
!
azx_dev
->
locked
)
return
;
snd_hdac_dsp_lock
(
azx_dev
);
/* reset BDL address */
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPL
,
0
);
snd_hdac_stream_writel
(
azx_dev
,
SD_BDLPU
,
0
);
snd_hdac_stream_writel
(
azx_dev
,
SD_CTL
,
0
);
azx_dev
->
bufsize
=
0
;
azx_dev
->
period_bytes
=
0
;
azx_dev
->
format_val
=
0
;
bus
->
io_ops
->
dma_free_pages
(
bus
,
dmab
);
dmab
->
area
=
NULL
;
spin_lock_irq
(
&
bus
->
reg_lock
);
azx_dev
->
locked
=
false
;
spin_unlock_irq
(
&
bus
->
reg_lock
);
snd_hdac_dsp_unlock
(
azx_dev
);
}
EXPORT_SYMBOL_GPL
(
snd_hdac_dsp_cleanup
);
#endif
/* CONFIG_SND_HDA_DSP_LOADER */
sound/pci/hda/Kconfig
浏览文件 @
8ab418d3
...
...
@@ -38,9 +38,6 @@ config SND_HDA_TEGRA
if SND_HDA
config SND_HDA_DSP_LOADER
bool
config SND_HDA_PREALLOC_SIZE
int "Pre-allocated buffer size for HD-audio driver"
range 0 32768
...
...
sound/pci/hda/Makefile
浏览文件 @
8ab418d3
snd-hda-intel-objs
:=
hda_intel.o
snd-hda-controller-objs
:=
hda_controller.o
snd-hda-tegra-objs
:=
hda_tegra.o
# for haswell power well
snd-hda-intel-$(CONFIG_SND_HDA_I915)
+=
hda_i915.o
snd-hda-codec-y
:=
hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
snd-hda-codec-y
+=
hda_controller.o
snd-hda-codec-$(CONFIG_PROC_FS)
+=
hda_proc.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP)
+=
hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP)
+=
hda_beep.o
...
...
@@ -27,7 +27,6 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver
obj-$(CONFIG_SND_HDA)
:=
snd-hda-codec.o
obj-$(CONFIG_SND_HDA)
+=
snd-hda-controller.o
# codec drivers
obj-$(CONFIG_SND_HDA_GENERIC)
+=
snd-hda-codec-generic.o
...
...
sound/pci/hda/hda_codec.c
浏览文件 @
8ab418d3
...
...
@@ -146,11 +146,11 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
bus
->
no_response_fallback
=
0
;
mutex_unlock
(
&
bus
->
core
.
cmd_mutex
);
snd_hda_power_down_pm
(
codec
);
if
(
!
codec_in_pm
(
codec
)
&&
res
&&
err
<
0
&&
bus
->
rirb_error
)
{
if
(
!
codec_in_pm
(
codec
)
&&
res
&&
err
==
-
EAGAIN
)
{
if
(
bus
->
response_reset
)
{
codec_dbg
(
codec
,
"resetting BUS due to fatal communication error
\n
"
);
bus
->
ops
.
bus_reset
(
bus
);
snd_hda_
bus_reset
(
bus
);
}
goto
again
;
}
...
...
@@ -436,9 +436,8 @@ static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
get_wcaps_type
(
wcaps
)
!=
AC_WID_PIN
)
return
0
;
parm
=
snd_hda_param_read
(
codec
,
nid
,
AC_PAR_DEVLIST_LEN
);
if
(
parm
==
-
1
&&
codec
->
bus
->
rirb_error
)
parm
=
0
;
if
(
_snd_hdac_read_parm
(
&
codec
->
core
,
nid
,
AC_PAR_DEVLIST_LEN
,
&
parm
))
return
0
;
/* error */
return
parm
&
AC_DEV_LIST_LEN_MASK
;
}
...
...
@@ -467,10 +466,9 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
devices
=
0
;
while
(
devices
<
dev_len
)
{
parm
=
snd_hda_codec_read
(
codec
,
nid
,
0
,
AC_VERB_GET_DEVICE_LIST
,
devices
);
if
(
parm
==
-
1
&&
codec
->
bus
->
rirb_error
)
break
;
if
(
snd_hdac_read
(
&
codec
->
core
,
nid
,
AC_VERB_GET_DEVICE_LIST
,
devices
,
&
parm
))
break
;
/* error */
for
(
i
=
0
;
i
<
8
;
i
++
)
{
dev_list
[
devices
]
=
(
u8
)
parm
;
...
...
@@ -483,96 +481,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
return
devices
;
}
/*
* destructor
*/
static
void
snd_hda_bus_free
(
struct
hda_bus
*
bus
)
{
if
(
!
bus
)
return
;
if
(
bus
->
ops
.
private_free
)
bus
->
ops
.
private_free
(
bus
);
snd_hdac_bus_exit
(
&
bus
->
core
);
kfree
(
bus
);
}
static
int
snd_hda_bus_dev_free
(
struct
snd_device
*
device
)
{
snd_hda_bus_free
(
device
->
device_data
);
return
0
;
}
static
int
snd_hda_bus_dev_disconnect
(
struct
snd_device
*
device
)
{
struct
hda_bus
*
bus
=
device
->
device_data
;
bus
->
shutdown
=
1
;
return
0
;
}
/* hdac_bus_ops translations */
static
int
_hda_bus_command
(
struct
hdac_bus
*
_bus
,
unsigned
int
cmd
)
{
struct
hda_bus
*
bus
=
container_of
(
_bus
,
struct
hda_bus
,
core
);
return
bus
->
ops
.
command
(
bus
,
cmd
);
}
static
int
_hda_bus_get_response
(
struct
hdac_bus
*
_bus
,
unsigned
int
addr
,
unsigned
int
*
res
)
{
struct
hda_bus
*
bus
=
container_of
(
_bus
,
struct
hda_bus
,
core
);
*
res
=
bus
->
ops
.
get_response
(
bus
,
addr
);
return
bus
->
rirb_error
?
-
EIO
:
0
;
}
static
const
struct
hdac_bus_ops
bus_ops
=
{
.
command
=
_hda_bus_command
,
.
get_response
=
_hda_bus_get_response
,
};
/**
* snd_hda_bus_new - create a HDA bus
* @card: the card entry
* @busp: the pointer to store the created bus instance
*
* Returns 0 if successful, or a negative error code.
*/
int
snd_hda_bus_new
(
struct
snd_card
*
card
,
struct
hda_bus
**
busp
)
{
struct
hda_bus
*
bus
;
int
err
;
static
struct
snd_device_ops
dev_ops
=
{
.
dev_disconnect
=
snd_hda_bus_dev_disconnect
,
.
dev_free
=
snd_hda_bus_dev_free
,
};
if
(
busp
)
*
busp
=
NULL
;
bus
=
kzalloc
(
sizeof
(
*
bus
),
GFP_KERNEL
);
if
(
!
bus
)
return
-
ENOMEM
;
err
=
snd_hdac_bus_init
(
&
bus
->
core
,
card
->
dev
,
&
bus_ops
);
if
(
err
<
0
)
{
kfree
(
bus
);
return
err
;
}
bus
->
card
=
card
;
mutex_init
(
&
bus
->
prepare_mutex
);
err
=
snd_device_new
(
card
,
SNDRV_DEV_BUS
,
bus
,
&
dev_ops
);
if
(
err
<
0
)
{
snd_hda_bus_free
(
bus
);
return
err
;
}
if
(
busp
)
*
busp
=
bus
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hda_bus_new
);
/*
* read widget caps for each widget and store in cache
*/
...
...
@@ -3283,311 +3191,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
return
0
;
}
/*
* stream formats
*/
struct
hda_rate_tbl
{
unsigned
int
hz
;
unsigned
int
alsa_bits
;
unsigned
int
hda_fmt
;
};
/* rate = base * mult / div */
#define HDA_RATE(base, mult, div) \
(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
(((div) - 1) << AC_FMT_DIV_SHIFT))
static
struct
hda_rate_tbl
rate_bits
[]
=
{
/* rate in Hz, ALSA rate bitmask, HDA format value */
/* autodetected value used in snd_hda_query_supported_pcm */
{
8000
,
SNDRV_PCM_RATE_8000
,
HDA_RATE
(
48
,
1
,
6
)
},
{
11025
,
SNDRV_PCM_RATE_11025
,
HDA_RATE
(
44
,
1
,
4
)
},
{
16000
,
SNDRV_PCM_RATE_16000
,
HDA_RATE
(
48
,
1
,
3
)
},
{
22050
,
SNDRV_PCM_RATE_22050
,
HDA_RATE
(
44
,
1
,
2
)
},
{
32000
,
SNDRV_PCM_RATE_32000
,
HDA_RATE
(
48
,
2
,
3
)
},
{
44100
,
SNDRV_PCM_RATE_44100
,
HDA_RATE
(
44
,
1
,
1
)
},
{
48000
,
SNDRV_PCM_RATE_48000
,
HDA_RATE
(
48
,
1
,
1
)
},
{
88200
,
SNDRV_PCM_RATE_88200
,
HDA_RATE
(
44
,
2
,
1
)
},
{
96000
,
SNDRV_PCM_RATE_96000
,
HDA_RATE
(
48
,
2
,
1
)
},
{
176400
,
SNDRV_PCM_RATE_176400
,
HDA_RATE
(
44
,
4
,
1
)
},
{
192000
,
SNDRV_PCM_RATE_192000
,
HDA_RATE
(
48
,
4
,
1
)
},
#define AC_PAR_PCM_RATE_BITS 11
/* up to bits 10, 384kHZ isn't supported properly */
/* not autodetected value */
{
9600
,
SNDRV_PCM_RATE_KNOT
,
HDA_RATE
(
48
,
1
,
5
)
},
{
0
}
/* terminator */
};
/**
* snd_hda_calc_stream_format - calculate format bitset
* @codec: HD-audio codec
* @rate: the sample rate
* @channels: the number of channels
* @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
* @maxbps: the max. bps
* @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant)
*
* Calculate the format bitset from the given rate, channels and th PCM format.
*
* Return zero if invalid.
*/
unsigned
int
snd_hda_calc_stream_format
(
struct
hda_codec
*
codec
,
unsigned
int
rate
,
unsigned
int
channels
,
unsigned
int
format
,
unsigned
int
maxbps
,
unsigned
short
spdif_ctls
)
{
int
i
;
unsigned
int
val
=
0
;
for
(
i
=
0
;
rate_bits
[
i
].
hz
;
i
++
)
if
(
rate_bits
[
i
].
hz
==
rate
)
{
val
=
rate_bits
[
i
].
hda_fmt
;
break
;
}
if
(
!
rate_bits
[
i
].
hz
)
{
codec_dbg
(
codec
,
"invalid rate %d
\n
"
,
rate
);
return
0
;
}
if
(
channels
==
0
||
channels
>
8
)
{
codec_dbg
(
codec
,
"invalid channels %d
\n
"
,
channels
);
return
0
;
}
val
|=
channels
-
1
;
switch
(
snd_pcm_format_width
(
format
))
{
case
8
:
val
|=
AC_FMT_BITS_8
;
break
;
case
16
:
val
|=
AC_FMT_BITS_16
;
break
;
case
20
:
case
24
:
case
32
:
if
(
maxbps
>=
32
||
format
==
SNDRV_PCM_FORMAT_FLOAT_LE
)
val
|=
AC_FMT_BITS_32
;
else
if
(
maxbps
>=
24
)
val
|=
AC_FMT_BITS_24
;
else
val
|=
AC_FMT_BITS_20
;
break
;
default:
codec_dbg
(
codec
,
"invalid format width %d
\n
"
,
snd_pcm_format_width
(
format
));
return
0
;
}
if
(
spdif_ctls
&
AC_DIG1_NONAUDIO
)
val
|=
AC_FMT_TYPE_NON_PCM
;
return
val
;
}
EXPORT_SYMBOL_GPL
(
snd_hda_calc_stream_format
);
static
unsigned
int
query_pcm_param
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
)
{
unsigned
int
val
=
0
;
if
(
nid
!=
codec
->
core
.
afg
&&
(
get_wcaps
(
codec
,
nid
)
&
AC_WCAP_FORMAT_OVRD
))
val
=
snd_hda_param_read
(
codec
,
nid
,
AC_PAR_PCM
);
if
(
!
val
||
val
==
-
1
)
val
=
snd_hda_param_read
(
codec
,
codec
->
core
.
afg
,
AC_PAR_PCM
);
if
(
!
val
||
val
==
-
1
)
return
0
;
return
val
;
}
static
unsigned
int
query_stream_param
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
)
{
unsigned
int
streams
=
snd_hda_param_read
(
codec
,
nid
,
AC_PAR_STREAM
);
if
(
!
streams
||
streams
==
-
1
)
streams
=
snd_hda_param_read
(
codec
,
codec
->
core
.
afg
,
AC_PAR_STREAM
);
if
(
!
streams
||
streams
==
-
1
)
return
0
;
return
streams
;
}
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec
* @nid: NID to query
* @ratesp: the pointer to store the detected rate bitflags
* @formatsp: the pointer to store the detected formats
* @bpsp: the pointer to store the detected format widths
*
* Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
* or @bsps argument is ignored.
*
* Returns 0 if successful, otherwise a negative error code.
*/
int
snd_hda_query_supported_pcm
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
,
u32
*
ratesp
,
u64
*
formatsp
,
unsigned
int
*
bpsp
)
{
unsigned
int
i
,
val
,
wcaps
;
wcaps
=
get_wcaps
(
codec
,
nid
);
val
=
query_pcm_param
(
codec
,
nid
);
if
(
ratesp
)
{
u32
rates
=
0
;
for
(
i
=
0
;
i
<
AC_PAR_PCM_RATE_BITS
;
i
++
)
{
if
(
val
&
(
1
<<
i
))
rates
|=
rate_bits
[
i
].
alsa_bits
;
}
if
(
rates
==
0
)
{
codec_err
(
codec
,
"rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)
\n
"
,
nid
,
val
,
(
wcaps
&
AC_WCAP_FORMAT_OVRD
)
?
1
:
0
);
return
-
EIO
;
}
*
ratesp
=
rates
;
}
if
(
formatsp
||
bpsp
)
{
u64
formats
=
0
;
unsigned
int
streams
,
bps
;
streams
=
query_stream_param
(
codec
,
nid
);
if
(
!
streams
)
return
-
EIO
;
bps
=
0
;
if
(
streams
&
AC_SUPFMT_PCM
)
{
if
(
val
&
AC_SUPPCM_BITS_8
)
{
formats
|=
SNDRV_PCM_FMTBIT_U8
;
bps
=
8
;
}
if
(
val
&
AC_SUPPCM_BITS_16
)
{
formats
|=
SNDRV_PCM_FMTBIT_S16_LE
;
bps
=
16
;
}
if
(
wcaps
&
AC_WCAP_DIGITAL
)
{
if
(
val
&
AC_SUPPCM_BITS_32
)
formats
|=
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
;
if
(
val
&
(
AC_SUPPCM_BITS_20
|
AC_SUPPCM_BITS_24
))
formats
|=
SNDRV_PCM_FMTBIT_S32_LE
;
if
(
val
&
AC_SUPPCM_BITS_24
)
bps
=
24
;
else
if
(
val
&
AC_SUPPCM_BITS_20
)
bps
=
20
;
}
else
if
(
val
&
(
AC_SUPPCM_BITS_20
|
AC_SUPPCM_BITS_24
|
AC_SUPPCM_BITS_32
))
{
formats
|=
SNDRV_PCM_FMTBIT_S32_LE
;
if
(
val
&
AC_SUPPCM_BITS_32
)
bps
=
32
;
else
if
(
val
&
AC_SUPPCM_BITS_24
)
bps
=
24
;
else
if
(
val
&
AC_SUPPCM_BITS_20
)
bps
=
20
;
}
}
#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
if (streams & AC_SUPFMT_FLOAT32) {
formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
if (!bps)
bps = 32;
}
#endif
if
(
streams
==
AC_SUPFMT_AC3
)
{
/* should be exclusive */
/* temporary hack: we have still no proper support
* for the direct AC3 stream...
*/
formats
|=
SNDRV_PCM_FMTBIT_U8
;
bps
=
8
;
}
if
(
formats
==
0
)
{
codec_err
(
codec
,
"formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)
\n
"
,
nid
,
val
,
(
wcaps
&
AC_WCAP_FORMAT_OVRD
)
?
1
:
0
,
streams
);
return
-
EIO
;
}
if
(
formatsp
)
*
formatsp
=
formats
;
if
(
bpsp
)
*
bpsp
=
bps
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_hda_query_supported_pcm
);
/**
* snd_hda_is_supported_format - Check the validity of the format
* @codec: HD-audio codec
* @nid: NID to check
* @format: the HD-audio format value to check
*
* Check whether the given node supports the format value.
*
* Returns 1 if supported, 0 if not.
*/
int
snd_hda_is_supported_format
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
,
unsigned
int
format
)
{
int
i
;
unsigned
int
val
=
0
,
rate
,
stream
;
val
=
query_pcm_param
(
codec
,
nid
);
if
(
!
val
)
return
0
;
rate
=
format
&
0xff00
;
for
(
i
=
0
;
i
<
AC_PAR_PCM_RATE_BITS
;
i
++
)
if
(
rate_bits
[
i
].
hda_fmt
==
rate
)
{
if
(
val
&
(
1
<<
i
))
break
;
return
0
;
}
if
(
i
>=
AC_PAR_PCM_RATE_BITS
)
return
0
;
stream
=
query_stream_param
(
codec
,
nid
);
if
(
!
stream
)
return
0
;
if
(
stream
&
AC_SUPFMT_PCM
)
{
switch
(
format
&
0xf0
)
{
case
0x00
:
if
(
!
(
val
&
AC_SUPPCM_BITS_8
))
return
0
;
break
;
case
0x10
:
if
(
!
(
val
&
AC_SUPPCM_BITS_16
))
return
0
;
break
;
case
0x20
:
if
(
!
(
val
&
AC_SUPPCM_BITS_20
))
return
0
;
break
;
case
0x30
:
if
(
!
(
val
&
AC_SUPPCM_BITS_24
))
return
0
;
break
;
case
0x40
:
if
(
!
(
val
&
AC_SUPPCM_BITS_32
))
return
0
;
break
;
default:
return
0
;
}
}
else
{
/* FIXME: check for float32 and AC3? */
}
return
1
;
}
EXPORT_SYMBOL_GPL
(
snd_hda_is_supported_format
);
/*
* PCM stuff
*/
...
...
@@ -3800,9 +3403,6 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
struct
hda_pcm
*
cpcm
;
int
dev
,
err
;
if
(
snd_BUG_ON
(
!
bus
->
ops
.
attach_pcm
))
return
-
EINVAL
;
err
=
snd_hda_codec_parse_pcms
(
codec
);
if
(
err
<
0
)
{
snd_hda_codec_reset
(
codec
);
...
...
@@ -3820,7 +3420,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
if
(
dev
<
0
)
continue
;
/* no fatal error */
cpcm
->
device
=
dev
;
err
=
bus
->
ops
.
attach_pc
m
(
bus
,
codec
,
cpcm
);
err
=
snd_hda_attach_pcm_strea
m
(
bus
,
codec
,
cpcm
);
if
(
err
<
0
)
{
codec_err
(
codec
,
"cannot attach PCM stream %d for codec #%d
\n
"
,
...
...
@@ -4490,10 +4090,10 @@ int snd_hda_add_imux_item(struct hda_codec *codec,
EXPORT_SYMBOL_GPL
(
snd_hda_add_imux_item
);
/**
* snd_hda_bus_reset - Reset the bus
* snd_hda_bus_reset
_codecs
- Reset the bus
* @bus: HD-audio bus
*/
void
snd_hda_bus_reset
(
struct
hda_bus
*
bus
)
void
snd_hda_bus_reset
_codecs
(
struct
hda_bus
*
bus
)
{
struct
hda_codec
*
codec
;
...
...
@@ -4508,7 +4108,6 @@ void snd_hda_bus_reset(struct hda_bus *bus)
#endif
}
}
EXPORT_SYMBOL_GPL
(
snd_hda_bus_reset
);
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
...
...
sound/pci/hda/hda_codec.h
浏览文件 @
8ab418d3
...
...
@@ -40,32 +40,6 @@ struct hda_codec;
struct
hda_pcm
;
struct
hda_pcm_stream
;
/* bus operators */
struct
hda_bus_ops
{
/* send a single command */
int
(
*
command
)(
struct
hda_bus
*
bus
,
unsigned
int
cmd
);
/* get a response from the last command */
unsigned
int
(
*
get_response
)(
struct
hda_bus
*
bus
,
unsigned
int
addr
);
/* free the private data */
void
(
*
private_free
)(
struct
hda_bus
*
);
/* attach a PCM stream */
int
(
*
attach_pcm
)(
struct
hda_bus
*
bus
,
struct
hda_codec
*
codec
,
struct
hda_pcm
*
pcm
);
/* reset bus for retry verb */
void
(
*
bus_reset
)(
struct
hda_bus
*
bus
);
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* prepare DSP transfer */
int
(
*
load_dsp_prepare
)(
struct
hda_bus
*
bus
,
unsigned
int
format
,
unsigned
int
byte_size
,
struct
snd_dma_buffer
*
bufp
);
/* start/stop DSP transfer */
void
(
*
load_dsp_trigger
)(
struct
hda_bus
*
bus
,
bool
start
);
/* clean up DSP transfer */
void
(
*
load_dsp_cleanup
)(
struct
hda_bus
*
bus
,
struct
snd_dma_buffer
*
dmab
);
#endif
};
/*
* codec bus
*
...
...
@@ -77,10 +51,8 @@ struct hda_bus {
struct
snd_card
*
card
;
void
*
private_data
;
struct
pci_dev
*
pci
;
const
char
*
modelname
;
struct
hda_bus_ops
ops
;
struct
mutex
prepare_mutex
;
...
...
@@ -92,7 +64,6 @@ struct hda_bus {
unsigned
int
allow_bus_reset
:
1
;
/* allow bus reset at fatal error */
/* status for codec/controller */
unsigned
int
shutdown
:
1
;
/* being unloaded */
unsigned
int
rirb_error
:
1
;
/* error in codec communication */
unsigned
int
response_reset
:
1
;
/* controller was reset */
unsigned
int
in_reset
:
1
;
/* during reset operation */
unsigned
int
no_response_fallback
:
1
;
/* don't fallback at RIRB error */
...
...
@@ -100,6 +71,9 @@ struct hda_bus {
int
primary_dig_out_type
;
/* primary digital out PCM type */
};
/* from hdac_bus to hda_bus */
#define to_hda_bus(bus) container_of(bus, struct hda_bus, core)
/*
* codec preset
*
...
...
@@ -328,7 +302,10 @@ struct hda_codec {
/*
* constructors
*/
int
snd_hda_bus_new
(
struct
snd_card
*
card
,
struct
hda_bus
**
busp
);
int
snd_hda_bus_new
(
struct
snd_card
*
card
,
const
struct
hdac_bus_ops
*
ops
,
const
struct
hdac_io_ops
*
io_ops
,
struct
hda_bus
**
busp
);
int
snd_hda_codec_new
(
struct
hda_bus
*
bus
,
struct
snd_card
*
card
,
unsigned
int
codec_addr
,
struct
hda_codec
**
codecp
);
int
snd_hda_codec_configure
(
struct
hda_codec
*
codec
);
...
...
@@ -367,8 +344,6 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t
nid
,
int
recursive
);
int
snd_hda_get_devices
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
,
u8
*
dev_list
,
int
max_devices
);
int
snd_hda_query_supported_pcm
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
,
u32
*
ratesp
,
u64
*
formatsp
,
unsigned
int
*
bpsp
);
struct
hda_verb
{
hda_nid_t
nid
;
...
...
@@ -460,17 +435,17 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
int
do_now
);
#define snd_hda_codec_cleanup_stream(codec, nid) \
__snd_hda_codec_cleanup_stream(codec, nid, 0)
unsigned
int
snd_hda_calc_stream_format
(
struct
hda_codec
*
codec
,
unsigned
int
rate
,
unsigned
int
channels
,
unsigned
int
format
,
unsigned
int
maxbps
,
unsigned
short
spdif_ctls
);
int
snd_hda_is_supported_format
(
struct
hda_codec
*
codec
,
hda_nid_t
nid
,
unsigned
int
format
);
#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \
snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp)
#define snd_hda_is_supported_format(codec, nid, fmt) \
snd_hdac_is_supported_format(&(codec)->core, nid, fmt)
extern
const
struct
snd_pcm_chmap_elem
snd_pcm_2_1_chmaps
[];
int
snd_hda_attach_pcm_stream
(
struct
hda_bus
*
_bus
,
struct
hda_codec
*
codec
,
struct
hda_pcm
*
cpcm
);
/*
* Misc
*/
...
...
@@ -481,6 +456,7 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
int
snd_hda_lock_devices
(
struct
hda_bus
*
bus
);
void
snd_hda_unlock_devices
(
struct
hda_bus
*
bus
);
void
snd_hda_bus_reset
(
struct
hda_bus
*
bus
);
void
snd_hda_bus_reset_codecs
(
struct
hda_bus
*
bus
);
/*
* power management
...
...
@@ -526,24 +502,12 @@ int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
#ifdef CONFIG_SND_HDA_DSP_LOADER
static
inline
int
snd_hda_codec_load_dsp_prepare
(
struct
hda_codec
*
codec
,
unsigned
int
format
,
unsigned
int
size
,
struct
snd_dma_buffer
*
bufp
)
{
return
codec
->
bus
->
ops
.
load_dsp_prepare
(
codec
->
bus
,
format
,
size
,
bufp
);
}
static
inline
void
snd_hda_codec_load_dsp_trigger
(
struct
hda_codec
*
codec
,
bool
start
)
{
return
codec
->
bus
->
ops
.
load_dsp_trigger
(
codec
->
bus
,
start
);
}
static
inline
void
snd_hda_codec_load_dsp_cleanup
(
struct
hda_codec
*
codec
,
struct
snd_dma_buffer
*
dmab
)
{
return
codec
->
bus
->
ops
.
load_dsp_cleanup
(
codec
->
bus
,
dmab
);
}
int
snd_hda_codec_load_dsp_prepare
(
struct
hda_codec
*
codec
,
unsigned
int
format
,
unsigned
int
size
,
struct
snd_dma_buffer
*
bufp
);
void
snd_hda_codec_load_dsp_trigger
(
struct
hda_codec
*
codec
,
bool
start
);
void
snd_hda_codec_load_dsp_cleanup
(
struct
hda_codec
*
codec
,
struct
snd_dma_buffer
*
dmab
);
#else
static
inline
int
snd_hda_codec_load_dsp_prepare
(
struct
hda_codec
*
codec
,
unsigned
int
format
,
...
...
sound/pci/hda/hda_controller.c
浏览文件 @
8ab418d3
此差异已折叠。
点击以展开。
sound/pci/hda/hda_controller.h
浏览文件 @
8ab418d3
...
...
@@ -21,135 +21,10 @@
#include <sound/pcm.h>
#include <sound/initval.h>
#include "hda_codec.h"
#include <sound/hda_register.h>
/*
* registers
*/
#define AZX_REG_GCAP 0x00
#define AZX_GCAP_64OK (1 << 0)
/* 64bit address support */
#define AZX_GCAP_NSDO (3 << 1)
/* # of serial data out signals */
#define AZX_GCAP_BSS (31 << 3)
/* # of bidirectional streams */
#define AZX_GCAP_ISS (15 << 8)
/* # of input streams */
#define AZX_GCAP_OSS (15 << 12)
/* # of output streams */
#define AZX_REG_VMIN 0x02
#define AZX_REG_VMAJ 0x03
#define AZX_REG_OUTPAY 0x04
#define AZX_REG_INPAY 0x06
#define AZX_REG_GCTL 0x08
#define AZX_GCTL_RESET (1 << 0)
/* controller reset */
#define AZX_GCTL_FCNTRL (1 << 1)
/* flush control */
#define AZX_GCTL_UNSOL (1 << 8)
/* accept unsol. response enable */
#define AZX_REG_WAKEEN 0x0c
#define AZX_REG_STATESTS 0x0e
#define AZX_REG_GSTS 0x10
#define AZX_GSTS_FSTS (1 << 1)
/* flush status */
#define AZX_REG_INTCTL 0x20
#define AZX_REG_INTSTS 0x24
#define AZX_REG_WALLCLK 0x30
/* 24Mhz source */
#define AZX_REG_OLD_SSYNC 0x34
/* SSYNC for old ICH */
#define AZX_REG_SSYNC 0x38
#define AZX_REG_CORBLBASE 0x40
#define AZX_REG_CORBUBASE 0x44
#define AZX_REG_CORBWP 0x48
#define AZX_REG_CORBRP 0x4a
#define AZX_CORBRP_RST (1 << 15)
/* read pointer reset */
#define AZX_REG_CORBCTL 0x4c
#define AZX_CORBCTL_RUN (1 << 1)
/* enable DMA */
#define AZX_CORBCTL_CMEIE (1 << 0)
/* enable memory error irq */
#define AZX_REG_CORBSTS 0x4d
#define AZX_CORBSTS_CMEI (1 << 0)
/* memory error indication */
#define AZX_REG_CORBSIZE 0x4e
#define AZX_REG_RIRBLBASE 0x50
#define AZX_REG_RIRBUBASE 0x54
#define AZX_REG_RIRBWP 0x58
#define AZX_RIRBWP_RST (1 << 15)
/* write pointer reset */
#define AZX_REG_RINTCNT 0x5a
#define AZX_REG_RIRBCTL 0x5c
#define AZX_RBCTL_IRQ_EN (1 << 0)
/* enable IRQ */
#define AZX_RBCTL_DMA_EN (1 << 1)
/* enable DMA */
#define AZX_RBCTL_OVERRUN_EN (1 << 2)
/* enable overrun irq */
#define AZX_REG_RIRBSTS 0x5d
#define AZX_RBSTS_IRQ (1 << 0)
/* response irq */
#define AZX_RBSTS_OVERRUN (1 << 2)
/* overrun irq */
#define AZX_REG_RIRBSIZE 0x5e
#define AZX_REG_IC 0x60
#define AZX_REG_IR 0x64
#define AZX_REG_IRS 0x68
#define AZX_IRS_VALID (1<<1)
#define AZX_IRS_BUSY (1<<0)
#define AZX_REG_DPLBASE 0x70
#define AZX_REG_DPUBASE 0x74
#define AZX_DPLBASE_ENABLE 0x1
/* Enable position buffer */
/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
enum
{
SDI0
,
SDI1
,
SDI2
,
SDI3
,
SDO0
,
SDO1
,
SDO2
,
SDO3
};
/* stream register offsets from stream base */
#define AZX_REG_SD_CTL 0x00
#define AZX_REG_SD_STS 0x03
#define AZX_REG_SD_LPIB 0x04
#define AZX_REG_SD_CBL 0x08
#define AZX_REG_SD_LVI 0x0c
#define AZX_REG_SD_FIFOW 0x0e
#define AZX_REG_SD_FIFOSIZE 0x10
#define AZX_REG_SD_FORMAT 0x12
#define AZX_REG_SD_BDLPL 0x18
#define AZX_REG_SD_BDLPU 0x1c
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE 4096
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
#define AZX_MAX_FRAG 32
/* max buffer size - no h/w limit, you can increase as you like */
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define AZX_MAX_CODECS 8
#define AZX_MAX_CODECS HDA_MAX_CODECS
#define AZX_DEFAULT_CODECS 4
#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01
/* stream reset bit */
#define SD_CTL_DMA_START 0x02
/* stream DMA start bit */
#define SD_CTL_STRIPE (3 << 16)
/* stripe control */
#define SD_CTL_TRAFFIC_PRIO (1 << 18)
/* traffic priority */
#define SD_CTL_DIR (1 << 19)
/* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
/* SD_CTL and SD_STS */
#define SD_INT_DESC_ERR 0x10
/* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08
/* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04
/* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE)
/* SD_STS */
#define SD_STS_FIFO_READY 0x20
/* FIFO ready */
/* INTCTL and INTSTS */
#define AZX_INT_ALL_STREAM 0xff
/* all stream interrupts */
#define AZX_INT_CTRL_EN 0x40000000
/* controller interrupt enable bit */
#define AZX_INT_GLOBAL_EN 0x80000000
/* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define AZX_MAX_CORB_ENTRIES 256
#define AZX_MAX_RIRB_ENTRIES 256
/* driver quirks (capabilities) */
/* bits 0-7 are used for indicating driver type */
...
...
@@ -183,40 +58,10 @@ enum {
AZX_SNOOP_TYPE_NVIDIA
,
};
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
struct
azx_dev
{
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
;
struct
hdac_stream
core
;
unsigned
int
irq_pending
:
1
;
unsigned
int
prepared
:
1
;
unsigned
int
locked
:
1
;
/*
* For VIA:
* A flag to ensure DMA position is 0
...
...
@@ -224,50 +69,17 @@ struct azx_dev {
*/
unsigned
int
insufficient
:
1
;
unsigned
int
wc_marked
:
1
;
unsigned
int
no_period_wakeup
:
1
;
struct
timecounter
azx_tc
;
struct
cyclecounter
azx_cc
;
int
delay_negative_threshold
;
#ifdef CONFIG_SND_HDA_DSP_LOADER
/* Allows dsp load to have sole access to the playback stream. */
struct
mutex
dsp_mutex
;
#endif
};
/* CORB/RIRB */
struct
azx_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
[
AZX_MAX_CODECS
];
/* number of pending requests */
u32
res
[
AZX_MAX_CODECS
];
/* last read value */
};
#define azx_stream(dev) (&(dev)->core)
#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core)
struct
azx
;
/* Functions to read/write to hda registers. */
struct
hda_controller_ops
{
/* Register Access */
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
azx
*
);
/* Allocation ops */
int
(
*
dma_alloc_pages
)(
struct
azx
*
chip
,
int
type
,
size_t
size
,
struct
snd_dma_buffer
*
buf
);
void
(
*
dma_free_pages
)(
struct
azx
*
chip
,
struct
snd_dma_buffer
*
buf
);
int
(
*
substream_alloc_pages
)(
struct
azx
*
chip
,
struct
snd_pcm_substream
*
substream
,
size_t
size
);
...
...
@@ -291,6 +103,8 @@ typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
typedef
int
(
*
azx_get_delay_callback_t
)(
struct
azx
*
,
struct
azx_dev
*
,
unsigned
int
pos
);
struct
azx
{
struct
hda_bus
bus
;
struct
snd_card
*
card
;
struct
pci_dev
*
pci
;
int
dev_index
;
...
...
@@ -312,35 +126,16 @@ struct azx {
azx_get_pos_callback_t
get_position
[
2
];
azx_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 */
/* streams (x num_streams) */
struct
azx_dev
*
azx_dev
;
/* PCM */
struct
list_head
pcm_list
;
/* azx_pcm list */
/* HD codec */
unsigned
short
codec_mask
;
int
codec_probe_mask
;
/* copied from probe_mask option */
struct
hda_bus
*
bus
;
unsigned
int
beep_mode
;
/* CORB/RIRB */
struct
azx_rb
corb
;
struct
azx_rb
rirb
;
/* CORB/RIRB and position buffers */
struct
snd_dma_buffer
rb
;
struct
snd_dma_buffer
posbuf
;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
const
struct
firmware
*
fw
;
#endif
...
...
@@ -349,7 +144,6 @@ struct azx {
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
;
...
...
@@ -359,14 +153,14 @@ struct azx {
unsigned
int
region_requested
:
1
;
unsigned
int
disabled
:
1
;
/* disabled by VGA-switcher */
/* for debugging */
unsigned
int
last_cmd
[
AZX_MAX_CODECS
];
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct
azx_dev
saved_azx_dev
;
#endif
};
#define azx_bus(chip) (&(chip)->bus.core)
#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
#ifdef CONFIG_X86
#define azx_snoop(chip) ((chip)->snoop)
#else
...
...
@@ -378,30 +172,17 @@ struct azx {
*/
#define azx_writel(chip, reg, value) \
((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)
)
snd_hdac_chip_writel(azx_bus(chip), reg, value
)
#define azx_readl(chip, reg) \
((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)
)
snd_hdac_chip_readl(azx_bus(chip), reg
)
#define azx_writew(chip, reg, value) \
((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)
)
snd_hdac_chip_writew(azx_bus(chip), reg, value
)
#define azx_readw(chip, reg) \
((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)
)
snd_hdac_chip_readw(azx_bus(chip), reg
)
#define azx_writeb(chip, reg, value) \
((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)
)
snd_hdac_chip_writeb(azx_bus(chip), reg, value
)
#define azx_readb(chip, reg) \
((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
#define azx_sd_writel(chip, dev, reg, value) \
((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readl(chip, dev, reg) \
((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writew(chip, dev, reg, value) \
((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readw(chip, dev, reg) \
((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_writeb(chip, dev, reg, value) \
((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg))
#define azx_sd_readb(chip, dev, reg) \
((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg))
snd_hdac_chip_readb(azx_bus(chip), reg)
#define azx_has_pm_runtime(chip) \
((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)
...
...
@@ -416,22 +197,27 @@ unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
unsigned
int
azx_get_pos_posbuf
(
struct
azx
*
chip
,
struct
azx_dev
*
azx_dev
);
/* Stream control. */
void
azx_st
ream_stop
(
struct
azx
*
chip
,
struct
azx_dev
*
azx_dev
);
void
azx_st
op_all_streams
(
struct
azx
*
chip
);
/* Allocation functions. */
int
azx_alloc_stream_pages
(
struct
azx
*
chip
);
void
azx_free_stream_pages
(
struct
azx
*
chip
);
#define azx_alloc_stream_pages(chip) \
snd_hdac_bus_alloc_stream_pages(azx_bus(chip))
#define azx_free_stream_pages(chip) \
snd_hdac_bus_free_stream_pages(azx_bus(chip))
/* Low level azx interface */
void
azx_init_chip
(
struct
azx
*
chip
,
bool
full_reset
);
void
azx_stop_chip
(
struct
azx
*
chip
);
void
azx_enter_link_reset
(
struct
azx
*
chip
);
#define azx_enter_link_reset(chip) \
snd_hdac_bus_enter_link_reset(azx_bus(chip))
irqreturn_t
azx_interrupt
(
int
irq
,
void
*
dev_id
);
/* Codec interface */
int
azx_bus_create
(
struct
azx
*
chip
,
const
char
*
model
);
int
azx_bus_init
(
struct
azx
*
chip
,
const
char
*
model
,
const
struct
hdac_io_ops
*
io_ops
);
int
azx_probe_codecs
(
struct
azx
*
chip
,
unsigned
int
max_slots
);
int
azx_codec_configure
(
struct
azx
*
chip
);
int
azx_init_stream
(
struct
azx
*
chip
);
int
azx_init_streams
(
struct
azx
*
chip
);
void
azx_free_streams
(
struct
azx
*
chip
);
#endif
/* __SOUND_HDA_CONTROLLER_H */
sound/pci/hda/hda_intel.c
浏览文件 @
8ab418d3
此差异已折叠。
点击以展开。
sound/pci/hda/hda_intel.h
浏览文件 @
8ab418d3
...
...
@@ -34,6 +34,7 @@ struct hda_intel {
/* extra flags */
unsigned
int
irq_pending_warned
:
1
;
unsigned
int
probe_continued
:
1
;
/* VGA-switcheroo setup */
unsigned
int
use_vga_switcheroo
:
1
;
...
...
sound/pci/hda/hda_intel_trace.h
浏览文件 @
8ab418d3
...
...
@@ -24,7 +24,7 @@ TRACE_EVENT(azx_pcm_trigger,
TP_fast_assign
(
__entry
->
card
=
(
chip
)
->
card
->
number
;
__entry
->
idx
=
(
dev
)
->
index
;
__entry
->
idx
=
(
dev
)
->
core
.
index
;
__entry
->
cmd
=
cmd
;
),
...
...
@@ -46,7 +46,7 @@ TRACE_EVENT(azx_get_position,
TP_fast_assign
(
__entry
->
card
=
(
chip
)
->
card
->
number
;
__entry
->
idx
=
(
dev
)
->
index
;
__entry
->
idx
=
(
dev
)
->
core
.
index
;
__entry
->
pos
=
pos
;
__entry
->
delay
=
delay
;
),
...
...
sound/pci/hda/hda_tegra.c
浏览文件 @
8ab418d3
此差异已折叠。
点击以展开。
sound/pci/hda/patch_ca0132.c
浏览文件 @
8ab418d3
...
...
@@ -2052,11 +2052,8 @@ static int dma_convert_to_hda_format(struct hda_codec *codec,
{
unsigned
int
format_val
;
format_val
=
snd_hda_calc_stream_format
(
codec
,
sample_rate
,
channels
,
SNDRV_PCM_FORMAT_S32_LE
,
32
,
0
);
format_val
=
snd_hdac_calc_stream_format
(
sample_rate
,
channels
,
SNDRV_PCM_FORMAT_S32_LE
,
32
,
0
);
if
(
hda_format
)
*
hda_format
=
(
unsigned
short
)
format_val
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录