mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (377 commits) ASoC: au1x: PSC-AC97 bugfixes ALSA: dummy - Increase MAX_PCM_SUBSTREAMS to 128 ALSA: dummy - Add debug proc file ALSA: Add const prefix to proc helper functions ALSA: Re-export snd_pcm_format_name() function ALSA: hda - Use auto model for HP laptops with ALC268 codec ALSA: cs46xx - Fix minimum period size ASoC: Fix WM835x Out4 capture enumeration ALSA: Remove unneeded ifdef from sound/core.h ALSA: Remove struct snd_monitor_file from public sound/core.h ASoC: Remove unuused hw_read_t sound: oxygen: work around MCE when changing volume ALSA: dummy - Fake buffer allocations ALSA: hda/realtek: Added support for CLEVO M540R subsystem, 6 channel + digital ASoC: fix pxa2xx-ac97.c breakage ALSA: dummy - Fix the timer calculation in systimer mode ALSA: dummy - Add more description ALSA: dummy - Better jiffies handling ALSA: dummy - Support high-res timer mode ALSA: Release v1.0.21 ...
This commit is contained in:
commit
a9c86d4259
@ -468,3 +468,27 @@ Why: cpu_policy_rwsem has a new cleaner definition making it local to
|
||||
cpufreq core and contained inside cpufreq.c. Other dependent
|
||||
drivers should not use it in order to safely avoid lockdep issues.
|
||||
Who: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
||||
|
||||
----------------------------
|
||||
|
||||
What: sound-slot/service-* module aliases and related clutters in
|
||||
sound/sound_core.c
|
||||
When: August 2010
|
||||
Why: OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR
|
||||
(14) and requests modules using custom sound-slot/service-*
|
||||
module aliases. The only benefit of doing this is allowing
|
||||
use of custom module aliases which might as well be considered
|
||||
a bug at this point. This preemptive claiming prevents
|
||||
alternative OSS implementations.
|
||||
|
||||
Till the feature is removed, the kernel will be requesting
|
||||
both sound-slot/service-* and the standard char-major-* module
|
||||
aliases and allow turning off the pre-claiming selectively via
|
||||
CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss
|
||||
kernel parameter.
|
||||
|
||||
After the transition phase is complete, both the custom module
|
||||
aliases and switches to disable it will go away. This removal
|
||||
will also allow making ALSA OSS emulation independent of
|
||||
sound_core. The dependency will be broken then too.
|
||||
Who: Tejun Heo <tj@kernel.org>
|
||||
|
@ -60,6 +60,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
slots - Reserve the slot index for the given driver.
|
||||
This option takes multiple strings.
|
||||
See "Module Autoloading Support" section for details.
|
||||
debug - Specifies the debug message level
|
||||
(0 = disable debug prints, 1 = normal debug messages,
|
||||
2 = verbose debug messages)
|
||||
This option appears only when CONFIG_SND_DEBUG=y.
|
||||
This option can be dynamically changed via sysfs
|
||||
/sys/modules/snd/parameters/debug file.
|
||||
|
||||
Module snd-pcm-oss
|
||||
------------------
|
||||
@ -513,6 +519,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
or input, but you may use this module for any application which
|
||||
requires a sound card (like RealPlayer).
|
||||
|
||||
pcm_devs - Number of PCM devices assigned to each card
|
||||
(default = 1, up to 4)
|
||||
pcm_substreams - Number of PCM substreams assigned to each PCM
|
||||
(default = 8, up to 16)
|
||||
hrtimer - Use hrtimer (=1, default) or system timer (=0)
|
||||
fake_buffer - Fake buffer allocations (default = 1)
|
||||
|
||||
When multiple PCM devices are created, snd-dummy gives different
|
||||
behavior to each PCM device:
|
||||
0 = interleaved with mmap support
|
||||
1 = non-interleaved with mmap support
|
||||
2 = interleaved without mmap
|
||||
3 = non-interleaved without mmap
|
||||
|
||||
As default, snd-dummy drivers doesn't allocate the real buffers
|
||||
but either ignores read/write or mmap a single dummy page to all
|
||||
buffer pages, in order to save the resouces. If your apps need
|
||||
the read/ written buffer data to be consistent, pass fake_buffer=0
|
||||
option.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-echo3g
|
||||
@ -768,6 +794,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.
|
||||
Passing -1 will make the driver to choose the appropriate
|
||||
value based on the controller chip.
|
||||
patch - Specifies the early "patch" files to modify the HD-audio
|
||||
setup before initializing the codecs. This option is
|
||||
available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
|
||||
See HD-Audio.txt for details.
|
||||
|
||||
[Single (global) options]
|
||||
single_cmd - Use single immediate commands to communicate with
|
||||
|
@ -114,8 +114,8 @@ ALC662/663/272
|
||||
samsung-nc10 Samsung NC10 mini notebook
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC882/885
|
||||
==========
|
||||
ALC882/883/885/888/889
|
||||
======================
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
arima Arima W820Di1
|
||||
@ -127,12 +127,8 @@ ALC882/885
|
||||
mbp3 Macbook Pro rev3
|
||||
imac24 iMac 24'' with jack detection
|
||||
w2jc ASUS W2JC
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC883/888
|
||||
==========
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
3stack-2ch-dig 3-jack with SPDIF I/O (ALC883)
|
||||
alc883-6stack-dig 6-jack digital with SPDIF I/O (ALC883)
|
||||
3stack-6ch 3-jack 6-channel
|
||||
3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
|
||||
6stack-dig-demo 6-jack digital for Intel demo board
|
||||
@ -140,6 +136,7 @@ ALC883/888
|
||||
acer-aspire Acer Aspire 9810
|
||||
acer-aspire-4930g Acer Aspire 4930G
|
||||
acer-aspire-6530g Acer Aspire 6530G
|
||||
acer-aspire-7730g Acer Aspire 7730G
|
||||
acer-aspire-8930g Acer Aspire 8930G
|
||||
medion Medion Laptops
|
||||
medion-md2 Medion MD2
|
||||
@ -155,10 +152,13 @@ ALC883/888
|
||||
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
|
||||
6stack-dell Dell machines with 6stack (Inspiron 530)
|
||||
mitac Mitac 8252D
|
||||
clevo-m540r Clevo M540R (6ch + digital)
|
||||
clevo-m720 Clevo M720 laptop series
|
||||
fujitsu-pi2515 Fujitsu AMILO Pi2515
|
||||
fujitsu-xa3530 Fujitsu AMILO XA3530
|
||||
3stack-6ch-intel Intel DG33* boards
|
||||
intel-alc889a Intel IbexPeak with ALC889A
|
||||
intel-x58 Intel DX58 with ALC889
|
||||
asus-p5q ASUS P5Q-EM boards
|
||||
mb31 MacBook 3,1
|
||||
sony-vaio-tt Sony VAIO TT
|
||||
@ -229,7 +229,7 @@ AD1984
|
||||
======
|
||||
basic default configuration
|
||||
thinkpad Lenovo Thinkpad T61/X61
|
||||
dell Dell T3400
|
||||
dell_desktop Dell T3400
|
||||
|
||||
AD1986A
|
||||
=======
|
||||
@ -258,6 +258,7 @@ Conexant 5045
|
||||
laptop-micsense Laptop with Mic sense (old model fujitsu)
|
||||
laptop-hpmicsense Laptop with HP and Mic senses
|
||||
benq Benq R55E
|
||||
laptop-hp530 HP 530 laptop
|
||||
test for testing/debugging purpose, almost all controls
|
||||
can be adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
@ -278,9 +279,16 @@ Conexant 5051
|
||||
hp-dv6736 HP dv6736
|
||||
lenovo-x200 Lenovo X200 laptop
|
||||
|
||||
Conexant 5066
|
||||
=============
|
||||
laptop Basic Laptop config (default)
|
||||
dell-laptop Dell laptops
|
||||
olpc-xo-1_5 OLPC XO 1.5
|
||||
|
||||
STAC9200
|
||||
========
|
||||
ref Reference board
|
||||
oqo OQO Model 2
|
||||
dell-d21 Dell (unknown)
|
||||
dell-d22 Dell (unknown)
|
||||
dell-d23 Dell (unknown)
|
||||
@ -368,10 +376,12 @@ STAC92HD73*
|
||||
===========
|
||||
ref Reference board
|
||||
no-jd BIOS setup but without jack-detection
|
||||
intel Intel DG45* mobos
|
||||
dell-m6-amic Dell desktops/laptops with analog mics
|
||||
dell-m6-dmic Dell desktops/laptops with digital mics
|
||||
dell-m6 Dell desktops/laptops with both type of mics
|
||||
dell-eq Dell desktops/laptops
|
||||
alienware Alienware M17x
|
||||
auto BIOS setup (default)
|
||||
|
||||
STAC92HD83*
|
||||
@ -385,3 +395,8 @@ STAC9872
|
||||
========
|
||||
vaio VAIO laptop without SPDIF
|
||||
auto BIOS setup (default)
|
||||
|
||||
Cirrus Logic CS4206/4207
|
||||
========================
|
||||
mbp55 MacBook Pro 5,5
|
||||
auto BIOS setup (default)
|
||||
|
@ -138,6 +138,10 @@ override the BIOS setup or to provide more comprehensive features.
|
||||
The driver checks PCI SSID and looks through the static configuration
|
||||
table until any matching entry is found. If you have a new machine,
|
||||
you may see a message like below:
|
||||
------------------------------------------------------------------------
|
||||
hda_codec: ALC880: BIOS auto-probing.
|
||||
------------------------------------------------------------------------
|
||||
Meanwhile, in the earlier versions, you would see a message like:
|
||||
------------------------------------------------------------------------
|
||||
hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
|
||||
------------------------------------------------------------------------
|
||||
@ -403,6 +407,66 @@ re-configure based on that state, run like below:
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
Early Patching
|
||||
~~~~~~~~~~~~~~
|
||||
When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a
|
||||
firmware file for modifying the HD-audio setup before initializing the
|
||||
codec. This can work basically like the reconfiguration via sysfs in
|
||||
the above, but it does it before the first codec configuration.
|
||||
|
||||
A patch file is a plain text file which looks like below:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
[codec]
|
||||
0x12345678 0xabcd1234 2
|
||||
|
||||
[model]
|
||||
auto
|
||||
|
||||
[pincfg]
|
||||
0x12 0x411111f0
|
||||
|
||||
[verb]
|
||||
0x20 0x500 0x03
|
||||
0x20 0x400 0xff
|
||||
|
||||
[hint]
|
||||
hp_detect = yes
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The file needs to have a line `[codec]`. The next line should contain
|
||||
three numbers indicating the codec vendor-id (0x12345678 in the
|
||||
example), the codec subsystem-id (0xabcd1234) and the address (2) of
|
||||
the codec. The rest patch entries are applied to this specified codec
|
||||
until another codec entry is given.
|
||||
|
||||
The `[model]` line allows to change the model name of the each codec.
|
||||
In the example above, it will be changed to model=auto.
|
||||
Note that this overrides the module option.
|
||||
|
||||
After the `[pincfg]` line, the contents are parsed as the initial
|
||||
default pin-configurations just like `user_pin_configs` sysfs above.
|
||||
The values can be shown in user_pin_configs sysfs file, too.
|
||||
|
||||
Similarly, the lines after `[verb]` are parsed as `init_verbs`
|
||||
sysfs entries, and the lines after `[hint]` are parsed as `hints`
|
||||
sysfs entries, respectively.
|
||||
|
||||
The hd-audio driver reads the file via request_firmware(). Thus,
|
||||
a patch file has to be located on the appropriate firmware path,
|
||||
typically, /lib/firmware. For example, when you pass the option
|
||||
`patch=hda-init.fw`, the file /lib/firmware/hda-init-fw must be
|
||||
present.
|
||||
|
||||
The patch module option is specific to each card instance, and you
|
||||
need to give one file name for each instance, separated by commas.
|
||||
For example, if you have two cards, one for an on-board analog and one
|
||||
for an HDMI video board, you may pass patch option like below:
|
||||
------------------------------------------------------------------------
|
||||
options snd-hda-intel patch=on-board-patch,hdmi-patch
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
Power-Saving
|
||||
~~~~~~~~~~~~
|
||||
The power-saving is a kind of auto-suspend of the device. When the
|
||||
|
@ -128,6 +128,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
|
||||
.rx_irq = INT_24XX_MCBSP1_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP1_IRQ_TX,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
.buffer_size = 0x6F,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP34XX_MCBSP2_BASE,
|
||||
@ -136,6 +137,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
|
||||
.rx_irq = INT_24XX_MCBSP2_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP2_IRQ_TX,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
.buffer_size = 0x3FF,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP34XX_MCBSP3_BASE,
|
||||
@ -144,6 +146,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
|
||||
.rx_irq = INT_24XX_MCBSP3_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP3_IRQ_TX,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
.buffer_size = 0x6F,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP34XX_MCBSP4_BASE,
|
||||
@ -152,6 +155,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
|
||||
.rx_irq = INT_24XX_MCBSP4_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP4_IRQ_TX,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
.buffer_size = 0x6F,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP34XX_MCBSP5_BASE,
|
||||
@ -160,6 +164,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
|
||||
.rx_irq = INT_24XX_MCBSP5_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP5_IRQ_TX,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
.buffer_size = 0x6F,
|
||||
},
|
||||
};
|
||||
#define OMAP34XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap34xx_mcbsp_pdata)
|
||||
|
@ -3,10 +3,12 @@
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
/*
|
||||
* @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
|
||||
* a -1 value means no gpio will be used for reset
|
||||
* @codec_pdata: AC97 codec platform_data
|
||||
|
||||
* reset_gpio should only be specified for pxa27x CPUs where a silicon
|
||||
* bug prevents correct operation of the reset line. If not specified,
|
||||
@ -20,6 +22,7 @@ typedef struct {
|
||||
void (*resume)(void *);
|
||||
void *priv;
|
||||
int reset_gpio;
|
||||
void *codec_pdata[AC97_BUS_MAX_DEVICES];
|
||||
} pxa2xx_audio_ops_t;
|
||||
|
||||
extern void pxa_set_ac97_info(pxa2xx_audio_ops_t *ops);
|
||||
|
@ -1127,6 +1127,11 @@ int omap_dma_running(void)
|
||||
void omap_dma_link_lch(int lch_head, int lch_queue)
|
||||
{
|
||||
if (omap_dma_in_1510_mode()) {
|
||||
if (lch_head == lch_queue) {
|
||||
dma_write(dma_read(CCR(lch_head)) | (3 << 8),
|
||||
CCR(lch_head));
|
||||
return;
|
||||
}
|
||||
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
|
||||
BUG();
|
||||
return;
|
||||
@ -1149,6 +1154,11 @@ EXPORT_SYMBOL(omap_dma_link_lch);
|
||||
void omap_dma_unlink_lch(int lch_head, int lch_queue)
|
||||
{
|
||||
if (omap_dma_in_1510_mode()) {
|
||||
if (lch_head == lch_queue) {
|
||||
dma_write(dma_read(CCR(lch_head)) & ~(3 << 8),
|
||||
CCR(lch_head));
|
||||
return;
|
||||
}
|
||||
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
|
||||
BUG();
|
||||
return;
|
||||
|
@ -134,6 +134,11 @@
|
||||
#define OMAP_MCBSP_REG_XCERG 0x74
|
||||
#define OMAP_MCBSP_REG_XCERH 0x78
|
||||
#define OMAP_MCBSP_REG_SYSCON 0x8C
|
||||
#define OMAP_MCBSP_REG_THRSH2 0x90
|
||||
#define OMAP_MCBSP_REG_THRSH1 0x94
|
||||
#define OMAP_MCBSP_REG_IRQST 0xA0
|
||||
#define OMAP_MCBSP_REG_IRQEN 0xA4
|
||||
#define OMAP_MCBSP_REG_WAKEUPEN 0xA8
|
||||
#define OMAP_MCBSP_REG_XCCR 0xAC
|
||||
#define OMAP_MCBSP_REG_RCCR 0xB0
|
||||
|
||||
@ -249,8 +254,27 @@
|
||||
#define RDISABLE 0x0001
|
||||
|
||||
/********************** McBSP SYSCONFIG bit definitions ********************/
|
||||
#define CLOCKACTIVITY(value) ((value)<<8)
|
||||
#define SIDLEMODE(value) ((value)<<3)
|
||||
#define ENAWAKEUP 0x0004
|
||||
#define SOFTRST 0x0002
|
||||
|
||||
/********************** McBSP DMA operating modes **************************/
|
||||
#define MCBSP_DMA_MODE_ELEMENT 0
|
||||
#define MCBSP_DMA_MODE_THRESHOLD 1
|
||||
#define MCBSP_DMA_MODE_FRAME 2
|
||||
|
||||
/********************** McBSP WAKEUPEN bit definitions *********************/
|
||||
#define XEMPTYEOFEN 0x4000
|
||||
#define XRDYEN 0x0400
|
||||
#define XEOFEN 0x0200
|
||||
#define XFSXEN 0x0100
|
||||
#define XSYNCERREN 0x0080
|
||||
#define RRDYEN 0x0008
|
||||
#define REOFEN 0x0004
|
||||
#define RFSREN 0x0002
|
||||
#define RSYNCERREN 0x0001
|
||||
|
||||
/* we don't do multichannel for now */
|
||||
struct omap_mcbsp_reg_cfg {
|
||||
u16 spcr2;
|
||||
@ -344,6 +368,9 @@ struct omap_mcbsp_platform_data {
|
||||
u8 dma_rx_sync, dma_tx_sync;
|
||||
u16 rx_irq, tx_irq;
|
||||
struct omap_mcbsp_ops *ops;
|
||||
#ifdef CONFIG_ARCH_OMAP34XX
|
||||
u16 buffer_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct omap_mcbsp {
|
||||
@ -377,6 +404,11 @@ struct omap_mcbsp {
|
||||
struct omap_mcbsp_platform_data *pdata;
|
||||
struct clk *iclk;
|
||||
struct clk *fclk;
|
||||
#ifdef CONFIG_ARCH_OMAP34XX
|
||||
int dma_op_mode;
|
||||
u16 max_tx_thres;
|
||||
u16 max_rx_thres;
|
||||
#endif
|
||||
};
|
||||
extern struct omap_mcbsp **mcbsp_ptr;
|
||||
extern int omap_mcbsp_count;
|
||||
@ -385,10 +417,25 @@ int omap_mcbsp_init(void);
|
||||
void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
|
||||
int size);
|
||||
void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config);
|
||||
#ifdef CONFIG_ARCH_OMAP34XX
|
||||
void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold);
|
||||
void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
|
||||
u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
|
||||
u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
|
||||
int omap_mcbsp_get_dma_op_mode(unsigned int id);
|
||||
#else
|
||||
static inline void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
|
||||
{ }
|
||||
static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
|
||||
{ }
|
||||
static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
|
||||
static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
|
||||
static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }
|
||||
#endif
|
||||
int omap_mcbsp_request(unsigned int id);
|
||||
void omap_mcbsp_free(unsigned int id);
|
||||
void omap_mcbsp_start(unsigned int id);
|
||||
void omap_mcbsp_stop(unsigned int id);
|
||||
void omap_mcbsp_start(unsigned int id, int tx, int rx);
|
||||
void omap_mcbsp_stop(unsigned int id, int tx, int rx);
|
||||
void omap_mcbsp_xmit_word(unsigned int id, u32 word);
|
||||
u32 omap_mcbsp_recv_word(unsigned int id);
|
||||
|
||||
|
@ -198,6 +198,170 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_config);
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP34XX
|
||||
/*
|
||||
* omap_mcbsp_set_tx_threshold configures how to deal
|
||||
* with transmit threshold. the threshold value and handler can be
|
||||
* configure in here.
|
||||
*/
|
||||
void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
void __iomem *io_base;
|
||||
|
||||
if (!cpu_is_omap34xx())
|
||||
return;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
|
||||
return;
|
||||
}
|
||||
mcbsp = id_to_mcbsp_ptr(id);
|
||||
io_base = mcbsp->io_base;
|
||||
|
||||
OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
|
||||
|
||||
/*
|
||||
* omap_mcbsp_set_rx_threshold configures how to deal
|
||||
* with receive threshold. the threshold value and handler can be
|
||||
* configure in here.
|
||||
*/
|
||||
void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
void __iomem *io_base;
|
||||
|
||||
if (!cpu_is_omap34xx())
|
||||
return;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
|
||||
return;
|
||||
}
|
||||
mcbsp = id_to_mcbsp_ptr(id);
|
||||
io_base = mcbsp->io_base;
|
||||
|
||||
OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
|
||||
|
||||
/*
|
||||
* omap_mcbsp_get_max_tx_thres just return the current configured
|
||||
* maximum threshold for transmission
|
||||
*/
|
||||
u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
|
||||
return -ENODEV;
|
||||
}
|
||||
mcbsp = id_to_mcbsp_ptr(id);
|
||||
|
||||
return mcbsp->max_tx_thres;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
|
||||
|
||||
/*
|
||||
* omap_mcbsp_get_max_rx_thres just return the current configured
|
||||
* maximum threshold for reception
|
||||
*/
|
||||
u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
|
||||
return -ENODEV;
|
||||
}
|
||||
mcbsp = id_to_mcbsp_ptr(id);
|
||||
|
||||
return mcbsp->max_rx_thres;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
|
||||
|
||||
/*
|
||||
* omap_mcbsp_get_dma_op_mode just return the current configured
|
||||
* operating mode for the mcbsp channel
|
||||
*/
|
||||
int omap_mcbsp_get_dma_op_mode(unsigned int id)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
int dma_op_mode;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
|
||||
return -ENODEV;
|
||||
}
|
||||
mcbsp = id_to_mcbsp_ptr(id);
|
||||
|
||||
spin_lock_irq(&mcbsp->lock);
|
||||
dma_op_mode = mcbsp->dma_op_mode;
|
||||
spin_unlock_irq(&mcbsp->lock);
|
||||
|
||||
return dma_op_mode;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
|
||||
|
||||
static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
|
||||
{
|
||||
/*
|
||||
* Enable wakup behavior, smart idle and all wakeups
|
||||
* REVISIT: some wakeups may be unnecessary
|
||||
*/
|
||||
if (cpu_is_omap34xx()) {
|
||||
u16 syscon;
|
||||
|
||||
syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
|
||||
syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
|
||||
|
||||
spin_lock_irq(&mcbsp->lock);
|
||||
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
|
||||
syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
|
||||
CLOCKACTIVITY(0x02));
|
||||
OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
|
||||
XRDYEN | RRDYEN);
|
||||
} else {
|
||||
syscon |= SIDLEMODE(0x01);
|
||||
}
|
||||
spin_unlock_irq(&mcbsp->lock);
|
||||
|
||||
OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
|
||||
{
|
||||
/*
|
||||
* Disable wakup behavior, smart idle and all wakeups
|
||||
*/
|
||||
if (cpu_is_omap34xx()) {
|
||||
u16 syscon;
|
||||
|
||||
syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
|
||||
syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
|
||||
/*
|
||||
* HW bug workaround - If no_idle mode is taken, we need to
|
||||
* go to smart_idle before going to always_idle, or the
|
||||
* device will not hit retention anymore.
|
||||
*/
|
||||
syscon |= SIDLEMODE(0x02);
|
||||
OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
|
||||
|
||||
syscon &= ~(SIDLEMODE(0x03));
|
||||
OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
|
||||
|
||||
OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
|
||||
static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We can choose between IRQ based or polled IO.
|
||||
* This needs to be called before omap_mcbsp_request().
|
||||
@ -257,6 +421,9 @@ int omap_mcbsp_request(unsigned int id)
|
||||
clk_enable(mcbsp->iclk);
|
||||
clk_enable(mcbsp->fclk);
|
||||
|
||||
/* Do procedure specific to omap34xx arch, if applicable */
|
||||
omap34xx_mcbsp_request(mcbsp);
|
||||
|
||||
/*
|
||||
* Make sure that transmitter, receiver and sample-rate generator are
|
||||
* not running before activating IRQs.
|
||||
@ -305,6 +472,9 @@ void omap_mcbsp_free(unsigned int id)
|
||||
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
|
||||
mcbsp->pdata->ops->free(id);
|
||||
|
||||
/* Do procedure specific to omap34xx arch, if applicable */
|
||||
omap34xx_mcbsp_free(mcbsp);
|
||||
|
||||
clk_disable(mcbsp->fclk);
|
||||
clk_disable(mcbsp->iclk);
|
||||
|
||||
@ -328,14 +498,15 @@ void omap_mcbsp_free(unsigned int id)
|
||||
EXPORT_SYMBOL(omap_mcbsp_free);
|
||||
|
||||
/*
|
||||
* Here we start the McBSP, by enabling the sample
|
||||
* generator, both transmitter and receivers,
|
||||
* and the frame sync.
|
||||
* Here we start the McBSP, by enabling transmitter, receiver or both.
|
||||
* If no transmitter or receiver is active prior calling, then sample-rate
|
||||
* generator and frame sync are started.
|
||||
*/
|
||||
void omap_mcbsp_start(unsigned int id)
|
||||
void omap_mcbsp_start(unsigned int id, int tx, int rx)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
void __iomem *io_base;
|
||||
int idle;
|
||||
u16 w;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
@ -348,32 +519,58 @@ void omap_mcbsp_start(unsigned int id)
|
||||
mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
|
||||
mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
|
||||
|
||||
/* Start the sample generator */
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
|
||||
idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
|
||||
OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
|
||||
|
||||
if (idle) {
|
||||
/* Start the sample generator */
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
|
||||
}
|
||||
|
||||
/* Enable transmitter and receiver */
|
||||
tx &= 1;
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w | tx);
|
||||
|
||||
rx &= 1;
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR1);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR1, w | rx);
|
||||
|
||||
udelay(100);
|
||||
/*
|
||||
* Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
|
||||
* REVISIT: 100us may give enough time for two CLKSRG, however
|
||||
* due to some unknown PM related, clock gating etc. reason it
|
||||
* is now at 500us.
|
||||
*/
|
||||
udelay(500);
|
||||
|
||||
/* Start frame sync */
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
|
||||
if (idle) {
|
||||
/* Start frame sync */
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
|
||||
}
|
||||
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
|
||||
/* Release the transmitter and receiver */
|
||||
w = OMAP_MCBSP_READ(io_base, XCCR);
|
||||
w &= ~(tx ? XDISABLE : 0);
|
||||
OMAP_MCBSP_WRITE(io_base, XCCR, w);
|
||||
w = OMAP_MCBSP_READ(io_base, RCCR);
|
||||
w &= ~(rx ? RDISABLE : 0);
|
||||
OMAP_MCBSP_WRITE(io_base, RCCR, w);
|
||||
}
|
||||
|
||||
/* Dump McBSP Regs */
|
||||
omap_mcbsp_dump_reg(id);
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_start);
|
||||
|
||||
void omap_mcbsp_stop(unsigned int id)
|
||||
void omap_mcbsp_stop(unsigned int id, int tx, int rx)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
void __iomem *io_base;
|
||||
int idle;
|
||||
u16 w;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
@ -385,16 +582,33 @@ void omap_mcbsp_stop(unsigned int id)
|
||||
io_base = mcbsp->io_base;
|
||||
|
||||
/* Reset transmitter */
|
||||
tx &= 1;
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
|
||||
w = OMAP_MCBSP_READ(io_base, XCCR);
|
||||
w |= (tx ? XDISABLE : 0);
|
||||
OMAP_MCBSP_WRITE(io_base, XCCR, w);
|
||||
}
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1));
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~tx);
|
||||
|
||||
/* Reset receiver */
|
||||
rx &= 1;
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
|
||||
w = OMAP_MCBSP_READ(io_base, RCCR);
|
||||
w |= (tx ? RDISABLE : 0);
|
||||
OMAP_MCBSP_WRITE(io_base, RCCR, w);
|
||||
}
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR1);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1));
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~rx);
|
||||
|
||||
/* Reset the sample rate generator */
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
|
||||
idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
|
||||
OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
|
||||
|
||||
if (idle) {
|
||||
/* Reset the sample rate generator */
|
||||
w = OMAP_MCBSP_READ(io_base, SPCR2);
|
||||
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_stop);
|
||||
|
||||
@ -883,6 +1097,149 @@ void omap_mcbsp_set_spi_mode(unsigned int id,
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP34XX
|
||||
#define max_thres(m) (mcbsp->pdata->buffer_size)
|
||||
#define valid_threshold(m, val) ((val) <= max_thres(m))
|
||||
#define THRESHOLD_PROP_BUILDER(prop) \
|
||||
static ssize_t prop##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
|
||||
\
|
||||
return sprintf(buf, "%u\n", mcbsp->prop); \
|
||||
} \
|
||||
\
|
||||
static ssize_t prop##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t size) \
|
||||
{ \
|
||||
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
|
||||
unsigned long val; \
|
||||
int status; \
|
||||
\
|
||||
status = strict_strtoul(buf, 0, &val); \
|
||||
if (status) \
|
||||
return status; \
|
||||
\
|
||||
if (!valid_threshold(mcbsp, val)) \
|
||||
return -EDOM; \
|
||||
\
|
||||
mcbsp->prop = val; \
|
||||
return size; \
|
||||
} \
|
||||
\
|
||||
static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
|
||||
|
||||
THRESHOLD_PROP_BUILDER(max_tx_thres);
|
||||
THRESHOLD_PROP_BUILDER(max_rx_thres);
|
||||
|
||||
static const char *dma_op_modes[] = {
|
||||
"element", "threshold", "frame",
|
||||
};
|
||||
|
||||
static ssize_t dma_op_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
|
||||
int dma_op_mode, i = 0;
|
||||
ssize_t len = 0;
|
||||
const char * const *s;
|
||||
|
||||
spin_lock_irq(&mcbsp->lock);
|
||||
dma_op_mode = mcbsp->dma_op_mode;
|
||||
spin_unlock_irq(&mcbsp->lock);
|
||||
|
||||
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
|
||||
if (dma_op_mode == i)
|
||||
len += sprintf(buf + len, "[%s] ", *s);
|
||||
else
|
||||
len += sprintf(buf + len, "%s ", *s);
|
||||
}
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t dma_op_mode_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
|
||||
const char * const *s;
|
||||
int i = 0;
|
||||
|
||||
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
|
||||
if (sysfs_streq(buf, *s))
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(dma_op_modes))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&mcbsp->lock);
|
||||
if (!mcbsp->free) {
|
||||
size = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
mcbsp->dma_op_mode = i;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irq(&mcbsp->lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
|
||||
|
||||
static const struct attribute *additional_attrs[] = {
|
||||
&dev_attr_max_tx_thres.attr,
|
||||
&dev_attr_max_rx_thres.attr,
|
||||
&dev_attr_dma_op_mode.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group additional_attr_group = {
|
||||
.attrs = (struct attribute **)additional_attrs,
|
||||
};
|
||||
|
||||
static inline int __devinit omap_additional_add(struct device *dev)
|
||||
{
|
||||
return sysfs_create_group(&dev->kobj, &additional_attr_group);
|
||||
}
|
||||
|
||||
static inline void __devexit omap_additional_remove(struct device *dev)
|
||||
{
|
||||
sysfs_remove_group(&dev->kobj, &additional_attr_group);
|
||||
}
|
||||
|
||||
static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
|
||||
{
|
||||
mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
|
||||
if (cpu_is_omap34xx()) {
|
||||
mcbsp->max_tx_thres = max_thres(mcbsp);
|
||||
mcbsp->max_rx_thres = max_thres(mcbsp);
|
||||
/*
|
||||
* REVISIT: Set dmap_op_mode to THRESHOLD as default
|
||||
* for mcbsp2 instances.
|
||||
*/
|
||||
if (omap_additional_add(mcbsp->dev))
|
||||
dev_warn(mcbsp->dev,
|
||||
"Unable to create additional controls\n");
|
||||
} else {
|
||||
mcbsp->max_tx_thres = -EINVAL;
|
||||
mcbsp->max_rx_thres = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
|
||||
{
|
||||
if (cpu_is_omap34xx())
|
||||
omap_additional_remove(mcbsp->dev);
|
||||
}
|
||||
#else
|
||||
static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
|
||||
static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
|
||||
#endif /* CONFIG_ARCH_OMAP34XX */
|
||||
|
||||
/*
|
||||
* McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
|
||||
* 730 has only 2 McBSP, and both of them are MPU peripherals.
|
||||
@ -953,6 +1310,10 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
|
||||
mcbsp->dev = &pdev->dev;
|
||||
mcbsp_ptr[id] = mcbsp;
|
||||
platform_set_drvdata(pdev, mcbsp);
|
||||
|
||||
/* Initialize mcbsp properties for OMAP34XX if needed / applicable */
|
||||
omap34xx_device_init(mcbsp);
|
||||
|
||||
return 0;
|
||||
|
||||
err_fclk:
|
||||
@ -976,6 +1337,8 @@ static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
|
||||
mcbsp->pdata->ops->free)
|
||||
mcbsp->pdata->ops->free(mcbsp->id);
|
||||
|
||||
omap34xx_device_exit(mcbsp);
|
||||
|
||||
clk_disable(mcbsp->fclk);
|
||||
clk_disable(mcbsp->iclk);
|
||||
clk_put(mcbsp->fclk);
|
||||
|
37
arch/arm/plat-s3c/include/plat/audio-simtec.h
Normal file
37
arch/arm/plat-s3c/include/plat/audio-simtec.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* arch/arm/plat-s3c/include/plat/audio-simtec.h
|
||||
*
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* http://armlinux.simtec.co.uk/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Simtec Audio support.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct s3c24xx_audio_simtec_pdata - platform data for simtec audio
|
||||
* @use_mpllin: Select codec clock from MPLLin
|
||||
* @output_cdclk: Need to output CDCLK to the codec
|
||||
* @have_mic: Set if we have a MIC socket
|
||||
* @have_lout: Set if we have a LineOut socket
|
||||
* @amp_gpio: GPIO pin to enable the AMP
|
||||
* @amp_gain: Option GPIO to control AMP gain
|
||||
*/
|
||||
struct s3c24xx_audio_simtec_pdata {
|
||||
unsigned int use_mpllin:1;
|
||||
unsigned int output_cdclk:1;
|
||||
|
||||
unsigned int have_mic:1;
|
||||
unsigned int have_lout:1;
|
||||
|
||||
int amp_gpio;
|
||||
int amp_gain[2];
|
||||
|
||||
void (*startup)(void);
|
||||
};
|
||||
|
||||
extern int simtec_audio_add(const char *codec_name,
|
||||
struct s3c24xx_audio_simtec_pdata *pdata);
|
@ -33,6 +33,11 @@
|
||||
#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
|
||||
#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
|
||||
|
||||
#define S3C64XX_IISMOD_BLC_16BIT (0 << 13)
|
||||
#define S3C64XX_IISMOD_BLC_8BIT (1 << 13)
|
||||
#define S3C64XX_IISMOD_BLC_24BIT (2 << 13)
|
||||
#define S3C64XX_IISMOD_BLC_MASK (3 << 13)
|
||||
|
||||
#define S3C64XX_IISMOD_IMS_PCLK (0 << 10)
|
||||
#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10)
|
||||
|
||||
|
@ -238,8 +238,10 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
|
||||
}
|
||||
|
||||
/**
|
||||
* register_chrdev() - Register a major number for character devices.
|
||||
* __register_chrdev() - create and register a cdev occupying a range of minors
|
||||
* @major: major device number or 0 for dynamic allocation
|
||||
* @baseminor: first of the requested range of minor numbers
|
||||
* @count: the number of minor numbers required
|
||||
* @name: name of this range of devices
|
||||
* @fops: file operations associated with this devices
|
||||
*
|
||||
@ -255,19 +257,17 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
|
||||
* /dev. It only helps to keep track of the different owners of devices. If
|
||||
* your module name has only one type of devices it's ok to use e.g. the name
|
||||
* of the module here.
|
||||
*
|
||||
* This function registers a range of 256 minor numbers. The first minor number
|
||||
* is 0.
|
||||
*/
|
||||
int register_chrdev(unsigned int major, const char *name,
|
||||
const struct file_operations *fops)
|
||||
int __register_chrdev(unsigned int major, unsigned int baseminor,
|
||||
unsigned int count, const char *name,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct char_device_struct *cd;
|
||||
struct cdev *cdev;
|
||||
char *s;
|
||||
int err = -ENOMEM;
|
||||
|
||||
cd = __register_chrdev_region(major, 0, 256, name);
|
||||
cd = __register_chrdev_region(major, baseminor, count, name);
|
||||
if (IS_ERR(cd))
|
||||
return PTR_ERR(cd);
|
||||
|
||||
@ -281,7 +281,7 @@ int register_chrdev(unsigned int major, const char *name,
|
||||
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
|
||||
*s = '!';
|
||||
|
||||
err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
|
||||
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -291,7 +291,7 @@ int register_chrdev(unsigned int major, const char *name,
|
||||
out:
|
||||
kobject_put(&cdev->kobj);
|
||||
out2:
|
||||
kfree(__unregister_chrdev_region(cd->major, 0, 256));
|
||||
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -317,10 +317,23 @@ void unregister_chrdev_region(dev_t from, unsigned count)
|
||||
}
|
||||
}
|
||||
|
||||
void unregister_chrdev(unsigned int major, const char *name)
|
||||
/**
|
||||
* __unregister_chrdev - unregister and destroy a cdev
|
||||
* @major: major device number
|
||||
* @baseminor: first of the range of minor numbers
|
||||
* @count: the number of minor numbers this cdev is occupying
|
||||
* @name: name of this range of devices
|
||||
*
|
||||
* Unregister and destroy the cdev occupying the region described by
|
||||
* @major, @baseminor and @count. This function undoes what
|
||||
* __register_chrdev() did.
|
||||
*/
|
||||
void __unregister_chrdev(unsigned int major, unsigned int baseminor,
|
||||
unsigned int count, const char *name)
|
||||
{
|
||||
struct char_device_struct *cd;
|
||||
cd = __unregister_chrdev_region(major, 0, 256);
|
||||
|
||||
cd = __unregister_chrdev_region(major, baseminor, count);
|
||||
if (cd && cd->cdev)
|
||||
cdev_del(cd->cdev);
|
||||
kfree(cd);
|
||||
@ -569,6 +582,6 @@ EXPORT_SYMBOL(cdev_alloc);
|
||||
EXPORT_SYMBOL(cdev_del);
|
||||
EXPORT_SYMBOL(cdev_add);
|
||||
EXPORT_SYMBOL(cdev_index);
|
||||
EXPORT_SYMBOL(register_chrdev);
|
||||
EXPORT_SYMBOL(unregister_chrdev);
|
||||
EXPORT_SYMBOL(__register_chrdev);
|
||||
EXPORT_SYMBOL(__unregister_chrdev);
|
||||
EXPORT_SYMBOL(directly_mappable_cdev_bdi);
|
||||
|
@ -1997,12 +1997,25 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *);
|
||||
#define CHRDEV_MAJOR_HASH_SIZE 255
|
||||
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
|
||||
extern int register_chrdev_region(dev_t, unsigned, const char *);
|
||||
extern int register_chrdev(unsigned int, const char *,
|
||||
const struct file_operations *);
|
||||
extern void unregister_chrdev(unsigned int, const char *);
|
||||
extern int __register_chrdev(unsigned int major, unsigned int baseminor,
|
||||
unsigned int count, const char *name,
|
||||
const struct file_operations *fops);
|
||||
extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
|
||||
unsigned int count, const char *name);
|
||||
extern void unregister_chrdev_region(dev_t, unsigned);
|
||||
extern void chrdev_show(struct seq_file *,off_t);
|
||||
|
||||
static inline int register_chrdev(unsigned int major, const char *name,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
return __register_chrdev(major, 0, 256, name, fops);
|
||||
}
|
||||
|
||||
static inline void unregister_chrdev(unsigned int major, const char *name)
|
||||
{
|
||||
__unregister_chrdev(major, 0, 256, name);
|
||||
}
|
||||
|
||||
/* fs/block_dev.c */
|
||||
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
|
||||
#define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */
|
||||
|
@ -23,7 +23,7 @@
|
||||
*/
|
||||
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
|
||||
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
|
||||
#define NR_LDISCS 19
|
||||
#define NR_LDISCS 20
|
||||
|
||||
/* line disciplines */
|
||||
#define N_TTY 0
|
||||
@ -47,6 +47,8 @@
|
||||
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
|
||||
#define N_PPS 18 /* Pulse per Second */
|
||||
|
||||
#define N_V253 19 /* Codec control over voice modem */
|
||||
|
||||
/*
|
||||
* This character is the same as _POSIX_VDISABLE: it cannot be used as
|
||||
* a c_cc[] character, but indicates that a particular special character
|
||||
|
@ -32,6 +32,9 @@
|
||||
#include "control.h"
|
||||
#include "info.h"
|
||||
|
||||
/* maximum number of devices on the AC97 bus */
|
||||
#define AC97_BUS_MAX_DEVICES 4
|
||||
|
||||
/*
|
||||
* AC'97 codec registers
|
||||
*/
|
||||
@ -642,4 +645,10 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime);
|
||||
/* ad hoc AC97 device driver access */
|
||||
extern struct bus_type ac97_bus_type;
|
||||
|
||||
/* AC97 platform_data adding function */
|
||||
static inline void snd_ac97_dev_add_pdata(struct snd_ac97 *ac97, void *data)
|
||||
{
|
||||
ac97->dev.platform_data = data;
|
||||
}
|
||||
|
||||
#endif /* __SOUND_AC97_CODEC_H */
|
||||
|
@ -138,7 +138,7 @@ struct snd_hwdep_dsp_image {
|
||||
* *
|
||||
*****************************************************************************/
|
||||
|
||||
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9)
|
||||
#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10)
|
||||
|
||||
typedef unsigned long snd_pcm_uframes_t;
|
||||
typedef signed long snd_pcm_sframes_t;
|
||||
|
@ -93,15 +93,6 @@ struct snd_device {
|
||||
|
||||
#define snd_device(n) list_entry(n, struct snd_device, list)
|
||||
|
||||
/* monitor files for graceful shutdown (hotplug) */
|
||||
|
||||
struct snd_monitor_file {
|
||||
struct file *file;
|
||||
const struct file_operations *disconnected_f_op;
|
||||
struct list_head shutdown_list; /* still need to shutdown */
|
||||
struct list_head list; /* link of monitor files */
|
||||
};
|
||||
|
||||
/* main structure for soundcard */
|
||||
|
||||
struct snd_card {
|
||||
@ -311,9 +302,7 @@ int snd_component_add(struct snd_card *card, const char *component);
|
||||
int snd_card_file_add(struct snd_card *card, struct file *file);
|
||||
int snd_card_file_remove(struct snd_card *card, struct file *file);
|
||||
|
||||
#ifndef snd_card_set_dev
|
||||
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
|
||||
#endif
|
||||
|
||||
/* device.c */
|
||||
|
||||
@ -340,18 +329,17 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size);
|
||||
struct resource;
|
||||
void release_and_free_resource(struct resource *res);
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
void snd_verbose_printk(const char *file, int line, const char *format, ...)
|
||||
__attribute__ ((format (printf, 3, 4)));
|
||||
#endif
|
||||
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
|
||||
void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
||||
__attribute__ ((format (printf, 3, 4)));
|
||||
#endif
|
||||
|
||||
/* --- */
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
|
||||
void __snd_printk(unsigned int level, const char *file, int line,
|
||||
const char *format, ...)
|
||||
__attribute__ ((format (printf, 4, 5)));
|
||||
#else
|
||||
#define __snd_printk(level, file, line, format, args...) \
|
||||
printk(format, ##args)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* snd_printk - printk wrapper
|
||||
* @fmt: format string
|
||||
@ -360,15 +348,9 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
||||
* when configured with CONFIG_SND_VERBOSE_PRINTK.
|
||||
*/
|
||||
#define snd_printk(fmt, args...) \
|
||||
snd_verbose_printk(__FILE__, __LINE__, fmt ,##args)
|
||||
#else
|
||||
#define snd_printk(fmt, args...) \
|
||||
printk(fmt ,##args)
|
||||
#endif
|
||||
__snd_printk(0, __FILE__, __LINE__, fmt, ##args)
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
/**
|
||||
* snd_printd - debug printk
|
||||
* @fmt: format string
|
||||
@ -377,11 +359,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
||||
* Ignored when CONFIG_SND_DEBUG is not set.
|
||||
*/
|
||||
#define snd_printd(fmt, args...) \
|
||||
snd_verbose_printd(__FILE__, __LINE__, fmt ,##args)
|
||||
#else
|
||||
#define snd_printd(fmt, args...) \
|
||||
printk(fmt ,##args)
|
||||
#endif
|
||||
__snd_printk(1, __FILE__, __LINE__, fmt, ##args)
|
||||
|
||||
/**
|
||||
* snd_BUG - give a BUG warning message and stack trace
|
||||
@ -428,9 +406,10 @@ static inline int __snd_bug_on(int cond)
|
||||
* Works like snd_printk() for debugging purposes.
|
||||
* Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
|
||||
*/
|
||||
#define snd_printdd(format, args...) snd_printk(format, ##args)
|
||||
#define snd_printdd(format, args...) \
|
||||
__snd_printk(2, __FILE__, __LINE__, format, ##args)
|
||||
#else
|
||||
#define snd_printdd(format, args...) /* nothing */
|
||||
#define snd_printdd(format, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
@ -438,12 +417,10 @@ static inline int __snd_bug_on(int cond)
|
||||
|
||||
/* for easier backward-porting */
|
||||
#if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
|
||||
#ifndef gameport_set_dev_parent
|
||||
#define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev))
|
||||
#define gameport_set_port_data(gp,r) ((gp)->port_data = (r))
|
||||
#define gameport_get_port_data(gp) (gp)->port_data
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* PCI quirk list helper */
|
||||
struct snd_pci_quirk {
|
||||
|
@ -110,13 +110,13 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
|
||||
static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
|
||||
#endif
|
||||
|
||||
int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \
|
||||
int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) \
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
int snd_info_init(void);
|
||||
int snd_info_done(void);
|
||||
|
||||
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
|
||||
char *snd_info_get_str(char *dest, char *src, int len);
|
||||
const char *snd_info_get_str(char *dest, const char *src, int len);
|
||||
struct snd_info_entry *snd_info_create_module_entry(struct module *module,
|
||||
const char *name,
|
||||
struct snd_info_entry *parent);
|
||||
|
@ -47,7 +47,11 @@ struct snd_dma_device {
|
||||
#define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */
|
||||
#define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */
|
||||
#define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */
|
||||
#else
|
||||
#define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* info for buffer allocation
|
||||
@ -60,6 +64,7 @@ struct snd_dma_buffer {
|
||||
void *private_data; /* private for allocator; don't touch */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
/*
|
||||
* Scatter-Gather generic device pages
|
||||
*/
|
||||
@ -107,6 +112,7 @@ static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
|
||||
{
|
||||
return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
|
||||
}
|
||||
#endif /* CONFIG_SND_DMA_SGBUF */
|
||||
|
||||
/* allocate/release a buffer */
|
||||
int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
|
||||
|
@ -902,6 +902,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
|
||||
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
|
||||
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
/*
|
||||
* SG-buffer handling
|
||||
*/
|
||||
@ -927,6 +928,28 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
|
||||
unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
|
||||
unsigned int ofs, unsigned int size);
|
||||
|
||||
#else /* !SND_DMA_SGBUF */
|
||||
/*
|
||||
* fake using a continuous buffer
|
||||
*/
|
||||
static inline dma_addr_t
|
||||
snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
|
||||
{
|
||||
return substream->runtime->dma_addr + ofs;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
|
||||
{
|
||||
return substream->runtime->dma_area + ofs;
|
||||
}
|
||||
|
||||
#define snd_pcm_sgbuf_ops_page NULL
|
||||
|
||||
#define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size) (size)
|
||||
|
||||
#endif /* SND_DMA_SGBUF */
|
||||
|
||||
/* handle mmap counter - PCM mmap callback should handle this counter properly */
|
||||
static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
|
||||
{
|
||||
@ -965,4 +988,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
|
||||
|
||||
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
|
||||
|
||||
const char *snd_pcm_format_name(snd_pcm_format_t format);
|
||||
|
||||
#endif /* __SOUND_PCM_H */
|
||||
|
83
include/sound/sh_fsi.h
Normal file
83
include/sound/sh_fsi.h
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef __SOUND_FSI_H
|
||||
#define __SOUND_FSI_H
|
||||
|
||||
/*
|
||||
* Fifo-attached Serial Interface (FSI) support for SH7724
|
||||
*
|
||||
* Copyright (C) 2009 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* flags format
|
||||
|
||||
* 0xABCDEEFF
|
||||
*
|
||||
* A: channel size for TDM (input)
|
||||
* B: channel size for TDM (ooutput)
|
||||
* C: inversion
|
||||
* D: mode
|
||||
* E: input format
|
||||
* F: output format
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
/* TDM channel */
|
||||
#define SH_FSI_SET_CH_I(x) ((x & 0xF) << 28)
|
||||
#define SH_FSI_SET_CH_O(x) ((x & 0xF) << 24)
|
||||
|
||||
#define SH_FSI_CH_IMASK 0xF0000000
|
||||
#define SH_FSI_CH_OMASK 0x0F000000
|
||||
#define SH_FSI_GET_CH_I(x) ((x & SH_FSI_CH_IMASK) >> 28)
|
||||
#define SH_FSI_GET_CH_O(x) ((x & SH_FSI_CH_OMASK) >> 24)
|
||||
|
||||
/* clock inversion */
|
||||
#define SH_FSI_INVERSION_MASK 0x00F00000
|
||||
#define SH_FSI_LRM_INV (1 << 20)
|
||||
#define SH_FSI_BRM_INV (1 << 21)
|
||||
#define SH_FSI_LRS_INV (1 << 22)
|
||||
#define SH_FSI_BRS_INV (1 << 23)
|
||||
|
||||
/* mode */
|
||||
#define SH_FSI_MODE_MASK 0x000F0000
|
||||
#define SH_FSI_IN_SLAVE_MODE (1 << 16) /* default master mode */
|
||||
#define SH_FSI_OUT_SLAVE_MODE (1 << 17) /* default master mode */
|
||||
|
||||
/* DI format */
|
||||
#define SH_FSI_FMT_MASK 0x000000FF
|
||||
#define SH_FSI_IFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
|
||||
#define SH_FSI_OFMT(x) (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
|
||||
#define SH_FSI_GET_IFMT(x) ((x >> 8) & SH_FSI_FMT_MASK)
|
||||
#define SH_FSI_GET_OFMT(x) ((x >> 0) & SH_FSI_FMT_MASK)
|
||||
|
||||
#define SH_FSI_FMT_MONO (1 << 0)
|
||||
#define SH_FSI_FMT_MONO_DELAY (1 << 1)
|
||||
#define SH_FSI_FMT_PCM (1 << 2)
|
||||
#define SH_FSI_FMT_I2S (1 << 3)
|
||||
#define SH_FSI_FMT_TDM (1 << 4)
|
||||
#define SH_FSI_FMT_TDM_DELAY (1 << 5)
|
||||
|
||||
#define SH_FSI_IFMT_TDM_CH(x) \
|
||||
(SH_FSI_IFMT(TDM) | SH_FSI_SET_CH_I(x))
|
||||
#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
|
||||
(SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
|
||||
|
||||
#define SH_FSI_OFMT_TDM_CH(x) \
|
||||
(SH_FSI_OFMT(TDM) | SH_FSI_SET_CH_O(x))
|
||||
#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
|
||||
(SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
|
||||
|
||||
struct sh_fsi_platform_info {
|
||||
unsigned long porta_flags;
|
||||
unsigned long portb_flags;
|
||||
};
|
||||
|
||||
extern struct snd_soc_dai fsi_soc_dai[2];
|
||||
extern struct snd_soc_platform fsi_soc_platform;
|
||||
|
||||
#endif /* __SOUND_FSI_H */
|
@ -27,8 +27,8 @@ struct snd_pcm_substream;
|
||||
#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
|
||||
#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */
|
||||
#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
|
||||
#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_A 3 /* L data MSB after FRM LRC */
|
||||
#define SND_SOC_DAIFMT_DSP_B 4 /* L data MSB during FRM LRC */
|
||||
#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
|
||||
|
||||
/* left and right justified also known as MSB and LSB respectively */
|
||||
@ -38,7 +38,7 @@ struct snd_pcm_substream;
|
||||
/*
|
||||
* DAI Clock gating.
|
||||
*
|
||||
* DAI bit clocks can be be gated (disabled) when not the DAI is not
|
||||
* DAI bit clocks can be be gated (disabled) when the DAI is not
|
||||
* sending or receiving PCM data in a frame. This can be used to save power.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
|
||||
@ -51,21 +51,21 @@ struct snd_pcm_substream;
|
||||
* format.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */
|
||||
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
|
||||
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
|
||||
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
|
||||
#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal BCLK + inv FRM */
|
||||
#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert BCLK + nor FRM */
|
||||
#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert BCLK + FRM */
|
||||
|
||||
/*
|
||||
* DAI hardware clock masters.
|
||||
*
|
||||
* This is wrt the codec, the inverse is true for the interface
|
||||
* i.e. if the codec is clk and frm master then the interface is
|
||||
* i.e. if the codec is clk and FRM master then the interface is
|
||||
* clk and frame slave.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
|
||||
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
|
||||
#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & FRM master */
|
||||
#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & FRM master */
|
||||
#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
|
||||
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
|
||||
#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & FRM slave */
|
||||
|
||||
#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
|
||||
#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
|
||||
@ -78,7 +78,13 @@ struct snd_pcm_substream;
|
||||
#define SND_SOC_CLOCK_IN 0
|
||||
#define SND_SOC_CLOCK_OUT 1
|
||||
|
||||
#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 |\
|
||||
SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S16_BE |\
|
||||
SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S20_3BE |\
|
||||
SNDRV_PCM_FMTBIT_S24_3LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_3BE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_BE)
|
||||
|
||||
@ -106,7 +112,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
|
||||
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
|
||||
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
|
||||
|
||||
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
@ -116,12 +122,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
|
||||
/*
|
||||
* Digital Audio Interface.
|
||||
*
|
||||
* Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
|
||||
* operations an capabilities. Codec and platfom drivers will register a this
|
||||
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
|
||||
* operations and capabilities. Codec and platform drivers will register this
|
||||
* structure for every DAI they have.
|
||||
*
|
||||
* This structure covers the clocking, formating and ALSA operations for each
|
||||
* interface a
|
||||
* interface.
|
||||
*/
|
||||
struct snd_soc_dai_ops {
|
||||
/*
|
||||
@ -140,7 +146,8 @@ struct snd_soc_dai_ops {
|
||||
*/
|
||||
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
|
||||
int (*set_tdm_slot)(struct snd_soc_dai *dai,
|
||||
unsigned int mask, int slots);
|
||||
unsigned int tx_mask, unsigned int rx_mask,
|
||||
int slots, int slot_width);
|
||||
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
/*
|
||||
@ -179,6 +186,7 @@ struct snd_soc_dai {
|
||||
int ac97_control;
|
||||
|
||||
struct device *dev;
|
||||
void *ac97_pdata; /* platform_data for the ac97 codec */
|
||||
|
||||
/* DAI callbacks */
|
||||
int (*probe)(struct platform_device *pdev,
|
||||
|
@ -137,6 +137,12 @@
|
||||
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
|
||||
|
||||
/* stream domain */
|
||||
#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
|
||||
{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
|
||||
.reg = wreg, .shift = wshift, .invert = winvert }
|
||||
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
|
||||
{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
|
||||
.reg = wreg, .shift = wshift, .invert = winvert }
|
||||
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
|
||||
{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
|
||||
.shift = wshift, .invert = winvert}
|
||||
@ -279,9 +285,11 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
|
||||
/* dapm events */
|
||||
int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
|
||||
int event);
|
||||
void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
|
||||
|
||||
/* dapm sys fs - used by the core */
|
||||
int snd_soc_dapm_sys_add(struct device *dev);
|
||||
void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec);
|
||||
|
||||
/* dapm audio pin control and status */
|
||||
int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
|
||||
@ -311,6 +319,8 @@ enum snd_soc_dapm_type {
|
||||
snd_soc_dapm_pre, /* machine specific pre widget - exec first */
|
||||
snd_soc_dapm_post, /* machine specific post widget - exec last */
|
||||
snd_soc_dapm_supply, /* power/clock supply */
|
||||
snd_soc_dapm_aif_in, /* audio interface input */
|
||||
snd_soc_dapm_aif_out, /* audio interface output */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -135,6 +135,28 @@
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
|
||||
#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
|
||||
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
|
||||
.tlv.p = (tlv_array), \
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .shift = shift_left, .rshift = shift_right, \
|
||||
.max = xmax, .invert = xinvert} }
|
||||
#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
|
||||
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
|
||||
.tlv.p = (tlv_array), \
|
||||
.info = snd_soc_info_volsw_2r, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
|
||||
.max = xmax, .invert = xinvert} }
|
||||
#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = snd_soc_info_bool_ext, \
|
||||
@ -183,14 +205,28 @@ struct snd_soc_jack_gpio;
|
||||
#endif
|
||||
|
||||
typedef int (*hw_write_t)(void *,const char* ,int);
|
||||
typedef int (*hw_read_t)(void *,char* ,int);
|
||||
|
||||
extern struct snd_ac97_bus_ops soc_ac97_ops;
|
||||
|
||||
enum snd_soc_control_type {
|
||||
SND_SOC_CUSTOM,
|
||||
SND_SOC_I2C,
|
||||
SND_SOC_SPI,
|
||||
};
|
||||
|
||||
int snd_soc_register_platform(struct snd_soc_platform *platform);
|
||||
void snd_soc_unregister_platform(struct snd_soc_platform *platform);
|
||||
int snd_soc_register_codec(struct snd_soc_codec *codec);
|
||||
void snd_soc_unregister_codec(struct snd_soc_codec *codec);
|
||||
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
|
||||
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
|
||||
int addr_bits, int data_bits,
|
||||
enum snd_soc_control_type control);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int snd_soc_suspend_device(struct device *dev);
|
||||
int snd_soc_resume_device(struct device *dev);
|
||||
#endif
|
||||
|
||||
/* pcm <-> DAI connect */
|
||||
void snd_soc_free_pcms(struct snd_soc_device *socdev);
|
||||
@ -216,9 +252,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
|
||||
|
||||
/* codec register bit access */
|
||||
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
|
||||
unsigned short mask, unsigned short value);
|
||||
unsigned int mask, unsigned int value);
|
||||
int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
|
||||
unsigned short mask, unsigned short value);
|
||||
unsigned int mask, unsigned int value);
|
||||
|
||||
int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
|
||||
struct snd_ac97_bus_ops *ops, int num);
|
||||
@ -356,8 +392,10 @@ struct snd_soc_codec {
|
||||
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
|
||||
int (*display_register)(struct snd_soc_codec *, char *,
|
||||
size_t, unsigned int);
|
||||
int (*volatile_register)(unsigned int);
|
||||
int (*readable_register)(unsigned int);
|
||||
hw_write_t hw_write;
|
||||
hw_read_t hw_read;
|
||||
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
|
||||
void *reg_cache;
|
||||
short reg_cache_size;
|
||||
short reg_cache_step;
|
||||
@ -369,8 +407,6 @@ struct snd_soc_codec {
|
||||
enum snd_soc_bias_level bias_level;
|
||||
enum snd_soc_bias_level suspend_bias_level;
|
||||
struct delayed_work delayed_work;
|
||||
struct list_head up_list;
|
||||
struct list_head down_list;
|
||||
|
||||
/* codec DAI's */
|
||||
struct snd_soc_dai *dai;
|
||||
@ -379,6 +415,7 @@ struct snd_soc_codec {
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_reg;
|
||||
struct dentry *debugfs_pop_time;
|
||||
struct dentry *debugfs_dapm;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -35,6 +35,8 @@
|
||||
#define SNDRV_CTL_TLVT_DB_SCALE 1 /* dB scale */
|
||||
#define SNDRV_CTL_TLVT_DB_LINEAR 2 /* linear volume */
|
||||
#define SNDRV_CTL_TLVT_DB_RANGE 3 /* dB range container */
|
||||
#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
|
||||
#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
|
||||
|
||||
#define TLV_DB_SCALE_ITEM(min, step, mute) \
|
||||
SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
|
||||
@ -42,6 +44,18 @@
|
||||
#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
|
||||
unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
|
||||
|
||||
/* dB scale specified with min/max values instead of step */
|
||||
#define TLV_DB_MINMAX_ITEM(min_dB, max_dB) \
|
||||
SNDRV_CTL_TLVT_DB_MINMAX, 2 * sizeof(unsigned int), \
|
||||
(min_dB), (max_dB)
|
||||
#define TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) \
|
||||
SNDRV_CTL_TLVT_DB_MINMAX_MUTE, 2 * sizeof(unsigned int), \
|
||||
(min_dB), (max_dB)
|
||||
#define DECLARE_TLV_DB_MINMAX(name, min_dB, max_dB) \
|
||||
unsigned int name[] = { TLV_DB_MINMAX_ITEM(min_dB, max_dB) }
|
||||
#define DECLARE_TLV_DB_MINMAX_MUTE(name, min_dB, max_dB) \
|
||||
unsigned int name[] = { TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) }
|
||||
|
||||
/* linear volume between min_dB and max_dB (.01dB unit) */
|
||||
#define TLV_DB_LINEAR_ITEM(min_dB, max_dB) \
|
||||
SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
|
||||
|
22
include/sound/uda1380.h
Normal file
22
include/sound/uda1380.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* UDA1380 ALSA SoC Codec driver
|
||||
*
|
||||
* Copyright 2009 Philipp Zabel
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __UDA1380_H
|
||||
#define __UDA1380_H
|
||||
|
||||
struct uda1380_platform_data {
|
||||
int gpio_power;
|
||||
int gpio_reset;
|
||||
int dac_clk;
|
||||
#define UDA1380_DAC_CLK_SYSCLK 0
|
||||
#define UDA1380_DAC_CLK_WSPLL 1
|
||||
};
|
||||
|
||||
#endif /* __UDA1380_H */
|
@ -1,3 +1,3 @@
|
||||
/* include/version.h */
|
||||
#define CONFIG_SND_VERSION "1.0.20"
|
||||
#define CONFIG_SND_VERSION "1.0.21"
|
||||
#define CONFIG_SND_DATE ""
|
||||
|
44
include/sound/wm8993.h
Normal file
44
include/sound/wm8993.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* linux/sound/wm8993.h -- Platform data for WM8993
|
||||
*
|
||||
* Copyright 2009 Wolfson Microelectronics. PLC.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SND_WM8993_H
|
||||
#define __LINUX_SND_WM8993_H
|
||||
|
||||
/* Note that EQ1 only contains the enable/disable bit so will be
|
||||
ignored but is included for simplicity.
|
||||
*/
|
||||
struct wm8993_retune_mobile_setting {
|
||||
const char *name;
|
||||
unsigned int rate;
|
||||
u16 config[24];
|
||||
};
|
||||
|
||||
struct wm8993_platform_data {
|
||||
struct wm8993_retune_mobile_setting *retune_configs;
|
||||
int num_retune_configs;
|
||||
|
||||
/* LINEOUT can be differential or single ended */
|
||||
unsigned int lineout1_diff:1;
|
||||
unsigned int lineout2_diff:1;
|
||||
|
||||
/* Common mode feedback */
|
||||
unsigned int lineout1fb:1;
|
||||
unsigned int lineout2fb:1;
|
||||
|
||||
/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
|
||||
unsigned int micbias1_lvl:1;
|
||||
unsigned int micbias2_lvl:1;
|
||||
|
||||
/* Jack detect threashold levels, see datasheet for values */
|
||||
unsigned int jd_scthr:2;
|
||||
unsigned int jd_thr:2;
|
||||
};
|
||||
|
||||
#endif
|
@ -331,6 +331,7 @@ struct snd_ymfpci {
|
||||
struct snd_ac97 *ac97;
|
||||
struct snd_rawmidi *rawmidi;
|
||||
struct snd_timer *timer;
|
||||
unsigned int timer_ticks;
|
||||
|
||||
struct pci_dev *pci;
|
||||
struct snd_card *card;
|
||||
|
@ -32,6 +32,34 @@ config SOUND_OSS_CORE
|
||||
bool
|
||||
default n
|
||||
|
||||
config SOUND_OSS_CORE_PRECLAIM
|
||||
bool "Preclaim OSS device numbers"
|
||||
depends on SOUND_OSS_CORE
|
||||
default y
|
||||
help
|
||||
With this option enabled, the kernel will claim all OSS device
|
||||
numbers if any OSS support (native or emulation) is enabled
|
||||
whether the respective module is loaded or not and try to load the
|
||||
appropriate module using sound-slot/service-* and char-major-*
|
||||
module aliases when one of the device numbers is opened. With
|
||||
this option disabled, kernel will only claim actually in-use
|
||||
device numbers and opening a missing device will generate only the
|
||||
standard char-major-* aliases.
|
||||
|
||||
The only visible difference is use of additional module aliases
|
||||
and whether OSS sound devices appear multiple times in
|
||||
/proc/devices. sound-slot/service-* module aliases are scheduled
|
||||
to be removed (ie. PRECLAIM won't be available) and this option is
|
||||
to make the transition easier. This option can be overridden
|
||||
during boot using the kernel parameter soundcore.preclaim_oss.
|
||||
|
||||
Disabling this allows alternative OSS implementations.
|
||||
|
||||
Please read Documentation/feature-removal-schedule.txt for
|
||||
details.
|
||||
|
||||
If unusre, say Y.
|
||||
|
||||
source "sound/oss/dmasound/Kconfig"
|
||||
|
||||
if !M68K
|
||||
|
@ -170,6 +170,13 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
|
||||
struct snd_ac97_bus *ac97_bus;
|
||||
struct snd_ac97_template ac97_template;
|
||||
int ret;
|
||||
pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
|
||||
|
||||
if (dev->id >= 0) {
|
||||
dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
|
||||
ret = -ENXIO;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
|
||||
THIS_MODULE, 0, &card);
|
||||
@ -200,6 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s (%s)", dev->dev.driver->name, card->mixername);
|
||||
|
||||
if (pdata && pdata->codec_pdata[0])
|
||||
snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
|
||||
snd_card_set_dev(card, &dev->dev);
|
||||
ret = snd_card_register(card);
|
||||
if (ret == 0) {
|
||||
@ -212,6 +221,7 @@ err_remove:
|
||||
err:
|
||||
if (card)
|
||||
snd_card_free(card);
|
||||
err_dev:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,9 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
|
||||
|
||||
if (!prtd || !prtd->params)
|
||||
return 0;
|
||||
|
||||
DCSR(prtd->dma_ch) &= ~DCSR_RUN;
|
||||
DCSR(prtd->dma_ch) = 0;
|
||||
DCMD(prtd->dma_ch) = 0;
|
||||
|
@ -206,4 +206,8 @@ config SND_PCM_XRUN_DEBUG
|
||||
config SND_VMASTER
|
||||
bool
|
||||
|
||||
config SND_DMA_SGBUF
|
||||
def_bool y
|
||||
depends on X86
|
||||
|
||||
source "sound/core/seq/Kconfig"
|
||||
|
@ -13,7 +13,7 @@ snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
|
||||
pcm_memory.o
|
||||
|
||||
snd-page-alloc-y := memalloc.o
|
||||
snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
|
||||
snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
|
||||
|
||||
snd-rawmidi-objs := rawmidi.o
|
||||
snd-timer-objs := timer.o
|
||||
|
@ -414,7 +414,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
|
||||
EXPORT_SYMBOL(snd_ctl_remove_id);
|
||||
|
||||
/**
|
||||
* snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
|
||||
* snd_ctl_remove_user_ctl - remove and release the unlocked user control
|
||||
* @file: active control handle
|
||||
* @id: the control id to remove
|
||||
*
|
||||
@ -423,8 +423,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id);
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code on failure.
|
||||
*/
|
||||
static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
|
||||
struct snd_ctl_elem_id *id)
|
||||
static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
|
||||
struct snd_ctl_elem_id *id)
|
||||
{
|
||||
struct snd_card *card = file->card;
|
||||
struct snd_kcontrol *kctl;
|
||||
@ -433,15 +433,23 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
|
||||
down_write(&card->controls_rwsem);
|
||||
kctl = snd_ctl_find_id(card, id);
|
||||
if (kctl == NULL) {
|
||||
up_write(&card->controls_rwsem);
|
||||
return -ENOENT;
|
||||
ret = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
for (idx = 0; idx < kctl->count; idx++)
|
||||
if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
|
||||
up_write(&card->controls_rwsem);
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto error;
|
||||
}
|
||||
ret = snd_ctl_remove(card, kctl);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
card->user_ctl_count--;
|
||||
error:
|
||||
up_write(&card->controls_rwsem);
|
||||
return ret;
|
||||
}
|
||||
@ -951,7 +959,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
|
||||
|
||||
if (card->user_ctl_count >= MAX_USER_CONTROLS)
|
||||
return -ENOMEM;
|
||||
if (info->count > 1024)
|
||||
if (info->count < 1)
|
||||
return -EINVAL;
|
||||
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
|
||||
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
|
||||
@ -1052,18 +1060,10 @@ static int snd_ctl_elem_remove(struct snd_ctl_file *file,
|
||||
struct snd_ctl_elem_id __user *_id)
|
||||
{
|
||||
struct snd_ctl_elem_id id;
|
||||
int err;
|
||||
|
||||
if (copy_from_user(&id, _id, sizeof(id)))
|
||||
return -EFAULT;
|
||||
err = snd_ctl_remove_unlocked_id(file, &id);
|
||||
if (! err) {
|
||||
struct snd_card *card = file->card;
|
||||
down_write(&card->controls_rwsem);
|
||||
card->user_ctl_count--;
|
||||
up_write(&card->controls_rwsem);
|
||||
}
|
||||
return err;
|
||||
return snd_ctl_remove_user_ctl(file, &id);
|
||||
}
|
||||
|
||||
static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
|
||||
|
@ -88,12 +88,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
|
||||
char *nbuf;
|
||||
|
||||
nsize = PAGE_ALIGN(nsize);
|
||||
nbuf = kmalloc(nsize, GFP_KERNEL);
|
||||
nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
|
||||
if (! nbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(nbuf, buffer->buffer, buffer->len);
|
||||
kfree(buffer->buffer);
|
||||
buffer->buffer = nbuf;
|
||||
buffer->len = nsize;
|
||||
return 0;
|
||||
@ -108,7 +106,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
|
||||
*
|
||||
* Returns the size of output string.
|
||||
*/
|
||||
int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
|
||||
int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int len, res;
|
||||
@ -727,7 +725,7 @@ EXPORT_SYMBOL(snd_info_get_line);
|
||||
* Returns the updated pointer of the original string so that
|
||||
* it can be used for the next call.
|
||||
*/
|
||||
char *snd_info_get_str(char *dest, char *src, int len)
|
||||
const char *snd_info_get_str(char *dest, const char *src, int len)
|
||||
{
|
||||
int c;
|
||||
|
||||
|
@ -31,6 +31,14 @@
|
||||
#include <sound/control.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
/* monitor files for graceful shutdown (hotplug) */
|
||||
struct snd_monitor_file {
|
||||
struct file *file;
|
||||
const struct file_operations *disconnected_f_op;
|
||||
struct list_head shutdown_list; /* still need to shutdown */
|
||||
struct list_head list; /* link of monitor files */
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(shutdown_lock);
|
||||
static LIST_HEAD(shutdown_files);
|
||||
|
||||
|
@ -199,6 +199,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
|
||||
case SNDRV_DMA_TYPE_DEV:
|
||||
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
snd_malloc_sgbuf_pages(device, size, dmab, NULL);
|
||||
break;
|
||||
@ -269,6 +271,8 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
|
||||
case SNDRV_DMA_TYPE_DEV:
|
||||
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
snd_free_sgbuf_pages(dmab);
|
||||
break;
|
||||
|
@ -24,6 +24,20 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
#define DEFAULT_DEBUG_LEVEL 2
|
||||
#else
|
||||
#define DEFAULT_DEBUG_LEVEL 1
|
||||
#endif
|
||||
|
||||
static int debug = DEFAULT_DEBUG_LEVEL;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0 = disable)");
|
||||
|
||||
#endif /* CONFIG_SND_DEBUG */
|
||||
|
||||
void release_and_free_resource(struct resource *res)
|
||||
{
|
||||
if (res) {
|
||||
@ -35,46 +49,53 @@ void release_and_free_resource(struct resource *res)
|
||||
EXPORT_SYMBOL(release_and_free_resource);
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
void snd_verbose_printk(const char *file, int line, const char *format, ...)
|
||||
/* strip the leading path if the given path is absolute */
|
||||
static const char *sanity_file_name(const char *path)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
|
||||
char tmp[] = "<0>";
|
||||
tmp[1] = format[1];
|
||||
printk("%sALSA %s:%d: ", tmp, file, line);
|
||||
format += 3;
|
||||
} else {
|
||||
printk("ALSA %s:%d: ", file, line);
|
||||
}
|
||||
va_start(args, format);
|
||||
vprintk(format, args);
|
||||
va_end(args);
|
||||
if (*path == '/')
|
||||
return strrchr(path, '/') + 1;
|
||||
else
|
||||
return path;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_verbose_printk);
|
||||
/* print file and line with a certain printk prefix */
|
||||
static int print_snd_pfx(unsigned int level, const char *path, int line,
|
||||
const char *format)
|
||||
{
|
||||
const char *file = sanity_file_name(path);
|
||||
char tmp[] = "<0>";
|
||||
const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT;
|
||||
int ret = 0;
|
||||
|
||||
if (format[0] == '<' && format[2] == '>') {
|
||||
tmp[1] = format[1];
|
||||
pfx = tmp;
|
||||
ret = 1;
|
||||
}
|
||||
printk("%sALSA %s:%d: ", pfx, file, line);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define print_snd_pfx(level, path, line, format) 0
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
|
||||
void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
||||
#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
|
||||
void __snd_printk(unsigned int level, const char *path, int line,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
|
||||
char tmp[] = "<0>";
|
||||
tmp[1] = format[1];
|
||||
printk("%sALSA %s:%d: ", tmp, file, line);
|
||||
format += 3;
|
||||
} else {
|
||||
printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
|
||||
}
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (debug < level)
|
||||
return;
|
||||
#endif
|
||||
va_start(args, format);
|
||||
if (print_snd_pfx(level, path, line, format))
|
||||
format += 3; /* skip the printk level-prefix */
|
||||
vprintk(format, args);
|
||||
va_end(args);
|
||||
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_verbose_printd);
|
||||
EXPORT_SYMBOL_GPL(__snd_printk);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
@ -1154,7 +1154,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_mixer_oss *mixer = entry->private_data;
|
||||
char line[128], str[32], idxstr[16], *cptr;
|
||||
char line[128], str[32], idxstr[16];
|
||||
const char *cptr;
|
||||
int ch, idx;
|
||||
struct snd_mixer_oss_assign_table *tbl;
|
||||
struct slot *slot;
|
||||
|
@ -1043,10 +1043,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
||||
runtime->oss.channels = params_channels(params);
|
||||
runtime->oss.rate = params_rate(params);
|
||||
|
||||
runtime->oss.params = 0;
|
||||
runtime->oss.prepare = 1;
|
||||
vfree(runtime->oss.buffer);
|
||||
runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
|
||||
if (!runtime->oss.buffer) {
|
||||
err = -ENOMEM;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
runtime->oss.params = 0;
|
||||
runtime->oss.prepare = 1;
|
||||
runtime->oss.buffer_used = 0;
|
||||
if (runtime->dma_area)
|
||||
snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
|
||||
@ -2836,7 +2841,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_pcm_str *pstr = entry->private_data;
|
||||
char line[128], str[32], task_name[32], *ptr;
|
||||
char line[128], str[32], task_name[32];
|
||||
const char *ptr;
|
||||
int idx1;
|
||||
struct snd_pcm_oss_setup *setup, *setup1, template;
|
||||
|
||||
|
@ -162,18 +162,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PROCFS
|
||||
|
||||
#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
|
||||
#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
|
||||
#define READY(v) [SNDRV_PCM_READY_##v] = #v
|
||||
#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
|
||||
#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
|
||||
#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
|
||||
#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
|
||||
#define START(v) [SNDRV_PCM_START_##v] = #v
|
||||
#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
|
||||
#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
|
||||
|
||||
static char *snd_pcm_format_names[] = {
|
||||
FORMAT(S8),
|
||||
@ -216,10 +205,23 @@ static char *snd_pcm_format_names[] = {
|
||||
FORMAT(U18_3BE),
|
||||
};
|
||||
|
||||
static const char *snd_pcm_format_name(snd_pcm_format_t format)
|
||||
const char *snd_pcm_format_name(snd_pcm_format_t format)
|
||||
{
|
||||
return snd_pcm_format_names[format];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_pcm_format_name);
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PROCFS
|
||||
|
||||
#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
|
||||
#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
|
||||
#define READY(v) [SNDRV_PCM_READY_##v] = #v
|
||||
#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
|
||||
#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
|
||||
#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
|
||||
#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
|
||||
#define START(v) [SNDRV_PCM_START_##v] = #v
|
||||
#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
|
||||
|
||||
static char *snd_pcm_stream_names[] = {
|
||||
STREAM(PLAYBACK),
|
||||
|
@ -197,12 +197,16 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
|
||||
avail = snd_pcm_capture_avail(runtime);
|
||||
if (avail > runtime->avail_max)
|
||||
runtime->avail_max = avail;
|
||||
if (avail >= runtime->stop_threshold) {
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
|
||||
if (avail >= runtime->buffer_size) {
|
||||
snd_pcm_drain_done(substream);
|
||||
else
|
||||
return -EPIPE;
|
||||
}
|
||||
} else {
|
||||
if (avail >= runtime->stop_threshold) {
|
||||
xrun(substream);
|
||||
return -EPIPE;
|
||||
return -EPIPE;
|
||||
}
|
||||
}
|
||||
if (avail >= runtime->control->avail_min)
|
||||
wake_up(&runtime->sleep);
|
||||
|
@ -304,6 +304,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
|
||||
|
||||
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
/**
|
||||
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
|
||||
* @substream: the pcm substream instance
|
||||
@ -349,6 +350,7 @@ unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
|
||||
#endif /* CONFIG_SND_DMA_SGBUF */
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
|
||||
|
@ -1343,8 +1343,6 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
|
||||
|
||||
static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
|
||||
{
|
||||
if (substream->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
substream->runtime->trigger_master = substream;
|
||||
return 0;
|
||||
}
|
||||
@ -1392,7 +1390,6 @@ static struct action_ops snd_pcm_action_drain_init = {
|
||||
struct drain_rec {
|
||||
struct snd_pcm_substream *substream;
|
||||
wait_queue_t wait;
|
||||
snd_pcm_uframes_t stop_threshold;
|
||||
};
|
||||
|
||||
static int snd_pcm_drop(struct snd_pcm_substream *substream);
|
||||
@ -1404,13 +1401,15 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream);
|
||||
* After this call, all streams are supposed to be either SETUP or DRAINING
|
||||
* (capture only) state.
|
||||
*/
|
||||
static int snd_pcm_drain(struct snd_pcm_substream *substream)
|
||||
static int snd_pcm_drain(struct snd_pcm_substream *substream,
|
||||
struct file *file)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_pcm_substream *s;
|
||||
int result = 0;
|
||||
int i, num_drecs;
|
||||
int nonblock = 0;
|
||||
struct drain_rec *drec, drec_tmp, *d;
|
||||
|
||||
card = substream->pcm->card;
|
||||
@ -1428,6 +1427,15 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
nonblock = 1;
|
||||
} else if (substream->f_flags & O_NONBLOCK)
|
||||
nonblock = 1;
|
||||
|
||||
if (nonblock)
|
||||
goto lock; /* no need to allocate waitqueues */
|
||||
|
||||
/* allocate temporary record for drain sync */
|
||||
down_read(&snd_pcm_link_rwsem);
|
||||
if (snd_pcm_stream_linked(substream)) {
|
||||
@ -1449,16 +1457,11 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
|
||||
d->substream = s;
|
||||
init_waitqueue_entry(&d->wait, current);
|
||||
add_wait_queue(&runtime->sleep, &d->wait);
|
||||
/* stop_threshold fixup to avoid endless loop when
|
||||
* stop_threshold > buffer_size
|
||||
*/
|
||||
d->stop_threshold = runtime->stop_threshold;
|
||||
if (runtime->stop_threshold > runtime->buffer_size)
|
||||
runtime->stop_threshold = runtime->buffer_size;
|
||||
}
|
||||
}
|
||||
up_read(&snd_pcm_link_rwsem);
|
||||
|
||||
lock:
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
/* resume pause */
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
|
||||
@ -1466,9 +1469,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
|
||||
|
||||
/* pre-start/stop - all running streams are changed to DRAINING state */
|
||||
result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
|
||||
if (result < 0) {
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
goto _error;
|
||||
if (result < 0)
|
||||
goto unlock;
|
||||
/* in non-blocking, we don't wait in ioctl but let caller poll */
|
||||
if (nonblock) {
|
||||
result = -EAGAIN;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@ -1504,18 +1510,18 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
|
||||
_error:
|
||||
for (i = 0; i < num_drecs; i++) {
|
||||
d = &drec[i];
|
||||
runtime = d->substream->runtime;
|
||||
remove_wait_queue(&runtime->sleep, &d->wait);
|
||||
runtime->stop_threshold = d->stop_threshold;
|
||||
if (!nonblock) {
|
||||
for (i = 0; i < num_drecs; i++) {
|
||||
d = &drec[i];
|
||||
runtime = d->substream->runtime;
|
||||
remove_wait_queue(&runtime->sleep, &d->wait);
|
||||
}
|
||||
if (drec != &drec_tmp)
|
||||
kfree(drec);
|
||||
}
|
||||
|
||||
if (drec != &drec_tmp)
|
||||
kfree(drec);
|
||||
snd_power_unlock(card);
|
||||
|
||||
return result;
|
||||
@ -2208,6 +2214,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
|
||||
case SNDRV_PCM_STATE_XRUN:
|
||||
ret = -EPIPE;
|
||||
goto __end;
|
||||
case SNDRV_PCM_STATE_SUSPENDED:
|
||||
ret = -ESTRPIPE;
|
||||
goto __end;
|
||||
default:
|
||||
ret = -EBADFD;
|
||||
goto __end;
|
||||
@ -2253,6 +2262,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
|
||||
case SNDRV_PCM_STATE_XRUN:
|
||||
ret = -EPIPE;
|
||||
goto __end;
|
||||
case SNDRV_PCM_STATE_SUSPENDED:
|
||||
ret = -ESTRPIPE;
|
||||
goto __end;
|
||||
default:
|
||||
ret = -EBADFD;
|
||||
goto __end;
|
||||
@ -2299,6 +2311,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
|
||||
case SNDRV_PCM_STATE_XRUN:
|
||||
ret = -EPIPE;
|
||||
goto __end;
|
||||
case SNDRV_PCM_STATE_SUSPENDED:
|
||||
ret = -ESTRPIPE;
|
||||
goto __end;
|
||||
default:
|
||||
ret = -EBADFD;
|
||||
goto __end;
|
||||
@ -2345,6 +2360,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
|
||||
case SNDRV_PCM_STATE_XRUN:
|
||||
ret = -EPIPE;
|
||||
goto __end;
|
||||
case SNDRV_PCM_STATE_SUSPENDED:
|
||||
ret = -ESTRPIPE;
|
||||
goto __end;
|
||||
default:
|
||||
ret = -EBADFD;
|
||||
goto __end;
|
||||
@ -2544,7 +2562,7 @@ static int snd_pcm_common_ioctl1(struct file *file,
|
||||
return snd_pcm_hw_params_old_user(substream, arg);
|
||||
#endif
|
||||
case SNDRV_PCM_IOCTL_DRAIN:
|
||||
return snd_pcm_drain(substream);
|
||||
return snd_pcm_drain(substream, file);
|
||||
case SNDRV_PCM_IOCTL_DROP:
|
||||
return snd_pcm_drop(substream);
|
||||
case SNDRV_PCM_IOCTL_PAUSE:
|
||||
|
@ -274,7 +274,7 @@ static int open_substream(struct snd_rawmidi *rmidi,
|
||||
return err;
|
||||
substream->opened = 1;
|
||||
if (substream->use_count++ == 0)
|
||||
substream->active_sensing = 1;
|
||||
substream->active_sensing = 0;
|
||||
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
|
||||
substream->append = 1;
|
||||
rmidi->streams[substream->stream].substream_opened++;
|
||||
|
@ -20,6 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sound/asoundef.h>
|
||||
#include "seq_oss_midi.h"
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_timer.h"
|
||||
@ -476,19 +477,20 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
|
||||
ev.source.port = dp->port;
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
|
||||
ev.type = SNDRV_SEQ_EVENT_SENSING;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0);
|
||||
}
|
||||
for (c = 0; c < 16; c++) {
|
||||
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
|
||||
ev.data.control.channel = c;
|
||||
ev.data.control.param = 123;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
|
||||
ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0);
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
|
||||
ev.data.control.param = 121;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
|
||||
ev.data.control.param =
|
||||
MIDI_CTL_RESET_CONTROLLERS;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0);
|
||||
ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
|
||||
ev.data.control.value = 0;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
|
||||
return -EINVAL;
|
||||
runtime = substream->runtime;
|
||||
if ((tmp = runtime->avail) < count) {
|
||||
snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
|
||||
if (printk_ratelimit())
|
||||
snd_printk(KERN_ERR "MIDI output buffer overrun\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
|
||||
@ -236,6 +237,7 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.avail_min = 1;
|
||||
params.buffer_size = output_buffer_size;
|
||||
params.no_active_sensing = 1;
|
||||
if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) {
|
||||
snd_rawmidi_kernel_release(&msynth->output_rfile);
|
||||
return err;
|
||||
@ -248,12 +250,9 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
|
||||
static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
struct seq_midisynth *msynth = private_data;
|
||||
unsigned char buf = 0xff; /* MIDI reset */
|
||||
|
||||
if (snd_BUG_ON(!msynth->output_rfile.output))
|
||||
return -EINVAL;
|
||||
/* sending single MIDI reset message to shut the device up */
|
||||
snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
|
||||
snd_rawmidi_drain_output(msynth->output_rfile.output);
|
||||
return snd_rawmidi_kernel_release(&msynth->output_rfile);
|
||||
}
|
||||
|
@ -353,7 +353,8 @@ static void master_free(struct snd_kcontrol *kcontrol)
|
||||
*
|
||||
* The optional argument @tlv can be used to specify the TLV information
|
||||
* for dB scale of the master control. It should be a single element
|
||||
* with #SNDRV_CTL_TLVT_DB_SCALE type, and should be the max 0dB.
|
||||
* with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
|
||||
* #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
|
||||
*/
|
||||
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
|
||||
const unsigned int *tlv)
|
||||
@ -384,7 +385,10 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
|
||||
kctl->private_free = master_free;
|
||||
|
||||
/* additional (constant) TLV read */
|
||||
if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
|
||||
if (tlv &&
|
||||
(tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
|
||||
tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
|
||||
tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
|
||||
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
||||
memcpy(master->tlv, tlv, sizeof(master->tlv));
|
||||
kctl->tlv.p = master->tlv;
|
||||
|
@ -25,12 +25,15 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||
@ -39,7 +42,7 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
|
||||
|
||||
#define MAX_PCM_DEVICES 4
|
||||
#define MAX_PCM_SUBSTREAMS 16
|
||||
#define MAX_PCM_SUBSTREAMS 128
|
||||
#define MAX_MIDI_DEVICES 2
|
||||
|
||||
#if 0 /* emu10k1 emulation */
|
||||
@ -148,6 +151,10 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
|
||||
static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
|
||||
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
|
||||
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
static int hrtimer = 1;
|
||||
#endif
|
||||
static int fake_buffer = 1;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
|
||||
@ -161,6 +168,12 @@ module_param_array(pcm_substreams, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
|
||||
//module_param_array(midi_devs, int, NULL, 0444);
|
||||
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
|
||||
module_param(fake_buffer, bool, 0444);
|
||||
MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
module_param(hrtimer, bool, 0644);
|
||||
MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
|
||||
#endif
|
||||
|
||||
static struct platform_device *devices[SNDRV_CARDS];
|
||||
|
||||
@ -171,119 +184,324 @@ static struct platform_device *devices[SNDRV_CARDS];
|
||||
#define MIXER_ADDR_CD 4
|
||||
#define MIXER_ADDR_LAST 4
|
||||
|
||||
struct dummy_timer_ops {
|
||||
int (*create)(struct snd_pcm_substream *);
|
||||
void (*free)(struct snd_pcm_substream *);
|
||||
int (*prepare)(struct snd_pcm_substream *);
|
||||
int (*start)(struct snd_pcm_substream *);
|
||||
int (*stop)(struct snd_pcm_substream *);
|
||||
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
|
||||
};
|
||||
|
||||
struct snd_dummy {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
spinlock_t mixer_lock;
|
||||
int mixer_volume[MIXER_ADDR_LAST+1][2];
|
||||
int capture_source[MIXER_ADDR_LAST+1][2];
|
||||
const struct dummy_timer_ops *timer_ops;
|
||||
};
|
||||
|
||||
struct snd_dummy_pcm {
|
||||
struct snd_dummy *dummy;
|
||||
/*
|
||||
* system timer interface
|
||||
*/
|
||||
|
||||
struct dummy_systimer_pcm {
|
||||
spinlock_t lock;
|
||||
struct timer_list timer;
|
||||
unsigned int pcm_buffer_size;
|
||||
unsigned int pcm_period_size;
|
||||
unsigned int pcm_bps; /* bytes per second */
|
||||
unsigned int pcm_hz; /* HZ */
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_buf_pos; /* position in buffer */
|
||||
unsigned long base_time;
|
||||
unsigned int frac_pos; /* fractional sample position (based HZ) */
|
||||
unsigned int frac_period_rest;
|
||||
unsigned int frac_buffer_size; /* buffer_size * HZ */
|
||||
unsigned int frac_period_size; /* period_size * HZ */
|
||||
unsigned int rate;
|
||||
int elapsed;
|
||||
struct snd_pcm_substream *substream;
|
||||
};
|
||||
|
||||
|
||||
static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm)
|
||||
static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
|
||||
{
|
||||
dpcm->timer.expires = 1 + jiffies;
|
||||
dpcm->timer.expires = jiffies +
|
||||
(dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate;
|
||||
add_timer(&dpcm->timer);
|
||||
}
|
||||
|
||||
static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm)
|
||||
static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
|
||||
{
|
||||
del_timer(&dpcm->timer);
|
||||
unsigned long delta;
|
||||
|
||||
delta = jiffies - dpcm->base_time;
|
||||
if (!delta)
|
||||
return;
|
||||
dpcm->base_time += delta;
|
||||
delta *= dpcm->rate;
|
||||
dpcm->frac_pos += delta;
|
||||
while (dpcm->frac_pos >= dpcm->frac_buffer_size)
|
||||
dpcm->frac_pos -= dpcm->frac_buffer_size;
|
||||
while (dpcm->frac_period_rest <= delta) {
|
||||
dpcm->elapsed++;
|
||||
dpcm->frac_period_rest += dpcm->frac_period_size;
|
||||
}
|
||||
dpcm->frac_period_rest -= delta;
|
||||
}
|
||||
|
||||
static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
static int dummy_systimer_start(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm = runtime->private_data;
|
||||
int err = 0;
|
||||
|
||||
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
|
||||
spin_lock(&dpcm->lock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
snd_card_dummy_pcm_timer_start(dpcm);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
snd_card_dummy_pcm_timer_stop(dpcm);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
dpcm->base_time = jiffies;
|
||||
dummy_systimer_rearm(dpcm);
|
||||
spin_unlock(&dpcm->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
static int dummy_systimer_stop(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
|
||||
spin_lock(&dpcm->lock);
|
||||
del_timer(&dpcm->timer);
|
||||
spin_unlock(&dpcm->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm = runtime->private_data;
|
||||
int bps;
|
||||
struct dummy_systimer_pcm *dpcm = runtime->private_data;
|
||||
|
||||
bps = snd_pcm_format_width(runtime->format) * runtime->rate *
|
||||
runtime->channels / 8;
|
||||
|
||||
if (bps <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
dpcm->pcm_bps = bps;
|
||||
dpcm->pcm_hz = HZ;
|
||||
dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream);
|
||||
dpcm->pcm_irq_pos = 0;
|
||||
dpcm->pcm_buf_pos = 0;
|
||||
|
||||
snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
|
||||
bytes_to_samples(runtime, runtime->dma_bytes));
|
||||
dpcm->frac_pos = 0;
|
||||
dpcm->rate = runtime->rate;
|
||||
dpcm->frac_buffer_size = runtime->buffer_size * HZ;
|
||||
dpcm->frac_period_size = runtime->period_size * HZ;
|
||||
dpcm->frac_period_rest = dpcm->frac_period_size;
|
||||
dpcm->elapsed = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_card_dummy_pcm_timer_function(unsigned long data)
|
||||
static void dummy_systimer_callback(unsigned long data)
|
||||
{
|
||||
struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data;
|
||||
struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data;
|
||||
unsigned long flags;
|
||||
int elapsed = 0;
|
||||
|
||||
spin_lock_irqsave(&dpcm->lock, flags);
|
||||
dpcm->timer.expires = 1 + jiffies;
|
||||
add_timer(&dpcm->timer);
|
||||
dpcm->pcm_irq_pos += dpcm->pcm_bps;
|
||||
dpcm->pcm_buf_pos += dpcm->pcm_bps;
|
||||
dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz;
|
||||
if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) {
|
||||
dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz;
|
||||
spin_unlock_irqrestore(&dpcm->lock, flags);
|
||||
dummy_systimer_update(dpcm);
|
||||
dummy_systimer_rearm(dpcm);
|
||||
elapsed = dpcm->elapsed;
|
||||
dpcm->elapsed = 0;
|
||||
spin_unlock_irqrestore(&dpcm->lock, flags);
|
||||
if (elapsed)
|
||||
snd_pcm_period_elapsed(dpcm->substream);
|
||||
} else
|
||||
spin_unlock_irqrestore(&dpcm->lock, flags);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
static snd_pcm_uframes_t
|
||||
dummy_systimer_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
|
||||
snd_pcm_uframes_t pos;
|
||||
|
||||
spin_lock(&dpcm->lock);
|
||||
dummy_systimer_update(dpcm);
|
||||
pos = dpcm->frac_pos / HZ;
|
||||
spin_unlock(&dpcm->lock);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int dummy_systimer_create(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_systimer_pcm *dpcm;
|
||||
|
||||
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
|
||||
if (!dpcm)
|
||||
return -ENOMEM;
|
||||
substream->runtime->private_data = dpcm;
|
||||
init_timer(&dpcm->timer);
|
||||
dpcm->timer.data = (unsigned long) dpcm;
|
||||
dpcm->timer.function = dummy_systimer_callback;
|
||||
spin_lock_init(&dpcm->lock);
|
||||
dpcm->substream = substream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dummy_systimer_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
kfree(substream->runtime->private_data);
|
||||
}
|
||||
|
||||
static struct dummy_timer_ops dummy_systimer_ops = {
|
||||
.create = dummy_systimer_create,
|
||||
.free = dummy_systimer_free,
|
||||
.prepare = dummy_systimer_prepare,
|
||||
.start = dummy_systimer_start,
|
||||
.stop = dummy_systimer_stop,
|
||||
.pointer = dummy_systimer_pointer,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
/*
|
||||
* hrtimer interface
|
||||
*/
|
||||
|
||||
struct dummy_hrtimer_pcm {
|
||||
ktime_t base_time;
|
||||
ktime_t period_time;
|
||||
atomic_t running;
|
||||
struct hrtimer timer;
|
||||
struct tasklet_struct tasklet;
|
||||
struct snd_pcm_substream *substream;
|
||||
};
|
||||
|
||||
static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
|
||||
if (atomic_read(&dpcm->running))
|
||||
snd_pcm_period_elapsed(dpcm->substream);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm;
|
||||
|
||||
dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
|
||||
if (!atomic_read(&dpcm->running))
|
||||
return HRTIMER_NORESTART;
|
||||
tasklet_schedule(&dpcm->tasklet);
|
||||
hrtimer_forward_now(timer, dpcm->period_time);
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
|
||||
|
||||
dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
|
||||
hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
|
||||
atomic_set(&dpcm->running, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
|
||||
|
||||
atomic_set(&dpcm->running, 0);
|
||||
hrtimer_cancel(&dpcm->timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
|
||||
{
|
||||
tasklet_kill(&dpcm->tasklet);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
dummy_hrtimer_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm = runtime->private_data;
|
||||
struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
|
||||
u64 delta;
|
||||
u32 pos;
|
||||
|
||||
return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
|
||||
delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer),
|
||||
dpcm->base_time);
|
||||
delta = div_u64(delta * runtime->rate + 999999, 1000000);
|
||||
div_u64_rem(delta, runtime->buffer_size, &pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware snd_card_dummy_playback =
|
||||
static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
|
||||
unsigned int period, rate;
|
||||
long sec;
|
||||
unsigned long nsecs;
|
||||
|
||||
dummy_hrtimer_sync(dpcm);
|
||||
period = runtime->period_size;
|
||||
rate = runtime->rate;
|
||||
sec = period / rate;
|
||||
period %= rate;
|
||||
nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate);
|
||||
dpcm->period_time = ktime_set(sec, nsecs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm;
|
||||
|
||||
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
|
||||
if (!dpcm)
|
||||
return -ENOMEM;
|
||||
substream->runtime->private_data = dpcm;
|
||||
hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
dpcm->timer.function = dummy_hrtimer_callback;
|
||||
dpcm->substream = substream;
|
||||
atomic_set(&dpcm->running, 0);
|
||||
tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
|
||||
(unsigned long)dpcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
|
||||
dummy_hrtimer_sync(dpcm);
|
||||
kfree(dpcm);
|
||||
}
|
||||
|
||||
static struct dummy_timer_ops dummy_hrtimer_ops = {
|
||||
.create = dummy_hrtimer_create,
|
||||
.free = dummy_hrtimer_free,
|
||||
.prepare = dummy_hrtimer_prepare,
|
||||
.start = dummy_hrtimer_start,
|
||||
.stop = dummy_hrtimer_stop,
|
||||
.pointer = dummy_hrtimer_pointer,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
/*
|
||||
* PCM interface
|
||||
*/
|
||||
|
||||
static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
return dummy->timer_ops->start(substream);
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
return dummy->timer_ops->stop(substream);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
|
||||
|
||||
return dummy->timer_ops->prepare(substream);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
|
||||
|
||||
return dummy->timer_ops->pointer(substream);
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware dummy_pcm_hardware = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = USE_FORMATS,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
@ -298,141 +516,152 @@ static struct snd_pcm_hardware snd_card_dummy_playback =
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware snd_card_dummy_capture =
|
||||
static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = USE_FORMATS,
|
||||
.rates = USE_RATE,
|
||||
.rate_min = USE_RATE_MIN,
|
||||
.rate_max = USE_RATE_MAX,
|
||||
.channels_min = USE_CHANNELS_MIN,
|
||||
.channels_max = USE_CHANNELS_MAX,
|
||||
.buffer_bytes_max = MAX_BUFFER_SIZE,
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = MAX_PERIOD_SIZE,
|
||||
.periods_min = USE_PERIODS_MIN,
|
||||
.periods_max = USE_PERIODS_MAX,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
kfree(runtime->private_data);
|
||||
if (fake_buffer) {
|
||||
/* runtime->dma_bytes has to be set manually to allow mmap */
|
||||
substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
|
||||
return 0;
|
||||
}
|
||||
return snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
}
|
||||
|
||||
static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
}
|
||||
|
||||
static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream)
|
||||
static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (fake_buffer)
|
||||
return 0;
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dummy_pcm *dpcm;
|
||||
|
||||
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
|
||||
if (! dpcm)
|
||||
return dpcm;
|
||||
init_timer(&dpcm->timer);
|
||||
dpcm->timer.data = (unsigned long) dpcm;
|
||||
dpcm->timer.function = snd_card_dummy_pcm_timer_function;
|
||||
spin_lock_init(&dpcm->lock);
|
||||
dpcm->substream = substream;
|
||||
return dpcm;
|
||||
}
|
||||
|
||||
static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
|
||||
static int dummy_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm;
|
||||
int err;
|
||||
|
||||
if ((dpcm = new_pcm_stream(substream)) == NULL)
|
||||
return -ENOMEM;
|
||||
runtime->private_data = dpcm;
|
||||
/* makes the infrastructure responsible for freeing dpcm */
|
||||
runtime->private_free = snd_card_dummy_runtime_free;
|
||||
runtime->hw = snd_card_dummy_playback;
|
||||
dummy->timer_ops = &dummy_systimer_ops;
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
if (hrtimer)
|
||||
dummy->timer_ops = &dummy_hrtimer_ops;
|
||||
#endif
|
||||
|
||||
err = dummy->timer_ops->create(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
runtime->hw = dummy_pcm_hardware;
|
||||
if (substream->pcm->device & 1) {
|
||||
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
|
||||
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
|
||||
}
|
||||
if (substream->pcm->device & 2)
|
||||
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
|
||||
err = add_playback_constraints(runtime);
|
||||
if (err < 0)
|
||||
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = add_playback_constraints(substream->runtime);
|
||||
else
|
||||
err = add_capture_constraints(substream->runtime);
|
||||
if (err < 0) {
|
||||
dummy->timer_ops->free(substream);
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm;
|
||||
int err;
|
||||
|
||||
if ((dpcm = new_pcm_stream(substream)) == NULL)
|
||||
return -ENOMEM;
|
||||
runtime->private_data = dpcm;
|
||||
/* makes the infrastructure responsible for freeing dpcm */
|
||||
runtime->private_free = snd_card_dummy_runtime_free;
|
||||
runtime->hw = snd_card_dummy_capture;
|
||||
if (substream->pcm->device == 1) {
|
||||
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
|
||||
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
|
||||
}
|
||||
if (substream->pcm->device & 2)
|
||||
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
|
||||
err = add_capture_constraints(runtime);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream)
|
||||
static int dummy_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
|
||||
dummy->timer_ops->free(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream)
|
||||
/*
|
||||
* dummy buffer handling
|
||||
*/
|
||||
|
||||
static void *dummy_page[2];
|
||||
|
||||
static void free_fake_buffer(void)
|
||||
{
|
||||
if (fake_buffer) {
|
||||
int i;
|
||||
for (i = 0; i < 2; i++)
|
||||
if (dummy_page[i]) {
|
||||
free_page((unsigned long)dummy_page[i]);
|
||||
dummy_page[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int alloc_fake_buffer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!fake_buffer)
|
||||
return 0;
|
||||
for (i = 0; i < 2; i++) {
|
||||
dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!dummy_page[i]) {
|
||||
free_fake_buffer();
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops snd_card_dummy_playback_ops = {
|
||||
.open = snd_card_dummy_playback_open,
|
||||
.close = snd_card_dummy_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_card_dummy_hw_params,
|
||||
.hw_free = snd_card_dummy_hw_free,
|
||||
.prepare = snd_card_dummy_pcm_prepare,
|
||||
.trigger = snd_card_dummy_pcm_trigger,
|
||||
.pointer = snd_card_dummy_pcm_pointer,
|
||||
static int dummy_pcm_copy(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t pos,
|
||||
void __user *dst, snd_pcm_uframes_t count)
|
||||
{
|
||||
return 0; /* do nothing */
|
||||
}
|
||||
|
||||
static int dummy_pcm_silence(struct snd_pcm_substream *substream,
|
||||
int channel, snd_pcm_uframes_t pos,
|
||||
snd_pcm_uframes_t count)
|
||||
{
|
||||
return 0; /* do nothing */
|
||||
}
|
||||
|
||||
static struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
|
||||
unsigned long offset)
|
||||
{
|
||||
return virt_to_page(dummy_page[substream->stream]); /* the same page */
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops dummy_pcm_ops = {
|
||||
.open = dummy_pcm_open,
|
||||
.close = dummy_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = dummy_pcm_hw_params,
|
||||
.hw_free = dummy_pcm_hw_free,
|
||||
.prepare = dummy_pcm_prepare,
|
||||
.trigger = dummy_pcm_trigger,
|
||||
.pointer = dummy_pcm_pointer,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops snd_card_dummy_capture_ops = {
|
||||
.open = snd_card_dummy_capture_open,
|
||||
.close = snd_card_dummy_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_card_dummy_hw_params,
|
||||
.hw_free = snd_card_dummy_hw_free,
|
||||
.prepare = snd_card_dummy_pcm_prepare,
|
||||
.trigger = snd_card_dummy_pcm_trigger,
|
||||
.pointer = snd_card_dummy_pcm_pointer,
|
||||
static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
|
||||
.open = dummy_pcm_open,
|
||||
.close = dummy_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = dummy_pcm_hw_params,
|
||||
.hw_free = dummy_pcm_hw_free,
|
||||
.prepare = dummy_pcm_prepare,
|
||||
.trigger = dummy_pcm_trigger,
|
||||
.pointer = dummy_pcm_pointer,
|
||||
.copy = dummy_pcm_copy,
|
||||
.silence = dummy_pcm_silence,
|
||||
.page = dummy_pcm_page,
|
||||
};
|
||||
|
||||
static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
|
||||
int substreams)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_pcm_ops *ops;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_new(dummy->card, "Dummy PCM", device,
|
||||
@ -440,17 +669,28 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
|
||||
if (err < 0)
|
||||
return err;
|
||||
dummy->pcm = pcm;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
|
||||
if (fake_buffer)
|
||||
ops = &dummy_pcm_ops_no_buf;
|
||||
else
|
||||
ops = &dummy_pcm_ops;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
|
||||
pcm->private_data = dummy;
|
||||
pcm->info_flags = 0;
|
||||
strcpy(pcm->name, "Dummy PCM");
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
0, 64*1024);
|
||||
if (!fake_buffer) {
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
0, 64*1024);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* mixer interface
|
||||
*/
|
||||
|
||||
#define DUMMY_VOLUME(xname, xindex, addr) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
||||
@ -581,6 +821,131 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
|
||||
/*
|
||||
* proc interface
|
||||
*/
|
||||
static void print_formats(struct snd_info_buffer *buffer)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
|
||||
if (dummy_pcm_hardware.formats & (1ULL << i))
|
||||
snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
|
||||
}
|
||||
}
|
||||
|
||||
static void print_rates(struct snd_info_buffer *buffer)
|
||||
{
|
||||
static int rates[] = {
|
||||
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
|
||||
64000, 88200, 96000, 176400, 192000,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS)
|
||||
snd_iprintf(buffer, " continuous");
|
||||
if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT)
|
||||
snd_iprintf(buffer, " knot");
|
||||
for (i = 0; i < ARRAY_SIZE(rates); i++)
|
||||
if (dummy_pcm_hardware.rates & (1 << i))
|
||||
snd_iprintf(buffer, " %d", rates[i]);
|
||||
}
|
||||
|
||||
#define get_dummy_int_ptr(ofs) \
|
||||
(unsigned int *)((char *)&dummy_pcm_hardware + (ofs))
|
||||
#define get_dummy_ll_ptr(ofs) \
|
||||
(unsigned long long *)((char *)&dummy_pcm_hardware + (ofs))
|
||||
|
||||
struct dummy_hw_field {
|
||||
const char *name;
|
||||
const char *format;
|
||||
unsigned int offset;
|
||||
unsigned int size;
|
||||
};
|
||||
#define FIELD_ENTRY(item, fmt) { \
|
||||
.name = #item, \
|
||||
.format = fmt, \
|
||||
.offset = offsetof(struct snd_pcm_hardware, item), \
|
||||
.size = sizeof(dummy_pcm_hardware.item) }
|
||||
|
||||
static struct dummy_hw_field fields[] = {
|
||||
FIELD_ENTRY(formats, "%#llx"),
|
||||
FIELD_ENTRY(rates, "%#x"),
|
||||
FIELD_ENTRY(rate_min, "%d"),
|
||||
FIELD_ENTRY(rate_max, "%d"),
|
||||
FIELD_ENTRY(channels_min, "%d"),
|
||||
FIELD_ENTRY(channels_max, "%d"),
|
||||
FIELD_ENTRY(buffer_bytes_max, "%ld"),
|
||||
FIELD_ENTRY(period_bytes_min, "%ld"),
|
||||
FIELD_ENTRY(period_bytes_max, "%ld"),
|
||||
FIELD_ENTRY(periods_min, "%d"),
|
||||
FIELD_ENTRY(periods_max, "%d"),
|
||||
};
|
||||
|
||||
static void dummy_proc_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fields); i++) {
|
||||
snd_iprintf(buffer, "%s ", fields[i].name);
|
||||
if (fields[i].size == sizeof(int))
|
||||
snd_iprintf(buffer, fields[i].format,
|
||||
*get_dummy_int_ptr(fields[i].offset));
|
||||
else
|
||||
snd_iprintf(buffer, fields[i].format,
|
||||
*get_dummy_ll_ptr(fields[i].offset));
|
||||
if (!strcmp(fields[i].name, "formats"))
|
||||
print_formats(buffer);
|
||||
else if (!strcmp(fields[i].name, "rates"))
|
||||
print_rates(buffer);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dummy_proc_write(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
char line[64];
|
||||
|
||||
while (!snd_info_get_line(buffer, line, sizeof(line))) {
|
||||
char item[20];
|
||||
const char *ptr;
|
||||
unsigned long long val;
|
||||
int i;
|
||||
|
||||
ptr = snd_info_get_str(item, line, sizeof(item));
|
||||
for (i = 0; i < ARRAY_SIZE(fields); i++) {
|
||||
if (!strcmp(item, fields[i].name))
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(fields))
|
||||
continue;
|
||||
snd_info_get_str(item, ptr, sizeof(item));
|
||||
if (strict_strtoull(item, 0, &val))
|
||||
continue;
|
||||
if (fields[i].size == sizeof(int))
|
||||
*get_dummy_int_ptr(fields[i].offset) = val;
|
||||
else
|
||||
*get_dummy_ll_ptr(fields[i].offset) = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void __devinit dummy_proc_init(struct snd_dummy *chip)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
|
||||
snd_info_set_text_ops(entry, chip, dummy_proc_read);
|
||||
entry->c.text.write = dummy_proc_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define dummy_proc_init(x)
|
||||
#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
|
||||
|
||||
static int __devinit snd_dummy_probe(struct platform_device *devptr)
|
||||
{
|
||||
struct snd_card *card;
|
||||
@ -610,6 +975,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
|
||||
strcpy(card->shortname, "Dummy");
|
||||
sprintf(card->longname, "Dummy %i", dev + 1);
|
||||
|
||||
dummy_proc_init(dummy);
|
||||
|
||||
snd_card_set_dev(card, &devptr->dev);
|
||||
|
||||
err = snd_card_register(card);
|
||||
@ -670,6 +1037,7 @@ static void snd_dummy_unregister_all(void)
|
||||
for (i = 0; i < ARRAY_SIZE(devices); ++i)
|
||||
platform_device_unregister(devices[i]);
|
||||
platform_driver_unregister(&snd_dummy_driver);
|
||||
free_fake_buffer();
|
||||
}
|
||||
|
||||
static int __init alsa_card_dummy_init(void)
|
||||
@ -680,6 +1048,12 @@ static int __init alsa_card_dummy_init(void)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = alloc_fake_buffer();
|
||||
if (err < 0) {
|
||||
platform_driver_unregister(&snd_dummy_driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
cards = 0;
|
||||
for (i = 0; i < SNDRV_CARDS; i++) {
|
||||
struct platform_device *device;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Driver for C-Media's CMI8330 soundcards.
|
||||
* Driver for C-Media's CMI8330 and CMI8329 soundcards.
|
||||
* Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
|
||||
* http://www.undergrad.math.uwaterloo.ca/~gstalusa
|
||||
*
|
||||
@ -35,7 +35,7 @@
|
||||
*
|
||||
* This card has two mixers and two PCM devices. I've cheesed it such
|
||||
* that recording and playback can be done through the same device.
|
||||
* The driver "magically" routes the capturing to the CMI8330 codec,
|
||||
* The driver "magically" routes the capturing to the AD1848 codec,
|
||||
* and playback to the SB16 codec. This allows for full-duplex mode
|
||||
* to some extent.
|
||||
* The utilities in alsa-utils are aware of both devices, so passing
|
||||
@ -64,7 +64,7 @@
|
||||
/*
|
||||
*/
|
||||
MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
|
||||
MODULE_DESCRIPTION("C-Media CMI8330");
|
||||
MODULE_DESCRIPTION("C-Media CMI8330/CMI8329");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
|
||||
|
||||
@ -86,38 +86,38 @@ static long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
||||
static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard.");
|
||||
MODULE_PARM_DESC(index, "Index value for CMI8330/CMI8329 soundcard.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for CMI8330 soundcard.");
|
||||
MODULE_PARM_DESC(id, "ID string for CMI8330/CMI8329 soundcard.");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard.");
|
||||
MODULE_PARM_DESC(enable, "Enable CMI8330/CMI8329 soundcard.");
|
||||
#ifdef CONFIG_PNP
|
||||
module_param_array(isapnp, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
|
||||
#endif
|
||||
|
||||
module_param_array(sbport, long, NULL, 0444);
|
||||
MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver.");
|
||||
MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver.");
|
||||
module_param_array(sbirq, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver.");
|
||||
MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver.");
|
||||
module_param_array(sbdma8, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver.");
|
||||
MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver.");
|
||||
module_param_array(sbdma16, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver.");
|
||||
MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver.");
|
||||
|
||||
module_param_array(wssport, long, NULL, 0444);
|
||||
MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver.");
|
||||
MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver.");
|
||||
module_param_array(wssirq, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver.");
|
||||
MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver.");
|
||||
module_param_array(wssdma, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver.");
|
||||
MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver.");
|
||||
|
||||
module_param_array(fmport, long, NULL, 0444);
|
||||
MODULE_PARM_DESC(fmport, "FM port # for CMI8330 driver.");
|
||||
MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver.");
|
||||
module_param_array(mpuport, long, NULL, 0444);
|
||||
MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330 driver.");
|
||||
MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver.");
|
||||
module_param_array(mpuirq, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330 MPU-401 port.");
|
||||
MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port.");
|
||||
#ifdef CONFIG_PNP
|
||||
static int isa_registered;
|
||||
static int pnp_registered;
|
||||
@ -156,6 +156,11 @@ static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
|
||||
|
||||
typedef int (*snd_pcm_open_callback_t)(struct snd_pcm_substream *);
|
||||
|
||||
enum card_type {
|
||||
CMI8330,
|
||||
CMI8329
|
||||
};
|
||||
|
||||
struct snd_cmi8330 {
|
||||
#ifdef CONFIG_PNP
|
||||
struct pnp_dev *cap;
|
||||
@ -172,11 +177,14 @@ struct snd_cmi8330 {
|
||||
snd_pcm_open_callback_t open;
|
||||
void *private_data; /* sb or wss */
|
||||
} streams[2];
|
||||
|
||||
enum card_type type;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
|
||||
static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
|
||||
{ .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } },
|
||||
{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
|
||||
{ .id = "" }
|
||||
};
|
||||
@ -304,7 +312,7 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
strcpy(card->mixername, "CMI8330/C3D");
|
||||
strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
|
||||
err = snd_ctl_add(card,
|
||||
@ -329,6 +337,9 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
|
||||
struct pnp_dev *pdev;
|
||||
int err;
|
||||
|
||||
/* CMI8329 has a device with ID A@@0001, CMI8330 does not */
|
||||
acard->type = (id->devs[3].id[0]) ? CMI8329 : CMI8330;
|
||||
|
||||
acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL);
|
||||
if (acard->cap == NULL)
|
||||
return -EBUSY;
|
||||
@ -345,38 +356,45 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
|
||||
|
||||
err = pnp_activate_dev(pdev);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "CMI8330/C3D PnP configure failure\n");
|
||||
snd_printk(KERN_ERR "AD1848 PnP configure failure\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
wssport[dev] = pnp_port_start(pdev, 0);
|
||||
wssdma[dev] = pnp_dma(pdev, 0);
|
||||
wssirq[dev] = pnp_irq(pdev, 0);
|
||||
fmport[dev] = pnp_port_start(pdev, 1);
|
||||
if (pnp_port_start(pdev, 1))
|
||||
fmport[dev] = pnp_port_start(pdev, 1);
|
||||
|
||||
/* allocate SB16 resources */
|
||||
pdev = acard->play;
|
||||
|
||||
err = pnp_activate_dev(pdev);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n");
|
||||
snd_printk(KERN_ERR "SB16 PnP configure failure\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
sbport[dev] = pnp_port_start(pdev, 0);
|
||||
sbdma8[dev] = pnp_dma(pdev, 0);
|
||||
sbdma16[dev] = pnp_dma(pdev, 1);
|
||||
sbirq[dev] = pnp_irq(pdev, 0);
|
||||
/* On CMI8239, the OPL3 port might be present in SB16 PnP resources */
|
||||
if (fmport[dev] == SNDRV_AUTO_PORT) {
|
||||
if (pnp_port_start(pdev, 1))
|
||||
fmport[dev] = pnp_port_start(pdev, 1);
|
||||
else
|
||||
fmport[dev] = 0x388; /* Or hardwired */
|
||||
}
|
||||
|
||||
/* allocate MPU-401 resources */
|
||||
pdev = acard->mpu;
|
||||
|
||||
err = pnp_activate_dev(pdev);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR
|
||||
"CMI8330/C3D (MPU-401) PnP configure failure\n");
|
||||
return -EBUSY;
|
||||
if (err < 0)
|
||||
snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n");
|
||||
else {
|
||||
mpuport[dev] = pnp_port_start(pdev, 0);
|
||||
mpuirq[dev] = pnp_irq(pdev, 0);
|
||||
}
|
||||
mpuport[dev] = pnp_port_start(pdev, 0);
|
||||
mpuirq[dev] = pnp_irq(pdev, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -430,9 +448,9 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *
|
||||
snd_cmi8330_capture_open
|
||||
};
|
||||
|
||||
if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0)
|
||||
if ((err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm)) < 0)
|
||||
return err;
|
||||
strcpy(pcm->name, "CMI8330");
|
||||
strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
|
||||
pcm->private_data = chip;
|
||||
|
||||
/* SB16 */
|
||||
@ -527,11 +545,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
|
||||
wssdma[dev], -1,
|
||||
WSS_HW_DETECT, 0, &acard->wss);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR PFX "(CMI8330) device busy??\n");
|
||||
snd_printk(KERN_ERR PFX "AD1848 device busy??\n");
|
||||
return err;
|
||||
}
|
||||
if (acard->wss->hardware != WSS_HW_CMI8330) {
|
||||
snd_printk(KERN_ERR PFX "(CMI8330) not found during probe\n");
|
||||
snd_printk(KERN_ERR PFX "AD1848 not found during probe\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -541,11 +559,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
|
||||
sbdma8[dev],
|
||||
sbdma16[dev],
|
||||
SB_HW_AUTO, &acard->sb)) < 0) {
|
||||
snd_printk(KERN_ERR PFX "(SB16) device busy??\n");
|
||||
snd_printk(KERN_ERR PFX "SB16 device busy??\n");
|
||||
return err;
|
||||
}
|
||||
if (acard->sb->hardware != SB_HW_16) {
|
||||
snd_printk(KERN_ERR PFX "(SB16) not found during probe\n");
|
||||
snd_printk(KERN_ERR PFX "SB16 not found during probe\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -585,8 +603,8 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
|
||||
mpuport[dev]);
|
||||
}
|
||||
|
||||
strcpy(card->driver, "CMI8330/C3D");
|
||||
strcpy(card->shortname, "C-Media CMI8330/C3D");
|
||||
strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
|
||||
strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
|
||||
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
|
||||
card->shortname,
|
||||
acard->wss->port,
|
||||
|
@ -127,15 +127,16 @@ static void midi_poll(unsigned long dummy)
|
||||
for (dev = 0; dev < num_midis; dev++)
|
||||
if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
|
||||
{
|
||||
int ok = 1;
|
||||
|
||||
while (DATA_AVAIL(midi_out_buf[dev]) && ok)
|
||||
while (DATA_AVAIL(midi_out_buf[dev]))
|
||||
{
|
||||
int ok;
|
||||
int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
|
||||
|
||||
spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
|
||||
ok = midi_devs[dev]->outputc(dev, c);
|
||||
spin_lock_irqsave(&lock, flags);
|
||||
if (!ok)
|
||||
break;
|
||||
midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
|
||||
midi_out_buf[dev]->len--;
|
||||
}
|
||||
|
@ -628,7 +628,7 @@ static void li_setup_dma(dma_chan_t *chan,
|
||||
ASSERT(!(buffer_paddr & 0xFF));
|
||||
chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
|
||||
|
||||
chan->cfgval = (!LI_CCFG_LOCK |
|
||||
chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) |
|
||||
SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
|
||||
desc->direction |
|
||||
mode |
|
||||
@ -638,9 +638,9 @@ static void li_setup_dma(dma_chan_t *chan,
|
||||
tmask = 13 - fragshift; /* See Lithium DMA Notes above. */
|
||||
ASSERT(size >= 2 && size <= 7);
|
||||
ASSERT(tmask >= 1 && tmask <= 7);
|
||||
chan->ctlval = (!LI_CCTL_RESET |
|
||||
chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) |
|
||||
SHIFT_FIELD(size, LI_CCTL_SIZE) |
|
||||
!LI_CCTL_DMA_ENABLE |
|
||||
(chan->ctlval & ~LI_CCTL_DMA_ENABLE) |
|
||||
SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
|
||||
SHIFT_FIELD(0, LI_CCTL_TPTR));
|
||||
|
||||
|
@ -135,11 +135,11 @@ config SND_AW2
|
||||
|
||||
|
||||
config SND_AZT3328
|
||||
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
tristate "Aztech AZF3328 / PCI168"
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_RAWMIDI
|
||||
help
|
||||
Say Y here to include support for Aztech AZF3328 (PCI168)
|
||||
soundcards.
|
||||
|
@ -478,45 +478,6 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CODEC_RESET
|
||||
|
||||
static int snd_ali_reset_codec(struct snd_ali *codec)
|
||||
{
|
||||
struct pci_dev *pci_dev;
|
||||
unsigned char bVal;
|
||||
unsigned int dwVal;
|
||||
unsigned short wCount, wReg;
|
||||
|
||||
pci_dev = codec->pci_m1533;
|
||||
|
||||
pci_read_config_dword(pci_dev, 0x7c, &dwVal);
|
||||
pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
|
||||
udelay(5000);
|
||||
pci_read_config_dword(pci_dev, 0x7c, &dwVal);
|
||||
pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
|
||||
udelay(5000);
|
||||
|
||||
bVal = inb(ALI_REG(codec,ALI_SCTRL));
|
||||
bVal |= 0x02;
|
||||
outb(ALI_REG(codec,ALI_SCTRL),bVal);
|
||||
udelay(5000);
|
||||
bVal = inb(ALI_REG(codec,ALI_SCTRL));
|
||||
bVal &= 0xfd;
|
||||
outb(ALI_REG(codec,ALI_SCTRL),bVal);
|
||||
udelay(15000);
|
||||
|
||||
wCount = 200;
|
||||
while (wCount--) {
|
||||
wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
|
||||
if ((wReg & 0x000f) == 0x000f)
|
||||
return 0;
|
||||
udelay(5000);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ALI 5451 Controller
|
||||
*/
|
||||
@ -561,22 +522,6 @@ static void snd_ali_disable_address_interrupt(struct snd_ali *codec)
|
||||
outl(gc, ALI_REG(codec, ALI_GC_CIR));
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
static void snd_ali_enable_voice_irq(struct snd_ali *codec,
|
||||
unsigned int channel)
|
||||
{
|
||||
unsigned int mask;
|
||||
struct snd_ali_channel_control *pchregs = &(codec->chregs);
|
||||
|
||||
snd_ali_printk("enable_voice_irq channel=%d\n",channel);
|
||||
|
||||
mask = 1 << (channel & 0x1f);
|
||||
pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten));
|
||||
pchregs->data.ainten |= mask;
|
||||
outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void snd_ali_disable_voice_irq(struct snd_ali *codec,
|
||||
unsigned int channel)
|
||||
{
|
||||
@ -677,16 +622,6 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel)
|
||||
{
|
||||
unsigned int mask = 1 << (channel & 0x1f);
|
||||
|
||||
snd_ali_printk("start_voice: channel=%d\n",channel);
|
||||
outl(mask, ALI_REG(codec,codec->chregs.regs.start));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
|
||||
{
|
||||
unsigned int mask = 1 << (channel & 0x1f);
|
||||
|
1110
sound/pci/azt3328.c
1110
sound/pci/azt3328.c
File diff suppressed because it is too large
Load Diff
@ -6,50 +6,59 @@
|
||||
|
||||
/*** main I/O area port indices ***/
|
||||
/* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
|
||||
#define AZF_IO_SIZE_CODEC 0x80
|
||||
#define AZF_IO_SIZE_CODEC_PM 0x70
|
||||
#define AZF_IO_SIZE_CTRL 0x80
|
||||
#define AZF_IO_SIZE_CTRL_PM 0x70
|
||||
|
||||
/* the driver initialisation suggests a layout of 4 main areas:
|
||||
* from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??).
|
||||
/* the driver initialisation suggests a layout of 4 areas
|
||||
* within the main card control I/O:
|
||||
* from 0x00 (playback codec), from 0x20 (recording codec)
|
||||
* and from 0x40 (most certainly I2S out codec).
|
||||
* And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
|
||||
* power management etc.???). */
|
||||
|
||||
/** playback area **/
|
||||
#define IDX_IO_PLAY_FLAGS 0x00 /* PU:0x0000 */
|
||||
#define AZF_IO_OFFS_CODEC_PLAYBACK 0x00
|
||||
#define AZF_IO_OFFS_CODEC_CAPTURE 0x20
|
||||
#define AZF_IO_OFFS_CODEC_I2S_OUT 0x40
|
||||
|
||||
#define IDX_IO_CODEC_DMA_FLAGS 0x00 /* PU:0x0000 */
|
||||
/* able to reactivate output after output muting due to 8/16bit
|
||||
* output change, just like 0x0002.
|
||||
* 0x0001 is the only bit that's able to start the DMA counter */
|
||||
#define DMA_RESUME 0x0001 /* paused if cleared ? */
|
||||
#define DMA_RESUME 0x0001 /* paused if cleared? */
|
||||
/* 0x0002 *temporarily* set during DMA stopping. hmm
|
||||
* both 0x0002 and 0x0004 set in playback setup. */
|
||||
/* able to reactivate output after output muting due to 8/16bit
|
||||
* output change, just like 0x0001. */
|
||||
#define DMA_PLAY_SOMETHING1 0x0002 /* \ alternated (toggled) */
|
||||
#define DMA_RUN_SOMETHING1 0x0002 /* \ alternated (toggled) */
|
||||
/* 0x0004: NOT able to reactivate output */
|
||||
#define DMA_PLAY_SOMETHING2 0x0004 /* / bits */
|
||||
#define DMA_RUN_SOMETHING2 0x0004 /* / bits */
|
||||
#define SOMETHING_ALMOST_ALWAYS_SET 0x0008 /* ???; can be modified */
|
||||
#define DMA_EPILOGUE_SOMETHING 0x0010
|
||||
#define DMA_SOMETHING_ELSE 0x0020 /* ??? */
|
||||
#define SOMETHING_UNMODIFIABLE 0xffc0 /* unused ? not modifiable */
|
||||
#define IDX_IO_PLAY_IRQTYPE 0x02 /* PU:0x0001 */
|
||||
#define SOMETHING_UNMODIFIABLE 0xffc0 /* unused? not modifiable */
|
||||
#define IDX_IO_CODEC_IRQTYPE 0x02 /* PU:0x0001 */
|
||||
/* write back to flags in case flags are set, in order to ACK IRQ in handler
|
||||
* (bit 1 of port 0x64 indicates interrupt for one of these three types)
|
||||
* sometimes in this case it just writes 0xffff to globally ACK all IRQs
|
||||
* settings written are not reflected when reading back, though.
|
||||
* seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */
|
||||
#define IRQ_PLAY_SOMETHING 0x0001 /* something & ACK */
|
||||
#define IRQ_FINISHED_PLAYBUF_1 0x0002 /* 1st dmabuf finished & ACK */
|
||||
#define IRQ_FINISHED_PLAYBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
|
||||
* seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */
|
||||
#define IRQ_SOMETHING 0x0001 /* something & ACK */
|
||||
#define IRQ_FINISHED_DMABUF_1 0x0002 /* 1st dmabuf finished & ACK */
|
||||
#define IRQ_FINISHED_DMABUF_2 0x0004 /* 2nd dmabuf finished & ACK */
|
||||
#define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
|
||||
#define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
|
||||
#define IRQMASK_UNMODIFIABLE 0xffe0 /* unused ? not modifiable */
|
||||
#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */
|
||||
#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */
|
||||
#define IDX_IO_PLAY_DMA_LEN_1 0x0c /* length of 1st DMA play area, PU:0x0000 */
|
||||
#define IDX_IO_PLAY_DMA_LEN_2 0x0e /* length of 2nd DMA play area, PU:0x0000 */
|
||||
#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
|
||||
#define IDX_IO_PLAY_DMA_CURROFS 0x14 /* offset within current DMA play area, PU:0x0000 */
|
||||
#define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */
|
||||
#define IRQMASK_UNMODIFIABLE 0xffe0 /* unused? not modifiable */
|
||||
/* start address of 1st DMA transfer area, PU:0x00000000 */
|
||||
#define IDX_IO_CODEC_DMA_START_1 0x04
|
||||
/* start address of 2nd DMA transfer area, PU:0x00000000 */
|
||||
#define IDX_IO_CODEC_DMA_START_2 0x08
|
||||
/* both lengths of DMA transfer areas, PU:0x00000000
|
||||
length1: offset 0x0c, length2: offset 0x0e */
|
||||
#define IDX_IO_CODEC_DMA_LENGTHS 0x0c
|
||||
#define IDX_IO_CODEC_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
|
||||
/* offset within current DMA transfer area, PU:0x0000 */
|
||||
#define IDX_IO_CODEC_DMA_CURROFS 0x14
|
||||
#define IDX_IO_CODEC_SOUNDFORMAT 0x16 /* PU:0x0010 */
|
||||
/* all unspecified bits can't be modified */
|
||||
#define SOUNDFORMAT_FREQUENCY_MASK 0x000f
|
||||
#define SOUNDFORMAT_XTAL1 0x00
|
||||
@ -76,6 +85,7 @@
|
||||
#define SOUNDFORMAT_FLAG_16BIT 0x0010
|
||||
#define SOUNDFORMAT_FLAG_2CHANNELS 0x0020
|
||||
|
||||
|
||||
/* define frequency helpers, for maximum value safety */
|
||||
enum azf_freq_t {
|
||||
#define AZF_FREQ(rate) AZF_FREQ_##rate = rate
|
||||
@ -96,29 +106,6 @@ enum azf_freq_t {
|
||||
#undef AZF_FREQ
|
||||
};
|
||||
|
||||
/** recording area (see also: playback bit flag definitions) **/
|
||||
#define IDX_IO_REC_FLAGS 0x20 /* ??, PU:0x0000 */
|
||||
#define IDX_IO_REC_IRQTYPE 0x22 /* ??, PU:0x0000 */
|
||||
#define IRQ_REC_SOMETHING 0x0001 /* something & ACK */
|
||||
#define IRQ_FINISHED_RECBUF_1 0x0002 /* 1st dmabuf finished & ACK */
|
||||
#define IRQ_FINISHED_RECBUF_2 0x0004 /* 2nd dmabuf finished & ACK */
|
||||
/* hmm, maybe these are just the corresponding *recording* flags ?
|
||||
* but OTOH they are most likely at port 0x22 instead */
|
||||
#define IRQMASK_SOME_STATUS_1 0x0008 /* \ related bits */
|
||||
#define IRQMASK_SOME_STATUS_2 0x0010 /* / (checked together in loop) */
|
||||
#define IDX_IO_REC_DMA_START_1 0x24 /* PU:0x00000000 */
|
||||
#define IDX_IO_REC_DMA_START_2 0x28 /* PU:0x00000000 */
|
||||
#define IDX_IO_REC_DMA_LEN_1 0x2c /* PU:0x0000 */
|
||||
#define IDX_IO_REC_DMA_LEN_2 0x2e /* PU:0x0000 */
|
||||
#define IDX_IO_REC_DMA_CURRPOS 0x30 /* PU:0x00000000 */
|
||||
#define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */
|
||||
#define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */
|
||||
|
||||
/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/
|
||||
#define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */
|
||||
/* general */
|
||||
#define IDX_IO_42H 0x42 /* PU:0x0001 */
|
||||
|
||||
/** DirectX timer, main interrupt area (FIXME: and something else?) **/
|
||||
#define IDX_IO_TIMER_VALUE 0x60 /* found this timer area by pure luck :-) */
|
||||
/* timer countdown value; triggers IRQ when timer is finished */
|
||||
@ -133,17 +120,19 @@ enum azf_freq_t {
|
||||
#define IDX_IO_IRQSTATUS 0x64
|
||||
/* some IRQ bit in here might also be used to signal a power-management timer
|
||||
* timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
|
||||
* Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
|
||||
* can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
|
||||
* OPL3 hardware contains several timers which confusingly in most cases
|
||||
* are NOT routed to an IRQ, but some designs (e.g. LM4560) DO support that,
|
||||
* so I wouldn't be surprised at all to discover that AZF3328
|
||||
* supports that thing as well... */
|
||||
|
||||
#define IRQ_PLAYBACK 0x0001
|
||||
#define IRQ_RECORDING 0x0002
|
||||
#define IRQ_UNKNOWN1 0x0004 /* most probably I2S port */
|
||||
#define IRQ_I2S_OUT 0x0004 /* this IS I2S, right!? (untested) */
|
||||
#define IRQ_GAMEPORT 0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
|
||||
#define IRQ_MPU401 0x0010
|
||||
#define IRQ_TIMER 0x0020 /* DirectX timer */
|
||||
#define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly I2S port? */
|
||||
#define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly I2S port? */
|
||||
#define IRQ_UNKNOWN2 0x0040 /* probably unused, or possibly OPL3 timer? */
|
||||
#define IRQ_UNKNOWN3 0x0080 /* probably unused, or possibly OPL3 timer? */
|
||||
#define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */
|
||||
/* this is set to e.g. 0x3ff or 0x300, and writable;
|
||||
* maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
|
||||
@ -206,7 +195,7 @@ enum azf_freq_t {
|
||||
/*** Gameport area port indices ***/
|
||||
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
|
||||
#define AZF_IO_SIZE_GAME 0x08
|
||||
#define AZF_IO_SIZE_GAME_PM 0x06
|
||||
#define AZF_IO_SIZE_GAME_PM 0x06
|
||||
|
||||
enum {
|
||||
AZF_GAME_LEGACY_IO_PORT = 0x200
|
||||
@ -272,6 +261,12 @@ enum {
|
||||
* 11 --> 1/200: */
|
||||
#define GAME_HWCFG_ADC_COUNTER_FREQ_MASK 0x06
|
||||
|
||||
/* FIXME: these values might be reversed... */
|
||||
#define GAME_HWCFG_ADC_COUNTER_FREQ_STD 0
|
||||
#define GAME_HWCFG_ADC_COUNTER_FREQ_1_2 1
|
||||
#define GAME_HWCFG_ADC_COUNTER_FREQ_1_20 2
|
||||
#define GAME_HWCFG_ADC_COUNTER_FREQ_1_200 3
|
||||
|
||||
/* enable gameport legacy I/O address (0x200)
|
||||
* I was unable to locate any configurability for a different address: */
|
||||
#define GAME_HWCFG_LEGACY_ADDRESS_ENABLE 0x08
|
||||
@ -281,6 +276,7 @@ enum {
|
||||
#define AZF_IO_SIZE_MPU_PM 0x04
|
||||
|
||||
/*** OPL3 synth ***/
|
||||
/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
|
||||
#define AZF_IO_SIZE_OPL3 0x08
|
||||
#define AZF_IO_SIZE_OPL3_PM 0x06
|
||||
/* hmm, given that a standard OPL3 has 4 registers only,
|
||||
@ -340,4 +336,7 @@ enum {
|
||||
#define SET_CHAN_LEFT 1
|
||||
#define SET_CHAN_RIGHT 2
|
||||
|
||||
/* helper macro to align I/O port ranges to 32bit I/O width */
|
||||
#define AZF_ALIGN(x) (((x) + 3) & (~3))
|
||||
|
||||
#endif /* __SOUND_AZT3328_H */
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
|
||||
#ifdef CONFIG_SND_CS46XX_NEW_DSP
|
||||
#define CS46XX_MIN_PERIOD_SIZE 1
|
||||
#define CS46XX_MIN_PERIOD_SIZE 64
|
||||
#define CS46XX_MAX_PERIOD_SIZE 1024*1024
|
||||
#else
|
||||
#define CS46XX_MIN_PERIOD_SIZE 2048
|
||||
|
@ -11,9 +11,12 @@
|
||||
|
||||
|
||||
/* Timer Registers */
|
||||
#define TIMER_TIMR 0x1B7004
|
||||
#define INTERRUPT_GIP 0x1B7010
|
||||
#define INTERRUPT_GIE 0x1B7014
|
||||
#define WC 0x1b7000
|
||||
#define TIMR 0x1b7004
|
||||
# define TIMR_IE (1<<15)
|
||||
# define TIMR_IP (1<<14)
|
||||
#define GIP 0x1b7010
|
||||
#define GIE 0x1b7014
|
||||
|
||||
/* I2C Registers */
|
||||
#define I2C_IF_ADDRESS 0x1B9000
|
||||
|
@ -63,7 +63,7 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
|
||||
amixer->input = rsc;
|
||||
if (NULL == rsc)
|
||||
if (!rsc)
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
|
||||
else
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk,
|
||||
@ -99,7 +99,7 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
amixer->sum = sum;
|
||||
if (NULL == sum) {
|
||||
if (!sum) {
|
||||
hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
|
||||
} else {
|
||||
hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
|
||||
@ -124,20 +124,20 @@ static int amixer_commit_write(struct amixer *amixer)
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
amixer->rsc.ops->master(&amixer->rsc);
|
||||
if (NULL != input)
|
||||
if (input)
|
||||
input->ops->master(input);
|
||||
|
||||
if (NULL != sum)
|
||||
if (sum)
|
||||
sum->rsc.ops->master(&sum->rsc);
|
||||
|
||||
for (i = 0; i < amixer->rsc.msr; i++) {
|
||||
hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
|
||||
if (NULL != input) {
|
||||
if (input) {
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk,
|
||||
input->ops->output_slot(input));
|
||||
input->ops->next_conj(input);
|
||||
}
|
||||
if (NULL != sum) {
|
||||
if (sum) {
|
||||
hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
|
||||
sum->rsc.ops->index(&sum->rsc));
|
||||
sum->rsc.ops->next_conj(&sum->rsc);
|
||||
@ -147,10 +147,10 @@ static int amixer_commit_write(struct amixer *amixer)
|
||||
amixer->rsc.ops->next_conj(&amixer->rsc);
|
||||
}
|
||||
amixer->rsc.ops->master(&amixer->rsc);
|
||||
if (NULL != input)
|
||||
if (input)
|
||||
input->ops->master(input);
|
||||
|
||||
if (NULL != sum)
|
||||
if (sum)
|
||||
sum->rsc.ops->master(&sum->rsc);
|
||||
|
||||
return 0;
|
||||
@ -303,7 +303,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
|
||||
|
||||
*ramixer_mgr = NULL;
|
||||
amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
|
||||
if (NULL == amixer_mgr)
|
||||
if (!amixer_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
|
||||
@ -456,7 +456,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
|
||||
|
||||
*rsum_mgr = NULL;
|
||||
sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
|
||||
if (NULL == sum_mgr)
|
||||
if (!sum_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
|
||||
|
@ -136,7 +136,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct ct_vm *vm;
|
||||
|
||||
if (NULL == apcm->substream)
|
||||
if (!apcm->substream)
|
||||
return 0;
|
||||
|
||||
runtime = apcm->substream->runtime;
|
||||
@ -144,7 +144,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
|
||||
apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes);
|
||||
|
||||
if (NULL == apcm->vm_block)
|
||||
if (!apcm->vm_block)
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
@ -154,7 +154,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
{
|
||||
struct ct_vm *vm;
|
||||
|
||||
if (NULL == apcm->vm_block)
|
||||
if (!apcm->vm_block)
|
||||
return;
|
||||
|
||||
vm = atc->vm;
|
||||
@ -231,16 +231,16 @@ atc_get_pitch(unsigned int input_rate, unsigned int output_rate)
|
||||
|
||||
static int select_rom(unsigned int pitch)
|
||||
{
|
||||
if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) {
|
||||
if (pitch > 0x00428f5c && pitch < 0x01b851ec) {
|
||||
/* 0.26 <= pitch <= 1.72 */
|
||||
return 1;
|
||||
} else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) {
|
||||
} else if (pitch == 0x01d66666 || pitch == 0x01d66667) {
|
||||
/* pitch == 1.8375 */
|
||||
return 2;
|
||||
} else if (0x02000000 == pitch) {
|
||||
} else if (pitch == 0x02000000) {
|
||||
/* pitch == 2 */
|
||||
return 3;
|
||||
} else if ((pitch >= 0x0) && (pitch <= 0x08000000)) {
|
||||
} else if (pitch >= 0x0 && pitch <= 0x08000000) {
|
||||
/* 0 <= pitch <= 8 */
|
||||
return 0;
|
||||
} else {
|
||||
@ -283,7 +283,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
/* Get AMIXER resource */
|
||||
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
|
||||
apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
|
||||
if (NULL == apcm->amixers) {
|
||||
if (!apcm->amixers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
@ -311,7 +311,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
INIT_VOL, atc->pcm[i+device*2]);
|
||||
mutex_unlock(&atc->atc_mutex);
|
||||
src = src->ops->next_interleave(src);
|
||||
if (NULL == src)
|
||||
if (!src)
|
||||
src = apcm->src;
|
||||
}
|
||||
|
||||
@ -334,7 +334,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
struct srcimp *srcimp;
|
||||
int i;
|
||||
|
||||
if (NULL != apcm->srcimps) {
|
||||
if (apcm->srcimps) {
|
||||
for (i = 0; i < apcm->n_srcimp; i++) {
|
||||
srcimp = apcm->srcimps[i];
|
||||
srcimp->ops->unmap(srcimp);
|
||||
@ -345,7 +345,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
apcm->srcimps = NULL;
|
||||
}
|
||||
|
||||
if (NULL != apcm->srccs) {
|
||||
if (apcm->srccs) {
|
||||
for (i = 0; i < apcm->n_srcc; i++) {
|
||||
src_mgr->put_src(src_mgr, apcm->srccs[i]);
|
||||
apcm->srccs[i] = NULL;
|
||||
@ -354,7 +354,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
apcm->srccs = NULL;
|
||||
}
|
||||
|
||||
if (NULL != apcm->amixers) {
|
||||
if (apcm->amixers) {
|
||||
for (i = 0; i < apcm->n_amixer; i++) {
|
||||
amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
|
||||
apcm->amixers[i] = NULL;
|
||||
@ -363,17 +363,17 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
apcm->amixers = NULL;
|
||||
}
|
||||
|
||||
if (NULL != apcm->mono) {
|
||||
if (apcm->mono) {
|
||||
sum_mgr->put_sum(sum_mgr, apcm->mono);
|
||||
apcm->mono = NULL;
|
||||
}
|
||||
|
||||
if (NULL != apcm->src) {
|
||||
if (apcm->src) {
|
||||
src_mgr->put_src(src_mgr, apcm->src);
|
||||
apcm->src = NULL;
|
||||
}
|
||||
|
||||
if (NULL != apcm->vm_block) {
|
||||
if (apcm->vm_block) {
|
||||
/* Undo device virtual mem map */
|
||||
ct_unmap_audio_buffer(atc, apcm);
|
||||
apcm->vm_block = NULL;
|
||||
@ -419,7 +419,7 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
src->ops->set_state(src, SRC_STATE_OFF);
|
||||
src->ops->commit_write(src);
|
||||
|
||||
if (NULL != apcm->srccs) {
|
||||
if (apcm->srccs) {
|
||||
for (i = 0; i < apcm->n_srcc; i++) {
|
||||
src = apcm->srccs[i];
|
||||
src->ops->set_bm(src, 0);
|
||||
@ -544,18 +544,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
|
||||
if (n_srcc) {
|
||||
apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
|
||||
if (NULL == apcm->srccs)
|
||||
if (!apcm->srccs)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (n_amixer) {
|
||||
apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
|
||||
if (NULL == apcm->amixers) {
|
||||
if (!apcm->amixers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
}
|
||||
apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
|
||||
if (NULL == apcm->srcimps) {
|
||||
if (!apcm->srcimps) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
@ -818,7 +818,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
|
||||
/* Get AMIXER resource */
|
||||
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
|
||||
apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
|
||||
if (NULL == apcm->amixers) {
|
||||
if (!apcm->amixers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
@ -919,7 +919,7 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
|
||||
amixer = apcm->amixers[i];
|
||||
amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
|
||||
src = src->ops->next_interleave(src);
|
||||
if (NULL == src)
|
||||
if (!src)
|
||||
src = apcm->src;
|
||||
}
|
||||
/* Connect to SPDIFOO */
|
||||
@ -1121,7 +1121,7 @@ static int atc_release_resources(struct ct_atc *atc)
|
||||
struct ct_mixer *mixer = NULL;
|
||||
|
||||
/* disconnect internal mixer objects */
|
||||
if (NULL != atc->mixer) {
|
||||
if (atc->mixer) {
|
||||
mixer = atc->mixer;
|
||||
mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
|
||||
mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
|
||||
@ -1131,7 +1131,7 @@ static int atc_release_resources(struct ct_atc *atc)
|
||||
mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
|
||||
}
|
||||
|
||||
if (NULL != atc->daios) {
|
||||
if (atc->daios) {
|
||||
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
|
||||
for (i = 0; i < atc->n_daio; i++) {
|
||||
daio = atc->daios[i];
|
||||
@ -1149,7 +1149,7 @@ static int atc_release_resources(struct ct_atc *atc)
|
||||
atc->daios = NULL;
|
||||
}
|
||||
|
||||
if (NULL != atc->pcm) {
|
||||
if (atc->pcm) {
|
||||
sum_mgr = atc->rsc_mgrs[SUM];
|
||||
for (i = 0; i < atc->n_pcm; i++)
|
||||
sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
|
||||
@ -1158,7 +1158,7 @@ static int atc_release_resources(struct ct_atc *atc)
|
||||
atc->pcm = NULL;
|
||||
}
|
||||
|
||||
if (NULL != atc->srcs) {
|
||||
if (atc->srcs) {
|
||||
src_mgr = atc->rsc_mgrs[SRC];
|
||||
for (i = 0; i < atc->n_src; i++)
|
||||
src_mgr->put_src(src_mgr, atc->srcs[i]);
|
||||
@ -1167,7 +1167,7 @@ static int atc_release_resources(struct ct_atc *atc)
|
||||
atc->srcs = NULL;
|
||||
}
|
||||
|
||||
if (NULL != atc->srcimps) {
|
||||
if (atc->srcimps) {
|
||||
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
|
||||
for (i = 0; i < atc->n_srcimp; i++) {
|
||||
srcimp = atc->srcimps[i];
|
||||
@ -1185,7 +1185,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (NULL == atc)
|
||||
if (!atc)
|
||||
return 0;
|
||||
|
||||
if (atc->timer) {
|
||||
@ -1196,21 +1196,20 @@ static int ct_atc_destroy(struct ct_atc *atc)
|
||||
atc_release_resources(atc);
|
||||
|
||||
/* Destroy internal mixer objects */
|
||||
if (NULL != atc->mixer)
|
||||
if (atc->mixer)
|
||||
ct_mixer_destroy(atc->mixer);
|
||||
|
||||
for (i = 0; i < NUM_RSCTYP; i++) {
|
||||
if ((NULL != rsc_mgr_funcs[i].destroy) &&
|
||||
(NULL != atc->rsc_mgrs[i]))
|
||||
if (rsc_mgr_funcs[i].destroy && atc->rsc_mgrs[i])
|
||||
rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
|
||||
|
||||
}
|
||||
|
||||
if (NULL != atc->hw)
|
||||
if (atc->hw)
|
||||
destroy_hw_obj((struct hw *)atc->hw);
|
||||
|
||||
/* Destroy device virtual memory manager object */
|
||||
if (NULL != atc->vm) {
|
||||
if (atc->vm) {
|
||||
ct_vm_destroy(atc->vm);
|
||||
atc->vm = NULL;
|
||||
}
|
||||
@ -1275,7 +1274,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
|
||||
alsa_dev_funcs[MIXER].public_name = atc->chip_name;
|
||||
|
||||
for (i = 0; i < NUM_CTALSADEVS; i++) {
|
||||
if (NULL == alsa_dev_funcs[i].create)
|
||||
if (!alsa_dev_funcs[i].create)
|
||||
continue;
|
||||
|
||||
err = alsa_dev_funcs[i].create(atc, i,
|
||||
@ -1312,7 +1311,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < NUM_RSCTYP; i++) {
|
||||
if (NULL == rsc_mgr_funcs[i].create)
|
||||
if (!rsc_mgr_funcs[i].create)
|
||||
continue;
|
||||
|
||||
err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
|
||||
@ -1339,19 +1338,19 @@ static int atc_get_resources(struct ct_atc *atc)
|
||||
int err, i;
|
||||
|
||||
atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
|
||||
if (NULL == atc->daios)
|
||||
if (!atc->daios)
|
||||
return -ENOMEM;
|
||||
|
||||
atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
|
||||
if (NULL == atc->srcs)
|
||||
if (!atc->srcs)
|
||||
return -ENOMEM;
|
||||
|
||||
atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
|
||||
if (NULL == atc->srcimps)
|
||||
if (!atc->srcimps)
|
||||
return -ENOMEM;
|
||||
|
||||
atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
|
||||
if (NULL == atc->pcm)
|
||||
if (!atc->pcm)
|
||||
return -ENOMEM;
|
||||
|
||||
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
|
||||
@ -1648,7 +1647,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
|
||||
*ratc = NULL;
|
||||
|
||||
atc = kzalloc(sizeof(*atc), GFP_KERNEL);
|
||||
if (NULL == atc)
|
||||
if (!atc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set operations */
|
||||
|
@ -173,7 +173,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
|
||||
int i;
|
||||
|
||||
entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
|
||||
if (NULL == entry)
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
@ -201,7 +201,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
|
||||
int i;
|
||||
|
||||
entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
|
||||
if (NULL == entry)
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
@ -228,7 +228,7 @@ static int dao_clear_left_input(struct dao *dao)
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (NULL == dao->imappers[0])
|
||||
if (!dao->imappers[0])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[0];
|
||||
@ -252,7 +252,7 @@ static int dao_clear_right_input(struct dao *dao)
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (NULL == dao->imappers[daio->rscl.msr])
|
||||
if (!dao->imappers[daio->rscl.msr])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[daio->rscl.msr];
|
||||
@ -408,7 +408,7 @@ static int dao_rsc_init(struct dao *dao,
|
||||
return err;
|
||||
|
||||
dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
|
||||
if (NULL == dao->imappers) {
|
||||
if (!dao->imappers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
@ -442,11 +442,11 @@ error1:
|
||||
|
||||
static int dao_rsc_uninit(struct dao *dao)
|
||||
{
|
||||
if (NULL != dao->imappers) {
|
||||
if (NULL != dao->imappers[0])
|
||||
if (dao->imappers) {
|
||||
if (dao->imappers[0])
|
||||
dao_clear_left_input(dao);
|
||||
|
||||
if (NULL != dao->imappers[dao->daio.rscl.msr])
|
||||
if (dao->imappers[dao->daio.rscl.msr])
|
||||
dao_clear_right_input(dao);
|
||||
|
||||
kfree(dao->imappers);
|
||||
@ -555,7 +555,7 @@ static int get_daio_rsc(struct daio_mgr *mgr,
|
||||
/* Allocate mem for daio resource */
|
||||
if (desc->type <= DAIO_OUT_MAX) {
|
||||
dao = kzalloc(sizeof(*dao), GFP_KERNEL);
|
||||
if (NULL == dao) {
|
||||
if (!dao) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
@ -566,7 +566,7 @@ static int get_daio_rsc(struct daio_mgr *mgr,
|
||||
*rdaio = &dao->daio;
|
||||
} else {
|
||||
dai = kzalloc(sizeof(*dai), GFP_KERNEL);
|
||||
if (NULL == dai) {
|
||||
if (!dai) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
@ -583,9 +583,9 @@ static int get_daio_rsc(struct daio_mgr *mgr,
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (NULL != dao)
|
||||
if (dao)
|
||||
kfree(dao);
|
||||
else if (NULL != dai)
|
||||
else if (dai)
|
||||
kfree(dai);
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
@ -663,7 +663,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
if ((0 == entry->addr) && (mgr->init_imap_added)) {
|
||||
if (!entry->addr && mgr->init_imap_added) {
|
||||
input_mapper_delete(&mgr->imappers, mgr->init_imap,
|
||||
daio_map_op, mgr);
|
||||
mgr->init_imap_added = 0;
|
||||
@ -707,7 +707,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
|
||||
|
||||
*rdaio_mgr = NULL;
|
||||
daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
|
||||
if (NULL == daio_mgr)
|
||||
if (!daio_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
|
||||
@ -718,7 +718,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
|
||||
spin_lock_init(&daio_mgr->imap_lock);
|
||||
INIT_LIST_HEAD(&daio_mgr->imappers);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (NULL == entry) {
|
||||
if (!entry) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -723,7 +723,7 @@ static int amixer_mgr_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
/*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;*/
|
||||
@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -1152,7 +1152,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
blk->i2sctl = hw_read_20kx(hw, I2SCTL);
|
||||
@ -1808,7 +1808,7 @@ static int uaa_to_xfi(struct pci_dev *pci)
|
||||
/* By default, Hendrix card UAA Bar0 should be using memory... */
|
||||
io_base = pci_resource_start(pci, 0);
|
||||
mem_base = ioremap(io_base, pci_resource_len(pci, 0));
|
||||
if (NULL == mem_base)
|
||||
if (!mem_base)
|
||||
return -ENOENT;
|
||||
|
||||
/* Read current mode from Mode Change Register */
|
||||
@ -1977,7 +1977,7 @@ static int hw_card_shutdown(struct hw *hw)
|
||||
|
||||
hw->irq = -1;
|
||||
|
||||
if (NULL != ((void *)hw->mem_base))
|
||||
if (hw->mem_base)
|
||||
iounmap((void *)hw->mem_base);
|
||||
|
||||
hw->mem_base = (unsigned long)NULL;
|
||||
@ -2274,7 +2274,7 @@ int __devinit create_20k1_hw_obj(struct hw **rhw)
|
||||
|
||||
*rhw = NULL;
|
||||
hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
|
||||
if (NULL == hw20k1)
|
||||
if (!hw20k1)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&hw20k1->reg_20k1_lock);
|
||||
|
@ -166,7 +166,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -492,7 +492,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -513,7 +513,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -891,7 +891,7 @@ static int dai_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -941,7 +941,7 @@ static int dao_get_ctrl_blk(void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
*rblk = blk;
|
||||
@ -1092,7 +1092,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
|
||||
|
||||
*rblk = NULL;
|
||||
blk = kzalloc(sizeof(*blk), GFP_KERNEL);
|
||||
if (NULL == blk)
|
||||
if (!blk)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
@ -1112,6 +1112,26 @@ static int daio_mgr_put_ctrl_blk(void *blk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Timer interrupt */
|
||||
static int set_timer_irq(struct hw *hw, int enable)
|
||||
{
|
||||
hw_write_20kx(hw, GIE, enable ? IT_INT : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_timer_tick(struct hw *hw, unsigned int ticks)
|
||||
{
|
||||
if (ticks)
|
||||
ticks |= TIMR_IE | TIMR_IP;
|
||||
hw_write_20kx(hw, TIMR, ticks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int get_wc(struct hw *hw)
|
||||
{
|
||||
return hw_read_20kx(hw, WC);
|
||||
}
|
||||
|
||||
/* Card hardware initialization block */
|
||||
struct dac_conf {
|
||||
unsigned int msr; /* master sample rate in rsrs */
|
||||
@ -1841,6 +1861,22 @@ static int hw_have_digit_io_switch(struct hw *hw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct hw *hw = dev_id;
|
||||
unsigned int status;
|
||||
|
||||
status = hw_read_20kx(hw, GIP);
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (hw->irq_callback)
|
||||
hw->irq_callback(hw->irq_callback_data, status);
|
||||
|
||||
hw_write_20kx(hw, GIP, status);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hw_card_start(struct hw *hw)
|
||||
{
|
||||
int err = 0;
|
||||
@ -1868,7 +1904,7 @@ static int hw_card_start(struct hw *hw)
|
||||
hw->io_base = pci_resource_start(hw->pci, 2);
|
||||
hw->mem_base = (unsigned long)ioremap(hw->io_base,
|
||||
pci_resource_len(hw->pci, 2));
|
||||
if (NULL == (void *)hw->mem_base) {
|
||||
if (!hw->mem_base) {
|
||||
err = -ENOENT;
|
||||
goto error2;
|
||||
}
|
||||
@ -1879,12 +1915,15 @@ static int hw_card_start(struct hw *hw)
|
||||
set_field(&gctl, GCTL_UAA, 0);
|
||||
hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
|
||||
|
||||
/*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
|
||||
atc->chip_details->nm_card, hw))) {
|
||||
goto error3;
|
||||
if (hw->irq < 0) {
|
||||
err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
|
||||
"ctxfi", hw);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
|
||||
goto error2;
|
||||
}
|
||||
hw->irq = pci->irq;
|
||||
}
|
||||
hw->irq = pci->irq;
|
||||
*/
|
||||
|
||||
pci_set_master(pci);
|
||||
|
||||
@ -1923,7 +1962,7 @@ static int hw_card_shutdown(struct hw *hw)
|
||||
|
||||
hw->irq = -1;
|
||||
|
||||
if (NULL != ((void *)hw->mem_base))
|
||||
if (hw->mem_base)
|
||||
iounmap((void *)hw->mem_base);
|
||||
|
||||
hw->mem_base = (unsigned long)NULL;
|
||||
@ -1972,7 +2011,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
|
||||
hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
|
||||
|
||||
/* Reset all global pending interrupts */
|
||||
hw_write_20kx(hw, INTERRUPT_GIE, 0);
|
||||
hw_write_20kx(hw, GIE, 0);
|
||||
/* Reset all SRC pending interrupts */
|
||||
hw_write_20kx(hw, SRC_IP, 0);
|
||||
|
||||
@ -2149,6 +2188,10 @@ static struct hw ct20k2_preset __devinitdata = {
|
||||
.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
|
||||
.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
|
||||
.daio_mgr_commit_write = daio_mgr_commit_write,
|
||||
|
||||
.set_timer_irq = set_timer_irq,
|
||||
.set_timer_tick = set_timer_tick,
|
||||
.get_wc = get_wc,
|
||||
};
|
||||
|
||||
int __devinit create_20k2_hw_obj(struct hw **rhw)
|
||||
|
@ -654,7 +654,7 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
|
||||
int err;
|
||||
|
||||
kctl = snd_ctl_new1(new, mixer->atc);
|
||||
if (NULL == kctl)
|
||||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
|
||||
if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
|
||||
@ -837,17 +837,17 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer)
|
||||
*rmixer = NULL;
|
||||
/* Allocate mem for mixer obj */
|
||||
mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
|
||||
if (NULL == mixer)
|
||||
if (!mixer)
|
||||
return -ENOMEM;
|
||||
|
||||
mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
|
||||
GFP_KERNEL);
|
||||
if (NULL == mixer->amixers) {
|
||||
if (!mixer->amixers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
|
||||
if (NULL == mixer->sums) {
|
||||
if (!mixer->sums) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
|
||||
{
|
||||
struct ct_atc_pcm *apcm = atc_pcm;
|
||||
|
||||
if (NULL == apcm->substream)
|
||||
if (!apcm->substream)
|
||||
return;
|
||||
|
||||
snd_pcm_period_elapsed(apcm->substream);
|
||||
@ -123,7 +123,7 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
|
||||
int err;
|
||||
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (NULL == apcm)
|
||||
if (!apcm)
|
||||
return -ENOMEM;
|
||||
|
||||
apcm->substream = substream;
|
||||
@ -271,7 +271,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
|
||||
int err;
|
||||
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (NULL == apcm)
|
||||
if (!apcm)
|
||||
return -ENOMEM;
|
||||
|
||||
apcm->started = 0;
|
||||
|
@ -144,7 +144,7 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
|
||||
rsc->msr = msr;
|
||||
rsc->hw = hw;
|
||||
rsc->ops = &rsc_generic_ops;
|
||||
if (NULL == hw) {
|
||||
if (!hw) {
|
||||
rsc->ctrl_blk = NULL;
|
||||
return 0;
|
||||
}
|
||||
@ -216,7 +216,7 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
|
||||
mgr->type = NUM_RSCTYP;
|
||||
|
||||
mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
|
||||
if (NULL == mgr->rscs)
|
||||
if (!mgr->rscs)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (type) {
|
||||
|
@ -441,7 +441,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
|
||||
else
|
||||
src = kzalloc(sizeof(*src), GFP_KERNEL);
|
||||
|
||||
if (NULL == src) {
|
||||
if (!src) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
@ -550,7 +550,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
|
||||
|
||||
*rsrc_mgr = NULL;
|
||||
src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
|
||||
if (NULL == src_mgr)
|
||||
if (!src_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
|
||||
@ -679,7 +679,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp,
|
||||
/* Reserve memory for imapper nodes */
|
||||
srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
|
||||
GFP_KERNEL);
|
||||
if (NULL == srcimp->imappers) {
|
||||
if (!srcimp->imappers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
@ -833,7 +833,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
|
||||
|
||||
*rsrcimp_mgr = NULL;
|
||||
srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
|
||||
if (NULL == srcimp_mgr)
|
||||
if (!srcimp_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
|
||||
@ -844,7 +844,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
|
||||
spin_lock_init(&srcimp_mgr->imap_lock);
|
||||
INIT_LIST_HEAD(&srcimp_mgr->imappers);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (NULL == entry) {
|
||||
if (!entry) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
|
||||
}
|
||||
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (NULL == block)
|
||||
if (!block)
|
||||
goto out;
|
||||
|
||||
block->addr = entry->addr;
|
||||
@ -181,7 +181,7 @@ int ct_vm_create(struct ct_vm **rvm)
|
||||
*rvm = NULL;
|
||||
|
||||
vm = kzalloc(sizeof(*vm), GFP_KERNEL);
|
||||
if (NULL == vm)
|
||||
if (!vm)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&vm->lock);
|
||||
@ -189,7 +189,7 @@ int ct_vm_create(struct ct_vm **rvm)
|
||||
/* Allocate page table pages */
|
||||
for (i = 0; i < CT_PTP_NUM; i++) {
|
||||
vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (NULL == vm->ptp[i])
|
||||
if (!vm->ptp[i])
|
||||
break;
|
||||
}
|
||||
if (!i) {
|
||||
|
@ -46,6 +46,20 @@ config SND_HDA_INPUT_JACK
|
||||
Say Y here to enable the jack plugging notification via
|
||||
input layer.
|
||||
|
||||
config SND_HDA_PATCH_LOADER
|
||||
bool "Support initialization patch loading for HD-audio"
|
||||
depends on EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select SND_HDA_HWDEP
|
||||
select SND_HDA_RECONFIG
|
||||
help
|
||||
Say Y here to allow the HD-audio driver to load a pseudo
|
||||
firmware file ("patch") for overriding the BIOS setup at
|
||||
start up. The "patch" file can be specified via patch module
|
||||
option, such as patch=hda-init.
|
||||
|
||||
This option turns on hwdep and reconfig features automatically.
|
||||
|
||||
config SND_HDA_CODEC_REALTEK
|
||||
bool "Build Realtek HD-audio codec support"
|
||||
default y
|
||||
@ -134,6 +148,19 @@ config SND_HDA_ELD
|
||||
def_bool y
|
||||
depends on SND_HDA_CODEC_INTELHDMI
|
||||
|
||||
config SND_HDA_CODEC_CIRRUS
|
||||
bool "Build Cirrus Logic codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Cirrus Logic codec support in
|
||||
snd-hda-intel driver, such as CS4206.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-cirrus.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_CONEXANT
|
||||
bool "Build Conexant HD-audio codec support"
|
||||
default y
|
||||
|
@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o
|
||||
snd-hda-codec-idt-objs := patch_sigmatel.o
|
||||
snd-hda-codec-si3054-objs := patch_si3054.o
|
||||
snd-hda-codec-atihdmi-objs := patch_atihdmi.o
|
||||
snd-hda-codec-cirrus-objs := patch_cirrus.o
|
||||
snd-hda-codec-ca0110-objs := patch_ca0110.o
|
||||
snd-hda-codec-conexant-objs := patch_conexant.o
|
||||
snd-hda-codec-via-objs := patch_via.o
|
||||
@ -41,6 +42,9 @@ endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CIRRUS
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CA0110
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
|
||||
endif
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <sound/core.h>
|
||||
#include "hda_beep.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
enum {
|
||||
DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
|
||||
@ -118,6 +119,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
||||
struct hda_beep *beep;
|
||||
int err;
|
||||
|
||||
if (!snd_hda_get_bool_hint(codec, "beep"))
|
||||
return 0; /* disabled explicitly */
|
||||
|
||||
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
|
||||
if (beep == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -44,6 +44,7 @@ struct hda_vendor_id {
|
||||
/* codec vendor labels */
|
||||
static struct hda_vendor_id hda_vendor_ids[] = {
|
||||
{ 0x1002, "ATI" },
|
||||
{ 0x1013, "Cirrus Logic" },
|
||||
{ 0x1057, "Motorola" },
|
||||
{ 0x1095, "Silicon Image" },
|
||||
{ 0x10de, "Nvidia" },
|
||||
@ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = (u32)(codec->addr & 0x0f) << 28;
|
||||
if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
|
||||
(verb & ~0xfff) || (parm & ~0xffff)) {
|
||||
printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
|
||||
codec->addr, direct, nid, verb, parm);
|
||||
return ~0;
|
||||
}
|
||||
|
||||
val = (u32)codec->addr << 28;
|
||||
val |= (u32)direct << 27;
|
||||
val |= (u32)nid << 20;
|
||||
val |= verb << 8;
|
||||
@ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
|
||||
struct hda_bus *bus = codec->bus;
|
||||
int err;
|
||||
|
||||
if (cmd == ~0)
|
||||
return -1;
|
||||
|
||||
if (res)
|
||||
*res = -1;
|
||||
again:
|
||||
@ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int parm;
|
||||
int i, conn_len, conns;
|
||||
unsigned int shift, num_elems, mask;
|
||||
unsigned int wcaps;
|
||||
hda_nid_t prev_nid;
|
||||
|
||||
if (snd_BUG_ON(!conn_list || max_conns <= 0))
|
||||
return -EINVAL;
|
||||
|
||||
wcaps = get_wcaps(codec, nid);
|
||||
if (!(wcaps & AC_WCAP_CONN_LIST) &&
|
||||
get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
|
||||
snd_printk(KERN_WARNING "hda_codec: "
|
||||
"connection list not available for 0x%x\n", nid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
|
||||
if (parm & AC_CLIST_LONG) {
|
||||
/* long form */
|
||||
@ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
/* single connection */
|
||||
parm = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CONNECT_LIST, 0);
|
||||
if (parm == -1 && codec->bus->rirb_error)
|
||||
return -EIO;
|
||||
conn_list[0] = parm & mask;
|
||||
return 1;
|
||||
}
|
||||
@ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
|
||||
int range_val;
|
||||
hda_nid_t val, n;
|
||||
|
||||
if (i % num_elems == 0)
|
||||
if (i % num_elems == 0) {
|
||||
parm = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CONNECT_LIST, i);
|
||||
if (parm == -1 && codec->bus->rirb_error)
|
||||
return -EIO;
|
||||
}
|
||||
range_val = !!(parm & (1 << (shift-1))); /* ranges */
|
||||
val = parm & mask;
|
||||
if (val == 0) {
|
||||
@ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec)
|
||||
for (i = 0; i < codec->num_nodes; i++, nid++) {
|
||||
struct hda_pincfg *pin;
|
||||
unsigned int wcaps = get_wcaps(codec, nid);
|
||||
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
|
||||
AC_WCAP_TYPE_SHIFT;
|
||||
unsigned int wid_type = get_wcaps_type(wcaps);
|
||||
if (wid_type != AC_WID_PIN)
|
||||
continue;
|
||||
pin = snd_array_new(&codec->init_pins);
|
||||
@ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
|
||||
int do_init, struct hda_codec **codecp)
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
struct hda_codec *codec;
|
||||
char component[31];
|
||||
@ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
|
||||
codec->afg ? codec->afg : codec->mfg,
|
||||
AC_PWRST_D0);
|
||||
|
||||
if (do_init) {
|
||||
err = snd_hda_codec_configure(codec);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
snd_hda_codec_proc_new(codec);
|
||||
|
||||
snd_hda_create_hwdep(codec);
|
||||
@ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec)
|
||||
err = init_unsol_queue(codec->bus);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_setup_stream - set up the codec for streaming
|
||||
@ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
||||
hda_nid_t nid;
|
||||
int i;
|
||||
|
||||
snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
|
||||
/* this delay seems necessary to avoid click noise at power-down */
|
||||
if (power_state == AC_PWRST_D3)
|
||||
msleep(100);
|
||||
snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
|
||||
power_state);
|
||||
msleep(10); /* partial workaround for "azx_get_response timeout" */
|
||||
/* partial workaround for "azx_get_response timeout" */
|
||||
if (power_state == AC_PWRST_D0)
|
||||
msleep(10);
|
||||
|
||||
nid = codec->start_nid;
|
||||
for (i = 0; i < codec->num_nodes; i++, nid++) {
|
||||
unsigned int wcaps = get_wcaps(codec, nid);
|
||||
if (wcaps & AC_WCAP_POWER) {
|
||||
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
|
||||
AC_WCAP_TYPE_SHIFT;
|
||||
unsigned int wid_type = get_wcaps_type(wcaps);
|
||||
if (power_state == AC_PWRST_D3 &&
|
||||
wid_type == AC_WID_PIN) {
|
||||
unsigned int pincap;
|
||||
@ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
|
||||
case 20:
|
||||
case 24:
|
||||
case 32:
|
||||
if (maxbps >= 32)
|
||||
if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
|
||||
val |= 0x40;
|
||||
else if (maxbps >= 24)
|
||||
val |= 0x30;
|
||||
@ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
|
||||
bps = 20;
|
||||
}
|
||||
}
|
||||
else if (streams == AC_SUPFMT_FLOAT32) {
|
||||
/* should be exclusive */
|
||||
if (streams & AC_SUPFMT_FLOAT32) {
|
||||
formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
|
||||
bps = 32;
|
||||
} else if (streams == AC_SUPFMT_AC3) {
|
||||
if (!bps)
|
||||
bps = 32;
|
||||
}
|
||||
if (streams == AC_SUPFMT_AC3) {
|
||||
/* should be exclusive */
|
||||
/* temporary hack: we have still no proper support
|
||||
* for the direct AC3 stream...
|
||||
@ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
|
||||
tbl = q;
|
||||
|
||||
if (tbl->value >= 0 && tbl->value < num_configs) {
|
||||
#ifdef CONFIG_SND_DEBUG_DETECT
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
char tmp[10];
|
||||
const char *model = NULL;
|
||||
if (models)
|
||||
@ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
||||
end_nid = codec->start_nid + codec->num_nodes;
|
||||
for (nid = codec->start_nid; nid < end_nid; nid++) {
|
||||
unsigned int wid_caps = get_wcaps(codec, nid);
|
||||
unsigned int wid_type =
|
||||
(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
|
||||
unsigned int wid_type = get_wcaps_type(wid_caps);
|
||||
unsigned int def_conf;
|
||||
short assoc, loc;
|
||||
|
||||
|
@ -830,7 +830,8 @@ enum {
|
||||
int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
|
||||
struct hda_bus **busp);
|
||||
int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
|
||||
int do_init, struct hda_codec **codecp);
|
||||
struct hda_codec **codecp);
|
||||
int snd_hda_codec_configure(struct hda_codec *codec);
|
||||
|
||||
/*
|
||||
* low level functions
|
||||
@ -938,6 +939,13 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {}
|
||||
#define snd_hda_codec_needs_resume(codec) 1
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
/*
|
||||
* patch firmware
|
||||
*/
|
||||
int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Codec modularization
|
||||
*/
|
||||
|
@ -121,11 +121,17 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
|
||||
if (node == NULL)
|
||||
return -ENOMEM;
|
||||
node->nid = nid;
|
||||
nconns = snd_hda_get_connections(codec, nid, conn_list,
|
||||
HDA_MAX_CONNECTIONS);
|
||||
if (nconns < 0) {
|
||||
kfree(node);
|
||||
return nconns;
|
||||
node->wid_caps = get_wcaps(codec, nid);
|
||||
node->type = get_wcaps_type(node->wid_caps);
|
||||
if (node->wid_caps & AC_WCAP_CONN_LIST) {
|
||||
nconns = snd_hda_get_connections(codec, nid, conn_list,
|
||||
HDA_MAX_CONNECTIONS);
|
||||
if (nconns < 0) {
|
||||
kfree(node);
|
||||
return nconns;
|
||||
}
|
||||
} else {
|
||||
nconns = 0;
|
||||
}
|
||||
if (nconns <= ARRAY_SIZE(node->slist))
|
||||
node->conn_list = node->slist;
|
||||
@ -140,8 +146,6 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
|
||||
}
|
||||
memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
|
||||
node->nconns = nconns;
|
||||
node->wid_caps = get_wcaps(codec, nid);
|
||||
node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
|
||||
|
||||
if (node->type == AC_WID_PIN) {
|
||||
node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
@ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t init_verbs_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static int parse_init_verbs(struct hda_codec *codec, const char *buf)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
struct hda_verb *v;
|
||||
int nid, verb, param;
|
||||
|
||||
@ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev,
|
||||
v->nid = nid;
|
||||
v->verb = verb;
|
||||
v->param = param;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t init_verbs_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
int err = parse_init_verbs(codec, buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -376,19 +385,15 @@ static void remove_trail_spaces(char *str)
|
||||
|
||||
#define MAX_HINTS 1024
|
||||
|
||||
static ssize_t hints_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static int parse_hints(struct hda_codec *codec, const char *buf)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
char *key, *val;
|
||||
struct hda_hint *hint;
|
||||
|
||||
while (isspace(*buf))
|
||||
buf++;
|
||||
if (!*buf || *buf == '#' || *buf == '\n')
|
||||
return count;
|
||||
return 0;
|
||||
if (*buf == '=')
|
||||
return -EINVAL;
|
||||
key = kstrndup_noeol(buf, 1024);
|
||||
@ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev,
|
||||
kfree(hint->key);
|
||||
hint->key = key;
|
||||
hint->val = val;
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
/* allocate a new hint entry */
|
||||
if (codec->hints.used >= MAX_HINTS)
|
||||
@ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev,
|
||||
}
|
||||
hint->key = key;
|
||||
hint->val = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hints_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
int err = parse_hints(codec, buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev,
|
||||
|
||||
#define MAX_PIN_CONFIGS 32
|
||||
|
||||
static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
|
||||
{
|
||||
int nid, cfg;
|
||||
|
||||
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
|
||||
return -EINVAL;
|
||||
if (!nid)
|
||||
return -EINVAL;
|
||||
return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
|
||||
}
|
||||
|
||||
static ssize_t user_pin_configs_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
|
||||
struct hda_codec *codec = hwdep->private_data;
|
||||
int nid, cfg;
|
||||
int err;
|
||||
|
||||
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
|
||||
return -EINVAL;
|
||||
if (!nid)
|
||||
return -EINVAL;
|
||||
err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
|
||||
int err = parse_user_pin_configs(codec, buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return count;
|
||||
@ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
|
||||
EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
|
||||
|
||||
#endif /* CONFIG_SND_HDA_RECONFIG */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
|
||||
/* parser mode */
|
||||
enum {
|
||||
LINE_MODE_NONE,
|
||||
LINE_MODE_CODEC,
|
||||
LINE_MODE_MODEL,
|
||||
LINE_MODE_PINCFG,
|
||||
LINE_MODE_VERB,
|
||||
LINE_MODE_HINT,
|
||||
NUM_LINE_MODES,
|
||||
};
|
||||
|
||||
static inline int strmatch(const char *a, const char *b)
|
||||
{
|
||||
return strnicmp(a, b, strlen(b)) == 0;
|
||||
}
|
||||
|
||||
/* parse the contents after the line "[codec]"
|
||||
* accept only the line with three numbers, and assign the current codec
|
||||
*/
|
||||
static void parse_codec_mode(char *buf, struct hda_bus *bus,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
unsigned int vendorid, subid, caddr;
|
||||
struct hda_codec *codec;
|
||||
|
||||
*codecp = NULL;
|
||||
if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
|
||||
list_for_each_entry(codec, &bus->codec_list, list) {
|
||||
if (codec->addr == caddr) {
|
||||
*codecp = codec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the contents after the other command tags, [pincfg], [verb],
|
||||
* [hint] and [model]
|
||||
* just pass to the sysfs helper (only when any codec was specified)
|
||||
*/
|
||||
static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
if (!*codecp)
|
||||
return;
|
||||
parse_user_pin_configs(*codecp, buf);
|
||||
}
|
||||
|
||||
static void parse_verb_mode(char *buf, struct hda_bus *bus,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
if (!*codecp)
|
||||
return;
|
||||
parse_init_verbs(*codecp, buf);
|
||||
}
|
||||
|
||||
static void parse_hint_mode(char *buf, struct hda_bus *bus,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
if (!*codecp)
|
||||
return;
|
||||
parse_hints(*codecp, buf);
|
||||
}
|
||||
|
||||
static void parse_model_mode(char *buf, struct hda_bus *bus,
|
||||
struct hda_codec **codecp)
|
||||
{
|
||||
if (!*codecp)
|
||||
return;
|
||||
kfree((*codecp)->modelname);
|
||||
(*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
|
||||
}
|
||||
|
||||
struct hda_patch_item {
|
||||
const char *tag;
|
||||
void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
|
||||
};
|
||||
|
||||
static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
|
||||
[LINE_MODE_CODEC] = { "[codec]", parse_codec_mode },
|
||||
[LINE_MODE_MODEL] = { "[model]", parse_model_mode },
|
||||
[LINE_MODE_VERB] = { "[verb]", parse_verb_mode },
|
||||
[LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode },
|
||||
[LINE_MODE_HINT] = { "[hint]", parse_hint_mode },
|
||||
};
|
||||
|
||||
/* check the line starting with '[' -- change the parser mode accodingly */
|
||||
static int parse_line_mode(char *buf, struct hda_bus *bus)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
|
||||
if (!patch_items[i].tag)
|
||||
continue;
|
||||
if (strmatch(buf, patch_items[i].tag))
|
||||
return i;
|
||||
}
|
||||
return LINE_MODE_NONE;
|
||||
}
|
||||
|
||||
/* copy one line from the buffer in fw, and update the fields in fw
|
||||
* return zero if it reaches to the end of the buffer, or non-zero
|
||||
* if successfully copied a line
|
||||
*
|
||||
* the spaces at the beginning and the end of the line are stripped
|
||||
*/
|
||||
static int get_line_from_fw(char *buf, int size, struct firmware *fw)
|
||||
{
|
||||
int len;
|
||||
const char *p = fw->data;
|
||||
while (isspace(*p) && fw->size) {
|
||||
p++;
|
||||
fw->size--;
|
||||
}
|
||||
if (!fw->size)
|
||||
return 0;
|
||||
if (size < fw->size)
|
||||
size = fw->size;
|
||||
|
||||
for (len = 0; len < fw->size; len++) {
|
||||
if (!*p)
|
||||
break;
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
len++;
|
||||
break;
|
||||
}
|
||||
if (len < size)
|
||||
*buf++ = *p++;
|
||||
}
|
||||
*buf = 0;
|
||||
fw->size -= len;
|
||||
fw->data = p;
|
||||
remove_trail_spaces(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* load a "patch" firmware file and parse it
|
||||
*/
|
||||
int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
|
||||
{
|
||||
int err;
|
||||
const struct firmware *fw;
|
||||
struct firmware tmp;
|
||||
char buf[128];
|
||||
struct hda_codec *codec;
|
||||
int line_mode;
|
||||
struct device *dev = bus->card->dev;
|
||||
|
||||
if (snd_BUG_ON(!dev))
|
||||
return -ENODEV;
|
||||
err = request_firmware(&fw, patch, dev);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
|
||||
patch);
|
||||
return err;
|
||||
}
|
||||
|
||||
tmp = *fw;
|
||||
line_mode = LINE_MODE_NONE;
|
||||
codec = NULL;
|
||||
while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
|
||||
if (!*buf || *buf == '#' || *buf == '\n')
|
||||
continue;
|
||||
if (*buf == '[')
|
||||
line_mode = parse_line_mode(buf, bus);
|
||||
else if (patch_items[line_mode].parser)
|
||||
patch_items[line_mode].parser(buf, bus, &codec);
|
||||
}
|
||||
release_firmware(fw);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_load_patch);
|
||||
#endif /* CONFIG_SND_HDA_PATCH_LOADER */
|
||||
|
@ -61,6 +61,9 @@ static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
|
||||
static int probe_only[SNDRV_CARDS];
|
||||
static int single_cmd;
|
||||
static int enable_msi;
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
static char *patch[SNDRV_CARDS];
|
||||
#endif
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
|
||||
@ -84,6 +87,10 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
|
||||
"(for debugging only).");
|
||||
module_param(enable_msi, int, 0444);
|
||||
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
module_param_array(patch, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
|
||||
@ -1331,8 +1338,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
|
||||
[AZX_DRIVER_TERA] = 1,
|
||||
};
|
||||
|
||||
static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
||||
int no_init)
|
||||
static int __devinit azx_codec_create(struct azx *chip, const char *model)
|
||||
{
|
||||
struct hda_bus_template bus_temp;
|
||||
int c, codecs, err;
|
||||
@ -1391,7 +1397,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
||||
for (c = 0; c < max_slots; c++) {
|
||||
if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
|
||||
struct hda_codec *codec;
|
||||
err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
|
||||
err = snd_hda_codec_new(chip->bus, c, &codec);
|
||||
if (err < 0)
|
||||
continue;
|
||||
codecs++;
|
||||
@ -1401,7 +1407,16 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
||||
snd_printk(KERN_ERR SFX "no codecs initialized\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* configure each codec instance */
|
||||
static int __devinit azx_codec_configure(struct azx *chip)
|
||||
{
|
||||
struct hda_codec *codec;
|
||||
list_for_each_entry(codec, &chip->bus->codec_list, list) {
|
||||
snd_hda_codec_configure(codec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2284,6 +2299,30 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* white-list for enable_msi
|
||||
*/
|
||||
static struct snd_pci_quirk msi_white_list[] __devinitdata = {
|
||||
SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
|
||||
{}
|
||||
};
|
||||
|
||||
static void __devinit check_msi(struct azx *chip)
|
||||
{
|
||||
const struct snd_pci_quirk *q;
|
||||
|
||||
chip->msi = enable_msi;
|
||||
if (chip->msi)
|
||||
return;
|
||||
q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
|
||||
if (q) {
|
||||
printk(KERN_INFO
|
||||
"hda_intel: msi for device %04x:%04x set to %d\n",
|
||||
q->subvendor, q->subdevice, q->value);
|
||||
chip->msi = q->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* constructor
|
||||
@ -2318,7 +2357,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
||||
chip->pci = pci;
|
||||
chip->irq = -1;
|
||||
chip->driver_type = driver_type;
|
||||
chip->msi = enable_msi;
|
||||
check_msi(chip);
|
||||
chip->dev_index = dev;
|
||||
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
|
||||
|
||||
@ -2526,15 +2565,32 @@ static int __devinit azx_probe(struct pci_dev *pci,
|
||||
return err;
|
||||
}
|
||||
|
||||
/* set this here since it's referred in snd_hda_load_patch() */
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
|
||||
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
card->private_data = chip;
|
||||
|
||||
/* create codec instances */
|
||||
err = azx_codec_create(chip, model[dev], probe_only[dev]);
|
||||
err = azx_codec_create(chip, model[dev]);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
#ifdef CONFIG_SND_HDA_PATCH_LOADER
|
||||
if (patch[dev]) {
|
||||
snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
|
||||
patch[dev]);
|
||||
err = snd_hda_load_patch(chip->bus, patch[dev]);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
}
|
||||
#endif
|
||||
if (!probe_only[dev]) {
|
||||
err = azx_codec_configure(chip);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* create PCM streams */
|
||||
err = snd_hda_build_pcms(chip->bus);
|
||||
@ -2546,8 +2602,6 @@ static int __devinit azx_probe(struct pci_dev *pci,
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
@ -2649,11 +2703,15 @@ static struct pci_device_id azx_ids[] = {
|
||||
/* this entry seems still valid -- i.e. without emu20kx chip */
|
||||
{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
|
||||
#endif
|
||||
/* AMD Generic, PCI class code and Vendor ID for HD Audio */
|
||||
/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
@ -99,7 +99,6 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
|
||||
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
||||
unsigned int *tlv, const char **slaves);
|
||||
int snd_hda_codec_reset(struct hda_codec *codec);
|
||||
int snd_hda_codec_configure(struct hda_codec *codec);
|
||||
|
||||
/* amp value bits */
|
||||
#define HDA_AMP_MUTE 0x80
|
||||
@ -408,6 +407,19 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
|
||||
return codec->wcaps[nid - codec->start_nid];
|
||||
}
|
||||
|
||||
/* get the widget type from widget capability bits */
|
||||
#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
|
||||
|
||||
static inline unsigned int get_wcaps_channels(u32 wcaps)
|
||||
{
|
||||
unsigned int chans;
|
||||
|
||||
chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
|
||||
chans = ((chans << 1) | 1) + 1;
|
||||
|
||||
return chans;
|
||||
}
|
||||
|
||||
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
|
||||
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
|
||||
unsigned int caps);
|
||||
|
@ -508,17 +508,14 @@ static void print_codec_info(struct snd_info_entry *entry,
|
||||
unsigned int wid_caps =
|
||||
snd_hda_param_read(codec, nid,
|
||||
AC_PAR_AUDIO_WIDGET_CAP);
|
||||
unsigned int wid_type =
|
||||
(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
|
||||
unsigned int wid_type = get_wcaps_type(wid_caps);
|
||||
hda_nid_t conn[HDA_MAX_CONNECTIONS];
|
||||
int conn_len = 0;
|
||||
|
||||
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
|
||||
get_wid_type_name(wid_type), wid_caps);
|
||||
if (wid_caps & AC_WCAP_STEREO) {
|
||||
unsigned int chans;
|
||||
chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13;
|
||||
chans = ((chans << 1) | 1) + 1;
|
||||
unsigned int chans = get_wcaps_channels(wid_caps);
|
||||
if (chans == 2)
|
||||
snd_iprintf(buffer, " Stereo");
|
||||
else
|
||||
|
@ -2982,7 +2982,8 @@ static int patch_ad1988(struct hda_codec *codec)
|
||||
board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
|
||||
ad1988_models, ad1988_cfg_tbl);
|
||||
if (board_config < 0) {
|
||||
printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
|
||||
printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
|
||||
codec->chip_name);
|
||||
board_config = AD1988_AUTO;
|
||||
}
|
||||
|
||||
@ -3702,19 +3703,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = {
|
||||
* Port F: Internal speakers
|
||||
*/
|
||||
|
||||
static struct hda_input_mux ad1884a_laptop_capture_source = {
|
||||
.num_items = 4,
|
||||
.items = {
|
||||
{ "Mic", 0x0 }, /* port-B */
|
||||
{ "Internal Mic", 0x1 }, /* port-C */
|
||||
{ "Dock Mic", 0x4 }, /* port-E */
|
||||
{ "Mix", 0x3 },
|
||||
},
|
||||
};
|
||||
static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
||||
int mute = (!ucontrol->value.integer.value[0] &&
|
||||
!ucontrol->value.integer.value[1]);
|
||||
/* toggle GPIO1 according to the mute state */
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
mute ? 0x02 : 0x0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Switch",
|
||||
.info = snd_hda_mixer_amp_switch_info,
|
||||
.get = snd_hda_mixer_amp_switch_get,
|
||||
.put = ad1884a_mobile_master_sw_put,
|
||||
.private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
|
||||
},
|
||||
HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
||||
@ -3729,36 +3740,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
/* The multiple "Capture Source" controls confuse alsamixer
|
||||
* So call somewhat different..
|
||||
*/
|
||||
/* .name = "Capture Source", */
|
||||
.name = "Input Source",
|
||||
.count = 2,
|
||||
.info = ad198x_mux_enum_info,
|
||||
.get = ad198x_mux_enum_get,
|
||||
.put = ad198x_mux_enum_put,
|
||||
},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
||||
int mute = (!ucontrol->value.integer.value[0] &&
|
||||
!ucontrol->value.integer.value[1]);
|
||||
/* toggle GPIO1 according to the mute state */
|
||||
snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
|
||||
mute ? 0x02 : 0x0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
||||
/*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
|
||||
@ -3828,6 +3812,63 @@ static int ad1884a_hp_init(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mute internal speaker if HP or docking HP is plugged */
|
||||
static void ad1884a_laptop_automute(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
present &= AC_PINSENSE_PRESENCE;
|
||||
if (!present) {
|
||||
present = snd_hda_codec_read(codec, 0x12, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0);
|
||||
present &= AC_PINSENSE_PRESENCE;
|
||||
}
|
||||
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
||||
present ? 0x00 : 0x02);
|
||||
}
|
||||
|
||||
/* switch to external mic if plugged */
|
||||
static void ad1884a_laptop_automic(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE)
|
||||
idx = 0;
|
||||
else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
|
||||
AC_PINSENSE_PRESENCE)
|
||||
idx = 4;
|
||||
else
|
||||
idx = 1;
|
||||
snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
|
||||
}
|
||||
|
||||
/* unsolicited event for HP jack sensing */
|
||||
static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
|
||||
unsigned int res)
|
||||
{
|
||||
switch (res >> 26) {
|
||||
case AD1884A_HP_EVENT:
|
||||
ad1884a_laptop_automute(codec);
|
||||
break;
|
||||
case AD1884A_MIC_EVENT:
|
||||
ad1884a_laptop_automic(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize jack-sensing, too */
|
||||
static int ad1884a_laptop_init(struct hda_codec *codec)
|
||||
{
|
||||
ad198x_init(codec);
|
||||
ad1884a_laptop_automute(codec);
|
||||
ad1884a_laptop_automic(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* additional verbs for laptop model */
|
||||
static struct hda_verb ad1884a_laptop_verbs[] = {
|
||||
/* Port-A (HP) pin - always unmuted */
|
||||
@ -3844,11 +3885,19 @@ static struct hda_verb ad1884a_laptop_verbs[] = {
|
||||
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
|
||||
/* Port-D (docking line-out) pin - default unmuted */
|
||||
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
/* analog mix */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
||||
/* unsolicited event for pin-sense */
|
||||
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
||||
{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
||||
{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
|
||||
{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
|
||||
/* allow to touch GPIO1 (for mute control) */
|
||||
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
|
||||
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
@ -4008,6 +4057,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
|
||||
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
|
||||
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
|
||||
SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
|
||||
{}
|
||||
};
|
||||
@ -4057,9 +4107,8 @@ static int patch_ad1884a(struct hda_codec *codec)
|
||||
spec->mixers[0] = ad1884a_laptop_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
spec->input_mux = &ad1884a_laptop_capture_source;
|
||||
codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
|
||||
codec->patch_ops.init = ad1884a_hp_init;
|
||||
codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
|
||||
codec->patch_ops.init = ad1884a_laptop_init;
|
||||
/* set the upper-limit for mixer amp to 0dB for avoiding the
|
||||
* possible damage by overloading
|
||||
*/
|
||||
|
@ -141,8 +141,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec)
|
||||
/* FIXME: we must check ELD and change the PCM parameters dynamically
|
||||
*/
|
||||
chans = get_wcaps(codec, CVT_NID);
|
||||
chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13;
|
||||
chans = ((chans << 1) | 1) + 1;
|
||||
chans = get_wcaps_channels(chans);
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
|
||||
|
||||
return 0;
|
||||
|
@ -459,8 +459,7 @@ static void parse_input(struct hda_codec *codec)
|
||||
nid = codec->start_nid;
|
||||
for (i = 0; i < codec->num_nodes; i++, nid++) {
|
||||
unsigned int wcaps = get_wcaps(codec, nid);
|
||||
unsigned int type = (wcaps & AC_WCAP_TYPE) >>
|
||||
AC_WCAP_TYPE_SHIFT;
|
||||
unsigned int type = get_wcaps_type(wcaps);
|
||||
if (type != AC_WID_AUD_IN)
|
||||
continue;
|
||||
if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
|
||||
|
1194
sound/pci/hda/patch_cirrus.c
Normal file
1194
sound/pci/hda/patch_cirrus.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -635,7 +635,8 @@ static int patch_cmi9880(struct hda_codec *codec)
|
||||
cmi9880_models,
|
||||
cmi9880_cfg_tbl);
|
||||
if (spec->board_config < 0) {
|
||||
snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
|
||||
snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
|
||||
codec->chip_name);
|
||||
spec->board_config = CMI_AUTO; /* try everything */
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,8 @@ struct conexant_spec {
|
||||
struct hda_input_mux private_imux;
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
|
||||
unsigned int dell_automute;
|
||||
unsigned int port_d_mode;
|
||||
};
|
||||
|
||||
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
@ -1908,6 +1910,480 @@ static int patch_cxt5051(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Conexant 5066 specific */
|
||||
|
||||
static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
|
||||
static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
|
||||
static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
|
||||
#define CXT5066_SPDIF_OUT 0x21
|
||||
|
||||
static struct hda_channel_mode cxt5066_modes[1] = {
|
||||
{ 2, NULL },
|
||||
};
|
||||
|
||||
static void cxt5066_update_speaker(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int pinctl;
|
||||
|
||||
snd_printdd("CXT5066: update speaker, hp_present=%d\n",
|
||||
spec->hp_present);
|
||||
|
||||
/* Port A (HP) */
|
||||
pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
|
||||
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinctl);
|
||||
|
||||
/* Port D (HP/LO) */
|
||||
pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
|
||||
? spec->port_d_mode : 0;
|
||||
snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinctl);
|
||||
|
||||
/* CLASS_D AMP */
|
||||
pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
|
||||
snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinctl);
|
||||
|
||||
if (spec->dell_automute) {
|
||||
/* DELL AIO Port Rule: PortA > PortD > IntSpk */
|
||||
pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
|
||||
? PIN_OUT : 0;
|
||||
snd_hda_codec_write(codec, 0x1c, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
|
||||
}
|
||||
}
|
||||
|
||||
/* turn on/off EAPD (+ mute HP) as a master switch */
|
||||
static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
if (!cxt_eapd_put(kcontrol, ucontrol))
|
||||
return 0;
|
||||
|
||||
cxt5066_update_speaker(codec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* toggle input of built-in and mic jack appropriately */
|
||||
static void cxt5066_automic(struct hda_codec *codec)
|
||||
{
|
||||
static struct hda_verb ext_mic_present[] = {
|
||||
/* enable external mic, port B */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
|
||||
/* switch to external mic input */
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
|
||||
|
||||
/* disable internal mic, port C */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
static struct hda_verb ext_mic_absent[] = {
|
||||
/* enable internal mic, port C */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
|
||||
/* switch to internal mic input */
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 1},
|
||||
|
||||
/* disable external mic, port B */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x1a, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
if (present) {
|
||||
snd_printdd("CXT5066: external microphone detected\n");
|
||||
snd_hda_sequence_write(codec, ext_mic_present);
|
||||
} else {
|
||||
snd_printdd("CXT5066: external microphone absent\n");
|
||||
snd_hda_sequence_write(codec, ext_mic_absent);
|
||||
}
|
||||
}
|
||||
|
||||
/* mute internal speaker if HP is plugged */
|
||||
static void cxt5066_hp_automute(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
unsigned int portA, portD;
|
||||
|
||||
/* Port A */
|
||||
portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE;
|
||||
|
||||
/* Port D */
|
||||
portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE) << 1;
|
||||
|
||||
spec->hp_present = !!(portA | portD);
|
||||
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
|
||||
portA, portD, spec->hp_present);
|
||||
cxt5066_update_speaker(codec);
|
||||
}
|
||||
|
||||
/* unsolicited event for jack sensing */
|
||||
static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
|
||||
switch (res >> 26) {
|
||||
case CONEXANT_HP_EVENT:
|
||||
cxt5066_hp_automute(codec);
|
||||
break;
|
||||
case CONEXANT_MIC_EVENT:
|
||||
cxt5066_automic(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct hda_input_mux cxt5066_analog_mic_boost = {
|
||||
.num_items = 5,
|
||||
.items = {
|
||||
{ "0dB", 0 },
|
||||
{ "10dB", 1 },
|
||||
{ "20dB", 2 },
|
||||
{ "30dB", 3 },
|
||||
{ "40dB", 4 },
|
||||
},
|
||||
};
|
||||
|
||||
static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo);
|
||||
}
|
||||
|
||||
static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
int val;
|
||||
|
||||
val = snd_hda_codec_read(codec, 0x17, 0,
|
||||
AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
|
||||
unsigned int idx;
|
||||
|
||||
if (!imux->num_items)
|
||||
return 0;
|
||||
idx = ucontrol->value.enumerated.item[0];
|
||||
if (idx >= imux->num_items)
|
||||
idx = imux->num_items - 1;
|
||||
|
||||
snd_hda_codec_write_cache(codec, 0x17, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
|
||||
imux->items[idx].index);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct hda_input_mux cxt5066_capture_source = {
|
||||
.num_items = 4,
|
||||
.items = {
|
||||
{ "Mic B", 0 },
|
||||
{ "Mic C", 1 },
|
||||
{ "Mic E", 2 },
|
||||
{ "Mic F", 3 },
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
|
||||
.ops = &snd_hda_bind_vol,
|
||||
.values = {
|
||||
HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
|
||||
HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
|
||||
0
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
|
||||
.ops = &snd_hda_bind_sw,
|
||||
.values = {
|
||||
HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
|
||||
HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
|
||||
0
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new cxt5066_mixer_master[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
|
||||
.info = snd_hda_mixer_amp_volume_info,
|
||||
.get = snd_hda_mixer_amp_volume_get,
|
||||
.put = snd_hda_mixer_amp_volume_put,
|
||||
.tlv = { .c = snd_hda_mixer_amp_tlv },
|
||||
/* offset by 28 volume steps to limit minimum gain to -46dB */
|
||||
.private_value =
|
||||
HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new cxt5066_mixers[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Switch",
|
||||
.info = cxt_eapd_info,
|
||||
.get = cxt_eapd_get,
|
||||
.put = cxt5066_hp_master_sw_put,
|
||||
.private_value = 0x1d,
|
||||
},
|
||||
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Mic Boost Capture Enum",
|
||||
.info = cxt5066_mic_boost_mux_enum_info,
|
||||
.get = cxt5066_mic_boost_mux_enum_get,
|
||||
.put = cxt5066_mic_boost_mux_enum_put,
|
||||
},
|
||||
|
||||
HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
|
||||
HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5066_init_verbs[] = {
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
|
||||
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
|
||||
|
||||
/* Speakers */
|
||||
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* HP, Amp */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* DAC1 */
|
||||
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
/* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
||||
|
||||
/* no digital microphone support yet */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Audio input selector */
|
||||
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
|
||||
|
||||
/* SPDIF route: PCM */
|
||||
{0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
{0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
|
||||
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
|
||||
/* EAPD */
|
||||
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
|
||||
/* not handling these yet */
|
||||
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5066_init_verbs_olpc[] = {
|
||||
/* Port A: headphones */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* Port B: external microphone */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
|
||||
/* Port C: internal microphone */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
|
||||
/* Port D: unused */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port E: unused, but has primary EAPD */
|
||||
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
|
||||
/* Port F: unused */
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port G: internal speakers */
|
||||
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* DAC1 */
|
||||
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
|
||||
/* DAC2: unused */
|
||||
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
|
||||
/* Disable digital microphone port */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Audio input selectors */
|
||||
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
|
||||
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
|
||||
|
||||
/* Disable SPDIF */
|
||||
{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* enable unsolicited events for Port A and B */
|
||||
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
|
||||
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/* initialize jack-sensing, too */
|
||||
static int cxt5066_init(struct hda_codec *codec)
|
||||
{
|
||||
snd_printdd("CXT5066: init\n");
|
||||
conexant_init(codec);
|
||||
if (codec->patch_ops.unsol_event) {
|
||||
cxt5066_hp_automute(codec);
|
||||
cxt5066_automic(codec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5066_DELL_LAPTOP, /* Dell Laptop */
|
||||
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
|
||||
CXT5066_MODELS
|
||||
};
|
||||
|
||||
static const char *cxt5066_models[CXT5066_MODELS] = {
|
||||
[CXT5066_LAPTOP] = "laptop",
|
||||
[CXT5066_DELL_LAPTOP] = "dell-laptop",
|
||||
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
|
||||
CXT5066_LAPTOP),
|
||||
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
|
||||
CXT5066_DELL_LAPTOP),
|
||||
{}
|
||||
};
|
||||
|
||||
static int patch_cxt5066(struct hda_codec *codec)
|
||||
{
|
||||
struct conexant_spec *spec;
|
||||
int board_config;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
codec->spec = spec;
|
||||
|
||||
codec->patch_ops = conexant_patch_ops;
|
||||
codec->patch_ops.init = cxt5066_init;
|
||||
|
||||
spec->dell_automute = 0;
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids);
|
||||
spec->multiout.dac_nids = cxt5066_dac_nids;
|
||||
spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT;
|
||||
spec->num_adc_nids = 1;
|
||||
spec->adc_nids = cxt5066_adc_nids;
|
||||
spec->capsrc_nids = cxt5066_capsrc_nids;
|
||||
spec->input_mux = &cxt5066_capture_source;
|
||||
|
||||
spec->port_d_mode = PIN_HP;
|
||||
|
||||
spec->num_init_verbs = 1;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs;
|
||||
spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes);
|
||||
spec->channel_mode = cxt5066_modes;
|
||||
spec->cur_adc = 0;
|
||||
spec->cur_adc_idx = 0;
|
||||
|
||||
board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
|
||||
cxt5066_models, cxt5066_cfg_tbl);
|
||||
switch (board_config) {
|
||||
default:
|
||||
case CXT5066_LAPTOP:
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
break;
|
||||
case CXT5066_DELL_LAPTOP:
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
|
||||
spec->port_d_mode = PIN_OUT;
|
||||
spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo;
|
||||
spec->num_init_verbs++;
|
||||
spec->dell_automute = 1;
|
||||
break;
|
||||
case CXT5066_OLPC_XO_1_5:
|
||||
codec->patch_ops.unsol_event = cxt5066_unsol_event;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs_olpc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
spec->port_d_mode = 0;
|
||||
|
||||
/* no S/PDIF out */
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
||||
/* input source automatically selected */
|
||||
spec->input_mux = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
@ -1919,12 +2395,15 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
|
||||
.patch = patch_cxt5047 },
|
||||
{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
|
||||
.patch = patch_cxt5051 },
|
||||
{ .id = 0x14f15066, .name = "CX20582 (Pebble)",
|
||||
.patch = patch_cxt5066 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15045");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15051");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15066");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Conexant HD-audio codec");
|
||||
|
@ -33,8 +33,8 @@
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
#define CVT_NID 0x02 /* audio converter */
|
||||
#define PIN_NID 0x03 /* HDMI output pin */
|
||||
static hda_nid_t cvt_nid; /* audio converter */
|
||||
static hda_nid_t pin_nid; /* HDMI output pin */
|
||||
|
||||
#define INTEL_HDMI_EVENT_TAG 0x08
|
||||
|
||||
@ -44,30 +44,6 @@ struct intel_hdmi_spec {
|
||||
struct hdmi_eld sink_eld;
|
||||
};
|
||||
|
||||
static struct hda_verb pinout_enable_verb[] = {
|
||||
{PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
static struct hda_verb unsolicited_response_verb[] = {
|
||||
{PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
|
||||
INTEL_HDMI_EVENT_TAG},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb def_chan_map[] = {
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
|
||||
{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
struct hdmi_audio_infoframe {
|
||||
u8 type; /* 0x84 */
|
||||
u8 ver; /* 0x01 */
|
||||
@ -244,11 +220,12 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
|
||||
static void hdmi_enable_output(struct hda_codec *codec)
|
||||
{
|
||||
/* Unmute */
|
||||
if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, PIN_NID, 0,
|
||||
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
|
||||
/* Enable pin out */
|
||||
snd_hda_sequence_write(codec, pinout_enable_verb);
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -256,8 +233,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
|
||||
*/
|
||||
static void hdmi_start_infoframe_trans(struct hda_codec *codec)
|
||||
{
|
||||
hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_BEST);
|
||||
}
|
||||
|
||||
@ -266,20 +243,20 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
|
||||
*/
|
||||
static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
|
||||
{
|
||||
hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
|
||||
AC_DIPXMIT_DISABLE);
|
||||
}
|
||||
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec)
|
||||
{
|
||||
return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
|
||||
return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
|
||||
AC_VERB_GET_CVT_CHAN_COUNT, 0);
|
||||
}
|
||||
|
||||
static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
|
||||
{
|
||||
snd_hda_codec_write(codec, CVT_NID, 0,
|
||||
snd_hda_codec_write(codec, cvt_nid, 0,
|
||||
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
|
||||
|
||||
if (chs != hdmi_get_channel_count(codec))
|
||||
@ -294,7 +271,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec)
|
||||
int slot;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
slot = snd_hda_codec_read(codec, CVT_NID, 0,
|
||||
slot = snd_hda_codec_read(codec, cvt_nid, 0,
|
||||
AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
||||
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
||||
slot >> 4, slot & 0x7);
|
||||
@ -307,7 +284,7 @@ static void hdmi_parse_eld(struct hda_codec *codec)
|
||||
struct intel_hdmi_spec *spec = codec->spec;
|
||||
struct hdmi_eld *eld = &spec->sink_eld;
|
||||
|
||||
if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
|
||||
if (!snd_hdmi_get_eld(eld, codec, pin_nid))
|
||||
snd_hdmi_show_eld(eld);
|
||||
}
|
||||
|
||||
@ -322,11 +299,11 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
|
||||
int i;
|
||||
int size;
|
||||
|
||||
size = snd_hdmi_get_eld_size(codec, PIN_NID);
|
||||
size = snd_hdmi_get_eld_size(codec, pin_nid);
|
||||
printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, PIN_NID, 0,
|
||||
size = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
|
||||
}
|
||||
@ -340,15 +317,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
|
||||
int size;
|
||||
int pi, bi;
|
||||
for (i = 0; i < 8; i++) {
|
||||
size = snd_hda_codec_read(codec, PIN_NID, 0,
|
||||
size = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_HDMI_DIP_SIZE, i);
|
||||
if (size == 0)
|
||||
continue;
|
||||
|
||||
hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
|
||||
hdmi_set_dip_index(codec, pin_nid, i, 0x0);
|
||||
for (j = 1; j < 1000; j++) {
|
||||
hdmi_write_dip_byte(codec, PIN_NID, 0x0);
|
||||
hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
|
||||
hdmi_write_dip_byte(codec, pin_nid, 0x0);
|
||||
hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
|
||||
if (pi != i)
|
||||
snd_printd(KERN_INFO "dip index %d: %d != %d\n",
|
||||
bi, pi, i);
|
||||
@ -376,9 +353,9 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
|
||||
sum += params[i];
|
||||
ai->checksum = - sum;
|
||||
|
||||
hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
|
||||
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
|
||||
for (i = 0; i < sizeof(ai); i++)
|
||||
hdmi_write_dip_byte(codec, PIN_NID, params[i]);
|
||||
hdmi_write_dip_byte(codec, pin_nid, params[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -465,6 +442,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
|
||||
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||
struct hdmi_audio_infoframe *ai)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!ai->CA)
|
||||
return;
|
||||
|
||||
@ -473,7 +452,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
|
||||
* ALSA sequence is front/surr/clfe/side?
|
||||
*/
|
||||
|
||||
snd_hda_sequence_write(codec, def_chan_map);
|
||||
for (i = 0; i < 8; i++)
|
||||
snd_hda_codec_write(codec, cvt_nid, 0,
|
||||
AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
(i << 4) | i);
|
||||
|
||||
hdmi_debug_channel_mapping(codec);
|
||||
}
|
||||
|
||||
@ -597,7 +580,6 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.nid = CVT_NID, /* NID to query formats and rates and setup streams */
|
||||
.ops = {
|
||||
.open = intel_hdmi_playback_pcm_open,
|
||||
.close = intel_hdmi_playback_pcm_close,
|
||||
@ -613,6 +595,9 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec)
|
||||
codec->num_pcms = 1;
|
||||
codec->pcm_info = info;
|
||||
|
||||
/* NID to query formats and rates and setup streams */
|
||||
intel_hdmi_pcm_playback.nid = cvt_nid;
|
||||
|
||||
info->name = "INTEL HDMI";
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
|
||||
@ -636,8 +621,9 @@ static int intel_hdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
hdmi_enable_output(codec);
|
||||
|
||||
snd_hda_sequence_write(codec, unsolicited_response_verb);
|
||||
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -657,7 +643,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
|
||||
.unsol_event = intel_hdmi_unsol_event,
|
||||
};
|
||||
|
||||
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
static int do_patch_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct intel_hdmi_spec *spec;
|
||||
|
||||
@ -667,7 +653,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->multiout.dig_out_nid = CVT_NID;
|
||||
spec->multiout.dig_out_nid = cvt_nid;
|
||||
|
||||
codec->spec = spec;
|
||||
codec->patch_ops = intel_hdmi_patch_ops;
|
||||
@ -679,12 +665,27 @@ static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_intel_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
cvt_nid = 0x02;
|
||||
pin_nid = 0x03;
|
||||
return do_patch_intel_hdmi(codec);
|
||||
}
|
||||
|
||||
static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
|
||||
{
|
||||
cvt_nid = 0x02;
|
||||
pin_nid = 0x04;
|
||||
return do_patch_intel_hdmi(codec);
|
||||
}
|
||||
|
||||
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
|
||||
{ .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
@ -694,6 +695,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862802");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862803");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862804");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80860054");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10951392");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -377,6 +377,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||
*/
|
||||
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
||||
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
@ -385,6 +386,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0002");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0003");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0006");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0007");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1339,8 +1339,7 @@ static int get_mux_nids(struct hda_codec *codec)
|
||||
for (i = 0; i < spec->num_adc_nids; i++) {
|
||||
nid = spec->adc_nids[i];
|
||||
while (nid) {
|
||||
type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
|
||||
>> AC_WCAP_TYPE_SHIFT;
|
||||
type = get_wcaps_type(get_wcaps(codec, nid));
|
||||
if (type == AC_WID_PIN)
|
||||
break;
|
||||
n = snd_hda_get_connections(codec, nid, conn,
|
||||
|
@ -379,6 +379,15 @@ struct snd_ice1712 {
|
||||
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
|
||||
void (*set_spdif_clock)(struct snd_ice1712 *ice);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int (*pm_suspend)(struct snd_ice1712 *);
|
||||
int (*pm_resume)(struct snd_ice1712 *);
|
||||
int pm_suspend_enabled:1;
|
||||
int pm_saved_is_spdif_master:1;
|
||||
unsigned int pm_saved_spdif_ctrl;
|
||||
unsigned char pm_saved_spdif_cfg;
|
||||
unsigned int pm_saved_route;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
@ -560,6 +560,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
spin_lock(&ice->reg_lock);
|
||||
old = inb(ICEMT1724(ice, DMA_CONTROL));
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START)
|
||||
@ -570,6 +571,10 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
spin_unlock(&ice->reg_lock);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
/* apps will have to restart stream */
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -2262,7 +2267,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
|
||||
|
||||
|
||||
|
||||
static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
|
||||
static void snd_vt1724_chip_reset(struct snd_ice1712 *ice)
|
||||
{
|
||||
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
|
||||
inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
|
||||
@ -2272,7 +2277,7 @@ static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
|
||||
static int snd_vt1724_chip_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
|
||||
outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
|
||||
@ -2287,6 +2292,14 @@ static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
|
||||
|
||||
outb(0, ICEREG1724(ice, POWERDOWN));
|
||||
|
||||
/* MPU_RX and TX irq masks are cleared later dynamically */
|
||||
outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
|
||||
|
||||
/* don't handle FIFO overrun/underruns (just yet),
|
||||
* since they cause machine lockups
|
||||
*/
|
||||
outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2431,6 +2444,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
||||
snd_vt1724_proc_init(ice);
|
||||
synchronize_irq(pci->irq);
|
||||
|
||||
card->private_data = ice;
|
||||
|
||||
err = pci_request_regions(pci, "ICE1724");
|
||||
if (err < 0) {
|
||||
kfree(ice);
|
||||
@ -2459,14 +2474,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* MPU_RX and TX irq masks are cleared later dynamically */
|
||||
outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
|
||||
|
||||
/* don't handle FIFO overrun/underruns (just yet),
|
||||
* since they cause machine lockups
|
||||
*/
|
||||
outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
|
||||
if (err < 0) {
|
||||
snd_vt1724_free(ice);
|
||||
@ -2650,11 +2657,96 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci)
|
||||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct snd_ice1712 *ice = card->private_data;
|
||||
|
||||
if (!ice->pm_suspend_enabled)
|
||||
return 0;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
|
||||
snd_pcm_suspend_all(ice->pcm);
|
||||
snd_pcm_suspend_all(ice->pcm_pro);
|
||||
snd_pcm_suspend_all(ice->pcm_ds);
|
||||
snd_ac97_suspend(ice->ac97);
|
||||
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
|
||||
ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
|
||||
ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
|
||||
ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
|
||||
if (ice->pm_suspend)
|
||||
ice->pm_suspend(ice);
|
||||
|
||||
pci_disable_device(pci);
|
||||
pci_save_state(pci);
|
||||
pci_set_power_state(pci, pci_choose_state(pci, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_vt1724_resume(struct pci_dev *pci)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct snd_ice1712 *ice = card->private_data;
|
||||
|
||||
if (!ice->pm_suspend_enabled)
|
||||
return 0;
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
|
||||
if (pci_enable_device(pci) < 0) {
|
||||
snd_card_disconnect(card);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
|
||||
snd_vt1724_chip_reset(ice);
|
||||
|
||||
if (snd_vt1724_chip_init(ice) < 0) {
|
||||
snd_card_disconnect(card);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (ice->pm_resume)
|
||||
ice->pm_resume(ice);
|
||||
|
||||
if (ice->pm_saved_is_spdif_master) {
|
||||
/* switching to external clock via SPDIF */
|
||||
ice->set_spdif_clock(ice);
|
||||
} else {
|
||||
/* internal on-card clock */
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
|
||||
}
|
||||
|
||||
update_spdif_bits(ice, ice->pm_saved_spdif_ctrl);
|
||||
|
||||
outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG));
|
||||
outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK));
|
||||
|
||||
if (ice->ac97)
|
||||
snd_ac97_resume(ice->ac97);
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pci_driver driver = {
|
||||
.name = "ICE1724",
|
||||
.id_table = snd_vt1724_ids,
|
||||
.probe = snd_vt1724_probe,
|
||||
.remove = __devexit_p(snd_vt1724_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_vt1724_suspend,
|
||||
.resume = snd_vt1724_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init alsa_card_ice1724_init(void)
|
||||
|
@ -1077,7 +1077,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
|
||||
/*
|
||||
* initialize the chip
|
||||
*/
|
||||
static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
|
||||
static void ak4396_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
static unsigned short ak4396_inits[] = {
|
||||
AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */
|
||||
@ -1087,9 +1087,37 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
|
||||
AK4396_RCH_ATT, 0x00,
|
||||
};
|
||||
|
||||
struct prodigy_hifi_spec *spec;
|
||||
unsigned int i;
|
||||
|
||||
/* initialize ak4396 codec */
|
||||
/* reset codec */
|
||||
ak4396_write(ice, AK4396_CTRL1, 0x86);
|
||||
msleep(100);
|
||||
ak4396_write(ice, AK4396_CTRL1, 0x87);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
|
||||
ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int __devinit prodigy_hd2_resume(struct snd_ice1712 *ice)
|
||||
{
|
||||
/* initialize ak4396 codec and restore previous mixer volumes */
|
||||
struct prodigy_hifi_spec *spec = ice->spec;
|
||||
int i;
|
||||
mutex_lock(&ice->gpio_mutex);
|
||||
ak4396_init(ice);
|
||||
for (i = 0; i < 2; i++)
|
||||
ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff);
|
||||
mutex_unlock(&ice->gpio_mutex);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct prodigy_hifi_spec *spec;
|
||||
|
||||
ice->vt1720 = 0;
|
||||
ice->vt1724 = 1;
|
||||
|
||||
@ -1112,14 +1140,12 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
|
||||
return -ENOMEM;
|
||||
ice->spec = spec;
|
||||
|
||||
/* initialize ak4396 codec */
|
||||
/* reset codec */
|
||||
ak4396_write(ice, AK4396_CTRL1, 0x86);
|
||||
msleep(100);
|
||||
ak4396_write(ice, AK4396_CTRL1, 0x87);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
|
||||
ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
|
||||
#ifdef CONFIG_PM
|
||||
ice->pm_resume = &prodigy_hd2_resume;
|
||||
ice->pm_suspend_enabled = 1;
|
||||
#endif
|
||||
|
||||
ak4396_init(ice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -215,17 +215,8 @@ EXPORT_SYMBOL(oxygen_write_spi);
|
||||
|
||||
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
/* should not need more than about 300 us */
|
||||
timeout = jiffies + msecs_to_jiffies(1);
|
||||
do {
|
||||
if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS)
|
||||
& OXYGEN_2WIRE_BUSY))
|
||||
break;
|
||||
udelay(1);
|
||||
cond_resched();
|
||||
} while (time_after_eq(timeout, jiffies));
|
||||
msleep(1);
|
||||
|
||||
oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
|
||||
oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
|
||||
|
@ -3294,15 +3294,33 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
||||
char *clock_source;
|
||||
int x;
|
||||
|
||||
if (hdsp_check_for_iobox (hdsp)) {
|
||||
snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n");
|
||||
status = hdsp_read(hdsp, HDSP_statusRegister);
|
||||
status2 = hdsp_read(hdsp, HDSP_status2Register);
|
||||
|
||||
snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name,
|
||||
hdsp->card->number + 1);
|
||||
snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
|
||||
hdsp->capture_buffer, hdsp->playback_buffer);
|
||||
snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
|
||||
hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
|
||||
snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
|
||||
snd_iprintf(buffer, "Control2 register: 0x%x\n",
|
||||
hdsp->control2_register);
|
||||
snd_iprintf(buffer, "Status register: 0x%x\n", status);
|
||||
snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
|
||||
|
||||
if (hdsp_check_for_iobox(hdsp)) {
|
||||
snd_iprintf(buffer, "No I/O box connected.\n"
|
||||
"Please connect one and upload firmware.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hdsp_check_for_firmware(hdsp, 0)) {
|
||||
if (hdsp->state & HDSP_FirmwareCached) {
|
||||
if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
|
||||
snd_iprintf(buffer, "Firmware loading from cache failed, please upload manually.\n");
|
||||
snd_iprintf(buffer, "Firmware loading from "
|
||||
"cache failed, "
|
||||
"please upload manually.\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -3319,18 +3337,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
||||
}
|
||||
}
|
||||
|
||||
status = hdsp_read(hdsp, HDSP_statusRegister);
|
||||
status2 = hdsp_read(hdsp, HDSP_status2Register);
|
||||
|
||||
snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1);
|
||||
snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
|
||||
hdsp->capture_buffer, hdsp->playback_buffer);
|
||||
snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
|
||||
hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
|
||||
snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
|
||||
snd_iprintf(buffer, "Control2 register: 0x%x\n", hdsp->control2_register);
|
||||
snd_iprintf(buffer, "Status register: 0x%x\n", status);
|
||||
snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
|
||||
snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff);
|
||||
snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0));
|
||||
snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
|
||||
@ -3351,7 +3357,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
||||
|
||||
snd_iprintf(buffer, "\n");
|
||||
|
||||
|
||||
switch (hdsp_clock_source(hdsp)) {
|
||||
case HDSP_CLOCK_SOURCE_AUTOSYNC:
|
||||
clock_source = "AutoSync";
|
||||
|
@ -834,7 +834,7 @@ static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
|
||||
status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
|
||||
if (status & 1) {
|
||||
if (chip->timer)
|
||||
snd_timer_interrupt(chip->timer, chip->timer->sticks);
|
||||
snd_timer_interrupt(chip->timer, chip->timer_ticks);
|
||||
}
|
||||
snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
|
||||
|
||||
@ -1885,8 +1885,18 @@ static int snd_ymfpci_timer_start(struct snd_timer *timer)
|
||||
unsigned int count;
|
||||
|
||||
chip = snd_timer_chip(timer);
|
||||
count = (timer->sticks << 1) - 1;
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
if (timer->sticks > 1) {
|
||||
chip->timer_ticks = timer->sticks;
|
||||
count = timer->sticks - 1;
|
||||
} else {
|
||||
/*
|
||||
* Divisor 1 is not allowed; fake it by using divisor 2 and
|
||||
* counting two ticks for each interrupt.
|
||||
*/
|
||||
chip->timer_ticks = 2;
|
||||
count = 2 - 1;
|
||||
}
|
||||
snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
|
||||
snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
|
||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||
@ -1909,14 +1919,14 @@ static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer,
|
||||
unsigned long *num, unsigned long *den)
|
||||
{
|
||||
*num = 1;
|
||||
*den = 48000;
|
||||
*den = 96000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_timer_hardware snd_ymfpci_timer_hw = {
|
||||
.flags = SNDRV_TIMER_HW_AUTO,
|
||||
.resolution = 20833, /* 1/fs = 20.8333...us */
|
||||
.ticks = 0x8000,
|
||||
.resolution = 10417, /* 1 / 96 kHz = 10.41666...us */
|
||||
.ticks = 0x10000,
|
||||
.start = snd_ymfpci_timer_start,
|
||||
.stop = snd_ymfpci_timer_stop,
|
||||
.precise_resolution = snd_ymfpci_timer_precise_resolution,
|
||||
|
@ -29,6 +29,7 @@ source "sound/soc/au1x/Kconfig"
|
||||
source "sound/soc/blackfin/Kconfig"
|
||||
source "sound/soc/davinci/Kconfig"
|
||||
source "sound/soc/fsl/Kconfig"
|
||||
source "sound/soc/imx/Kconfig"
|
||||
source "sound/soc/omap/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/s3c24xx/Kconfig"
|
||||
|
@ -1,4 +1,4 @@
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
|
||||
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
|
||||
obj-$(CONFIG_SND_SOC) += codecs/
|
||||
@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SOC) += au1x/
|
||||
obj-$(CONFIG_SND_SOC) += blackfin/
|
||||
obj-$(CONFIG_SND_SOC) += davinci/
|
||||
obj-$(CONFIG_SND_SOC) += fsl/
|
||||
obj-$(CONFIG_SND_SOC) += imx/
|
||||
obj-$(CONFIG_SND_SOC) += omap/
|
||||
obj-$(CONFIG_SND_SOC) += pxa/
|
||||
obj-$(CONFIG_SND_SOC) += s3c24xx/
|
||||
|
@ -56,133 +56,32 @@
|
||||
|
||||
#define MCLK_RATE 12000000
|
||||
|
||||
/*
|
||||
* As shipped the board does not have inputs. However, it is relatively
|
||||
* straightforward to modify the board to hook them up so support is left
|
||||
* in the driver.
|
||||
*/
|
||||
#undef ENABLE_MIC_INPUT
|
||||
|
||||
static struct clk *mclk;
|
||||
|
||||
static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
|
||||
MCLK_RATE, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
clk_disable(mclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
|
||||
|
||||
dev_dbg(rtd->socdev->dev, "shutdown");
|
||||
}
|
||||
|
||||
static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
|
||||
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
|
||||
struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
|
||||
struct ssc_device *ssc = ssc_p->ssc;
|
||||
int ret;
|
||||
|
||||
unsigned int rate;
|
||||
int cmr_div, period;
|
||||
|
||||
if (ssc == NULL) {
|
||||
printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set codec DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set cpu DAI configuration */
|
||||
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The SSC clock dividers depend on the sample rate. The CMR.DIV
|
||||
* field divides the system master clock MCK to drive the SSC TK
|
||||
* signal which provides the codec BCLK. The TCMR.PERIOD and
|
||||
* RCMR.PERIOD fields further divide the BCLK signal to drive
|
||||
* the SSC TF and RF signals which provide the codec DACLRC and
|
||||
* ADCLRC clocks.
|
||||
*
|
||||
* The dividers were determined through trial and error, where a
|
||||
* CMR.DIV value is chosen such that the resulting BCLK value is
|
||||
* divisible, or almost divisible, by (2 * sample rate), and then
|
||||
* the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
|
||||
*/
|
||||
rate = params_rate(params);
|
||||
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */
|
||||
period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
|
||||
break;
|
||||
case 11025:
|
||||
cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */
|
||||
period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
|
||||
break;
|
||||
case 16000:
|
||||
cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */
|
||||
period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
|
||||
break;
|
||||
case 22050:
|
||||
cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */
|
||||
period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */
|
||||
break;
|
||||
case 32000:
|
||||
cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */
|
||||
period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
|
||||
break;
|
||||
case 44100:
|
||||
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
|
||||
period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */
|
||||
break;
|
||||
case 48000:
|
||||
cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */
|
||||
period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
|
||||
break;
|
||||
case 88200:
|
||||
cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
|
||||
period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */
|
||||
break;
|
||||
case 96000:
|
||||
cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */
|
||||
period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "unsupported rate %d"
|
||||
" on at91sam9g20ek board\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set the MCK divider for BCLK */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* set the BCLK divider for DACLRC */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai,
|
||||
ATMEL_SSC_TCMR_PERIOD, period);
|
||||
} else {
|
||||
/* set the BCLK divider for ADCLRC */
|
||||
ret = snd_soc_dai_set_clkdiv(cpu_dai,
|
||||
ATMEL_SSC_RCMR_PERIOD, period);
|
||||
}
|
||||
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
|
||||
}
|
||||
|
||||
static struct snd_soc_ops at91sam9g20ek_ops = {
|
||||
.startup = at91sam9g20ek_startup,
|
||||
.hw_params = at91sam9g20ek_hw_params,
|
||||
.shutdown = at91sam9g20ek_shutdown,
|
||||
};
|
||||
|
||||
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
|
||||
@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = {
|
||||
*/
|
||||
static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct snd_soc_dai *codec_dai = &codec->dai[0];
|
||||
int ret;
|
||||
|
||||
printk(KERN_DEBUG
|
||||
"at91sam9g20ek_wm8731 "
|
||||
": at91sam9g20ek_wm8731_init() called\n");
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
|
||||
MCLK_RATE, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Add specific widgets */
|
||||
snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
|
||||
ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
|
||||
@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
|
||||
snd_soc_dapm_nc_pin(codec, "RLINEIN");
|
||||
snd_soc_dapm_nc_pin(codec, "LLINEIN");
|
||||
|
||||
/* always connected */
|
||||
#ifdef ENABLE_MIC_INPUT
|
||||
snd_soc_dapm_enable_pin(codec, "Int Mic");
|
||||
#else
|
||||
snd_soc_dapm_nc_pin(codec, "Int Mic");
|
||||
#endif
|
||||
|
||||
/* always connected */
|
||||
snd_soc_dapm_enable_pin(codec, "Ext Spk");
|
||||
|
||||
snd_soc_dapm_sync(codec);
|
||||
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Au12x0/Au1550 PSC ALSA ASoC audio support.
|
||||
*
|
||||
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
|
||||
* Manuel Lauss <mano@roarinelk.homelinux.net>
|
||||
* (c) 2007-2009 MSC Vertriebsges.m.b.H.,
|
||||
* Manuel Lauss <manuel.lauss@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@ -19,6 +19,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -29,6 +30,9 @@
|
||||
|
||||
#include "psc.h"
|
||||
|
||||
/* how often to retry failed codec register reads/writes */
|
||||
#define AC97_RW_RETRIES 5
|
||||
|
||||
#define AC97_DIR \
|
||||
(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
|
||||
|
||||
@ -45,6 +49,9 @@
|
||||
#define AC97PCR_CLRFIFO(stype) \
|
||||
((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
|
||||
|
||||
#define AC97STAT_BUSY(stype) \
|
||||
((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
|
||||
|
||||
/* instance data. There can be only one, MacLeod!!!! */
|
||||
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
|
||||
|
||||
@ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
|
||||
{
|
||||
/* FIXME */
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
|
||||
unsigned short data, tmo;
|
||||
|
||||
au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
|
||||
au_sync();
|
||||
|
||||
tmo = 1000;
|
||||
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
|
||||
udelay(2);
|
||||
|
||||
if (!tmo)
|
||||
data = 0xffff;
|
||||
else
|
||||
data = au_readl(AC97_CDC(pscdata)) & 0xffff;
|
||||
unsigned short data, retry, tmo;
|
||||
|
||||
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
|
||||
au_sync();
|
||||
|
||||
return data;
|
||||
retry = AC97_RW_RETRIES;
|
||||
do {
|
||||
mutex_lock(&pscdata->lock);
|
||||
|
||||
au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
|
||||
AC97_CDC(pscdata));
|
||||
au_sync();
|
||||
|
||||
tmo = 2000;
|
||||
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
|
||||
&& --tmo)
|
||||
udelay(2);
|
||||
|
||||
data = au_readl(AC97_CDC(pscdata)) & 0xffff;
|
||||
|
||||
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
|
||||
au_sync();
|
||||
|
||||
mutex_unlock(&pscdata->lock);
|
||||
} while (--retry && !tmo);
|
||||
|
||||
return retry ? data : 0xffff;
|
||||
}
|
||||
|
||||
/* AC97 controller writes to codec register */
|
||||
@ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
|
||||
{
|
||||
/* FIXME */
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
|
||||
unsigned int tmo;
|
||||
|
||||
au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
|
||||
au_sync();
|
||||
tmo = 1000;
|
||||
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
|
||||
au_sync();
|
||||
unsigned int tmo, retry;
|
||||
|
||||
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
|
||||
au_sync();
|
||||
|
||||
retry = AC97_RW_RETRIES;
|
||||
do {
|
||||
mutex_lock(&pscdata->lock);
|
||||
|
||||
au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
|
||||
AC97_CDC(pscdata));
|
||||
au_sync();
|
||||
|
||||
tmo = 2000;
|
||||
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
|
||||
&& --tmo)
|
||||
udelay(2);
|
||||
|
||||
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
|
||||
au_sync();
|
||||
|
||||
mutex_unlock(&pscdata->lock);
|
||||
} while (--retry && !tmo);
|
||||
}
|
||||
|
||||
/* AC97 controller asserts a warm reset */
|
||||
@ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
|
||||
au_sync();
|
||||
|
||||
/* wait for PSC to indicate it's ready */
|
||||
i = 100000;
|
||||
i = 1000;
|
||||
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
|
||||
au_sync();
|
||||
msleep(1);
|
||||
|
||||
if (i == 0) {
|
||||
printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
|
||||
@ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
|
||||
au_sync();
|
||||
|
||||
/* wait for AC97 core to become ready */
|
||||
i = 100000;
|
||||
i = 1000;
|
||||
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
|
||||
au_sync();
|
||||
msleep(1);
|
||||
if (i == 0)
|
||||
printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
|
||||
}
|
||||
@ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
/* FIXME */
|
||||
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
|
||||
unsigned long r, stat;
|
||||
unsigned long r, ro, stat;
|
||||
int chans, stype = SUBSTREAM_TYPE(substream);
|
||||
|
||||
chans = params_channels(params);
|
||||
|
||||
r = au_readl(AC97_CFG(pscdata));
|
||||
r = ro = au_readl(AC97_CFG(pscdata));
|
||||
stat = au_readl(AC97_STAT(pscdata));
|
||||
|
||||
/* already active? */
|
||||
@ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
(pscdata->rate != params_rate(params)))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* disable AC97 device controller first */
|
||||
au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
|
||||
au_sync();
|
||||
|
||||
/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
|
||||
r &= ~PSC_AC97CFG_LEN_MASK;
|
||||
@ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
r |= PSC_AC97CFG_RXSLOT_ENA(4);
|
||||
}
|
||||
|
||||
/* finally enable the AC97 controller again */
|
||||
/* do we need to poke the hardware? */
|
||||
if (!(r ^ ro))
|
||||
goto out;
|
||||
|
||||
/* ac97 engine is about to be disabled */
|
||||
mutex_lock(&pscdata->lock);
|
||||
|
||||
/* disable AC97 device controller first... */
|
||||
au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
|
||||
au_sync();
|
||||
|
||||
/* ...wait for it... */
|
||||
while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
|
||||
asm volatile ("nop");
|
||||
|
||||
/* ...write config... */
|
||||
au_writel(r, AC97_CFG(pscdata));
|
||||
au_sync();
|
||||
|
||||
/* ...enable the AC97 controller again... */
|
||||
au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
|
||||
au_sync();
|
||||
|
||||
/* ...and wait for ready bit */
|
||||
while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
|
||||
asm volatile ("nop");
|
||||
|
||||
mutex_unlock(&pscdata->lock);
|
||||
|
||||
pscdata->cfg = r;
|
||||
pscdata->rate = params_rate(params);
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
|
||||
au_sync();
|
||||
au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
|
||||
au_sync();
|
||||
break;
|
||||
@ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
|
||||
au_sync();
|
||||
|
||||
while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
|
||||
asm volatile ("nop");
|
||||
|
||||
au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
|
||||
au_sync();
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
|
||||
if (!au1xpsc_ac97_workdata)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&au1xpsc_ac97_workdata->lock);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
@ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
|
||||
goto out1;
|
||||
|
||||
/* configuration: max dma trigger threshold, enable ac97 */
|
||||
au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
|
||||
PSC_AC97CFG_TT_FIFO8 |
|
||||
PSC_AC97CFG_DE_ENABLE;
|
||||
au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
|
||||
PSC_AC97CFG_TT_FIFO8 |
|
||||
PSC_AC97CFG_DE_ENABLE;
|
||||
|
||||
/* preserve PSC clock source set up by platform (dev.platform_data
|
||||
* is already occupied by soc layer)
|
||||
@ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
|
||||
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
|
||||
MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user