mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08: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: (250 commits) ALSA: hda: Storage class should be before const qualifier ASoC: tpa6130a2: Remove CPVSS and HPVdd supplies ASoC: tpa6130a2: Define output pins with SND_SOC_DAPM_OUTPUT ASoC: sdp4430 - add sdp4430 pcm ops to DAI. ASoC: TWL6040: Enable earphone path in codec ASoC: SDP4430: Add support for Earphone speaker ASoC: SDP4430: Add sdp4430 machine driver ASoC: tlv320dac33: Avoid powering off while in BIAS_OFF ASoC: tlv320dac33: Use dev_dbg in dac33_hard_power function ALSA: sound/pci/asihpi: Use kzalloc ALSA: hdmi - dont fail on extra nodes ALSA: intelhdmi - add id for the CougarPoint chipset ALSA: intelhdmi - user friendly codec name ALSA: intelhdmi - add dependency on SND_DYNAMIC_MINORS ALSA: asihpi: incorrect range check ALSA: asihpi: testing the wrong variable ALSA: es1688: add pedantic range checks ARM: McBSP: Add support for omap4 in McBSP driver ARM: McBSP: Fix request for irq in OMAP4 OMAP: McBSP: Add 32-bit mode support ...
This commit is contained in:
commit
7f06a8b26a
@ -5518,34 +5518,41 @@ struct _snd_pcm_runtime {
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
For the raw data, <structfield>size</structfield> field must be
|
||||
set properly. This specifies the maximum size of the proc file access.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The callback is much more complicated than the text-file
|
||||
version. You need to use a low-level I/O functions such as
|
||||
The read/write callbacks of raw mode are more direct than the text mode.
|
||||
You need to use a low-level I/O functions such as
|
||||
<function>copy_from/to_user()</function> to transfer the
|
||||
data.
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
static long my_file_io_read(struct snd_info_entry *entry,
|
||||
static ssize_t my_file_io_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file,
|
||||
char *buf,
|
||||
unsigned long count,
|
||||
unsigned long pos)
|
||||
size_t count,
|
||||
loff_t pos)
|
||||
{
|
||||
long size = count;
|
||||
if (pos + size > local_max_size)
|
||||
size = local_max_size - pos;
|
||||
if (copy_to_user(buf, local_data + pos, size))
|
||||
if (copy_to_user(buf, local_data + pos, count))
|
||||
return -EFAULT;
|
||||
return size;
|
||||
return count;
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
If the size of the info entry has been set up properly,
|
||||
<structfield>count</structfield> and <structfield>pos</structfield> are
|
||||
guaranteed to fit within 0 and the given size.
|
||||
You don't have to check the range in the callbacks unless any
|
||||
other condition is required.
|
||||
|
||||
</para>
|
||||
|
||||
</chapter>
|
||||
|
@ -227,6 +227,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-asihpi
|
||||
-----------------
|
||||
|
||||
Module for AudioScience ASI soundcards
|
||||
|
||||
enable_hpi_hwdep - enable HPI hwdep for AudioScience soundcard
|
||||
|
||||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-atiixp
|
||||
-----------------
|
||||
|
||||
@ -622,28 +632,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-es968
|
||||
----------------
|
||||
|
||||
Module for sound cards based on ESS ES968 chip (PnP only).
|
||||
|
||||
This module supports multiple cards, PnP and autoprobe.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-es1688
|
||||
-----------------
|
||||
|
||||
Module for ESS AudioDrive ES-1688 and ES-688 sound cards.
|
||||
|
||||
port - port # for ES-1688 chip (0x220,0x240,0x260)
|
||||
fm_port - port # for OPL3 (option; share the same port as default)
|
||||
isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
|
||||
mpu_port - port # for MPU-401 port (0x300,0x310,0x320,0x330), -1 = disable (default)
|
||||
irq - IRQ # for ES-1688 chip (5,7,9,10)
|
||||
mpu_irq - IRQ # for MPU-401 port (5,7,9,10)
|
||||
fm_port - port # for OPL3 (option; share the same port as default)
|
||||
|
||||
with isapnp=0, the following additional options are available:
|
||||
port - port # for ES-1688 chip (0x220,0x240,0x260)
|
||||
irq - IRQ # for ES-1688 chip (5,7,9,10)
|
||||
dma8 - DMA # for ES-1688 chip (0,1,3)
|
||||
|
||||
This module supports multiple cards and autoprobe (without MPU-401 port).
|
||||
This module supports multiple cards and autoprobe (without MPU-401 port)
|
||||
and PnP with the ES968 chip.
|
||||
|
||||
Module snd-es18xx
|
||||
-----------------
|
||||
|
@ -204,7 +204,6 @@ generic parser regardless of the codec. Usually the codec-specific
|
||||
parser is much better than the generic parser (as now). Thus this
|
||||
option is more about the debugging purpose.
|
||||
|
||||
|
||||
Speaker and Headphone Output
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
One of the most frequent (and obvious) bugs with HD-audio is the
|
||||
@ -600,6 +599,9 @@ probing, the proc file is available, so you can get the raw codec
|
||||
information before modified by the driver. Of course, the driver
|
||||
isn't usable with `probe_only=1`. But you can continue the
|
||||
configuration via hwdep sysfs file if hda-reconfig option is enabled.
|
||||
Using `probe_only` mask 2 skips the reset of HDA codecs (use
|
||||
`probe_only=3` as module option). The hwdep interface can be used
|
||||
to determine the BIOS codec initialization.
|
||||
|
||||
|
||||
hda-verb
|
||||
|
@ -600,7 +600,11 @@ static __init void dm365_evm_init(void)
|
||||
/* maybe setup mmc1/etc ... _after_ mmc0 */
|
||||
evm_init_cpld();
|
||||
|
||||
#ifdef CONFIG_SND_DM365_AIC3X_CODEC
|
||||
dm365_init_asp(&dm365_evm_snd_data);
|
||||
#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
|
||||
dm365_init_vc(&dm365_evm_snd_data);
|
||||
#endif
|
||||
dm365_init_rtc();
|
||||
dm365_init_ks(&dm365evm_ks_data);
|
||||
|
||||
|
@ -187,32 +187,28 @@ static struct omap_mcbsp_platform_data omap44xx_mcbsp_pdata[] = {
|
||||
.phys_base = OMAP44XX_MCBSP1_BASE,
|
||||
.dma_rx_sync = OMAP44XX_DMA_MCBSP1_RX,
|
||||
.dma_tx_sync = OMAP44XX_DMA_MCBSP1_TX,
|
||||
.rx_irq = INT_24XX_MCBSP1_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP1_IRQ_TX,
|
||||
.tx_irq = OMAP44XX_IRQ_MCBSP1,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP44XX_MCBSP2_BASE,
|
||||
.dma_rx_sync = OMAP44XX_DMA_MCBSP2_RX,
|
||||
.dma_tx_sync = OMAP44XX_DMA_MCBSP2_TX,
|
||||
.rx_irq = INT_24XX_MCBSP2_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP2_IRQ_TX,
|
||||
.tx_irq = OMAP44XX_IRQ_MCBSP2,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP44XX_MCBSP3_BASE,
|
||||
.dma_rx_sync = OMAP44XX_DMA_MCBSP3_RX,
|
||||
.dma_tx_sync = OMAP44XX_DMA_MCBSP3_TX,
|
||||
.rx_irq = INT_24XX_MCBSP3_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP3_IRQ_TX,
|
||||
.tx_irq = OMAP44XX_IRQ_MCBSP3,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
},
|
||||
{
|
||||
.phys_base = OMAP44XX_MCBSP4_BASE,
|
||||
.dma_rx_sync = OMAP44XX_DMA_MCBSP4_RX,
|
||||
.dma_tx_sync = OMAP44XX_DMA_MCBSP4_TX,
|
||||
.rx_irq = INT_24XX_MCBSP4_IRQ_RX,
|
||||
.tx_irq = INT_24XX_MCBSP4_IRQ_TX,
|
||||
.tx_irq = OMAP44XX_IRQ_MCBSP4,
|
||||
.ops = &omap2_mcbsp_ops,
|
||||
},
|
||||
};
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include <mach/regs-mem.h>
|
||||
#include <mach/regs-lcd.h>
|
||||
#include <mach/regs-sdi.h>
|
||||
#include <plat/regs-s3c2412-iis.h>
|
||||
#include <plat/regs-iis.h>
|
||||
#include <plat/regs-spi.h>
|
||||
|
||||
@ -119,13 +118,11 @@ static struct s3c24xx_dma_map __initdata s3c2412_dma_mappings[] = {
|
||||
.name = "i2s-sdi",
|
||||
.channels = MAP(S3C2412_DMAREQSEL_I2SRX),
|
||||
.channels_rx = MAP(S3C2412_DMAREQSEL_I2SRX),
|
||||
.hw_addr.from = S3C2410_PA_IIS + S3C2412_IISRXD,
|
||||
},
|
||||
[DMACH_I2S_OUT] = {
|
||||
.name = "i2s-sdo",
|
||||
.channels = MAP(S3C2412_DMAREQSEL_I2STX),
|
||||
.channels_rx = MAP(S3C2412_DMAREQSEL_I2STX),
|
||||
.hw_addr.to = S3C2410_PA_IIS + S3C2412_IISTXD,
|
||||
},
|
||||
[DMACH_USB_EP1] = {
|
||||
.name = "usb-ep1",
|
||||
|
@ -149,6 +149,8 @@
|
||||
#define OMAP_MCBSP_REG_WAKEUPEN 0xA8
|
||||
#define OMAP_MCBSP_REG_XCCR 0xAC
|
||||
#define OMAP_MCBSP_REG_RCCR 0xB0
|
||||
#define OMAP_MCBSP_REG_XBUFFSTAT 0xB4
|
||||
#define OMAP_MCBSP_REG_RBUFFSTAT 0xB8
|
||||
#define OMAP_MCBSP_REG_SSELCR 0xBC
|
||||
|
||||
#define OMAP_ST_REG_REV 0x00
|
||||
@ -471,6 +473,8 @@ 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);
|
||||
u16 omap_mcbsp_get_tx_delay(unsigned int id);
|
||||
u16 omap_mcbsp_get_rx_delay(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)
|
||||
@ -479,6 +483,8 @@ 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 u16 omap_mcbsp_get_tx_delay(unsigned int id) { return 0; }
|
||||
static inline u16 omap_mcbsp_get_rx_delay(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);
|
||||
|
@ -489,7 +489,7 @@ void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
|
||||
if (!cpu_is_omap34xx())
|
||||
if (!cpu_is_omap34xx() && !cpu_is_omap44xx())
|
||||
return;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
@ -511,7 +511,7 @@ void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
|
||||
if (!cpu_is_omap34xx())
|
||||
if (!cpu_is_omap34xx() && !cpu_is_omap44xx())
|
||||
return;
|
||||
|
||||
if (!omap_mcbsp_check_valid_id(id)) {
|
||||
@ -560,6 +560,61 @@ u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
|
||||
|
||||
#define MCBSP2_FIFO_SIZE 0x500 /* 1024 + 256 locations */
|
||||
#define MCBSP1345_FIFO_SIZE 0x80 /* 128 locations */
|
||||
/*
|
||||
* omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
|
||||
*/
|
||||
u16 omap_mcbsp_get_tx_delay(unsigned int id)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
u16 buffstat;
|
||||
|
||||
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);
|
||||
|
||||
/* Returns the number of free locations in the buffer */
|
||||
buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
|
||||
|
||||
/* Number of slots are different in McBSP ports */
|
||||
if (mcbsp->id == 2)
|
||||
return MCBSP2_FIFO_SIZE - buffstat;
|
||||
else
|
||||
return MCBSP1345_FIFO_SIZE - buffstat;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_get_tx_delay);
|
||||
|
||||
/*
|
||||
* omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
|
||||
* to reach the threshold value (when the DMA will be triggered to read it)
|
||||
*/
|
||||
u16 omap_mcbsp_get_rx_delay(unsigned int id)
|
||||
{
|
||||
struct omap_mcbsp *mcbsp;
|
||||
u16 buffstat, threshold;
|
||||
|
||||
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);
|
||||
|
||||
/* Returns the number of used locations in the buffer */
|
||||
buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
|
||||
/* RX threshold */
|
||||
threshold = MCBSP_READ(mcbsp, THRSH1);
|
||||
|
||||
/* Return the number of location till we reach the threshold limit */
|
||||
if (threshold <= buffstat)
|
||||
return 0;
|
||||
else
|
||||
return threshold - buffstat;
|
||||
}
|
||||
EXPORT_SYMBOL(omap_mcbsp_get_rx_delay);
|
||||
|
||||
/*
|
||||
* omap_mcbsp_get_dma_op_mode just return the current configured
|
||||
* operating mode for the mcbsp channel
|
||||
@ -587,7 +642,7 @@ 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()) {
|
||||
if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
|
||||
u16 syscon;
|
||||
|
||||
syscon = MCBSP_READ(mcbsp, SYSCON);
|
||||
@ -610,7 +665,7 @@ static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
|
||||
/*
|
||||
* Disable wakup behavior, smart idle and all wakeups
|
||||
*/
|
||||
if (cpu_is_omap34xx()) {
|
||||
if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
|
||||
u16 syscon;
|
||||
|
||||
syscon = MCBSP_READ(mcbsp, SYSCON);
|
||||
@ -724,14 +779,17 @@ int omap_mcbsp_request(unsigned int id)
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
init_completion(&mcbsp->rx_irq_completion);
|
||||
err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler,
|
||||
if (mcbsp->rx_irq) {
|
||||
init_completion(&mcbsp->rx_irq_completion);
|
||||
err = request_irq(mcbsp->rx_irq,
|
||||
omap_mcbsp_rx_irq_handler,
|
||||
0, "McBSP", (void *)mcbsp);
|
||||
if (err != 0) {
|
||||
dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
|
||||
"for McBSP%d\n", mcbsp->rx_irq,
|
||||
mcbsp->id);
|
||||
goto err_free_irq;
|
||||
if (err != 0) {
|
||||
dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
|
||||
"for McBSP%d\n", mcbsp->rx_irq,
|
||||
mcbsp->id);
|
||||
goto err_free_irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -781,7 +839,8 @@ void omap_mcbsp_free(unsigned int id)
|
||||
|
||||
if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
|
||||
/* Free IRQs */
|
||||
free_irq(mcbsp->rx_irq, (void *)mcbsp);
|
||||
if (mcbsp->rx_irq)
|
||||
free_irq(mcbsp->rx_irq, (void *)mcbsp);
|
||||
free_irq(mcbsp->tx_irq, (void *)mcbsp);
|
||||
}
|
||||
|
||||
@ -855,7 +914,7 @@ void omap_mcbsp_start(unsigned int id, int tx, int rx)
|
||||
MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
|
||||
}
|
||||
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
|
||||
/* Release the transmitter and receiver */
|
||||
w = MCBSP_READ_CACHE(mcbsp, XCCR);
|
||||
w &= ~(tx ? XDISABLE : 0);
|
||||
@ -885,7 +944,7 @@ void omap_mcbsp_stop(unsigned int id, int tx, int rx)
|
||||
|
||||
/* Reset transmitter */
|
||||
tx &= 1;
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
|
||||
w = MCBSP_READ_CACHE(mcbsp, XCCR);
|
||||
w |= (tx ? XDISABLE : 0);
|
||||
MCBSP_WRITE(mcbsp, XCCR, w);
|
||||
@ -895,7 +954,7 @@ void omap_mcbsp_stop(unsigned int id, int tx, int rx)
|
||||
|
||||
/* Reset receiver */
|
||||
rx &= 1;
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx()) {
|
||||
if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
|
||||
w = MCBSP_READ_CACHE(mcbsp, RCCR);
|
||||
w |= (rx ? RDISABLE : 0);
|
||||
MCBSP_WRITE(mcbsp, RCCR, w);
|
||||
|
@ -81,6 +81,18 @@ static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value);
|
||||
}
|
||||
|
||||
static int wm8994_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
|
||||
struct wm8994 *wm8994 = wm8994_gpio->wm8994;
|
||||
|
||||
if (!wm8994->irq_base)
|
||||
return -EINVAL;
|
||||
|
||||
return wm8994->irq_base + offset;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||
{
|
||||
|
@ -53,6 +53,10 @@ config MFD_SH_MOBILE_SDHI
|
||||
This driver supports the SDHI hardware block found in many
|
||||
SuperH Mobile SoCs.
|
||||
|
||||
config MFD_DAVINCI_VOICECODEC
|
||||
tristate
|
||||
select MFD_CORE
|
||||
|
||||
config MFD_DM355EVM_MSP
|
||||
bool "DaVinci DM355 EVM microcontroller"
|
||||
depends on I2C && MACH_DAVINCI_DM355_EVM
|
||||
@ -297,9 +301,9 @@ config MFD_WM8350_I2C
|
||||
selected to enable support for the functionality of the chip.
|
||||
|
||||
config MFD_WM8994
|
||||
tristate "Support Wolfson Microelectronics WM8994"
|
||||
bool "Support Wolfson Microelectronics WM8994"
|
||||
select MFD_CORE
|
||||
depends on I2C
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
help
|
||||
The WM8994 is a highly integrated hi-fi CODEC designed for
|
||||
smartphone applicatiosn. As well as audio functionality it
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
|
||||
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
|
||||
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
|
||||
|
||||
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||
|
||||
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
|
||||
@ -25,7 +26,7 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
|
||||
wm8350-objs += wm8350-irq.o
|
||||
obj-$(CONFIG_MFD_WM8350) += wm8350.o
|
||||
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
|
||||
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o
|
||||
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
|
||||
|
||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||
obj-$(CONFIG_MENELAUS) += menelaus.o
|
||||
|
190
drivers/mfd/davinci_voicecodec.c
Normal file
190
drivers/mfd/davinci_voicecodec.c
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* DaVinci Voice Codec Core Interface for TI platforms
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments, Inc
|
||||
*
|
||||
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include <linux/mfd/davinci_voicecodec.h>
|
||||
|
||||
u32 davinci_vc_read(struct davinci_vc *davinci_vc, int reg)
|
||||
{
|
||||
return __raw_readl(davinci_vc->base + reg);
|
||||
}
|
||||
|
||||
void davinci_vc_write(struct davinci_vc *davinci_vc,
|
||||
int reg, u32 val)
|
||||
{
|
||||
__raw_writel(val, davinci_vc->base + reg);
|
||||
}
|
||||
|
||||
static int __init davinci_vc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_vc *davinci_vc;
|
||||
struct resource *res, *mem;
|
||||
struct mfd_cell *cell = NULL;
|
||||
int ret;
|
||||
|
||||
davinci_vc = kzalloc(sizeof(struct davinci_vc), GFP_KERNEL);
|
||||
if (!davinci_vc) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"could not allocate memory for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
davinci_vc->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(davinci_vc->clk)) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"could not get the clock for voice codec\n");
|
||||
ret = -ENODEV;
|
||||
goto fail1;
|
||||
}
|
||||
clk_enable(davinci_vc->clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no mem resource\n");
|
||||
ret = -ENODEV;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
davinci_vc->pbase = res->start;
|
||||
davinci_vc->base_size = resource_size(res);
|
||||
|
||||
mem = request_mem_region(davinci_vc->pbase, davinci_vc->base_size,
|
||||
pdev->name);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "VCIF region already claimed\n");
|
||||
ret = -EBUSY;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
davinci_vc->base = ioremap(davinci_vc->pbase, davinci_vc->base_size);
|
||||
if (!davinci_vc->base) {
|
||||
dev_err(&pdev->dev, "can't ioremap mem resource.\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no DMA resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
|
||||
davinci_vc->davinci_vcif.dma_tx_addr =
|
||||
(dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_WFIFO);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no DMA resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
|
||||
davinci_vc->davinci_vcif.dma_rx_addr =
|
||||
(dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_RFIFO);
|
||||
|
||||
davinci_vc->dev = &pdev->dev;
|
||||
davinci_vc->pdev = pdev;
|
||||
|
||||
/* Voice codec interface client */
|
||||
cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL];
|
||||
cell->name = "davinci_vcif";
|
||||
cell->driver_data = davinci_vc;
|
||||
|
||||
/* Voice codec CQ93VC client */
|
||||
cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL];
|
||||
cell->name = "cq93vc";
|
||||
cell->driver_data = davinci_vc;
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
|
||||
DAVINCI_VC_CELLS, NULL, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "fail to register client devices\n");
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail4:
|
||||
iounmap(davinci_vc->base);
|
||||
fail3:
|
||||
release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
|
||||
fail2:
|
||||
clk_disable(davinci_vc->clk);
|
||||
clk_put(davinci_vc->clk);
|
||||
davinci_vc->clk = NULL;
|
||||
fail1:
|
||||
kfree(davinci_vc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit davinci_vc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_vc *davinci_vc = platform_get_drvdata(pdev);
|
||||
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
|
||||
iounmap(davinci_vc->base);
|
||||
release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
|
||||
|
||||
clk_disable(davinci_vc->clk);
|
||||
clk_put(davinci_vc->clk);
|
||||
davinci_vc->clk = NULL;
|
||||
|
||||
kfree(davinci_vc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver davinci_vc_driver = {
|
||||
.driver = {
|
||||
.name = "davinci_voicecodec",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __devexit_p(davinci_vc_remove),
|
||||
};
|
||||
|
||||
static int __init davinci_vc_init(void)
|
||||
{
|
||||
return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe);
|
||||
}
|
||||
module_init(davinci_vc_init);
|
||||
|
||||
static void __exit davinci_vc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&davinci_vc_driver);
|
||||
}
|
||||
module_exit(davinci_vc_exit);
|
||||
|
||||
MODULE_AUTHOR("Miguel Aguilar");
|
||||
MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface");
|
||||
MODULE_LICENSE("GPL");
|
@ -109,7 +109,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
|
||||
defined(CONFIG_SND_SOC_TWL6030) || defined(CONFIG_SND_SOC_TWL6030_MODULE)
|
||||
defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE)
|
||||
#define twl_has_codec() true
|
||||
#else
|
||||
#define twl_has_codec() false
|
||||
@ -708,7 +708,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
|
||||
/* Phoenix*/
|
||||
if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
|
||||
sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
|
||||
child = add_child(sub_chip_id, "twl6030_codec",
|
||||
child = add_child(sub_chip_id, "twl6040_codec",
|
||||
pdata->codec, sizeof(*pdata->codec),
|
||||
false, 0, 0);
|
||||
if (IS_ERR(child))
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
#include <linux/mfd/wm831x/pdata.h>
|
||||
#include <linux/mfd/wm831x/gpio.h>
|
||||
#include <linux/mfd/wm831x/irq.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -388,12 +389,41 @@ static void wm831x_irq_mask(unsigned int irq)
|
||||
wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static int wm831x_irq_set_type(unsigned int irq, unsigned int type)
|
||||
{
|
||||
struct wm831x *wm831x = get_irq_chip_data(irq);
|
||||
int val;
|
||||
|
||||
irq = irq - wm831x->irq_base;
|
||||
|
||||
if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11)
|
||||
return -EINVAL;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
val = WM831X_GPN_INT_MODE;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
val = WM831X_GPN_POL;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq,
|
||||
WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
|
||||
}
|
||||
|
||||
static struct irq_chip wm831x_irq_chip = {
|
||||
.name = "wm831x",
|
||||
.bus_lock = wm831x_irq_lock,
|
||||
.bus_sync_unlock = wm831x_irq_sync_unlock,
|
||||
.mask = wm831x_irq_mask,
|
||||
.unmask = wm831x_irq_unmask,
|
||||
.set_type = wm831x_irq_set_type,
|
||||
};
|
||||
|
||||
/* The processing of the primary interrupt occurs in a thread so that
|
||||
|
@ -173,9 +173,34 @@ static struct mfd_cell wm8994_regulator_devs[] = {
|
||||
{ .name = "wm8994-ldo", .id = 2 },
|
||||
};
|
||||
|
||||
static struct resource wm8994_codec_resources[] = {
|
||||
{
|
||||
.start = WM8994_IRQ_TEMP_SHUT,
|
||||
.end = WM8994_IRQ_TEMP_WARN,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource wm8994_gpio_resources[] = {
|
||||
{
|
||||
.start = WM8994_IRQ_GPIO(1),
|
||||
.end = WM8994_IRQ_GPIO(11),
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell wm8994_devs[] = {
|
||||
{ .name = "wm8994-codec" },
|
||||
{ .name = "wm8994-gpio" },
|
||||
{
|
||||
.name = "wm8994-codec",
|
||||
.num_resources = ARRAY_SIZE(wm8994_codec_resources),
|
||||
.resources = wm8994_codec_resources,
|
||||
},
|
||||
|
||||
{
|
||||
.name = "wm8994-gpio",
|
||||
.num_resources = ARRAY_SIZE(wm8994_gpio_resources),
|
||||
.resources = wm8994_gpio_resources,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@ -236,6 +261,11 @@ static int wm8994_device_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
|
||||
WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
|
||||
|
||||
ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
|
||||
&wm8994->ldo_regs);
|
||||
if (ret < 0)
|
||||
@ -348,6 +378,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
|
||||
|
||||
|
||||
if (pdata) {
|
||||
wm8994->irq_base = pdata->irq_base;
|
||||
wm8994->gpio_base = pdata->gpio_base;
|
||||
|
||||
/* GPIO configuration is only applied if it's non-zero */
|
||||
@ -375,16 +406,20 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
|
||||
WM8994_LDO1_DISCH, 0);
|
||||
}
|
||||
|
||||
wm8994_irq_init(wm8994);
|
||||
|
||||
ret = mfd_add_devices(wm8994->dev, -1,
|
||||
wm8994_devs, ARRAY_SIZE(wm8994_devs),
|
||||
NULL, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
|
||||
goto err_enable;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
wm8994_irq_exit(wm8994);
|
||||
err_enable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
|
||||
wm8994->supplies);
|
||||
@ -401,6 +436,7 @@ err:
|
||||
static void wm8994_device_exit(struct wm8994 *wm8994)
|
||||
{
|
||||
mfd_remove_devices(wm8994->dev);
|
||||
wm8994_irq_exit(wm8994);
|
||||
regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
|
||||
wm8994->supplies);
|
||||
regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
|
||||
@ -469,6 +505,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
|
||||
wm8994->control_data = i2c;
|
||||
wm8994->read_dev = wm8994_i2c_read_device;
|
||||
wm8994->write_dev = wm8994_i2c_write_device;
|
||||
wm8994->irq = i2c->irq;
|
||||
|
||||
return wm8994_device_init(wm8994, id->driver_data, i2c->irq);
|
||||
}
|
||||
|
310
drivers/mfd/wm8994-irq.c
Normal file
310
drivers/mfd/wm8994-irq.c
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* wm8994-irq.c -- Interrupt controller support for Wolfson WM8994
|
||||
*
|
||||
* Copyright 2010 Wolfson Microelectronics PLC.
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/mfd/wm8994/core.h>
|
||||
#include <linux/mfd/wm8994/registers.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
struct wm8994_irq_data {
|
||||
int reg;
|
||||
int mask;
|
||||
};
|
||||
|
||||
static struct wm8994_irq_data wm8994_irqs[] = {
|
||||
[WM8994_IRQ_TEMP_SHUT] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_TEMP_SHUT_EINT,
|
||||
},
|
||||
[WM8994_IRQ_MIC1_DET] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_MIC1_DET_EINT,
|
||||
},
|
||||
[WM8994_IRQ_MIC1_SHRT] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_MIC1_SHRT_EINT,
|
||||
},
|
||||
[WM8994_IRQ_MIC2_DET] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_MIC2_DET_EINT,
|
||||
},
|
||||
[WM8994_IRQ_MIC2_SHRT] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_MIC2_SHRT_EINT,
|
||||
},
|
||||
[WM8994_IRQ_FLL1_LOCK] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_FLL1_LOCK_EINT,
|
||||
},
|
||||
[WM8994_IRQ_FLL2_LOCK] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_FLL2_LOCK_EINT,
|
||||
},
|
||||
[WM8994_IRQ_SRC1_LOCK] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_SRC1_LOCK_EINT,
|
||||
},
|
||||
[WM8994_IRQ_SRC2_LOCK] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_SRC2_LOCK_EINT,
|
||||
},
|
||||
[WM8994_IRQ_AIF1DRC1_SIG_DET] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_AIF1DRC1_SIG_DET,
|
||||
},
|
||||
[WM8994_IRQ_AIF1DRC2_SIG_DET] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_AIF1DRC2_SIG_DET_EINT,
|
||||
},
|
||||
[WM8994_IRQ_AIF2DRC_SIG_DET] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_AIF2DRC_SIG_DET_EINT,
|
||||
},
|
||||
[WM8994_IRQ_FIFOS_ERR] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_FIFOS_ERR_EINT,
|
||||
},
|
||||
[WM8994_IRQ_WSEQ_DONE] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_WSEQ_DONE_EINT,
|
||||
},
|
||||
[WM8994_IRQ_DCS_DONE] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_DCS_DONE_EINT,
|
||||
},
|
||||
[WM8994_IRQ_TEMP_WARN] = {
|
||||
.reg = 2,
|
||||
.mask = WM8994_TEMP_WARN_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(1)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP1_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(2)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP2_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(3)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP3_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(4)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP4_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(5)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP5_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(6)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP6_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(7)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP7_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(8)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP8_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(9)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP8_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(10)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP10_EINT,
|
||||
},
|
||||
[WM8994_IRQ_GPIO(11)] = {
|
||||
.reg = 1,
|
||||
.mask = WM8994_GP11_EINT,
|
||||
},
|
||||
};
|
||||
|
||||
static inline int irq_data_to_status_reg(struct wm8994_irq_data *irq_data)
|
||||
{
|
||||
return WM8994_INTERRUPT_STATUS_1 - 1 + irq_data->reg;
|
||||
}
|
||||
|
||||
static inline int irq_data_to_mask_reg(struct wm8994_irq_data *irq_data)
|
||||
{
|
||||
return WM8994_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
|
||||
}
|
||||
|
||||
static inline struct wm8994_irq_data *irq_to_wm8994_irq(struct wm8994 *wm8994,
|
||||
int irq)
|
||||
{
|
||||
return &wm8994_irqs[irq - wm8994->irq_base];
|
||||
}
|
||||
|
||||
static void wm8994_irq_lock(unsigned int irq)
|
||||
{
|
||||
struct wm8994 *wm8994 = get_irq_chip_data(irq);
|
||||
|
||||
mutex_lock(&wm8994->irq_lock);
|
||||
}
|
||||
|
||||
static void wm8994_irq_sync_unlock(unsigned int irq)
|
||||
{
|
||||
struct wm8994 *wm8994 = get_irq_chip_data(irq);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
|
||||
/* If there's been a change in the mask write it back
|
||||
* to the hardware. */
|
||||
if (wm8994->irq_masks_cur[i] != wm8994->irq_masks_cache[i]) {
|
||||
wm8994->irq_masks_cache[i] = wm8994->irq_masks_cur[i];
|
||||
wm8994_reg_write(wm8994,
|
||||
WM8994_INTERRUPT_STATUS_1_MASK + i,
|
||||
wm8994->irq_masks_cur[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&wm8994->irq_lock);
|
||||
}
|
||||
|
||||
static void wm8994_irq_unmask(unsigned int irq)
|
||||
{
|
||||
struct wm8994 *wm8994 = get_irq_chip_data(irq);
|
||||
struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq);
|
||||
|
||||
wm8994->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
|
||||
}
|
||||
|
||||
static void wm8994_irq_mask(unsigned int irq)
|
||||
{
|
||||
struct wm8994 *wm8994 = get_irq_chip_data(irq);
|
||||
struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq);
|
||||
|
||||
wm8994->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static struct irq_chip wm8994_irq_chip = {
|
||||
.name = "wm8994",
|
||||
.bus_lock = wm8994_irq_lock,
|
||||
.bus_sync_unlock = wm8994_irq_sync_unlock,
|
||||
.mask = wm8994_irq_mask,
|
||||
.unmask = wm8994_irq_unmask,
|
||||
};
|
||||
|
||||
/* The processing of the primary interrupt occurs in a thread so that
|
||||
* we can interact with the device over I2C or SPI. */
|
||||
static irqreturn_t wm8994_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct wm8994 *wm8994 = data;
|
||||
unsigned int i;
|
||||
u16 status[WM8994_NUM_IRQ_REGS];
|
||||
int ret;
|
||||
|
||||
ret = wm8994_bulk_read(wm8994, WM8994_INTERRUPT_STATUS_1,
|
||||
WM8994_NUM_IRQ_REGS, status);
|
||||
if (ret < 0) {
|
||||
dev_err(wm8994->dev, "Failed to read interrupt status: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Apply masking */
|
||||
for (i = 0; i < WM8994_NUM_IRQ_REGS; i++)
|
||||
status[i] &= ~wm8994->irq_masks_cur[i];
|
||||
|
||||
/* Report */
|
||||
for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
|
||||
if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
|
||||
handle_nested_irq(wm8994->irq_base + i);
|
||||
}
|
||||
|
||||
/* Ack any unmasked IRQs */
|
||||
for (i = 0; i < ARRAY_SIZE(status); i++) {
|
||||
if (status[i])
|
||||
wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1 + i,
|
||||
status[i]);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int wm8994_irq_init(struct wm8994 *wm8994)
|
||||
{
|
||||
int i, cur_irq, ret;
|
||||
|
||||
mutex_init(&wm8994->irq_lock);
|
||||
|
||||
/* Mask the individual interrupt sources */
|
||||
for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
|
||||
wm8994->irq_masks_cur[i] = 0xffff;
|
||||
wm8994->irq_masks_cache[i] = 0xffff;
|
||||
wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK + i,
|
||||
0xffff);
|
||||
}
|
||||
|
||||
if (!wm8994->irq) {
|
||||
dev_warn(wm8994->dev,
|
||||
"No interrupt specified, no interrupts\n");
|
||||
wm8994->irq_base = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!wm8994->irq_base) {
|
||||
dev_err(wm8994->dev,
|
||||
"No interrupt base specified, no interrupts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register them with genirq */
|
||||
for (cur_irq = wm8994->irq_base;
|
||||
cur_irq < ARRAY_SIZE(wm8994_irqs) + wm8994->irq_base;
|
||||
cur_irq++) {
|
||||
set_irq_chip_data(cur_irq, wm8994);
|
||||
set_irq_chip_and_handler(cur_irq, &wm8994_irq_chip,
|
||||
handle_edge_irq);
|
||||
set_irq_nested_thread(cur_irq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(cur_irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(wm8994->irq, NULL, wm8994_irq_thread,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"wm8994", wm8994);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8994->dev, "Failed to request IRQ %d: %d\n",
|
||||
wm8994->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable top level interrupt if it was masked */
|
||||
wm8994_reg_write(wm8994, WM8994_INTERRUPT_CONTROL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wm8994_irq_exit(struct wm8994 *wm8994)
|
||||
{
|
||||
if (wm8994->irq)
|
||||
free_irq(wm8994->irq, wm8994);
|
||||
}
|
@ -569,9 +569,9 @@ struct twl4030_codec_data {
|
||||
struct twl4030_codec_audio_data *audio;
|
||||
struct twl4030_codec_vibra_data *vibra;
|
||||
|
||||
/* twl6030 */
|
||||
int audpwron_gpio; /* audio power-on gpio */
|
||||
int naudint_irq; /* audio interrupt */
|
||||
/* twl6040 */
|
||||
int audpwron_gpio; /* audio power-on gpio */
|
||||
int naudint_irq; /* audio interrupt */
|
||||
};
|
||||
|
||||
struct twl4030_platform_data {
|
||||
|
126
include/linux/mfd/davinci_voicecodec.h
Normal file
126
include/linux/mfd/davinci_voicecodec.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* DaVinci Voice Codec Core Interface for TI platforms
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments, Inc
|
||||
*
|
||||
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_DAVINCI_VOICECODEC_H_
|
||||
#define __LINUX_MFD_DAVINIC_VOICECODEC_H_
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
|
||||
#include <mach/edma.h>
|
||||
|
||||
/*
|
||||
* Register values.
|
||||
*/
|
||||
#define DAVINCI_VC_PID 0x00
|
||||
#define DAVINCI_VC_CTRL 0x04
|
||||
#define DAVINCI_VC_INTEN 0x08
|
||||
#define DAVINCI_VC_INTSTATUS 0x0c
|
||||
#define DAVINCI_VC_INTCLR 0x10
|
||||
#define DAVINCI_VC_EMUL_CTRL 0x14
|
||||
#define DAVINCI_VC_RFIFO 0x20
|
||||
#define DAVINCI_VC_WFIFO 0x24
|
||||
#define DAVINCI_VC_FIFOSTAT 0x28
|
||||
#define DAVINCI_VC_TST_CTRL 0x2C
|
||||
#define DAVINCI_VC_REG05 0x94
|
||||
#define DAVINCI_VC_REG09 0xA4
|
||||
#define DAVINCI_VC_REG12 0xB0
|
||||
|
||||
/* DAVINCI_VC_CTRL bit fields */
|
||||
#define DAVINCI_VC_CTRL_MASK 0x5500
|
||||
#define DAVINCI_VC_CTRL_RSTADC BIT(0)
|
||||
#define DAVINCI_VC_CTRL_RSTDAC BIT(1)
|
||||
#define DAVINCI_VC_CTRL_RD_BITS_8 BIT(4)
|
||||
#define DAVINCI_VC_CTRL_RD_UNSIGNED BIT(5)
|
||||
#define DAVINCI_VC_CTRL_WD_BITS_8 BIT(6)
|
||||
#define DAVINCI_VC_CTRL_WD_UNSIGNED BIT(7)
|
||||
#define DAVINCI_VC_CTRL_RFIFOEN BIT(8)
|
||||
#define DAVINCI_VC_CTRL_RFIFOCL BIT(9)
|
||||
#define DAVINCI_VC_CTRL_RFIFOMD_WORD_1 BIT(10)
|
||||
#define DAVINCI_VC_CTRL_WFIFOEN BIT(12)
|
||||
#define DAVINCI_VC_CTRL_WFIFOCL BIT(13)
|
||||
#define DAVINCI_VC_CTRL_WFIFOMD_WORD_1 BIT(14)
|
||||
|
||||
/* DAVINCI_VC_INT bit fields */
|
||||
#define DAVINCI_VC_INT_MASK 0x3F
|
||||
#define DAVINCI_VC_INT_RDRDY_MASK BIT(0)
|
||||
#define DAVINCI_VC_INT_RERROVF_MASK BIT(1)
|
||||
#define DAVINCI_VC_INT_RERRUDR_MASK BIT(2)
|
||||
#define DAVINCI_VC_INT_WDREQ_MASK BIT(3)
|
||||
#define DAVINCI_VC_INT_WERROVF_MASKBIT BIT(4)
|
||||
#define DAVINCI_VC_INT_WERRUDR_MASK BIT(5)
|
||||
|
||||
/* DAVINCI_VC_REG05 bit fields */
|
||||
#define DAVINCI_VC_REG05_PGA_GAIN 0x07
|
||||
|
||||
/* DAVINCI_VC_REG09 bit fields */
|
||||
#define DAVINCI_VC_REG09_MUTE 0x40
|
||||
#define DAVINCI_VC_REG09_DIG_ATTEN 0x3F
|
||||
|
||||
/* DAVINCI_VC_REG12 bit fields */
|
||||
#define DAVINCI_VC_REG12_POWER_ALL_ON 0xFD
|
||||
#define DAVINCI_VC_REG12_POWER_ALL_OFF 0x00
|
||||
|
||||
#define DAVINCI_VC_CELLS 2
|
||||
|
||||
enum davinci_vc_cells {
|
||||
DAVINCI_VC_VCIF_CELL,
|
||||
DAVINCI_VC_CQ93VC_CELL,
|
||||
};
|
||||
|
||||
struct davinci_vcif {
|
||||
struct platform_device *pdev;
|
||||
u32 dma_tx_channel;
|
||||
u32 dma_rx_channel;
|
||||
dma_addr_t dma_tx_addr;
|
||||
dma_addr_t dma_rx_addr;
|
||||
};
|
||||
|
||||
struct cq93vc {
|
||||
struct platform_device *pdev;
|
||||
struct snd_soc_codec *codec;
|
||||
u32 sysclk;
|
||||
};
|
||||
|
||||
struct davinci_vc;
|
||||
|
||||
struct davinci_vc {
|
||||
/* Device data */
|
||||
struct device *dev;
|
||||
struct platform_device *pdev;
|
||||
struct clk *clk;
|
||||
|
||||
/* Memory resources */
|
||||
void __iomem *base;
|
||||
resource_size_t pbase;
|
||||
size_t base_size;
|
||||
|
||||
/* MFD cells */
|
||||
struct mfd_cell cells[DAVINCI_VC_CELLS];
|
||||
|
||||
/* Client devices */
|
||||
struct davinci_vcif davinci_vcif;
|
||||
struct cq93vc cq93vc;
|
||||
};
|
||||
|
||||
#endif
|
@ -492,6 +492,8 @@
|
||||
*/
|
||||
#define WM8350_JACK_L_LVL 0x0800
|
||||
#define WM8350_JACK_R_LVL 0x0400
|
||||
#define WM8350_JACK_MICSCD_LVL 0x0200
|
||||
#define WM8350_JACK_MICSD_LVL 0x0100
|
||||
|
||||
/*
|
||||
* WM8350 Platform setup
|
||||
|
@ -15,14 +15,38 @@
|
||||
#ifndef __MFD_WM8994_CORE_H__
|
||||
#define __MFD_WM8994_CORE_H__
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
struct regulator_dev;
|
||||
struct regulator_bulk_data;
|
||||
|
||||
#define WM8994_NUM_GPIO_REGS 11
|
||||
#define WM8994_NUM_LDO_REGS 2
|
||||
#define WM8994_NUM_LDO_REGS 2
|
||||
#define WM8994_NUM_IRQ_REGS 2
|
||||
|
||||
#define WM8994_IRQ_TEMP_SHUT 0
|
||||
#define WM8994_IRQ_MIC1_DET 1
|
||||
#define WM8994_IRQ_MIC1_SHRT 2
|
||||
#define WM8994_IRQ_MIC2_DET 3
|
||||
#define WM8994_IRQ_MIC2_SHRT 4
|
||||
#define WM8994_IRQ_FLL1_LOCK 5
|
||||
#define WM8994_IRQ_FLL2_LOCK 6
|
||||
#define WM8994_IRQ_SRC1_LOCK 7
|
||||
#define WM8994_IRQ_SRC2_LOCK 8
|
||||
#define WM8994_IRQ_AIF1DRC1_SIG_DET 9
|
||||
#define WM8994_IRQ_AIF1DRC2_SIG_DET 10
|
||||
#define WM8994_IRQ_AIF2DRC_SIG_DET 11
|
||||
#define WM8994_IRQ_FIFOS_ERR 12
|
||||
#define WM8994_IRQ_WSEQ_DONE 13
|
||||
#define WM8994_IRQ_DCS_DONE 14
|
||||
#define WM8994_IRQ_TEMP_WARN 15
|
||||
|
||||
/* GPIOs in the chip are numbered from 1-11 */
|
||||
#define WM8994_IRQ_GPIO(x) (x + WM8994_IRQ_TEMP_WARN)
|
||||
|
||||
struct wm8994 {
|
||||
struct mutex io_lock;
|
||||
struct mutex irq_lock;
|
||||
|
||||
struct device *dev;
|
||||
int (*read_dev)(struct wm8994 *wm8994, unsigned short reg,
|
||||
@ -33,6 +57,11 @@ struct wm8994 {
|
||||
void *control_data;
|
||||
|
||||
int gpio_base;
|
||||
int irq_base;
|
||||
|
||||
int irq;
|
||||
u16 irq_masks_cur[WM8994_NUM_IRQ_REGS];
|
||||
u16 irq_masks_cache[WM8994_NUM_IRQ_REGS];
|
||||
|
||||
/* Used over suspend/resume */
|
||||
u16 ldo_regs[WM8994_NUM_LDO_REGS];
|
||||
@ -51,4 +80,26 @@ int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
|
||||
int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
|
||||
int count, u16 *buf);
|
||||
|
||||
|
||||
/* Helper to save on boilerplate */
|
||||
static inline int wm8994_request_irq(struct wm8994 *wm8994, int irq,
|
||||
irq_handler_t handler, const char *name,
|
||||
void *data)
|
||||
{
|
||||
if (!wm8994->irq_base)
|
||||
return -EINVAL;
|
||||
return request_threaded_irq(wm8994->irq_base + irq, NULL, handler,
|
||||
IRQF_TRIGGER_RISING, name,
|
||||
data);
|
||||
}
|
||||
static inline void wm8994_free_irq(struct wm8994 *wm8994, int irq, void *data)
|
||||
{
|
||||
if (!wm8994->irq_base)
|
||||
return;
|
||||
free_irq(wm8994->irq_base + irq, data);
|
||||
}
|
||||
|
||||
int wm8994_irq_init(struct wm8994 *wm8994);
|
||||
void wm8994_irq_exit(struct wm8994 *wm8994);
|
||||
|
||||
#endif
|
||||
|
@ -70,6 +70,7 @@ struct wm8994_pdata {
|
||||
|
||||
struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO];
|
||||
|
||||
int irq_base; /** Base IRQ number for WM8994, required for IRQs */
|
||||
|
||||
int num_drc_cfgs;
|
||||
struct wm8994_drc_cfg *drc_cfgs;
|
||||
|
378
include/linux/usb/audio-v2.h
Normal file
378
include/linux/usb/audio-v2.h
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Daniel Mack <daniel@caiaq.de>
|
||||
*
|
||||
* This software is distributed under the terms of the GNU General Public
|
||||
* License ("GPL") version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This file holds USB constants and structures defined
|
||||
* by the USB Device Class Definition for Audio Devices in version 2.0.
|
||||
* Comments below reference relevant sections of the documents contained
|
||||
* in http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_AUDIO_V2_H
|
||||
#define __LINUX_USB_AUDIO_V2_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* v1.0 and v2.0 of this standard have many things in common. For the rest
|
||||
* of the definitions, please refer to audio.h */
|
||||
|
||||
/* 4.7.2.1 Clock Source Descriptor */
|
||||
|
||||
struct uac_clock_source_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bClockID;
|
||||
__u8 bmAttributes;
|
||||
__u8 bmControls;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 iClockSource;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.7.2.2 Clock Source Descriptor */
|
||||
|
||||
struct uac_clock_selector_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bClockID;
|
||||
__u8 bNrInPins;
|
||||
__u8 bmControls;
|
||||
__u8 baCSourceID[];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.7.2.4 Input terminal descriptor */
|
||||
|
||||
struct uac2_input_terminal_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bTerminalID;
|
||||
__u16 wTerminalType;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 bCSourceID;
|
||||
__u8 bNrChannels;
|
||||
__u32 bmChannelConfig;
|
||||
__u8 iChannelNames;
|
||||
__u16 bmControls;
|
||||
__u8 iTerminal;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.7.2.5 Output terminal descriptor */
|
||||
|
||||
struct uac2_output_terminal_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bTerminalID;
|
||||
__u16 wTerminalType;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 bSourceID;
|
||||
__u8 bCSourceID;
|
||||
__u16 bmControls;
|
||||
__u8 iTerminal;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
|
||||
/* 4.7.2.8 Feature Unit Descriptor */
|
||||
|
||||
struct uac2_feature_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUnitID;
|
||||
__u8 bSourceID;
|
||||
/* bmaControls is actually u32,
|
||||
* but u8 is needed for the hybrid parser */
|
||||
__u8 bmaControls[0]; /* variable length */
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 4.9.2 Class-Specific AS Interface Descriptor */
|
||||
|
||||
struct uac_as_header_descriptor_v2 {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bTerminalLink;
|
||||
__u8 bmControls;
|
||||
__u8 bFormatType;
|
||||
__u32 bmFormats;
|
||||
__u8 bNrChannels;
|
||||
__u32 bmChannelConfig;
|
||||
__u8 iChannelNames;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* 6.1 Interrupt Data Message */
|
||||
|
||||
#define UAC2_INTERRUPT_DATA_MSG_VENDOR (1 << 0)
|
||||
#define UAC2_INTERRUPT_DATA_MSG_EP (1 << 1)
|
||||
|
||||
struct uac2_interrupt_data_msg {
|
||||
__u8 bInfo;
|
||||
__u8 bAttribute;
|
||||
__le16 wValue;
|
||||
__le16 wIndex;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* A.7 Audio Function Category Codes */
|
||||
#define UAC2_FUNCTION_SUBCLASS_UNDEFINED 0x00
|
||||
#define UAC2_FUNCTION_DESKTOP_SPEAKER 0x01
|
||||
#define UAC2_FUNCTION_HOME_THEATER 0x02
|
||||
#define UAC2_FUNCTION_MICROPHONE 0x03
|
||||
#define UAC2_FUNCTION_HEADSET 0x04
|
||||
#define UAC2_FUNCTION_TELEPHONE 0x05
|
||||
#define UAC2_FUNCTION_CONVERTER 0x06
|
||||
#define UAC2_FUNCTION_SOUND_RECORDER 0x07
|
||||
#define UAC2_FUNCTION_IO_BOX 0x08
|
||||
#define UAC2_FUNCTION_MUSICAL_INSTRUMENT 0x09
|
||||
#define UAC2_FUNCTION_PRO_AUDIO 0x0a
|
||||
#define UAC2_FUNCTION_AUDIO_VIDEO 0x0b
|
||||
#define UAC2_FUNCTION_CONTROL_PANEL 0x0c
|
||||
#define UAC2_FUNCTION_OTHER 0xff
|
||||
|
||||
/* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */
|
||||
/* see audio.h for the rest, which is identical to v1 */
|
||||
#define UAC2_EFFECT_UNIT 0x07
|
||||
#define UAC2_PROCESSING_UNIT_V2 0x08
|
||||
#define UAC2_EXTENSION_UNIT_V2 0x09
|
||||
#define UAC2_CLOCK_SOURCE 0x0a
|
||||
#define UAC2_CLOCK_SELECTOR 0x0b
|
||||
#define UAC2_CLOCK_MULTIPLIER 0x0c
|
||||
#define UAC2_SAMPLE_RATE_CONVERTER 0x0d
|
||||
|
||||
/* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */
|
||||
/* see audio.h for the rest, which is identical to v1 */
|
||||
#define UAC2_ENCODER 0x03
|
||||
#define UAC2_DECODER 0x04
|
||||
|
||||
/* A.11 Effect Unit Effect Types */
|
||||
#define UAC2_EFFECT_UNDEFINED 0x00
|
||||
#define UAC2_EFFECT_PARAM_EQ 0x01
|
||||
#define UAC2_EFFECT_REVERB 0x02
|
||||
#define UAC2_EFFECT_MOD_DELAY 0x03
|
||||
#define UAC2_EFFECT_DYN_RANGE_COMP 0x04
|
||||
|
||||
/* A.12 Processing Unit Process Types */
|
||||
#define UAC2_PROCESS_UNDEFINED 0x00
|
||||
#define UAC2_PROCESS_UP_DOWNMIX 0x01
|
||||
#define UAC2_PROCESS_DOLBY_PROLOCIC 0x02
|
||||
#define UAC2_PROCESS_STEREO_EXTENDER 0x03
|
||||
|
||||
/* A.14 Audio Class-Specific Request Codes */
|
||||
#define UAC2_CS_CUR 0x01
|
||||
#define UAC2_CS_RANGE 0x02
|
||||
#define UAC2_CS_MEM 0x03
|
||||
|
||||
/* A.15 Encoder Type Codes */
|
||||
#define UAC2_ENCODER_UNDEFINED 0x00
|
||||
#define UAC2_ENCODER_OTHER 0x01
|
||||
#define UAC2_ENCODER_MPEG 0x02
|
||||
#define UAC2_ENCODER_AC3 0x03
|
||||
#define UAC2_ENCODER_WMA 0x04
|
||||
#define UAC2_ENCODER_DTS 0x05
|
||||
|
||||
/* A.16 Decoder Type Codes */
|
||||
#define UAC2_DECODER_UNDEFINED 0x00
|
||||
#define UAC2_DECODER_OTHER 0x01
|
||||
#define UAC2_DECODER_MPEG 0x02
|
||||
#define UAC2_DECODER_AC3 0x03
|
||||
#define UAC2_DECODER_WMA 0x04
|
||||
#define UAC2_DECODER_DTS 0x05
|
||||
|
||||
/* A.17.1 Clock Source Control Selectors */
|
||||
#define UAC2_CS_UNDEFINED 0x00
|
||||
#define UAC2_CS_CONTROL_SAM_FREQ 0x01
|
||||
#define UAC2_CS_CONTROL_CLOCK_VALID 0x02
|
||||
|
||||
/* A.17.2 Clock Selector Control Selectors */
|
||||
#define UAC2_CX_UNDEFINED 0x00
|
||||
#define UAC2_CX_CLOCK_SELECTOR 0x01
|
||||
|
||||
/* A.17.3 Clock Multiplier Control Selectors */
|
||||
#define UAC2_CM_UNDEFINED 0x00
|
||||
#define UAC2_CM_NUMERATOR 0x01
|
||||
#define UAC2_CM_DENOMINTATOR 0x02
|
||||
|
||||
/* A.17.4 Terminal Control Selectors */
|
||||
#define UAC2_TE_UNDEFINED 0x00
|
||||
#define UAC2_TE_COPY_PROTECT 0x01
|
||||
#define UAC2_TE_CONNECTOR 0x02
|
||||
#define UAC2_TE_OVERLOAD 0x03
|
||||
#define UAC2_TE_CLUSTER 0x04
|
||||
#define UAC2_TE_UNDERFLOW 0x05
|
||||
#define UAC2_TE_OVERFLOW 0x06
|
||||
#define UAC2_TE_LATENCY 0x07
|
||||
|
||||
/* A.17.5 Mixer Control Selectors */
|
||||
#define UAC2_MU_UNDEFINED 0x00
|
||||
#define UAC2_MU_MIXER 0x01
|
||||
#define UAC2_MU_CLUSTER 0x02
|
||||
#define UAC2_MU_UNDERFLOW 0x03
|
||||
#define UAC2_MU_OVERFLOW 0x04
|
||||
#define UAC2_MU_LATENCY 0x05
|
||||
|
||||
/* A.17.6 Selector Control Selectors */
|
||||
#define UAC2_SU_UNDEFINED 0x00
|
||||
#define UAC2_SU_SELECTOR 0x01
|
||||
#define UAC2_SU_LATENCY 0x02
|
||||
|
||||
/* A.17.7 Feature Unit Control Selectors */
|
||||
/* see audio.h for the rest, which is identical to v1 */
|
||||
#define UAC2_FU_INPUT_GAIN 0x0b
|
||||
#define UAC2_FU_INPUT_GAIN_PAD 0x0c
|
||||
#define UAC2_FU_PHASE_INVERTER 0x0d
|
||||
#define UAC2_FU_UNDERFLOW 0x0e
|
||||
#define UAC2_FU_OVERFLOW 0x0f
|
||||
#define UAC2_FU_LATENCY 0x10
|
||||
|
||||
/* A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors */
|
||||
#define UAC2_PE_UNDEFINED 0x00
|
||||
#define UAC2_PE_ENABLE 0x01
|
||||
#define UAC2_PE_CENTERFREQ 0x02
|
||||
#define UAC2_PE_QFACTOR 0x03
|
||||
#define UAC2_PE_GAIN 0x04
|
||||
#define UAC2_PE_UNDERFLOW 0x05
|
||||
#define UAC2_PE_OVERFLOW 0x06
|
||||
#define UAC2_PE_LATENCY 0x07
|
||||
|
||||
/* A.17.8.2 Reverberation Effect Unit Control Selectors */
|
||||
#define UAC2_RV_UNDEFINED 0x00
|
||||
#define UAC2_RV_ENABLE 0x01
|
||||
#define UAC2_RV_TYPE 0x02
|
||||
#define UAC2_RV_LEVEL 0x03
|
||||
#define UAC2_RV_TIME 0x04
|
||||
#define UAC2_RV_FEEDBACK 0x05
|
||||
#define UAC2_RV_PREDELAY 0x06
|
||||
#define UAC2_RV_DENSITY 0x07
|
||||
#define UAC2_RV_HIFREQ_ROLLOFF 0x08
|
||||
#define UAC2_RV_UNDERFLOW 0x09
|
||||
#define UAC2_RV_OVERFLOW 0x0a
|
||||
#define UAC2_RV_LATENCY 0x0b
|
||||
|
||||
/* A.17.8.3 Modulation Delay Effect Control Selectors */
|
||||
#define UAC2_MD_UNDEFINED 0x00
|
||||
#define UAC2_MD_ENABLE 0x01
|
||||
#define UAC2_MD_BALANCE 0x02
|
||||
#define UAC2_MD_RATE 0x03
|
||||
#define UAC2_MD_DEPTH 0x04
|
||||
#define UAC2_MD_TIME 0x05
|
||||
#define UAC2_MD_FEEDBACK 0x06
|
||||
#define UAC2_MD_UNDERFLOW 0x07
|
||||
#define UAC2_MD_OVERFLOW 0x08
|
||||
#define UAC2_MD_LATENCY 0x09
|
||||
|
||||
/* A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors */
|
||||
#define UAC2_DR_UNDEFINED 0x00
|
||||
#define UAC2_DR_ENABLE 0x01
|
||||
#define UAC2_DR_COMPRESSION_RATE 0x02
|
||||
#define UAC2_DR_MAXAMPL 0x03
|
||||
#define UAC2_DR_THRESHOLD 0x04
|
||||
#define UAC2_DR_ATTACK_TIME 0x05
|
||||
#define UAC2_DR_RELEASE_TIME 0x06
|
||||
#define UAC2_DR_UNDEFLOW 0x07
|
||||
#define UAC2_DR_OVERFLOW 0x08
|
||||
#define UAC2_DR_LATENCY 0x09
|
||||
|
||||
/* A.17.9.1 Up/Down-mix Processing Unit Control Selectors */
|
||||
#define UAC2_UD_UNDEFINED 0x00
|
||||
#define UAC2_UD_ENABLE 0x01
|
||||
#define UAC2_UD_MODE_SELECT 0x02
|
||||
#define UAC2_UD_CLUSTER 0x03
|
||||
#define UAC2_UD_UNDERFLOW 0x04
|
||||
#define UAC2_UD_OVERFLOW 0x05
|
||||
#define UAC2_UD_LATENCY 0x06
|
||||
|
||||
/* A.17.9.2 Dolby Prologic[tm] Processing Unit Control Selectors */
|
||||
#define UAC2_DP_UNDEFINED 0x00
|
||||
#define UAC2_DP_ENABLE 0x01
|
||||
#define UAC2_DP_MODE_SELECT 0x02
|
||||
#define UAC2_DP_CLUSTER 0x03
|
||||
#define UAC2_DP_UNDERFFLOW 0x04
|
||||
#define UAC2_DP_OVERFLOW 0x05
|
||||
#define UAC2_DP_LATENCY 0x06
|
||||
|
||||
/* A.17.9.3 Stereo Expander Processing Unit Control Selectors */
|
||||
#define UAC2_ST_EXT_UNDEFINED 0x00
|
||||
#define UAC2_ST_EXT_ENABLE 0x01
|
||||
#define UAC2_ST_EXT_WIDTH 0x02
|
||||
#define UAC2_ST_EXT_UNDEFLOW 0x03
|
||||
#define UAC2_ST_EXT_OVERFLOW 0x04
|
||||
#define UAC2_ST_EXT_LATENCY 0x05
|
||||
|
||||
/* A.17.10 Extension Unit Control Selectors */
|
||||
#define UAC2_XU_UNDEFINED 0x00
|
||||
#define UAC2_XU_ENABLE 0x01
|
||||
#define UAC2_XU_CLUSTER 0x02
|
||||
#define UAC2_XU_UNDERFLOW 0x03
|
||||
#define UAC2_XU_OVERFLOW 0x04
|
||||
#define UAC2_XU_LATENCY 0x05
|
||||
|
||||
/* A.17.11 AudioStreaming Interface Control Selectors */
|
||||
#define UAC2_AS_UNDEFINED 0x00
|
||||
#define UAC2_AS_ACT_ALT_SETTING 0x01
|
||||
#define UAC2_AS_VAL_ALT_SETTINGS 0x02
|
||||
#define UAC2_AS_AUDIO_DATA_FORMAT 0x03
|
||||
|
||||
/* A.17.12 Encoder Control Selectors */
|
||||
#define UAC2_EN_UNDEFINED 0x00
|
||||
#define UAC2_EN_BIT_RATE 0x01
|
||||
#define UAC2_EN_QUALITY 0x02
|
||||
#define UAC2_EN_VBR 0x03
|
||||
#define UAC2_EN_TYPE 0x04
|
||||
#define UAC2_EN_UNDERFLOW 0x05
|
||||
#define UAC2_EN_OVERFLOW 0x06
|
||||
#define UAC2_EN_ENCODER_ERROR 0x07
|
||||
#define UAC2_EN_PARAM1 0x08
|
||||
#define UAC2_EN_PARAM2 0x09
|
||||
#define UAC2_EN_PARAM3 0x0a
|
||||
#define UAC2_EN_PARAM4 0x0b
|
||||
#define UAC2_EN_PARAM5 0x0c
|
||||
#define UAC2_EN_PARAM6 0x0d
|
||||
#define UAC2_EN_PARAM7 0x0e
|
||||
#define UAC2_EN_PARAM8 0x0f
|
||||
|
||||
/* A.17.13.1 MPEG Decoder Control Selectors */
|
||||
#define UAC2_MPEG_UNDEFINED 0x00
|
||||
#define UAC2_MPEG_DUAL_CHANNEL 0x01
|
||||
#define UAC2_MPEG_SECOND_STEREO 0x02
|
||||
#define UAC2_MPEG_MULTILINGUAL 0x03
|
||||
#define UAC2_MPEG_DYN_RANGE 0x04
|
||||
#define UAC2_MPEG_SCALING 0x05
|
||||
#define UAC2_MPEG_HILO_SCALING 0x06
|
||||
#define UAC2_MPEG_UNDERFLOW 0x07
|
||||
#define UAC2_MPEG_OVERFLOW 0x08
|
||||
#define UAC2_MPEG_DECODER_ERROR 0x09
|
||||
|
||||
/* A17.13.2 AC3 Decoder Control Selectors */
|
||||
#define UAC2_AC3_UNDEFINED 0x00
|
||||
#define UAC2_AC3_MODE 0x01
|
||||
#define UAC2_AC3_DYN_RANGE 0x02
|
||||
#define UAC2_AC3_SCALING 0x03
|
||||
#define UAC2_AC3_HILO_SCALING 0x04
|
||||
#define UAC2_AC3_UNDERFLOW 0x05
|
||||
#define UAC2_AC3_OVERFLOW 0x06
|
||||
#define UAC2_AC3_DECODER_ERROR 0x07
|
||||
|
||||
/* A17.13.3 WMA Decoder Control Selectors */
|
||||
#define UAC2_WMA_UNDEFINED 0x00
|
||||
#define UAC2_WMA_UNDERFLOW 0x01
|
||||
#define UAC2_WMA_OVERFLOW 0x02
|
||||
#define UAC2_WMA_DECODER_ERROR 0x03
|
||||
|
||||
/* A17.13.4 DTS Decoder Control Selectors */
|
||||
#define UAC2_DTS_UNDEFINED 0x00
|
||||
#define UAC2_DTS_UNDERFLOW 0x01
|
||||
#define UAC2_DTS_OVERFLOW 0x02
|
||||
#define UAC2_DTS_DECODER_ERROR 0x03
|
||||
|
||||
/* A17.14 Endpoint Control Selectors */
|
||||
#define UAC2_EP_CS_UNDEFINED 0x00
|
||||
#define UAC2_EP_CS_PITCH 0x01
|
||||
#define UAC2_EP_CS_DATA_OVERRUN 0x02
|
||||
#define UAC2_EP_CS_DATA_UNDERRUN 0x03
|
||||
|
||||
#endif /* __LINUX_USB_AUDIO_V2_H */
|
||||
|
@ -13,6 +13,9 @@
|
||||
* Comments below reference relevant sections of that document:
|
||||
*
|
||||
* http://www.usb.org/developers/devclass_docs/audio10.pdf
|
||||
*
|
||||
* Types and defines in this file are either specific to version 1.0 of
|
||||
* this standard or common for newer versions.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_AUDIO_H
|
||||
@ -20,14 +23,15 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* bInterfaceProtocol values to denote the version of the standard used */
|
||||
#define UAC_VERSION_1 0x00
|
||||
#define UAC_VERSION_2 0x20
|
||||
|
||||
/* A.2 Audio Interface Subclass Codes */
|
||||
#define USB_SUBCLASS_AUDIOCONTROL 0x01
|
||||
#define USB_SUBCLASS_AUDIOSTREAMING 0x02
|
||||
#define USB_SUBCLASS_MIDISTREAMING 0x03
|
||||
|
||||
#define UAC_VERSION_1 0x00
|
||||
#define UAC_VERSION_2 0x20
|
||||
|
||||
/* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
|
||||
#define UAC_HEADER 0x01
|
||||
#define UAC_INPUT_TERMINAL 0x02
|
||||
@ -38,15 +42,6 @@
|
||||
#define UAC_PROCESSING_UNIT_V1 0x07
|
||||
#define UAC_EXTENSION_UNIT_V1 0x08
|
||||
|
||||
/* UAC v2.0 types */
|
||||
#define UAC_EFFECT_UNIT 0x07
|
||||
#define UAC_PROCESSING_UNIT_V2 0x08
|
||||
#define UAC_EXTENSION_UNIT_V2 0x09
|
||||
#define UAC_CLOCK_SOURCE 0x0a
|
||||
#define UAC_CLOCK_SELECTOR 0x0b
|
||||
#define UAC_CLOCK_MULTIPLIER 0x0c
|
||||
#define UAC_SAMPLE_RATE_CONVERTER 0x0d
|
||||
|
||||
/* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
|
||||
#define UAC_AS_GENERAL 0x01
|
||||
#define UAC_FORMAT_TYPE 0x02
|
||||
@ -78,10 +73,6 @@
|
||||
|
||||
#define UAC_GET_STAT 0xff
|
||||
|
||||
/* Audio class v2.0 handles all the parameter calls differently */
|
||||
#define UAC2_CS_CUR 0x01
|
||||
#define UAC2_CS_RANGE 0x02
|
||||
|
||||
/* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
|
||||
#define UAC_MS_HEADER 0x01
|
||||
#define UAC_MIDI_IN_JACK 0x02
|
||||
@ -190,6 +181,156 @@ struct uac_feature_unit_descriptor_##ch { \
|
||||
__u8 iFeature; \
|
||||
} __attribute__ ((packed))
|
||||
|
||||
/* 4.3.2.3 Mixer Unit Descriptor */
|
||||
struct uac_mixer_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUnitID;
|
||||
__u8 bNrInPins;
|
||||
__u8 baSourceID[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline __u8 uac_mixer_unit_bNrChannels(struct uac_mixer_unit_descriptor *desc)
|
||||
{
|
||||
return desc->baSourceID[desc->bNrInPins];
|
||||
}
|
||||
|
||||
static inline __u32 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
if (protocol == UAC_VERSION_1)
|
||||
return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
|
||||
desc->baSourceID[desc->bNrInPins + 1];
|
||||
else
|
||||
return (desc->baSourceID[desc->bNrInPins + 4] << 24) |
|
||||
(desc->baSourceID[desc->bNrInPins + 3] << 16) |
|
||||
(desc->baSourceID[desc->bNrInPins + 2] << 8) |
|
||||
(desc->baSourceID[desc->bNrInPins + 1]);
|
||||
}
|
||||
|
||||
static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
return (protocol == UAC_VERSION_1) ?
|
||||
desc->baSourceID[desc->bNrInPins + 3] :
|
||||
desc->baSourceID[desc->bNrInPins + 5];
|
||||
}
|
||||
|
||||
static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
return (protocol == UAC_VERSION_1) ?
|
||||
&desc->baSourceID[desc->bNrInPins + 4] :
|
||||
&desc->baSourceID[desc->bNrInPins + 6];
|
||||
}
|
||||
|
||||
static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
|
||||
{
|
||||
__u8 *raw = (__u8 *) desc;
|
||||
return raw[desc->bLength - 1];
|
||||
}
|
||||
|
||||
/* 4.3.2.4 Selector Unit Descriptor */
|
||||
struct uac_selector_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUintID;
|
||||
__u8 bNrInPins;
|
||||
__u8 baSourceID[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
|
||||
{
|
||||
__u8 *raw = (__u8 *) desc;
|
||||
return raw[9 + desc->bLength - 1];
|
||||
}
|
||||
|
||||
/* 4.3.2.5 Feature Unit Descriptor */
|
||||
struct uac_feature_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUnitID;
|
||||
__u8 bSourceID;
|
||||
__u8 bControlSize;
|
||||
__u8 bmaControls[0]; /* variable length */
|
||||
} __attribute__((packed));
|
||||
|
||||
static inline __u8 uac_feature_unit_iFeature(struct uac_feature_unit_descriptor *desc)
|
||||
{
|
||||
__u8 *raw = (__u8 *) desc;
|
||||
return raw[desc->bLength - 1];
|
||||
}
|
||||
|
||||
/* 4.3.2.6 Processing Unit Descriptors */
|
||||
struct uac_processing_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUnitID;
|
||||
__u16 wProcessType;
|
||||
__u8 bNrInPins;
|
||||
__u8 baSourceID[];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline __u8 uac_processing_unit_bNrChannels(struct uac_processing_unit_descriptor *desc)
|
||||
{
|
||||
return desc->baSourceID[desc->bNrInPins];
|
||||
}
|
||||
|
||||
static inline __u32 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
if (protocol == UAC_VERSION_1)
|
||||
return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
|
||||
desc->baSourceID[desc->bNrInPins + 1];
|
||||
else
|
||||
return (desc->baSourceID[desc->bNrInPins + 4] << 24) |
|
||||
(desc->baSourceID[desc->bNrInPins + 3] << 16) |
|
||||
(desc->baSourceID[desc->bNrInPins + 2] << 8) |
|
||||
(desc->baSourceID[desc->bNrInPins + 1]);
|
||||
}
|
||||
|
||||
static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
return (protocol == UAC_VERSION_1) ?
|
||||
desc->baSourceID[desc->bNrInPins + 3] :
|
||||
desc->baSourceID[desc->bNrInPins + 5];
|
||||
}
|
||||
|
||||
static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
return (protocol == UAC_VERSION_1) ?
|
||||
desc->baSourceID[desc->bNrInPins + 4] :
|
||||
desc->baSourceID[desc->bNrInPins + 6];
|
||||
}
|
||||
|
||||
static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
return (protocol == UAC_VERSION_1) ?
|
||||
&desc->baSourceID[desc->bNrInPins + 5] :
|
||||
&desc->baSourceID[desc->bNrInPins + 7];
|
||||
}
|
||||
|
||||
static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
__u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
|
||||
return desc->baSourceID[desc->bNrInPins + control_size];
|
||||
}
|
||||
|
||||
static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc,
|
||||
int protocol)
|
||||
{
|
||||
__u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
|
||||
return &desc->baSourceID[desc->bNrInPins + control_size + 1];
|
||||
}
|
||||
|
||||
/* 4.5.2 Class-Specific AS Interface Descriptor */
|
||||
struct uac_as_header_descriptor_v1 {
|
||||
__u8 bLength; /* in bytes: 7 */
|
||||
@ -200,19 +341,6 @@ struct uac_as_header_descriptor_v1 {
|
||||
__le16 wFormatTag; /* The Audio Data Format */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct uac_as_header_descriptor_v2 {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bTerminalLink;
|
||||
__u8 bmControls;
|
||||
__u8 bFormatType;
|
||||
__u32 bmFormats;
|
||||
__u8 bNrChannels;
|
||||
__u32 bmChannelConfig;
|
||||
__u8 iChannelNames;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define UAC_DT_AS_HEADER_SIZE 7
|
||||
|
||||
/* Formats - A.1.1 Audio Data Format Type I Codes */
|
||||
@ -277,7 +405,6 @@ struct uac_format_type_i_ext_descriptor {
|
||||
__u8 bSideBandProtocol;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* Formats - Audio Data Format Type I Codes */
|
||||
|
||||
#define UAC_FORMAT_TYPE_II_MPEG 0x1001
|
||||
@ -329,38 +456,15 @@ struct uac_iso_endpoint_descriptor {
|
||||
__u8 bmAttributes;
|
||||
__u8 bLockDelayUnits;
|
||||
__le16 wLockDelay;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
#define UAC_ISO_ENDPOINT_DESC_SIZE 7
|
||||
|
||||
#define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01
|
||||
#define UAC_EP_CS_ATTR_PITCH_CONTROL 0x02
|
||||
#define UAC_EP_CS_ATTR_FILL_MAX 0x80
|
||||
|
||||
/* Audio class v2.0: CLOCK_SOURCE descriptor */
|
||||
|
||||
struct uac_clock_source_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bClockID;
|
||||
__u8 bmAttributes;
|
||||
__u8 bmControls;
|
||||
__u8 bAssocTerminal;
|
||||
__u8 iClockSource;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* A.10.2 Feature Unit Control Selectors */
|
||||
|
||||
struct uac_feature_unit_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u8 bDescriptorSubtype;
|
||||
__u8 bUnitID;
|
||||
__u8 bSourceID;
|
||||
__u8 bControlSize;
|
||||
__u8 controls[0]; /* variable length */
|
||||
} __attribute__((packed));
|
||||
|
||||
#define UAC_FU_CONTROL_UNDEFINED 0x00
|
||||
#define UAC_MUTE_CONTROL 0x01
|
||||
#define UAC_VOLUME_CONTROL 0x02
|
||||
@ -384,6 +488,21 @@ struct uac_feature_unit_descriptor {
|
||||
#define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1))
|
||||
#define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_CONTROL - 1))
|
||||
|
||||
/* status word format (3.7.1.1) */
|
||||
|
||||
#define UAC1_STATUS_TYPE_ORIG_MASK 0x0f
|
||||
#define UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF 0x0
|
||||
#define UAC1_STATUS_TYPE_ORIG_AUDIO_STREAM_IF 0x1
|
||||
#define UAC1_STATUS_TYPE_ORIG_AUDIO_STREAM_EP 0x2
|
||||
|
||||
#define UAC1_STATUS_TYPE_IRQ_PENDING (1 << 7)
|
||||
#define UAC1_STATUS_TYPE_MEM_CHANGED (1 << 6)
|
||||
|
||||
struct uac1_status_word {
|
||||
__u8 bStatusType;
|
||||
__u8 bOriginator;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct usb_audio_control {
|
||||
|
@ -574,7 +574,7 @@ enum {
|
||||
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
|
||||
|
||||
struct snd_timer_id {
|
||||
int dev_class;
|
||||
int dev_class;
|
||||
int dev_sclass;
|
||||
int card;
|
||||
int device;
|
||||
@ -762,7 +762,7 @@ struct snd_ctl_elem_id {
|
||||
snd_ctl_elem_iface_t iface; /* interface identifier */
|
||||
unsigned int device; /* device/client number */
|
||||
unsigned int subdevice; /* subdevice (substream) number */
|
||||
unsigned char name[44]; /* ASCII name of item */
|
||||
unsigned char name[44]; /* ASCII name of item */
|
||||
unsigned int index; /* index of item */
|
||||
};
|
||||
|
||||
@ -809,7 +809,7 @@ struct snd_ctl_elem_info {
|
||||
struct snd_ctl_elem_value {
|
||||
struct snd_ctl_elem_id id; /* W: element ID */
|
||||
unsigned int indirect: 1; /* W: indirect access - obsoleted */
|
||||
union {
|
||||
union {
|
||||
union {
|
||||
long value[128];
|
||||
long *value_ptr; /* obsoleted */
|
||||
@ -827,15 +827,15 @@ struct snd_ctl_elem_value {
|
||||
unsigned char *data_ptr; /* obsoleted */
|
||||
} bytes;
|
||||
struct snd_aes_iec958 iec958;
|
||||
} value; /* RO */
|
||||
} value; /* RO */
|
||||
struct timespec tstamp;
|
||||
unsigned char reserved[128-sizeof(struct timespec)];
|
||||
unsigned char reserved[128-sizeof(struct timespec)];
|
||||
};
|
||||
|
||||
struct snd_ctl_tlv {
|
||||
unsigned int numid; /* control element numeric identification */
|
||||
unsigned int length; /* in bytes aligned to 4 */
|
||||
unsigned int tlv[0]; /* first TLV */
|
||||
unsigned int numid; /* control element numeric identification */
|
||||
unsigned int length; /* in bytes aligned to 4 */
|
||||
unsigned int tlv[0]; /* first TLV */
|
||||
};
|
||||
|
||||
#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)
|
||||
@ -886,8 +886,8 @@ struct snd_ctl_event {
|
||||
unsigned int mask;
|
||||
struct snd_ctl_elem_id id;
|
||||
} elem;
|
||||
unsigned char data8[60];
|
||||
} data;
|
||||
unsigned char data8[60];
|
||||
} data;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -44,7 +44,6 @@ struct snd_es1688 {
|
||||
unsigned char pad;
|
||||
unsigned int dma_size;
|
||||
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
@ -108,14 +107,16 @@ struct snd_es1688 {
|
||||
void snd_es1688_mixer_write(struct snd_es1688 *chip, unsigned char reg, unsigned char data);
|
||||
|
||||
int snd_es1688_create(struct snd_card *card,
|
||||
struct snd_es1688 *chip,
|
||||
unsigned long port,
|
||||
unsigned long mpu_port,
|
||||
int irq,
|
||||
int mpu_irq,
|
||||
int dma8,
|
||||
unsigned short hardware,
|
||||
struct snd_es1688 ** rchip);
|
||||
int snd_es1688_pcm(struct snd_es1688 *chip, int device, struct snd_pcm ** rpcm);
|
||||
int snd_es1688_mixer(struct snd_es1688 *chip);
|
||||
unsigned short hardware);
|
||||
int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device,
|
||||
struct snd_pcm **rpcm);
|
||||
int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip);
|
||||
int snd_es1688_reset(struct snd_es1688 *chip);
|
||||
|
||||
#endif /* __SOUND_ES1688_H */
|
||||
|
@ -51,18 +51,18 @@ struct snd_info_entry_ops {
|
||||
unsigned short mode, void **file_private_data);
|
||||
int (*release)(struct snd_info_entry *entry,
|
||||
unsigned short mode, void *file_private_data);
|
||||
long (*read)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos);
|
||||
long (*write)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, const char __user *buf,
|
||||
unsigned long count, unsigned long pos);
|
||||
long long (*llseek)(struct snd_info_entry *entry,
|
||||
void *file_private_data, struct file *file,
|
||||
long long offset, int orig);
|
||||
unsigned int(*poll)(struct snd_info_entry *entry,
|
||||
void *file_private_data, struct file *file,
|
||||
poll_table *wait);
|
||||
ssize_t (*read)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos);
|
||||
ssize_t (*write)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, const char __user *buf,
|
||||
size_t count, loff_t pos);
|
||||
loff_t (*llseek)(struct snd_info_entry *entry,
|
||||
void *file_private_data, struct file *file,
|
||||
loff_t offset, int orig);
|
||||
unsigned int (*poll)(struct snd_info_entry *entry,
|
||||
void *file_private_data, struct file *file,
|
||||
poll_table *wait);
|
||||
int (*ioctl)(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, unsigned int cmd, unsigned long arg);
|
||||
int (*mmap)(struct snd_info_entry *entry, void *file_private_data,
|
||||
|
@ -42,6 +42,11 @@ enum snd_jack_types {
|
||||
SND_JACK_MECHANICAL = 0x0008, /* If detected separately */
|
||||
SND_JACK_VIDEOOUT = 0x0010,
|
||||
SND_JACK_AVOUT = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT,
|
||||
|
||||
/* Kept separate from switches to facilitate implementation */
|
||||
SND_JACK_BTN_0 = 0x4000,
|
||||
SND_JACK_BTN_1 = 0x2000,
|
||||
SND_JACK_BTN_2 = 0x1000,
|
||||
};
|
||||
|
||||
struct snd_jack {
|
||||
@ -50,6 +55,7 @@ struct snd_jack {
|
||||
int type;
|
||||
const char *id;
|
||||
char name[100];
|
||||
unsigned int key[3]; /* Keep in sync with definitions above */
|
||||
void *private_data;
|
||||
void (*private_free)(struct snd_jack *);
|
||||
};
|
||||
@ -59,6 +65,8 @@ struct snd_jack {
|
||||
int snd_jack_new(struct snd_card *card, const char *id, int type,
|
||||
struct snd_jack **jack);
|
||||
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
|
||||
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
|
||||
int keytype);
|
||||
|
||||
void snd_jack_report(struct snd_jack *jack, int status);
|
||||
|
||||
|
@ -182,6 +182,12 @@ struct snd_soc_dai_ops {
|
||||
struct snd_soc_dai *);
|
||||
int (*trigger)(struct snd_pcm_substream *, int,
|
||||
struct snd_soc_dai *);
|
||||
/*
|
||||
* For hardware based FIFO caused delay reporting.
|
||||
* Optional.
|
||||
*/
|
||||
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -215,7 +221,6 @@ struct snd_soc_dai {
|
||||
unsigned int symmetric_rates:1;
|
||||
|
||||
/* DAI runtime info */
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_soc_codec *codec;
|
||||
unsigned int active;
|
||||
unsigned char pop_wait:1;
|
||||
|
@ -339,6 +339,9 @@ int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin);
|
||||
int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin);
|
||||
int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin);
|
||||
int snd_soc_dapm_sync(struct snd_soc_codec *codec);
|
||||
int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec,
|
||||
const char *pin);
|
||||
int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin);
|
||||
|
||||
/* dapm widget types */
|
||||
enum snd_soc_dapm_type {
|
||||
@ -425,9 +428,8 @@ struct snd_soc_dapm_widget {
|
||||
unsigned char connected:1; /* connected codec pin */
|
||||
unsigned char new:1; /* cnew complete */
|
||||
unsigned char ext:1; /* has external widgets */
|
||||
unsigned char muted:1; /* muted for pop reduction */
|
||||
unsigned char suspend:1; /* was active before suspend */
|
||||
unsigned char pmdown:1; /* waiting for timeout */
|
||||
unsigned char force:1; /* force state */
|
||||
unsigned char ignore_suspend:1; /* kept enabled over suspend */
|
||||
|
||||
int (*power_check)(struct snd_soc_dapm_widget *w);
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -29,10 +30,10 @@
|
||||
#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
|
||||
((unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .shift = xshift, .rshift = xshift, .max = xmax, \
|
||||
.invert = xinvert})
|
||||
.platform_max = xmax, .invert = xinvert})
|
||||
#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
|
||||
((unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .max = xmax, .invert = xinvert})
|
||||
{.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert})
|
||||
#define SOC_SINGLE(xname, reg, shift, max, invert) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
|
||||
@ -52,14 +53,14 @@
|
||||
.put = snd_soc_put_volsw, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .shift = shift_left, .rshift = shift_right, \
|
||||
.max = xmax, .invert = xinvert} }
|
||||
.max = xmax, .platform_max = xmax, .invert = xinvert} }
|
||||
#define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||
.info = snd_soc_info_volsw_2r, \
|
||||
.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
|
||||
.max = xmax, .invert = xinvert} }
|
||||
.max = xmax, .platform_max = xmax, .invert = xinvert} }
|
||||
#define SOC_DOUBLE_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
|
||||
@ -69,7 +70,7 @@
|
||||
.put = snd_soc_put_volsw, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .shift = shift_left, .rshift = shift_right,\
|
||||
.max = xmax, .invert = xinvert} }
|
||||
.max = xmax, .platform_max = xmax, .invert = xinvert} }
|
||||
#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
|
||||
@ -79,7 +80,7 @@
|
||||
.get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
|
||||
.max = xmax, .invert = xinvert} }
|
||||
.max = xmax, .platform_max = xmax, .invert = xinvert} }
|
||||
#define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
|
||||
@ -88,7 +89,8 @@
|
||||
.info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
|
||||
.put = snd_soc_put_volsw_s8, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .min = xmin, .max = xmax} }
|
||||
{.reg = xreg, .min = xmin, .max = xmax, \
|
||||
.platform_max = xmax} }
|
||||
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
|
||||
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
|
||||
.max = xmax, .texts = xtexts }
|
||||
@ -125,7 +127,7 @@
|
||||
.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} }
|
||||
.max = xmax, .platform_max = xmax, .invert = xinvert} }
|
||||
#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
@ -145,7 +147,7 @@
|
||||
.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} }
|
||||
.max = xmax, .platform_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), \
|
||||
@ -156,7 +158,7 @@
|
||||
.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} }
|
||||
.max = xmax, .platform_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, \
|
||||
@ -212,6 +214,7 @@ struct snd_soc_dai_mode;
|
||||
struct snd_soc_pcm_runtime;
|
||||
struct snd_soc_dai;
|
||||
struct snd_soc_platform;
|
||||
struct snd_soc_dai_link;
|
||||
struct snd_soc_codec;
|
||||
struct soc_enum;
|
||||
struct snd_soc_ac97_ops;
|
||||
@ -260,6 +263,10 @@ int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
|
||||
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
|
||||
int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
|
||||
struct snd_soc_jack_pin *pins);
|
||||
void snd_soc_jack_notifier_register(struct snd_soc_jack *jack,
|
||||
struct notifier_block *nb);
|
||||
void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack,
|
||||
struct notifier_block *nb);
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
|
||||
struct snd_soc_jack_gpio *gpios);
|
||||
@ -320,6 +327,8 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_soc_limit_volume(struct snd_soc_codec *codec,
|
||||
const char *name, int max);
|
||||
|
||||
/**
|
||||
* struct snd_soc_jack_pin - Describes a pin to update based on jack detection
|
||||
@ -363,6 +372,7 @@ struct snd_soc_jack {
|
||||
struct snd_soc_card *card;
|
||||
struct list_head pins;
|
||||
int status;
|
||||
struct blocking_notifier_head notifier;
|
||||
};
|
||||
|
||||
/* SoC PCM stream information */
|
||||
@ -374,7 +384,7 @@ struct snd_soc_pcm_stream {
|
||||
unsigned int rate_max; /* max rate */
|
||||
unsigned int channels_min; /* min channels */
|
||||
unsigned int channels_max; /* max channels */
|
||||
unsigned int active:1; /* stream is in use */
|
||||
unsigned int active; /* stream is in use */
|
||||
void *dma_data; /* used by platform code */
|
||||
};
|
||||
|
||||
@ -407,7 +417,7 @@ struct snd_soc_codec {
|
||||
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
|
||||
unsigned int active;
|
||||
unsigned int pcm_devs;
|
||||
void *private_data;
|
||||
void *drvdata;
|
||||
|
||||
/* codec IO */
|
||||
void *control_data; /* codec control (i2c/3wire) data */
|
||||
@ -462,14 +472,21 @@ struct snd_soc_platform {
|
||||
|
||||
int (*probe)(struct platform_device *pdev);
|
||||
int (*remove)(struct platform_device *pdev);
|
||||
int (*suspend)(struct snd_soc_dai *dai);
|
||||
int (*resume)(struct snd_soc_dai *dai);
|
||||
int (*suspend)(struct snd_soc_dai_link *dai_link);
|
||||
int (*resume)(struct snd_soc_dai_link *dai_link);
|
||||
|
||||
/* pcm creation and destruction */
|
||||
int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
|
||||
struct snd_pcm *);
|
||||
void (*pcm_free)(struct snd_pcm *);
|
||||
|
||||
/*
|
||||
* For platform caused delay reporting.
|
||||
* Optional.
|
||||
*/
|
||||
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
|
||||
struct snd_soc_dai *);
|
||||
|
||||
/* platform stream ops */
|
||||
struct snd_pcm_ops *pcm_ops;
|
||||
};
|
||||
@ -489,6 +506,9 @@ struct snd_soc_dai_link {
|
||||
/* codec/machine specific init - e.g. add machine controls */
|
||||
int (*init)(struct snd_soc_codec *codec);
|
||||
|
||||
/* Keep DAI active over suspend */
|
||||
unsigned int ignore_suspend:1;
|
||||
|
||||
/* Symmetry requirements */
|
||||
unsigned int symmetric_rates:1;
|
||||
|
||||
@ -553,7 +573,7 @@ struct snd_soc_pcm_runtime {
|
||||
|
||||
/* mixer control */
|
||||
struct soc_mixer_control {
|
||||
int min, max;
|
||||
int min, max, platform_max;
|
||||
unsigned int reg, rreg, shift, rshift, invert;
|
||||
};
|
||||
|
||||
@ -583,6 +603,17 @@ static inline unsigned int snd_soc_write(struct snd_soc_codec *codec,
|
||||
return codec->write(codec, reg, val);
|
||||
}
|
||||
|
||||
static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
|
||||
void *data)
|
||||
{
|
||||
codec->drvdata = data;
|
||||
}
|
||||
|
||||
static inline void *snd_soc_codec_get_drvdata(struct snd_soc_codec *codec)
|
||||
{
|
||||
return codec->drvdata;
|
||||
}
|
||||
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#endif
|
||||
|
17
include/sound/tlv320aic3x.h
Normal file
17
include/sound/tlv320aic3x.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Platform data for Texas Instruments TLV320AIC3x codec
|
||||
*
|
||||
* Author: Jarkko Nikula <jhnikula@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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __TLV320AIC3x_H__
|
||||
#define __TLV320AIC3x_H__
|
||||
|
||||
struct aic3x_pdata {
|
||||
int gpio_reset; /* < 0 if not used */
|
||||
};
|
||||
|
||||
#endif
|
@ -15,6 +15,7 @@
|
||||
|
||||
struct tlv320dac33_platform_data {
|
||||
int power_gpio;
|
||||
int keep_bclk; /* Keep the BCLK running in FIFO modes */
|
||||
u8 burst_bclkdiv;
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,7 @@ struct uda134x_platform_data {
|
||||
#define UDA134X_UDA1340 1
|
||||
#define UDA134X_UDA1341 2
|
||||
#define UDA134X_UDA1344 3
|
||||
#define UDA134X_UDA1345 4
|
||||
};
|
||||
|
||||
#endif /* _UDA134X_H */
|
||||
|
@ -1,3 +1,3 @@
|
||||
/* include/version.h */
|
||||
#define CONFIG_SND_VERSION "1.0.22.1"
|
||||
#define CONFIG_SND_VERSION "1.0.23"
|
||||
#define CONFIG_SND_DATE ""
|
||||
|
249
include/sound/wm8903.h
Normal file
249
include/sound/wm8903.h
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* linux/sound/wm8903.h -- Platform data for WM8903
|
||||
*
|
||||
* Copyright 2010 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_WM8903_H
|
||||
#define __LINUX_SND_WM8903_H
|
||||
|
||||
/* Used to enable configuration of a GPIO to all zeros */
|
||||
#define WM8903_GPIO_NO_CONFIG 0x8000
|
||||
|
||||
/*
|
||||
* R6 (0x06) - Mic Bias Control 0
|
||||
*/
|
||||
#define WM8903_MICDET_HYST_ENA 0x0080 /* MICDET_HYST_ENA */
|
||||
#define WM8903_MICDET_HYST_ENA_MASK 0x0080 /* MICDET_HYST_ENA */
|
||||
#define WM8903_MICDET_HYST_ENA_SHIFT 7 /* MICDET_HYST_ENA */
|
||||
#define WM8903_MICDET_HYST_ENA_WIDTH 1 /* MICDET_HYST_ENA */
|
||||
#define WM8903_MICDET_THR_MASK 0x0070 /* MICDET_THR - [6:4] */
|
||||
#define WM8903_MICDET_THR_SHIFT 4 /* MICDET_THR - [6:4] */
|
||||
#define WM8903_MICDET_THR_WIDTH 3 /* MICDET_THR - [6:4] */
|
||||
#define WM8903_MICSHORT_THR_MASK 0x000C /* MICSHORT_THR - [3:2] */
|
||||
#define WM8903_MICSHORT_THR_SHIFT 2 /* MICSHORT_THR - [3:2] */
|
||||
#define WM8903_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [3:2] */
|
||||
#define WM8903_MICDET_ENA 0x0002 /* MICDET_ENA */
|
||||
#define WM8903_MICDET_ENA_MASK 0x0002 /* MICDET_ENA */
|
||||
#define WM8903_MICDET_ENA_SHIFT 1 /* MICDET_ENA */
|
||||
#define WM8903_MICDET_ENA_WIDTH 1 /* MICDET_ENA */
|
||||
#define WM8903_MICBIAS_ENA 0x0001 /* MICBIAS_ENA */
|
||||
#define WM8903_MICBIAS_ENA_MASK 0x0001 /* MICBIAS_ENA */
|
||||
#define WM8903_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */
|
||||
#define WM8903_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */
|
||||
|
||||
/*
|
||||
* R116 (0x74) - GPIO Control 1
|
||||
*/
|
||||
#define WM8903_GP1_FN_MASK 0x1F00 /* GP1_FN - [12:8] */
|
||||
#define WM8903_GP1_FN_SHIFT 8 /* GP1_FN - [12:8] */
|
||||
#define WM8903_GP1_FN_WIDTH 5 /* GP1_FN - [12:8] */
|
||||
#define WM8903_GP1_DIR 0x0080 /* GP1_DIR */
|
||||
#define WM8903_GP1_DIR_MASK 0x0080 /* GP1_DIR */
|
||||
#define WM8903_GP1_DIR_SHIFT 7 /* GP1_DIR */
|
||||
#define WM8903_GP1_DIR_WIDTH 1 /* GP1_DIR */
|
||||
#define WM8903_GP1_OP_CFG 0x0040 /* GP1_OP_CFG */
|
||||
#define WM8903_GP1_OP_CFG_MASK 0x0040 /* GP1_OP_CFG */
|
||||
#define WM8903_GP1_OP_CFG_SHIFT 6 /* GP1_OP_CFG */
|
||||
#define WM8903_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */
|
||||
#define WM8903_GP1_IP_CFG 0x0020 /* GP1_IP_CFG */
|
||||
#define WM8903_GP1_IP_CFG_MASK 0x0020 /* GP1_IP_CFG */
|
||||
#define WM8903_GP1_IP_CFG_SHIFT 5 /* GP1_IP_CFG */
|
||||
#define WM8903_GP1_IP_CFG_WIDTH 1 /* GP1_IP_CFG */
|
||||
#define WM8903_GP1_LVL 0x0010 /* GP1_LVL */
|
||||
#define WM8903_GP1_LVL_MASK 0x0010 /* GP1_LVL */
|
||||
#define WM8903_GP1_LVL_SHIFT 4 /* GP1_LVL */
|
||||
#define WM8903_GP1_LVL_WIDTH 1 /* GP1_LVL */
|
||||
#define WM8903_GP1_PD 0x0008 /* GP1_PD */
|
||||
#define WM8903_GP1_PD_MASK 0x0008 /* GP1_PD */
|
||||
#define WM8903_GP1_PD_SHIFT 3 /* GP1_PD */
|
||||
#define WM8903_GP1_PD_WIDTH 1 /* GP1_PD */
|
||||
#define WM8903_GP1_PU 0x0004 /* GP1_PU */
|
||||
#define WM8903_GP1_PU_MASK 0x0004 /* GP1_PU */
|
||||
#define WM8903_GP1_PU_SHIFT 2 /* GP1_PU */
|
||||
#define WM8903_GP1_PU_WIDTH 1 /* GP1_PU */
|
||||
#define WM8903_GP1_INTMODE 0x0002 /* GP1_INTMODE */
|
||||
#define WM8903_GP1_INTMODE_MASK 0x0002 /* GP1_INTMODE */
|
||||
#define WM8903_GP1_INTMODE_SHIFT 1 /* GP1_INTMODE */
|
||||
#define WM8903_GP1_INTMODE_WIDTH 1 /* GP1_INTMODE */
|
||||
#define WM8903_GP1_DB 0x0001 /* GP1_DB */
|
||||
#define WM8903_GP1_DB_MASK 0x0001 /* GP1_DB */
|
||||
#define WM8903_GP1_DB_SHIFT 0 /* GP1_DB */
|
||||
#define WM8903_GP1_DB_WIDTH 1 /* GP1_DB */
|
||||
|
||||
/*
|
||||
* R117 (0x75) - GPIO Control 2
|
||||
*/
|
||||
#define WM8903_GP2_FN_MASK 0x1F00 /* GP2_FN - [12:8] */
|
||||
#define WM8903_GP2_FN_SHIFT 8 /* GP2_FN - [12:8] */
|
||||
#define WM8903_GP2_FN_WIDTH 5 /* GP2_FN - [12:8] */
|
||||
#define WM8903_GP2_DIR 0x0080 /* GP2_DIR */
|
||||
#define WM8903_GP2_DIR_MASK 0x0080 /* GP2_DIR */
|
||||
#define WM8903_GP2_DIR_SHIFT 7 /* GP2_DIR */
|
||||
#define WM8903_GP2_DIR_WIDTH 1 /* GP2_DIR */
|
||||
#define WM8903_GP2_OP_CFG 0x0040 /* GP2_OP_CFG */
|
||||
#define WM8903_GP2_OP_CFG_MASK 0x0040 /* GP2_OP_CFG */
|
||||
#define WM8903_GP2_OP_CFG_SHIFT 6 /* GP2_OP_CFG */
|
||||
#define WM8903_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */
|
||||
#define WM8903_GP2_IP_CFG 0x0020 /* GP2_IP_CFG */
|
||||
#define WM8903_GP2_IP_CFG_MASK 0x0020 /* GP2_IP_CFG */
|
||||
#define WM8903_GP2_IP_CFG_SHIFT 5 /* GP2_IP_CFG */
|
||||
#define WM8903_GP2_IP_CFG_WIDTH 1 /* GP2_IP_CFG */
|
||||
#define WM8903_GP2_LVL 0x0010 /* GP2_LVL */
|
||||
#define WM8903_GP2_LVL_MASK 0x0010 /* GP2_LVL */
|
||||
#define WM8903_GP2_LVL_SHIFT 4 /* GP2_LVL */
|
||||
#define WM8903_GP2_LVL_WIDTH 1 /* GP2_LVL */
|
||||
#define WM8903_GP2_PD 0x0008 /* GP2_PD */
|
||||
#define WM8903_GP2_PD_MASK 0x0008 /* GP2_PD */
|
||||
#define WM8903_GP2_PD_SHIFT 3 /* GP2_PD */
|
||||
#define WM8903_GP2_PD_WIDTH 1 /* GP2_PD */
|
||||
#define WM8903_GP2_PU 0x0004 /* GP2_PU */
|
||||
#define WM8903_GP2_PU_MASK 0x0004 /* GP2_PU */
|
||||
#define WM8903_GP2_PU_SHIFT 2 /* GP2_PU */
|
||||
#define WM8903_GP2_PU_WIDTH 1 /* GP2_PU */
|
||||
#define WM8903_GP2_INTMODE 0x0002 /* GP2_INTMODE */
|
||||
#define WM8903_GP2_INTMODE_MASK 0x0002 /* GP2_INTMODE */
|
||||
#define WM8903_GP2_INTMODE_SHIFT 1 /* GP2_INTMODE */
|
||||
#define WM8903_GP2_INTMODE_WIDTH 1 /* GP2_INTMODE */
|
||||
#define WM8903_GP2_DB 0x0001 /* GP2_DB */
|
||||
#define WM8903_GP2_DB_MASK 0x0001 /* GP2_DB */
|
||||
#define WM8903_GP2_DB_SHIFT 0 /* GP2_DB */
|
||||
#define WM8903_GP2_DB_WIDTH 1 /* GP2_DB */
|
||||
|
||||
/*
|
||||
* R118 (0x76) - GPIO Control 3
|
||||
*/
|
||||
#define WM8903_GP3_FN_MASK 0x1F00 /* GP3_FN - [12:8] */
|
||||
#define WM8903_GP3_FN_SHIFT 8 /* GP3_FN - [12:8] */
|
||||
#define WM8903_GP3_FN_WIDTH 5 /* GP3_FN - [12:8] */
|
||||
#define WM8903_GP3_DIR 0x0080 /* GP3_DIR */
|
||||
#define WM8903_GP3_DIR_MASK 0x0080 /* GP3_DIR */
|
||||
#define WM8903_GP3_DIR_SHIFT 7 /* GP3_DIR */
|
||||
#define WM8903_GP3_DIR_WIDTH 1 /* GP3_DIR */
|
||||
#define WM8903_GP3_OP_CFG 0x0040 /* GP3_OP_CFG */
|
||||
#define WM8903_GP3_OP_CFG_MASK 0x0040 /* GP3_OP_CFG */
|
||||
#define WM8903_GP3_OP_CFG_SHIFT 6 /* GP3_OP_CFG */
|
||||
#define WM8903_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */
|
||||
#define WM8903_GP3_IP_CFG 0x0020 /* GP3_IP_CFG */
|
||||
#define WM8903_GP3_IP_CFG_MASK 0x0020 /* GP3_IP_CFG */
|
||||
#define WM8903_GP3_IP_CFG_SHIFT 5 /* GP3_IP_CFG */
|
||||
#define WM8903_GP3_IP_CFG_WIDTH 1 /* GP3_IP_CFG */
|
||||
#define WM8903_GP3_LVL 0x0010 /* GP3_LVL */
|
||||
#define WM8903_GP3_LVL_MASK 0x0010 /* GP3_LVL */
|
||||
#define WM8903_GP3_LVL_SHIFT 4 /* GP3_LVL */
|
||||
#define WM8903_GP3_LVL_WIDTH 1 /* GP3_LVL */
|
||||
#define WM8903_GP3_PD 0x0008 /* GP3_PD */
|
||||
#define WM8903_GP3_PD_MASK 0x0008 /* GP3_PD */
|
||||
#define WM8903_GP3_PD_SHIFT 3 /* GP3_PD */
|
||||
#define WM8903_GP3_PD_WIDTH 1 /* GP3_PD */
|
||||
#define WM8903_GP3_PU 0x0004 /* GP3_PU */
|
||||
#define WM8903_GP3_PU_MASK 0x0004 /* GP3_PU */
|
||||
#define WM8903_GP3_PU_SHIFT 2 /* GP3_PU */
|
||||
#define WM8903_GP3_PU_WIDTH 1 /* GP3_PU */
|
||||
#define WM8903_GP3_INTMODE 0x0002 /* GP3_INTMODE */
|
||||
#define WM8903_GP3_INTMODE_MASK 0x0002 /* GP3_INTMODE */
|
||||
#define WM8903_GP3_INTMODE_SHIFT 1 /* GP3_INTMODE */
|
||||
#define WM8903_GP3_INTMODE_WIDTH 1 /* GP3_INTMODE */
|
||||
#define WM8903_GP3_DB 0x0001 /* GP3_DB */
|
||||
#define WM8903_GP3_DB_MASK 0x0001 /* GP3_DB */
|
||||
#define WM8903_GP3_DB_SHIFT 0 /* GP3_DB */
|
||||
#define WM8903_GP3_DB_WIDTH 1 /* GP3_DB */
|
||||
|
||||
/*
|
||||
* R119 (0x77) - GPIO Control 4
|
||||
*/
|
||||
#define WM8903_GP4_FN_MASK 0x1F00 /* GP4_FN - [12:8] */
|
||||
#define WM8903_GP4_FN_SHIFT 8 /* GP4_FN - [12:8] */
|
||||
#define WM8903_GP4_FN_WIDTH 5 /* GP4_FN - [12:8] */
|
||||
#define WM8903_GP4_DIR 0x0080 /* GP4_DIR */
|
||||
#define WM8903_GP4_DIR_MASK 0x0080 /* GP4_DIR */
|
||||
#define WM8903_GP4_DIR_SHIFT 7 /* GP4_DIR */
|
||||
#define WM8903_GP4_DIR_WIDTH 1 /* GP4_DIR */
|
||||
#define WM8903_GP4_OP_CFG 0x0040 /* GP4_OP_CFG */
|
||||
#define WM8903_GP4_OP_CFG_MASK 0x0040 /* GP4_OP_CFG */
|
||||
#define WM8903_GP4_OP_CFG_SHIFT 6 /* GP4_OP_CFG */
|
||||
#define WM8903_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */
|
||||
#define WM8903_GP4_IP_CFG 0x0020 /* GP4_IP_CFG */
|
||||
#define WM8903_GP4_IP_CFG_MASK 0x0020 /* GP4_IP_CFG */
|
||||
#define WM8903_GP4_IP_CFG_SHIFT 5 /* GP4_IP_CFG */
|
||||
#define WM8903_GP4_IP_CFG_WIDTH 1 /* GP4_IP_CFG */
|
||||
#define WM8903_GP4_LVL 0x0010 /* GP4_LVL */
|
||||
#define WM8903_GP4_LVL_MASK 0x0010 /* GP4_LVL */
|
||||
#define WM8903_GP4_LVL_SHIFT 4 /* GP4_LVL */
|
||||
#define WM8903_GP4_LVL_WIDTH 1 /* GP4_LVL */
|
||||
#define WM8903_GP4_PD 0x0008 /* GP4_PD */
|
||||
#define WM8903_GP4_PD_MASK 0x0008 /* GP4_PD */
|
||||
#define WM8903_GP4_PD_SHIFT 3 /* GP4_PD */
|
||||
#define WM8903_GP4_PD_WIDTH 1 /* GP4_PD */
|
||||
#define WM8903_GP4_PU 0x0004 /* GP4_PU */
|
||||
#define WM8903_GP4_PU_MASK 0x0004 /* GP4_PU */
|
||||
#define WM8903_GP4_PU_SHIFT 2 /* GP4_PU */
|
||||
#define WM8903_GP4_PU_WIDTH 1 /* GP4_PU */
|
||||
#define WM8903_GP4_INTMODE 0x0002 /* GP4_INTMODE */
|
||||
#define WM8903_GP4_INTMODE_MASK 0x0002 /* GP4_INTMODE */
|
||||
#define WM8903_GP4_INTMODE_SHIFT 1 /* GP4_INTMODE */
|
||||
#define WM8903_GP4_INTMODE_WIDTH 1 /* GP4_INTMODE */
|
||||
#define WM8903_GP4_DB 0x0001 /* GP4_DB */
|
||||
#define WM8903_GP4_DB_MASK 0x0001 /* GP4_DB */
|
||||
#define WM8903_GP4_DB_SHIFT 0 /* GP4_DB */
|
||||
#define WM8903_GP4_DB_WIDTH 1 /* GP4_DB */
|
||||
|
||||
/*
|
||||
* R120 (0x78) - GPIO Control 5
|
||||
*/
|
||||
#define WM8903_GP5_FN_MASK 0x1F00 /* GP5_FN - [12:8] */
|
||||
#define WM8903_GP5_FN_SHIFT 8 /* GP5_FN - [12:8] */
|
||||
#define WM8903_GP5_FN_WIDTH 5 /* GP5_FN - [12:8] */
|
||||
#define WM8903_GP5_DIR 0x0080 /* GP5_DIR */
|
||||
#define WM8903_GP5_DIR_MASK 0x0080 /* GP5_DIR */
|
||||
#define WM8903_GP5_DIR_SHIFT 7 /* GP5_DIR */
|
||||
#define WM8903_GP5_DIR_WIDTH 1 /* GP5_DIR */
|
||||
#define WM8903_GP5_OP_CFG 0x0040 /* GP5_OP_CFG */
|
||||
#define WM8903_GP5_OP_CFG_MASK 0x0040 /* GP5_OP_CFG */
|
||||
#define WM8903_GP5_OP_CFG_SHIFT 6 /* GP5_OP_CFG */
|
||||
#define WM8903_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */
|
||||
#define WM8903_GP5_IP_CFG 0x0020 /* GP5_IP_CFG */
|
||||
#define WM8903_GP5_IP_CFG_MASK 0x0020 /* GP5_IP_CFG */
|
||||
#define WM8903_GP5_IP_CFG_SHIFT 5 /* GP5_IP_CFG */
|
||||
#define WM8903_GP5_IP_CFG_WIDTH 1 /* GP5_IP_CFG */
|
||||
#define WM8903_GP5_LVL 0x0010 /* GP5_LVL */
|
||||
#define WM8903_GP5_LVL_MASK 0x0010 /* GP5_LVL */
|
||||
#define WM8903_GP5_LVL_SHIFT 4 /* GP5_LVL */
|
||||
#define WM8903_GP5_LVL_WIDTH 1 /* GP5_LVL */
|
||||
#define WM8903_GP5_PD 0x0008 /* GP5_PD */
|
||||
#define WM8903_GP5_PD_MASK 0x0008 /* GP5_PD */
|
||||
#define WM8903_GP5_PD_SHIFT 3 /* GP5_PD */
|
||||
#define WM8903_GP5_PD_WIDTH 1 /* GP5_PD */
|
||||
#define WM8903_GP5_PU 0x0004 /* GP5_PU */
|
||||
#define WM8903_GP5_PU_MASK 0x0004 /* GP5_PU */
|
||||
#define WM8903_GP5_PU_SHIFT 2 /* GP5_PU */
|
||||
#define WM8903_GP5_PU_WIDTH 1 /* GP5_PU */
|
||||
#define WM8903_GP5_INTMODE 0x0002 /* GP5_INTMODE */
|
||||
#define WM8903_GP5_INTMODE_MASK 0x0002 /* GP5_INTMODE */
|
||||
#define WM8903_GP5_INTMODE_SHIFT 1 /* GP5_INTMODE */
|
||||
#define WM8903_GP5_INTMODE_WIDTH 1 /* GP5_INTMODE */
|
||||
#define WM8903_GP5_DB 0x0001 /* GP5_DB */
|
||||
#define WM8903_GP5_DB_MASK 0x0001 /* GP5_DB */
|
||||
#define WM8903_GP5_DB_SHIFT 0 /* GP5_DB */
|
||||
#define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */
|
||||
|
||||
struct wm8903_platform_data {
|
||||
bool irq_active_low; /* Set if IRQ active low, default high */
|
||||
|
||||
/* Default register value for R6 (Mic bias), used to configure
|
||||
* microphone detection. In conjunction with gpio_cfg this
|
||||
* can be used to route the microphone status signals out onto
|
||||
* the GPIOs for use with snd_soc_jack_add_gpios().
|
||||
*/
|
||||
u16 micdet_cfg;
|
||||
|
||||
int micdet_delay; /* Delay after microphone detection (ms) */
|
||||
|
||||
u32 gpio_cfg[5]; /* Default register values for GPIO pin mux */
|
||||
};
|
||||
|
||||
#endif
|
@ -15,8 +15,111 @@
|
||||
#ifndef __MFD_WM8994_PDATA_H__
|
||||
#define __MFD_WM8994_PDATA_H__
|
||||
|
||||
#define WM8904_DRC_REGS 4
|
||||
#define WM8904_EQ_REGS 25
|
||||
/* Used to enable configuration of a GPIO to all zeros */
|
||||
#define WM8904_GPIO_NO_CONFIG 0x8000
|
||||
|
||||
/*
|
||||
* R6 (0x06) - Mic Bias Control 0
|
||||
*/
|
||||
#define WM8904_MICDET_THR_MASK 0x0070 /* MICDET_THR - [6:4] */
|
||||
#define WM8904_MICDET_THR_SHIFT 4 /* MICDET_THR - [6:4] */
|
||||
#define WM8904_MICDET_THR_WIDTH 3 /* MICDET_THR - [6:4] */
|
||||
#define WM8904_MICSHORT_THR_MASK 0x000C /* MICSHORT_THR - [3:2] */
|
||||
#define WM8904_MICSHORT_THR_SHIFT 2 /* MICSHORT_THR - [3:2] */
|
||||
#define WM8904_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [3:2] */
|
||||
#define WM8904_MICDET_ENA 0x0002 /* MICDET_ENA */
|
||||
#define WM8904_MICDET_ENA_MASK 0x0002 /* MICDET_ENA */
|
||||
#define WM8904_MICDET_ENA_SHIFT 1 /* MICDET_ENA */
|
||||
#define WM8904_MICDET_ENA_WIDTH 1 /* MICDET_ENA */
|
||||
#define WM8904_MICBIAS_ENA 0x0001 /* MICBIAS_ENA */
|
||||
#define WM8904_MICBIAS_ENA_MASK 0x0001 /* MICBIAS_ENA */
|
||||
#define WM8904_MICBIAS_ENA_SHIFT 0 /* MICBIAS_ENA */
|
||||
#define WM8904_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */
|
||||
|
||||
/*
|
||||
* R7 (0x07) - Mic Bias Control 1
|
||||
*/
|
||||
#define WM8904_MIC_DET_FILTER_ENA 0x8000 /* MIC_DET_FILTER_ENA */
|
||||
#define WM8904_MIC_DET_FILTER_ENA_MASK 0x8000 /* MIC_DET_FILTER_ENA */
|
||||
#define WM8904_MIC_DET_FILTER_ENA_SHIFT 15 /* MIC_DET_FILTER_ENA */
|
||||
#define WM8904_MIC_DET_FILTER_ENA_WIDTH 1 /* MIC_DET_FILTER_ENA */
|
||||
#define WM8904_MIC_SHORT_FILTER_ENA 0x4000 /* MIC_SHORT_FILTER_ENA */
|
||||
#define WM8904_MIC_SHORT_FILTER_ENA_MASK 0x4000 /* MIC_SHORT_FILTER_ENA */
|
||||
#define WM8904_MIC_SHORT_FILTER_ENA_SHIFT 14 /* MIC_SHORT_FILTER_ENA */
|
||||
#define WM8904_MIC_SHORT_FILTER_ENA_WIDTH 1 /* MIC_SHORT_FILTER_ENA */
|
||||
#define WM8904_MICBIAS_SEL_MASK 0x0007 /* MICBIAS_SEL - [2:0] */
|
||||
#define WM8904_MICBIAS_SEL_SHIFT 0 /* MICBIAS_SEL - [2:0] */
|
||||
#define WM8904_MICBIAS_SEL_WIDTH 3 /* MICBIAS_SEL - [2:0] */
|
||||
|
||||
|
||||
/*
|
||||
* R121 (0x79) - GPIO Control 1
|
||||
*/
|
||||
#define WM8904_GPIO1_PU 0x0020 /* GPIO1_PU */
|
||||
#define WM8904_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */
|
||||
#define WM8904_GPIO1_PU_SHIFT 5 /* GPIO1_PU */
|
||||
#define WM8904_GPIO1_PU_WIDTH 1 /* GPIO1_PU */
|
||||
#define WM8904_GPIO1_PD 0x0010 /* GPIO1_PD */
|
||||
#define WM8904_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */
|
||||
#define WM8904_GPIO1_PD_SHIFT 4 /* GPIO1_PD */
|
||||
#define WM8904_GPIO1_PD_WIDTH 1 /* GPIO1_PD */
|
||||
#define WM8904_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */
|
||||
#define WM8904_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */
|
||||
#define WM8904_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */
|
||||
|
||||
/*
|
||||
* R122 (0x7A) - GPIO Control 2
|
||||
*/
|
||||
#define WM8904_GPIO2_PU 0x0020 /* GPIO2_PU */
|
||||
#define WM8904_GPIO2_PU_MASK 0x0020 /* GPIO2_PU */
|
||||
#define WM8904_GPIO2_PU_SHIFT 5 /* GPIO2_PU */
|
||||
#define WM8904_GPIO2_PU_WIDTH 1 /* GPIO2_PU */
|
||||
#define WM8904_GPIO2_PD 0x0010 /* GPIO2_PD */
|
||||
#define WM8904_GPIO2_PD_MASK 0x0010 /* GPIO2_PD */
|
||||
#define WM8904_GPIO2_PD_SHIFT 4 /* GPIO2_PD */
|
||||
#define WM8904_GPIO2_PD_WIDTH 1 /* GPIO2_PD */
|
||||
#define WM8904_GPIO2_SEL_MASK 0x000F /* GPIO2_SEL - [3:0] */
|
||||
#define WM8904_GPIO2_SEL_SHIFT 0 /* GPIO2_SEL - [3:0] */
|
||||
#define WM8904_GPIO2_SEL_WIDTH 4 /* GPIO2_SEL - [3:0] */
|
||||
|
||||
/*
|
||||
* R123 (0x7B) - GPIO Control 3
|
||||
*/
|
||||
#define WM8904_GPIO3_PU 0x0020 /* GPIO3_PU */
|
||||
#define WM8904_GPIO3_PU_MASK 0x0020 /* GPIO3_PU */
|
||||
#define WM8904_GPIO3_PU_SHIFT 5 /* GPIO3_PU */
|
||||
#define WM8904_GPIO3_PU_WIDTH 1 /* GPIO3_PU */
|
||||
#define WM8904_GPIO3_PD 0x0010 /* GPIO3_PD */
|
||||
#define WM8904_GPIO3_PD_MASK 0x0010 /* GPIO3_PD */
|
||||
#define WM8904_GPIO3_PD_SHIFT 4 /* GPIO3_PD */
|
||||
#define WM8904_GPIO3_PD_WIDTH 1 /* GPIO3_PD */
|
||||
#define WM8904_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */
|
||||
#define WM8904_GPIO3_SEL_SHIFT 0 /* GPIO3_SEL - [3:0] */
|
||||
#define WM8904_GPIO3_SEL_WIDTH 4 /* GPIO3_SEL - [3:0] */
|
||||
|
||||
/*
|
||||
* R124 (0x7C) - GPIO Control 4
|
||||
*/
|
||||
#define WM8904_GPI7_ENA 0x0200 /* GPI7_ENA */
|
||||
#define WM8904_GPI7_ENA_MASK 0x0200 /* GPI7_ENA */
|
||||
#define WM8904_GPI7_ENA_SHIFT 9 /* GPI7_ENA */
|
||||
#define WM8904_GPI7_ENA_WIDTH 1 /* GPI7_ENA */
|
||||
#define WM8904_GPI8_ENA 0x0100 /* GPI8_ENA */
|
||||
#define WM8904_GPI8_ENA_MASK 0x0100 /* GPI8_ENA */
|
||||
#define WM8904_GPI8_ENA_SHIFT 8 /* GPI8_ENA */
|
||||
#define WM8904_GPI8_ENA_WIDTH 1 /* GPI8_ENA */
|
||||
#define WM8904_GPIO_BCLK_MODE_ENA 0x0080 /* GPIO_BCLK_MODE_ENA */
|
||||
#define WM8904_GPIO_BCLK_MODE_ENA_MASK 0x0080 /* GPIO_BCLK_MODE_ENA */
|
||||
#define WM8904_GPIO_BCLK_MODE_ENA_SHIFT 7 /* GPIO_BCLK_MODE_ENA */
|
||||
#define WM8904_GPIO_BCLK_MODE_ENA_WIDTH 1 /* GPIO_BCLK_MODE_ENA */
|
||||
#define WM8904_GPIO_BCLK_SEL_MASK 0x000F /* GPIO_BCLK_SEL - [3:0] */
|
||||
#define WM8904_GPIO_BCLK_SEL_SHIFT 0 /* GPIO_BCLK_SEL - [3:0] */
|
||||
#define WM8904_GPIO_BCLK_SEL_WIDTH 4 /* GPIO_BCLK_SEL - [3:0] */
|
||||
|
||||
#define WM8904_MIC_REGS 2
|
||||
#define WM8904_GPIO_REGS 4
|
||||
#define WM8904_DRC_REGS 4
|
||||
#define WM8904_EQ_REGS 25
|
||||
|
||||
/**
|
||||
* DRC configurations are specified with a label and a set of register
|
||||
@ -52,6 +155,9 @@ struct wm8904_pdata {
|
||||
|
||||
int num_retune_mobile_cfgs;
|
||||
struct wm8904_retune_mobile_cfg *retune_mobile_cfgs;
|
||||
|
||||
u32 gpio_cfg[WM8904_GPIO_REGS];
|
||||
u32 mic_cfg[WM8904_MIC_REGS];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
24
include/sound/wm8960.h
Normal file
24
include/sound/wm8960.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* wm8960.h -- WM8960 Soc Audio driver platform data
|
||||
*
|
||||
* 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 _WM8960_PDATA_H
|
||||
#define _WM8960_PDATA_H
|
||||
|
||||
#define WM8960_DRES_400R 0
|
||||
#define WM8960_DRES_200R 1
|
||||
#define WM8960_DRES_600R 2
|
||||
#define WM8960_DRES_150R 3
|
||||
#define WM8960_DRES_MAX 3
|
||||
|
||||
struct wm8960_data {
|
||||
bool capless; /* Headphone outputs configured in capless mode */
|
||||
|
||||
int dres; /* Discharge resistance for headphone outputs */
|
||||
};
|
||||
|
||||
#endif
|
28
include/sound/wm9090.h
Normal file
28
include/sound/wm9090.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* linux/sound/wm9090.h -- Platform data for WM9090
|
||||
*
|
||||
* Copyright 2009, 2010 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_WM9090_H
|
||||
#define __LINUX_SND_WM9090_H
|
||||
|
||||
struct wm9090_platform_data {
|
||||
/* Line inputs 1 & 2 can optionally be differential */
|
||||
unsigned int lin1_diff:1;
|
||||
unsigned int lin2_diff:1;
|
||||
|
||||
/* AGC configuration. This is intended to protect the speaker
|
||||
* against overdriving and will therefore depend on the
|
||||
* hardware setup with incorrect runtime configuration
|
||||
* potentially causing hardware damage.
|
||||
*/
|
||||
unsigned int agc_ena:1;
|
||||
u16 agc[3];
|
||||
};
|
||||
|
||||
#endif
|
@ -12,7 +12,7 @@ config SND_ATMEL_AC97C
|
||||
tristate "Atmel AC97 Controller (AC97C) driver"
|
||||
select SND_PCM
|
||||
select SND_AC97_CODEC
|
||||
depends on DW_DMAC && AVR32
|
||||
depends on (DW_DMAC && AVR32) || ARCH_AT91
|
||||
help
|
||||
ALSA sound driver for the Atmel AC97 controller.
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/atmel_pdc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
@ -31,6 +32,10 @@
|
||||
|
||||
#include <linux/dw_dmac.h>
|
||||
|
||||
#include <mach/cpu.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/gpio.h>
|
||||
|
||||
#include "ac97c.h"
|
||||
|
||||
enum {
|
||||
@ -63,6 +68,7 @@ struct atmel_ac97c {
|
||||
u64 cur_format;
|
||||
unsigned int cur_rate;
|
||||
unsigned long flags;
|
||||
int playback_period, capture_period;
|
||||
/* Serialize access to opened variable */
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
@ -242,10 +248,12 @@ static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
|
||||
if (retval == 1)
|
||||
if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.tx_chan);
|
||||
|
||||
if (cpu_is_at32ap7000()) {
|
||||
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
|
||||
if (retval == 1)
|
||||
if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.tx_chan);
|
||||
}
|
||||
/* Set restrictions to params. */
|
||||
mutex_lock(&opened_mutex);
|
||||
chip->cur_rate = params_rate(hw_params);
|
||||
@ -266,9 +274,14 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
|
||||
if (retval == 1)
|
||||
if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.rx_chan);
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
|
||||
if (retval == 1)
|
||||
if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.rx_chan);
|
||||
}
|
||||
|
||||
/* Set restrictions to params. */
|
||||
mutex_lock(&opened_mutex);
|
||||
@ -282,16 +295,20 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
|
||||
if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.tx_chan);
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.tx_chan);
|
||||
}
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
|
||||
if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.rx_chan);
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_free(chip->dma.rx_chan);
|
||||
}
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
@ -299,9 +316,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int block_size = frames_to_bytes(runtime, runtime->period_size);
|
||||
unsigned long word = ac97c_readl(chip, OCA);
|
||||
int retval;
|
||||
|
||||
chip->playback_period = 0;
|
||||
word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
|
||||
|
||||
/* assign channels to AC97C channel A */
|
||||
@ -320,11 +339,16 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
|
||||
ac97c_writel(chip, OCA, word);
|
||||
|
||||
/* configure sample format and size */
|
||||
word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
|
||||
word = ac97c_readl(chip, CAMR);
|
||||
if (chip->opened <= 1)
|
||||
word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
|
||||
else
|
||||
word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
|
||||
|
||||
switch (runtime->format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
word |= AC97C_CMR_CEM_LITTLE;
|
||||
if (cpu_is_at32ap7000())
|
||||
word |= AC97C_CMR_CEM_LITTLE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
|
||||
word &= ~(AC97C_CMR_CEM_LITTLE);
|
||||
@ -363,9 +387,18 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
|
||||
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
|
||||
runtime->rate);
|
||||
|
||||
if (!test_bit(DMA_TX_READY, &chip->flags))
|
||||
retval = atmel_ac97c_prepare_dma(chip, substream,
|
||||
DMA_TO_DEVICE);
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (!test_bit(DMA_TX_READY, &chip->flags))
|
||||
retval = atmel_ac97c_prepare_dma(chip, substream,
|
||||
DMA_TO_DEVICE);
|
||||
} else {
|
||||
/* Initialize and start the PDC */
|
||||
writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
|
||||
writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
|
||||
writel(runtime->dma_addr + block_size,
|
||||
chip->regs + ATMEL_PDC_TNPR);
|
||||
writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -374,9 +407,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int block_size = frames_to_bytes(runtime, runtime->period_size);
|
||||
unsigned long word = ac97c_readl(chip, ICA);
|
||||
int retval;
|
||||
|
||||
chip->capture_period = 0;
|
||||
word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
|
||||
|
||||
/* assign channels to AC97C channel A */
|
||||
@ -395,11 +430,16 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
|
||||
ac97c_writel(chip, ICA, word);
|
||||
|
||||
/* configure sample format and size */
|
||||
word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
|
||||
word = ac97c_readl(chip, CAMR);
|
||||
if (chip->opened <= 1)
|
||||
word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
|
||||
else
|
||||
word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
|
||||
|
||||
switch (runtime->format) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
word |= AC97C_CMR_CEM_LITTLE;
|
||||
if (cpu_is_at32ap7000())
|
||||
word |= AC97C_CMR_CEM_LITTLE;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
|
||||
word &= ~(AC97C_CMR_CEM_LITTLE);
|
||||
@ -438,9 +478,18 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
|
||||
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
|
||||
runtime->rate);
|
||||
|
||||
if (!test_bit(DMA_RX_READY, &chip->flags))
|
||||
retval = atmel_ac97c_prepare_dma(chip, substream,
|
||||
DMA_FROM_DEVICE);
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (!test_bit(DMA_RX_READY, &chip->flags))
|
||||
retval = atmel_ac97c_prepare_dma(chip, substream,
|
||||
DMA_FROM_DEVICE);
|
||||
} else {
|
||||
/* Initialize and start the PDC */
|
||||
writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
|
||||
writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
|
||||
writel(runtime->dma_addr + block_size,
|
||||
chip->regs + ATMEL_PDC_RNPR);
|
||||
writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -449,7 +498,7 @@ static int
|
||||
atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned long camr;
|
||||
unsigned long camr, ptcr = 0;
|
||||
int retval = 0;
|
||||
|
||||
camr = ac97c_readl(chip, CAMR);
|
||||
@ -458,15 +507,22 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
retval = dw_dma_cyclic_start(chip->dma.tx_chan);
|
||||
if (retval)
|
||||
goto out;
|
||||
camr |= AC97C_CMR_CENA;
|
||||
if (cpu_is_at32ap7000()) {
|
||||
retval = dw_dma_cyclic_start(chip->dma.tx_chan);
|
||||
if (retval)
|
||||
goto out;
|
||||
} else {
|
||||
ptcr = ATMEL_PDC_TXTEN;
|
||||
}
|
||||
camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
dw_dma_cyclic_stop(chip->dma.tx_chan);
|
||||
if (cpu_is_at32ap7000())
|
||||
dw_dma_cyclic_stop(chip->dma.tx_chan);
|
||||
else
|
||||
ptcr |= ATMEL_PDC_TXTDIS;
|
||||
if (chip->opened <= 1)
|
||||
camr &= ~AC97C_CMR_CENA;
|
||||
break;
|
||||
@ -476,6 +532,8 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
}
|
||||
|
||||
ac97c_writel(chip, CAMR, camr);
|
||||
if (!cpu_is_at32ap7000())
|
||||
writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
@ -484,24 +542,32 @@ static int
|
||||
atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned long camr;
|
||||
unsigned long camr, ptcr = 0;
|
||||
int retval = 0;
|
||||
|
||||
camr = ac97c_readl(chip, CAMR);
|
||||
ptcr = readl(chip->regs + ATMEL_PDC_PTSR);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
retval = dw_dma_cyclic_start(chip->dma.rx_chan);
|
||||
if (retval)
|
||||
goto out;
|
||||
camr |= AC97C_CMR_CENA;
|
||||
if (cpu_is_at32ap7000()) {
|
||||
retval = dw_dma_cyclic_start(chip->dma.rx_chan);
|
||||
if (retval)
|
||||
goto out;
|
||||
} else {
|
||||
ptcr = ATMEL_PDC_RXTEN;
|
||||
}
|
||||
camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
dw_dma_cyclic_stop(chip->dma.rx_chan);
|
||||
if (cpu_is_at32ap7000())
|
||||
dw_dma_cyclic_stop(chip->dma.rx_chan);
|
||||
else
|
||||
ptcr |= (ATMEL_PDC_RXTDIS);
|
||||
if (chip->opened <= 1)
|
||||
camr &= ~AC97C_CMR_CENA;
|
||||
break;
|
||||
@ -511,6 +577,8 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
}
|
||||
|
||||
ac97c_writel(chip, CAMR, camr);
|
||||
if (!cpu_is_at32ap7000())
|
||||
writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
@ -523,7 +591,10 @@ atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
|
||||
snd_pcm_uframes_t frames;
|
||||
unsigned long bytes;
|
||||
|
||||
bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
|
||||
if (cpu_is_at32ap7000())
|
||||
bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
|
||||
else
|
||||
bytes = readl(chip->regs + ATMEL_PDC_TPR);
|
||||
bytes -= runtime->dma_addr;
|
||||
|
||||
frames = bytes_to_frames(runtime, bytes);
|
||||
@ -540,7 +611,10 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
|
||||
snd_pcm_uframes_t frames;
|
||||
unsigned long bytes;
|
||||
|
||||
bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
|
||||
if (cpu_is_at32ap7000())
|
||||
bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
|
||||
else
|
||||
bytes = readl(chip->regs + ATMEL_PDC_RPR);
|
||||
bytes -= runtime->dma_addr;
|
||||
|
||||
frames = bytes_to_frames(runtime, bytes);
|
||||
@ -578,8 +652,11 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
|
||||
u32 sr = ac97c_readl(chip, SR);
|
||||
u32 casr = ac97c_readl(chip, CASR);
|
||||
u32 cosr = ac97c_readl(chip, COSR);
|
||||
u32 camr = ac97c_readl(chip, CAMR);
|
||||
|
||||
if (sr & AC97C_SR_CAEVT) {
|
||||
struct snd_pcm_runtime *runtime;
|
||||
int offset, next_period, block_size;
|
||||
dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
|
||||
casr & AC97C_CSR_OVRUN ? " OVRUN" : "",
|
||||
casr & AC97C_CSR_RXRDY ? " RXRDY" : "",
|
||||
@ -587,6 +664,50 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
|
||||
casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
|
||||
casr & AC97C_CSR_TXRDY ? " TXRDY" : "",
|
||||
!casr ? " NONE" : "");
|
||||
if (!cpu_is_at32ap7000()) {
|
||||
if ((casr & camr) & AC97C_CSR_ENDTX) {
|
||||
runtime = chip->playback_substream->runtime;
|
||||
block_size = frames_to_bytes(runtime,
|
||||
runtime->period_size);
|
||||
chip->playback_period++;
|
||||
|
||||
if (chip->playback_period == runtime->periods)
|
||||
chip->playback_period = 0;
|
||||
next_period = chip->playback_period + 1;
|
||||
if (next_period == runtime->periods)
|
||||
next_period = 0;
|
||||
|
||||
offset = block_size * next_period;
|
||||
|
||||
writel(runtime->dma_addr + offset,
|
||||
chip->regs + ATMEL_PDC_TNPR);
|
||||
writel(block_size / 2,
|
||||
chip->regs + ATMEL_PDC_TNCR);
|
||||
|
||||
snd_pcm_period_elapsed(
|
||||
chip->playback_substream);
|
||||
}
|
||||
if ((casr & camr) & AC97C_CSR_ENDRX) {
|
||||
runtime = chip->capture_substream->runtime;
|
||||
block_size = frames_to_bytes(runtime,
|
||||
runtime->period_size);
|
||||
chip->capture_period++;
|
||||
|
||||
if (chip->capture_period == runtime->periods)
|
||||
chip->capture_period = 0;
|
||||
next_period = chip->capture_period + 1;
|
||||
if (next_period == runtime->periods)
|
||||
next_period = 0;
|
||||
|
||||
offset = block_size * next_period;
|
||||
|
||||
writel(runtime->dma_addr + offset,
|
||||
chip->regs + ATMEL_PDC_RNPR);
|
||||
writel(block_size / 2,
|
||||
chip->regs + ATMEL_PDC_RNCR);
|
||||
snd_pcm_period_elapsed(chip->capture_substream);
|
||||
}
|
||||
}
|
||||
retval = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -608,15 +729,50 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
|
||||
/* Playback */
|
||||
{
|
||||
.exclusive = 1,
|
||||
.r = { {
|
||||
.slots = ((1 << AC97_SLOT_PCM_LEFT)
|
||||
| (1 << AC97_SLOT_PCM_RIGHT)),
|
||||
} },
|
||||
},
|
||||
/* PCM in */
|
||||
{
|
||||
.stream = 1,
|
||||
.exclusive = 1,
|
||||
.r = { {
|
||||
.slots = ((1 << AC97_SLOT_PCM_LEFT)
|
||||
| (1 << AC97_SLOT_PCM_RIGHT)),
|
||||
} }
|
||||
},
|
||||
/* Mic in */
|
||||
{
|
||||
.stream = 1,
|
||||
.exclusive = 1,
|
||||
.r = { {
|
||||
.slots = (1<<AC97_SLOT_MIC),
|
||||
} }
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_pcm_hardware hw = atmel_ac97c_hw;
|
||||
int capture, playback, retval;
|
||||
int capture, playback, retval, err;
|
||||
|
||||
capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
|
||||
if (!cpu_is_at32ap7000()) {
|
||||
err = snd_ac97_pcm_assign(chip->ac97_bus,
|
||||
ARRAY_SIZE(at91_ac97_pcm_defs),
|
||||
at91_ac97_pcm_defs);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
retval = snd_pcm_new(chip->card, chip->card->shortname,
|
||||
chip->pdev->id, playback, capture, &pcm);
|
||||
if (retval)
|
||||
@ -775,7 +931,12 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
pclk = clk_get(&pdev->dev, "pclk");
|
||||
if (cpu_is_at32ap7000()) {
|
||||
pclk = clk_get(&pdev->dev, "pclk");
|
||||
} else {
|
||||
pclk = clk_get(&pdev->dev, "ac97_clk");
|
||||
}
|
||||
|
||||
if (IS_ERR(pclk)) {
|
||||
dev_dbg(&pdev->dev, "no peripheral clock\n");
|
||||
return PTR_ERR(pclk);
|
||||
@ -844,43 +1005,52 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
|
||||
goto err_ac97_bus;
|
||||
}
|
||||
|
||||
if (pdata->rx_dws.dma_dev) {
|
||||
struct dw_dma_slave *dws = &pdata->rx_dws;
|
||||
dma_cap_mask_t mask;
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (pdata->rx_dws.dma_dev) {
|
||||
struct dw_dma_slave *dws = &pdata->rx_dws;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
dws->rx_reg = regs->start + AC97C_CARHR + 2;
|
||||
dws->rx_reg = regs->start + AC97C_CARHR + 2;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
|
||||
chip->dma.rx_chan = dma_request_channel(mask, filter,
|
||||
dws);
|
||||
|
||||
dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
|
||||
dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
|
||||
dev_name(&chip->dma.rx_chan->dev->device));
|
||||
set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
}
|
||||
set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
}
|
||||
|
||||
if (pdata->tx_dws.dma_dev) {
|
||||
struct dw_dma_slave *dws = &pdata->tx_dws;
|
||||
dma_cap_mask_t mask;
|
||||
if (pdata->tx_dws.dma_dev) {
|
||||
struct dw_dma_slave *dws = &pdata->tx_dws;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
dws->tx_reg = regs->start + AC97C_CATHR + 2;
|
||||
dws->tx_reg = regs->start + AC97C_CATHR + 2;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
chip->dma.tx_chan = dma_request_channel(mask, filter, dws);
|
||||
chip->dma.tx_chan = dma_request_channel(mask, filter,
|
||||
dws);
|
||||
|
||||
dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
|
||||
dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
|
||||
dev_name(&chip->dma.tx_chan->dev->device));
|
||||
set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
}
|
||||
set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
}
|
||||
|
||||
if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
|
||||
!test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
|
||||
dev_dbg(&pdev->dev, "DMA not available\n");
|
||||
retval = -ENODEV;
|
||||
goto err_dma;
|
||||
if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
|
||||
!test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
|
||||
dev_dbg(&pdev->dev, "DMA not available\n");
|
||||
retval = -ENODEV;
|
||||
goto err_dma;
|
||||
}
|
||||
} else {
|
||||
/* Just pretend that we have DMA channel(for at91 i is actually
|
||||
* the PDC) */
|
||||
set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
}
|
||||
|
||||
retval = atmel_ac97c_pcm_new(chip);
|
||||
@ -897,20 +1067,22 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p\n",
|
||||
chip->regs);
|
||||
dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n",
|
||||
chip->regs, irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dma:
|
||||
if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.tx_chan);
|
||||
clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
chip->dma.rx_chan = NULL;
|
||||
chip->dma.tx_chan = NULL;
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.tx_chan);
|
||||
clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
chip->dma.rx_chan = NULL;
|
||||
chip->dma.tx_chan = NULL;
|
||||
}
|
||||
err_ac97_bus:
|
||||
snd_card_set_dev(card, NULL);
|
||||
|
||||
@ -934,10 +1106,12 @@ static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
|
||||
struct snd_card *card = platform_get_drvdata(pdev);
|
||||
struct atmel_ac97c *chip = card->private_data;
|
||||
|
||||
if (test_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_stop(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_stop(chip->dma.tx_chan);
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (test_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_stop(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_stop(chip->dma.tx_chan);
|
||||
}
|
||||
clk_disable(chip->pclk);
|
||||
|
||||
return 0;
|
||||
@ -949,11 +1123,12 @@ static int atmel_ac97c_resume(struct platform_device *pdev)
|
||||
struct atmel_ac97c *chip = card->private_data;
|
||||
|
||||
clk_enable(chip->pclk);
|
||||
if (test_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_start(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_start(chip->dma.tx_chan);
|
||||
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (test_bit(DMA_RX_READY, &chip->flags))
|
||||
dw_dma_cyclic_start(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_READY, &chip->flags))
|
||||
dw_dma_cyclic_start(chip->dma.tx_chan);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@ -978,14 +1153,16 @@ static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
|
||||
iounmap(chip->regs);
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.tx_chan);
|
||||
clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
chip->dma.rx_chan = NULL;
|
||||
chip->dma.tx_chan = NULL;
|
||||
if (cpu_is_at32ap7000()) {
|
||||
if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.rx_chan);
|
||||
if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
|
||||
dma_release_channel(chip->dma.tx_chan);
|
||||
clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
|
||||
clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
|
||||
chip->dma.rx_chan = NULL;
|
||||
chip->dma.tx_chan = NULL;
|
||||
}
|
||||
|
||||
snd_card_set_dev(card, NULL);
|
||||
snd_card_free(card);
|
||||
|
@ -50,6 +50,10 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
|
||||
struct snd_ctl_file *ctl;
|
||||
int err;
|
||||
|
||||
err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
|
||||
if (!card) {
|
||||
err = -ENODEV;
|
||||
@ -1388,6 +1392,7 @@ static const struct file_operations snd_ctl_f_ops =
|
||||
.read = snd_ctl_read,
|
||||
.open = snd_ctl_open,
|
||||
.release = snd_ctl_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_ctl_poll,
|
||||
.unlocked_ioctl = snd_ctl_ioctl,
|
||||
.compat_ioctl = snd_ctl_ioctl_compat,
|
||||
|
@ -164,40 +164,44 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
struct snd_info_private_data *data;
|
||||
struct snd_info_entry *entry;
|
||||
loff_t ret;
|
||||
loff_t ret = -EINVAL, size;
|
||||
|
||||
data = file->private_data;
|
||||
entry = data->entry;
|
||||
lock_kernel();
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_TEXT:
|
||||
switch (orig) {
|
||||
case SEEK_SET:
|
||||
file->f_pos = offset;
|
||||
ret = file->f_pos;
|
||||
goto out;
|
||||
case SEEK_CUR:
|
||||
file->f_pos += offset;
|
||||
ret = file->f_pos;
|
||||
goto out;
|
||||
case SEEK_END:
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->llseek) {
|
||||
ret = entry->c.ops->llseek(entry,
|
||||
data->file_private_data,
|
||||
file, offset, orig);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
mutex_lock(&entry->access);
|
||||
if (entry->content == SNDRV_INFO_CONTENT_DATA &&
|
||||
entry->c.ops->llseek) {
|
||||
offset = entry->c.ops->llseek(entry,
|
||||
data->file_private_data,
|
||||
file, offset, orig);
|
||||
goto out;
|
||||
}
|
||||
ret = -ENXIO;
|
||||
out:
|
||||
unlock_kernel();
|
||||
if (entry->content == SNDRV_INFO_CONTENT_DATA)
|
||||
size = entry->size;
|
||||
else
|
||||
size = 0;
|
||||
switch (orig) {
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
offset += file->f_pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
if (!size)
|
||||
goto out;
|
||||
offset += size;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
if (offset < 0)
|
||||
goto out;
|
||||
if (size && offset > size)
|
||||
offset = size;
|
||||
file->f_pos = offset;
|
||||
ret = offset;
|
||||
out:
|
||||
mutex_unlock(&entry->access);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -232,10 +236,15 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
|
||||
return -EFAULT;
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->read)
|
||||
if (pos >= entry->size)
|
||||
return 0;
|
||||
if (entry->c.ops->read) {
|
||||
size = entry->size - pos;
|
||||
size = min(count, size);
|
||||
size = entry->c.ops->read(entry,
|
||||
data->file_private_data,
|
||||
file, buffer, count, pos);
|
||||
file, buffer, size, pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((ssize_t) size > 0)
|
||||
@ -282,10 +291,13 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
|
||||
size = count;
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->write)
|
||||
if (entry->c.ops->write && count > 0) {
|
||||
size_t maxsize = entry->size - pos;
|
||||
count = min(count, maxsize);
|
||||
size = entry->c.ops->write(entry,
|
||||
data->file_private_data,
|
||||
file, buffer, count, pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((ssize_t) size > 0)
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <sound/jack.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
static int jack_types[] = {
|
||||
static int jack_switch_types[] = {
|
||||
SW_HEADPHONE_INSERT,
|
||||
SW_MICROPHONE_INSERT,
|
||||
SW_LINEOUT_INSERT,
|
||||
@ -56,7 +56,7 @@ static int snd_jack_dev_register(struct snd_device *device)
|
||||
{
|
||||
struct snd_jack *jack = device->device_data;
|
||||
struct snd_card *card = device->card;
|
||||
int err;
|
||||
int err, i;
|
||||
|
||||
snprintf(jack->name, sizeof(jack->name), "%s %s",
|
||||
card->shortname, jack->id);
|
||||
@ -66,6 +66,19 @@ static int snd_jack_dev_register(struct snd_device *device)
|
||||
if (!jack->input_dev->dev.parent)
|
||||
jack->input_dev->dev.parent = snd_card_get_device_link(card);
|
||||
|
||||
/* Add capabilities for any keys that are enabled */
|
||||
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
|
||||
int testbit = SND_JACK_BTN_0 >> i;
|
||||
|
||||
if (!(jack->type & testbit))
|
||||
continue;
|
||||
|
||||
if (!jack->key[i])
|
||||
jack->key[i] = BTN_0 + i;
|
||||
|
||||
input_set_capability(jack->input_dev, EV_KEY, jack->key[i]);
|
||||
}
|
||||
|
||||
err = input_register_device(jack->input_dev);
|
||||
if (err == 0)
|
||||
jack->registered = 1;
|
||||
@ -113,10 +126,10 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
|
||||
|
||||
jack->type = type;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(jack_types); i++)
|
||||
for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++)
|
||||
if (type & (1 << i))
|
||||
input_set_capability(jack->input_dev, EV_SW,
|
||||
jack_types[i]);
|
||||
jack_switch_types[i]);
|
||||
|
||||
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
|
||||
if (err < 0)
|
||||
@ -151,6 +164,43 @@ void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
|
||||
}
|
||||
EXPORT_SYMBOL(snd_jack_set_parent);
|
||||
|
||||
/**
|
||||
* snd_jack_set_key - Set a key mapping on a jack
|
||||
*
|
||||
* @jack: The jack to configure
|
||||
* @type: Jack report type for this key
|
||||
* @keytype: Input layer key type to be reported
|
||||
*
|
||||
* Map a SND_JACK_BTN_ button type to an input layer key, allowing
|
||||
* reporting of keys on accessories via the jack abstraction. If no
|
||||
* mapping is provided but keys are enabled in the jack type then
|
||||
* BTN_n numeric buttons will be reported.
|
||||
*
|
||||
* Note that this is intended to be use by simple devices with small
|
||||
* numbers of keys that can be reported. It is also possible to
|
||||
* access the input device directly - devices with complex input
|
||||
* capabilities on accessories should consider doing this rather than
|
||||
* using this abstraction.
|
||||
*
|
||||
* This function may only be called prior to registration of the jack.
|
||||
*/
|
||||
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
|
||||
int keytype)
|
||||
{
|
||||
int key = fls(SND_JACK_BTN_0) - fls(type);
|
||||
|
||||
WARN_ON(jack->registered);
|
||||
|
||||
if (!keytype || key >= ARRAY_SIZE(jack->key))
|
||||
return -EINVAL;
|
||||
|
||||
jack->type |= type;
|
||||
jack->key[key] = keytype;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_jack_set_key);
|
||||
|
||||
/**
|
||||
* snd_jack_report - Report the current status of a jack
|
||||
*
|
||||
@ -164,10 +214,19 @@ void snd_jack_report(struct snd_jack *jack, int status)
|
||||
if (!jack)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(jack_types); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
|
||||
int testbit = SND_JACK_BTN_0 >> i;
|
||||
|
||||
if (jack->type & testbit)
|
||||
input_report_key(jack->input_dev, jack->key[i],
|
||||
status & testbit);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
|
||||
int testbit = 1 << i;
|
||||
if (jack->type & testbit)
|
||||
input_report_switch(jack->input_dev, jack_types[i],
|
||||
input_report_switch(jack->input_dev,
|
||||
jack_switch_types[i],
|
||||
status & testbit);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,10 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
|
||||
struct snd_mixer_oss_file *fmixer;
|
||||
int err;
|
||||
|
||||
err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
card = snd_lookup_oss_minor_data(iminor(inode),
|
||||
SNDRV_OSS_DEVICE_TYPE_MIXER);
|
||||
if (card == NULL)
|
||||
@ -397,6 +401,7 @@ static const struct file_operations snd_mixer_oss_f_ops =
|
||||
.owner = THIS_MODULE,
|
||||
.open = snd_mixer_oss_open,
|
||||
.release = snd_mixer_oss_release,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = snd_mixer_oss_ioctl,
|
||||
.compat_ioctl = snd_mixer_oss_ioctl_compat,
|
||||
};
|
||||
|
@ -2379,6 +2379,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
|
||||
int nonblock;
|
||||
wait_queue_t wait;
|
||||
|
||||
err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pcm = snd_lookup_oss_minor_data(iminor(inode),
|
||||
SNDRV_OSS_DEVICE_TYPE_PCM);
|
||||
if (pcm == NULL) {
|
||||
@ -2977,6 +2981,7 @@ static const struct file_operations snd_pcm_oss_f_reg =
|
||||
.write = snd_pcm_oss_write,
|
||||
.open = snd_pcm_oss_open,
|
||||
.release = snd_pcm_oss_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_pcm_oss_poll,
|
||||
.unlocked_ioctl = snd_pcm_oss_ioctl,
|
||||
.compat_ioctl = snd_pcm_oss_ioctl_compat,
|
||||
|
@ -2112,7 +2112,9 @@ static int snd_pcm_open_file(struct file *file,
|
||||
static int snd_pcm_playback_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
int err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm = snd_lookup_minor_data(iminor(inode),
|
||||
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
|
||||
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
@ -2121,7 +2123,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)
|
||||
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
int err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm = snd_lookup_minor_data(iminor(inode),
|
||||
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
|
||||
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
|
||||
@ -3312,18 +3316,13 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
|
||||
struct snd_pcm_file * pcm_file;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
int err = -ENXIO;
|
||||
|
||||
lock_kernel();
|
||||
pcm_file = file->private_data;
|
||||
substream = pcm_file->substream;
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
goto out;
|
||||
return -ENXIO;
|
||||
runtime = substream->runtime;
|
||||
err = fasync_helper(fd, file, on, &runtime->fasync);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return err;
|
||||
return fasync_helper(fd, file, on, &runtime->fasync);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3443,14 +3442,28 @@ out:
|
||||
#endif /* CONFIG_SND_SUPPORT_OLD_API */
|
||||
|
||||
#ifndef CONFIG_MMU
|
||||
unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr,
|
||||
unsigned long len, unsigned long pgoff,
|
||||
unsigned long flags)
|
||||
static unsigned long snd_pcm_get_unmapped_area(struct file *file,
|
||||
unsigned long addr,
|
||||
unsigned long len,
|
||||
unsigned long pgoff,
|
||||
unsigned long flags)
|
||||
{
|
||||
return 0;
|
||||
struct snd_pcm_file *pcm_file = file->private_data;
|
||||
struct snd_pcm_substream *substream = pcm_file->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned long offset = pgoff << PAGE_SHIFT;
|
||||
|
||||
switch (offset) {
|
||||
case SNDRV_PCM_MMAP_OFFSET_STATUS:
|
||||
return (unsigned long)runtime->status;
|
||||
case SNDRV_PCM_MMAP_OFFSET_CONTROL:
|
||||
return (unsigned long)runtime->control;
|
||||
default:
|
||||
return (unsigned long)runtime->dma_area + offset;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define dummy_get_unmapped_area NULL
|
||||
# define snd_pcm_get_unmapped_area NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -3464,12 +3477,13 @@ const struct file_operations snd_pcm_f_ops[2] = {
|
||||
.aio_write = snd_pcm_aio_write,
|
||||
.open = snd_pcm_playback_open,
|
||||
.release = snd_pcm_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_pcm_playback_poll,
|
||||
.unlocked_ioctl = snd_pcm_playback_ioctl,
|
||||
.compat_ioctl = snd_pcm_ioctl_compat,
|
||||
.mmap = snd_pcm_mmap,
|
||||
.fasync = snd_pcm_fasync,
|
||||
.get_unmapped_area = dummy_get_unmapped_area,
|
||||
.get_unmapped_area = snd_pcm_get_unmapped_area,
|
||||
},
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
@ -3477,11 +3491,12 @@ const struct file_operations snd_pcm_f_ops[2] = {
|
||||
.aio_read = snd_pcm_aio_read,
|
||||
.open = snd_pcm_capture_open,
|
||||
.release = snd_pcm_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_pcm_capture_poll,
|
||||
.unlocked_ioctl = snd_pcm_capture_ioctl,
|
||||
.compat_ioctl = snd_pcm_ioctl_compat,
|
||||
.mmap = snd_pcm_mmap,
|
||||
.fasync = snd_pcm_fasync,
|
||||
.get_unmapped_area = dummy_get_unmapped_area,
|
||||
.get_unmapped_area = snd_pcm_get_unmapped_area,
|
||||
}
|
||||
};
|
||||
|
@ -376,6 +376,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
|
||||
if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
|
||||
return -EINVAL; /* invalid combination */
|
||||
|
||||
err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (maj == snd_major) {
|
||||
rmidi = snd_lookup_minor_data(iminor(inode),
|
||||
SNDRV_DEVICE_TYPE_RAWMIDI);
|
||||
@ -1391,6 +1395,7 @@ static const struct file_operations snd_rawmidi_f_ops =
|
||||
.write = snd_rawmidi_write,
|
||||
.open = snd_rawmidi_open,
|
||||
.release = snd_rawmidi_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_rawmidi_poll,
|
||||
.unlocked_ioctl = snd_rawmidi_ioctl,
|
||||
.compat_ioctl = snd_rawmidi_ioctl_compat,
|
||||
|
@ -318,6 +318,11 @@ static int snd_seq_open(struct inode *inode, struct file *file)
|
||||
int c, mode; /* client id */
|
||||
struct snd_seq_client *client;
|
||||
struct snd_seq_user_client *user;
|
||||
int err;
|
||||
|
||||
err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (mutex_lock_interruptible(®ister_mutex))
|
||||
return -ERESTARTSYS;
|
||||
@ -2550,6 +2555,7 @@ static const struct file_operations snd_seq_f_ops =
|
||||
.write = snd_seq_write,
|
||||
.open = snd_seq_open,
|
||||
.release = snd_seq_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_seq_poll,
|
||||
.unlocked_ioctl = snd_seq_ioctl,
|
||||
.compat_ioctl = snd_seq_ioctl_compat,
|
||||
|
@ -120,7 +120,29 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
|
||||
|
||||
EXPORT_SYMBOL(snd_lookup_minor_data);
|
||||
|
||||
static int __snd_open(struct inode *inode, struct file *file)
|
||||
#ifdef CONFIG_MODULES
|
||||
static struct snd_minor *autoload_device(unsigned int minor)
|
||||
{
|
||||
int dev;
|
||||
mutex_unlock(&sound_mutex); /* release lock temporarily */
|
||||
dev = SNDRV_MINOR_DEVICE(minor);
|
||||
if (dev == SNDRV_MINOR_CONTROL) {
|
||||
/* /dev/aloadC? */
|
||||
int card = SNDRV_MINOR_CARD(minor);
|
||||
if (snd_cards[card] == NULL)
|
||||
snd_request_card(card);
|
||||
} else if (dev == SNDRV_MINOR_GLOBAL) {
|
||||
/* /dev/aloadSEQ */
|
||||
snd_request_other(minor);
|
||||
}
|
||||
mutex_lock(&sound_mutex); /* reacuire lock */
|
||||
return snd_minors[minor];
|
||||
}
|
||||
#else /* !CONFIG_MODULES */
|
||||
#define autoload_device(minor) NULL
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
static int snd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
struct snd_minor *mptr = NULL;
|
||||
@ -129,55 +151,36 @@ static int __snd_open(struct inode *inode, struct file *file)
|
||||
|
||||
if (minor >= ARRAY_SIZE(snd_minors))
|
||||
return -ENODEV;
|
||||
mutex_lock(&sound_mutex);
|
||||
mptr = snd_minors[minor];
|
||||
if (mptr == NULL) {
|
||||
#ifdef CONFIG_MODULES
|
||||
int dev = SNDRV_MINOR_DEVICE(minor);
|
||||
if (dev == SNDRV_MINOR_CONTROL) {
|
||||
/* /dev/aloadC? */
|
||||
int card = SNDRV_MINOR_CARD(minor);
|
||||
if (snd_cards[card] == NULL)
|
||||
snd_request_card(card);
|
||||
} else if (dev == SNDRV_MINOR_GLOBAL) {
|
||||
/* /dev/aloadSEQ */
|
||||
snd_request_other(minor);
|
||||
}
|
||||
#ifndef CONFIG_SND_DYNAMIC_MINORS
|
||||
/* /dev/snd/{controlC?,seq} */
|
||||
mptr = snd_minors[minor];
|
||||
if (mptr == NULL)
|
||||
#endif
|
||||
#endif
|
||||
mptr = autoload_device(minor);
|
||||
if (!mptr) {
|
||||
mutex_unlock(&sound_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
old_fops = file->f_op;
|
||||
file->f_op = fops_get(mptr->f_ops);
|
||||
if (file->f_op == NULL) {
|
||||
file->f_op = old_fops;
|
||||
return -ENODEV;
|
||||
err = -ENODEV;
|
||||
}
|
||||
if (file->f_op->open)
|
||||
mutex_unlock(&sound_mutex);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (file->f_op->open) {
|
||||
err = file->f_op->open(inode, file);
|
||||
if (err) {
|
||||
fops_put(file->f_op);
|
||||
file->f_op = fops_get(old_fops);
|
||||
if (err) {
|
||||
fops_put(file->f_op);
|
||||
file->f_op = fops_get(old_fops);
|
||||
}
|
||||
}
|
||||
fops_put(old_fops);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* BKL pushdown: nasty #ifdef avoidance wrapper */
|
||||
static int snd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lock_kernel();
|
||||
ret = __snd_open(inode, file);
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations snd_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -1238,6 +1238,11 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
|
||||
static int snd_timer_user_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct snd_timer_user *tu;
|
||||
int err;
|
||||
|
||||
err = nonseekable_open(inode, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
tu = kzalloc(sizeof(*tu), GFP_KERNEL);
|
||||
if (tu == NULL)
|
||||
@ -1922,6 +1927,7 @@ static const struct file_operations snd_timer_f_ops =
|
||||
.read = snd_timer_user_read,
|
||||
.open = snd_timer_user_open,
|
||||
.release = snd_timer_user_release,
|
||||
.llseek = no_llseek,
|
||||
.poll = snd_timer_user_poll,
|
||||
.unlocked_ioctl = snd_timer_user_ioctl,
|
||||
.compat_ioctl = snd_timer_user_ioctl_compat,
|
||||
|
@ -49,77 +49,45 @@ static int snd_opl4_mem_proc_release(struct snd_info_entry *entry,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long snd_opl4_mem_proc_read(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, char __user *_buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_opl4_mem_proc_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *_buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
struct snd_opl4 *opl4 = entry->private_data;
|
||||
long size;
|
||||
char* buf;
|
||||
|
||||
size = count;
|
||||
if (pos + size > entry->size)
|
||||
size = entry->size - pos;
|
||||
if (size > 0) {
|
||||
buf = vmalloc(size);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
snd_opl4_read_memory(opl4, buf, pos, size);
|
||||
if (copy_to_user(_buf, buf, size)) {
|
||||
vfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
buf = vmalloc(count);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
snd_opl4_read_memory(opl4, buf, pos, count);
|
||||
if (copy_to_user(_buf, buf, count)) {
|
||||
vfree(buf);
|
||||
return size;
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
vfree(buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static long snd_opl4_mem_proc_write(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, const char __user *_buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_opl4_mem_proc_write(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file,
|
||||
const char __user *_buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
struct snd_opl4 *opl4 = entry->private_data;
|
||||
long size;
|
||||
char *buf;
|
||||
|
||||
size = count;
|
||||
if (pos + size > entry->size)
|
||||
size = entry->size - pos;
|
||||
if (size > 0) {
|
||||
buf = vmalloc(size);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(buf, _buf, size)) {
|
||||
vfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
snd_opl4_write_memory(opl4, buf, pos, size);
|
||||
buf = vmalloc(count);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(buf, _buf, count)) {
|
||||
vfree(buf);
|
||||
return size;
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long long snd_opl4_mem_proc_llseek(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, long long offset, int orig)
|
||||
{
|
||||
switch (orig) {
|
||||
case SEEK_SET:
|
||||
file->f_pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
file->f_pos += offset;
|
||||
break;
|
||||
case SEEK_END: /* offset is negative */
|
||||
file->f_pos = entry->size + offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (file->f_pos > entry->size)
|
||||
file->f_pos = entry->size;
|
||||
return file->f_pos;
|
||||
snd_opl4_write_memory(opl4, buf, pos, count);
|
||||
vfree(buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
|
||||
@ -127,7 +95,6 @@ static struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
|
||||
.release = snd_opl4_mem_proc_release,
|
||||
.read = snd_opl4_mem_proc_read,
|
||||
.write = snd_opl4_mem_proc_write,
|
||||
.llseek = snd_opl4_mem_proc_llseek,
|
||||
};
|
||||
|
||||
int snd_opl4_create_proc(struct snd_opl4 *opl4)
|
||||
|
@ -98,7 +98,8 @@ int snd_i2c_bus_create(struct snd_card *card, const char *name,
|
||||
bus->master = master;
|
||||
}
|
||||
strlcpy(bus->name, name, sizeof(bus->name));
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops)) < 0) {
|
||||
err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops);
|
||||
if (err < 0) {
|
||||
snd_i2c_bus_free(bus);
|
||||
return err;
|
||||
}
|
||||
@ -246,7 +247,8 @@ static int snd_i2c_bit_sendbyte(struct snd_i2c_bus *bus, unsigned char data)
|
||||
|
||||
for (i = 7; i >= 0; i--)
|
||||
snd_i2c_bit_send(bus, !!(data & (1 << i)));
|
||||
if ((err = snd_i2c_bit_ack(bus)) < 0)
|
||||
err = snd_i2c_bit_ack(bus);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
@ -278,12 +280,14 @@ static int snd_i2c_bit_sendbytes(struct snd_i2c_device *device,
|
||||
if (device->flags & SND_I2C_DEVICE_ADDRTEN)
|
||||
return -EIO; /* not yet implemented */
|
||||
snd_i2c_bit_start(bus);
|
||||
if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) {
|
||||
err = snd_i2c_bit_sendbyte(bus, device->addr << 1);
|
||||
if (err < 0) {
|
||||
snd_i2c_bit_hw_stop(bus);
|
||||
return err;
|
||||
}
|
||||
while (count-- > 0) {
|
||||
if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) {
|
||||
err = snd_i2c_bit_sendbyte(bus, *bytes++);
|
||||
if (err < 0) {
|
||||
snd_i2c_bit_hw_stop(bus);
|
||||
return err;
|
||||
}
|
||||
@ -302,12 +306,14 @@ static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
|
||||
if (device->flags & SND_I2C_DEVICE_ADDRTEN)
|
||||
return -EIO; /* not yet implemented */
|
||||
snd_i2c_bit_start(bus);
|
||||
if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) {
|
||||
err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1);
|
||||
if (err < 0) {
|
||||
snd_i2c_bit_hw_stop(bus);
|
||||
return err;
|
||||
}
|
||||
while (count-- > 0) {
|
||||
if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) {
|
||||
err = snd_i2c_bit_readbyte(bus, count == 0);
|
||||
if (err < 0) {
|
||||
snd_i2c_bit_hw_stop(bus);
|
||||
return err;
|
||||
}
|
||||
|
@ -128,26 +128,14 @@ config SND_CS4236
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-cs4236.
|
||||
|
||||
config SND_ES968
|
||||
tristate "Generic ESS ES968 driver"
|
||||
depends on PNP
|
||||
select ISAPNP
|
||||
select SND_MPU401_UART
|
||||
select SND_SB8_DSP
|
||||
help
|
||||
Say Y here to include support for ESS AudioDrive ES968 chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-es968.
|
||||
|
||||
config SND_ES1688
|
||||
tristate "Generic ESS ES688/ES1688 driver"
|
||||
tristate "Generic ESS ES688/ES1688 and ES968 PnP driver"
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y here to include support for ESS AudioDrive ES688 or
|
||||
ES1688 chips.
|
||||
ES1688 chips. Also, this module support cards with ES968 PnP chip.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-es1688.
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/isapnp.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/moduleparam.h>
|
||||
@ -45,8 +46,13 @@ MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100},"
|
||||
"{ESS,ES688 AudioDrive,pnp:ESS6881},"
|
||||
"{ESS,ES1688 AudioDrive,pnp:ESS1681}}");
|
||||
|
||||
MODULE_ALIAS("snd_es968");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
||||
#ifdef CONFIG_PNP
|
||||
static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
|
||||
#endif
|
||||
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
|
||||
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
|
||||
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */
|
||||
@ -60,6 +66,10 @@ MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
#ifdef CONFIG_PNP
|
||||
module_param_array(isapnp, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
|
||||
#endif
|
||||
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
|
||||
module_param_array(port, long, NULL, 0444);
|
||||
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
|
||||
@ -74,14 +84,21 @@ MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
|
||||
module_param_array(dma8, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
#define is_isapnp_selected(dev) isapnp[dev]
|
||||
#else
|
||||
#define is_isapnp_selected(dev) 0
|
||||
#endif
|
||||
|
||||
static int __devinit snd_es1688_match(struct device *dev, unsigned int n)
|
||||
{
|
||||
return enable[n];
|
||||
return enable[n] && !is_isapnp_selected(n);
|
||||
}
|
||||
|
||||
static int __devinit snd_es1688_legacy_create(struct snd_card *card,
|
||||
struct device *dev, unsigned int n, struct snd_es1688 **rchip)
|
||||
static int __devinit snd_es1688_legacy_create(struct snd_card *card,
|
||||
struct device *dev, unsigned int n)
|
||||
{
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
static long possible_ports[] = {0x220, 0x240, 0x260};
|
||||
static int possible_irqs[] = {5, 9, 10, 7, -1};
|
||||
static int possible_dmas[] = {1, 3, 0, -1};
|
||||
@ -104,47 +121,39 @@ static int __devinit snd_es1688_legacy_create(struct snd_card *card,
|
||||
}
|
||||
|
||||
if (port[n] != SNDRV_AUTO_PORT)
|
||||
return snd_es1688_create(card, port[n], mpu_port[n], irq[n],
|
||||
mpu_irq[n], dma8[n], ES1688_HW_AUTO, rchip);
|
||||
return snd_es1688_create(card, chip, port[n], mpu_port[n],
|
||||
irq[n], mpu_irq[n], dma8[n], ES1688_HW_AUTO);
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
port[n] = possible_ports[i];
|
||||
error = snd_es1688_create(card, port[n], mpu_port[n], irq[n],
|
||||
mpu_irq[n], dma8[n], ES1688_HW_AUTO, rchip);
|
||||
error = snd_es1688_create(card, chip, port[n], mpu_port[n],
|
||||
irq[n], mpu_irq[n], dma8[n], ES1688_HW_AUTO);
|
||||
} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devinit snd_es1688_probe(struct device *dev, unsigned int n)
|
||||
static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct snd_es1688 *chip;
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
struct snd_opl3 *opl3;
|
||||
struct snd_pcm *pcm;
|
||||
int error;
|
||||
|
||||
error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
|
||||
error = snd_es1688_pcm(card, chip, 0, &pcm);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = snd_es1688_legacy_create(card, dev, n, &chip);
|
||||
error = snd_es1688_mixer(card, chip);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
return error;
|
||||
|
||||
error = snd_es1688_pcm(chip, 0, &pcm);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
error = snd_es1688_mixer(chip);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
strcpy(card->driver, "ES1688");
|
||||
strcpy(card->shortname, pcm->name);
|
||||
sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i", pcm->name,
|
||||
chip->port, chip->irq, chip->dma8);
|
||||
strlcpy(card->driver, "ES1688", sizeof(card->driver));
|
||||
strlcpy(card->shortname, pcm->name, sizeof(card->shortname));
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port,
|
||||
chip->irq, chip->dma8);
|
||||
|
||||
if (fm_port[n] == SNDRV_AUTO_PORT)
|
||||
fm_port[n] = port[n]; /* share the same port */
|
||||
@ -152,12 +161,12 @@ static int __devinit snd_es1688_probe(struct device *dev, unsigned int n)
|
||||
if (fm_port[n] > 0) {
|
||||
if (snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
|
||||
OPL3_HW_OPL3, 0, &opl3) < 0)
|
||||
dev_warn(dev,
|
||||
dev_warn(card->dev,
|
||||
"opl3 not detected at 0x%lx\n", fm_port[n]);
|
||||
else {
|
||||
error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,23 +176,41 @@ static int __devinit snd_es1688_probe(struct device *dev, unsigned int n)
|
||||
chip->mpu_port, 0,
|
||||
mpu_irq[n], IRQF_DISABLED, NULL);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
return error;
|
||||
}
|
||||
|
||||
return snd_card_register(card);
|
||||
}
|
||||
|
||||
static int __devinit snd_es1688_isa_probe(struct device *dev, unsigned int n)
|
||||
{
|
||||
struct snd_card *card;
|
||||
int error;
|
||||
|
||||
error = snd_card_create(index[n], id[n], THIS_MODULE,
|
||||
sizeof(struct snd_es1688), &card);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = snd_es1688_legacy_create(card, dev, n);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
snd_card_set_dev(card, dev);
|
||||
|
||||
error = snd_card_register(card);
|
||||
error = snd_es1688_probe(card, n);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
dev_set_drvdata(dev, card);
|
||||
return 0;
|
||||
|
||||
out: snd_card_free(card);
|
||||
return 0;
|
||||
out:
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit snd_es1688_remove(struct device *dev, unsigned int n)
|
||||
static int __devexit snd_es1688_isa_remove(struct device *dev, unsigned int n)
|
||||
{
|
||||
snd_card_free(dev_get_drvdata(dev));
|
||||
dev_set_drvdata(dev, NULL);
|
||||
@ -192,8 +219,8 @@ static int __devexit snd_es1688_remove(struct device *dev, unsigned int n)
|
||||
|
||||
static struct isa_driver snd_es1688_driver = {
|
||||
.match = snd_es1688_match,
|
||||
.probe = snd_es1688_probe,
|
||||
.remove = __devexit_p(snd_es1688_remove),
|
||||
.probe = snd_es1688_isa_probe,
|
||||
.remove = __devexit_p(snd_es1688_isa_remove),
|
||||
#if 0 /* FIXME */
|
||||
.suspend = snd_es1688_suspend,
|
||||
.resume = snd_es1688_resume,
|
||||
@ -203,14 +230,142 @@ static struct isa_driver snd_es1688_driver = {
|
||||
}
|
||||
};
|
||||
|
||||
static int snd_es968_pnp_is_probed;
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
|
||||
struct pnp_card_link *pcard,
|
||||
const struct pnp_card_device_id *pid)
|
||||
{
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
struct pnp_dev *pdev;
|
||||
int error;
|
||||
|
||||
pdev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
|
||||
if (pdev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
error = pnp_activate_dev(pdev);
|
||||
if (error < 0) {
|
||||
snd_printk(KERN_ERR "ES968 pnp configure failure\n");
|
||||
return error;
|
||||
}
|
||||
port[n] = pnp_port_start(pdev, 0);
|
||||
dma8[n] = pnp_dma(pdev, 0);
|
||||
irq[n] = pnp_irq(pdev, 0);
|
||||
|
||||
return snd_es1688_create(card, chip, port[n], mpu_port[n], irq[n],
|
||||
mpu_irq[n], dma8[n], ES1688_HW_AUTO);
|
||||
}
|
||||
|
||||
static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
|
||||
const struct pnp_card_device_id *pid)
|
||||
{
|
||||
struct snd_card *card;
|
||||
static unsigned int dev;
|
||||
int error;
|
||||
struct snd_es1688 *chip;
|
||||
|
||||
if (snd_es968_pnp_is_probed)
|
||||
return -EBUSY;
|
||||
for ( ; dev < SNDRV_CARDS; dev++) {
|
||||
if (enable[dev] && isapnp[dev])
|
||||
break;
|
||||
}
|
||||
if (dev == SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
|
||||
error = snd_card_create(index[dev], id[dev], THIS_MODULE,
|
||||
sizeof(struct snd_es1688), &card);
|
||||
if (error < 0)
|
||||
return error;
|
||||
chip = card->private_data;
|
||||
|
||||
error = snd_card_es968_pnp(card, dev, pcard, pid);
|
||||
if (error < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
snd_card_set_dev(card, &pcard->card->dev);
|
||||
error = snd_es1688_probe(card, dev);
|
||||
if (error < 0)
|
||||
return error;
|
||||
pnp_set_card_drvdata(pcard, card);
|
||||
snd_es968_pnp_is_probed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
|
||||
{
|
||||
snd_card_free(pnp_get_card_drvdata(pcard));
|
||||
pnp_set_card_drvdata(pcard, NULL);
|
||||
snd_es968_pnp_is_probed = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_es968_pnp_suspend(struct pnp_card_link *pcard,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = pnp_get_card_drvdata(pcard);
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
|
||||
{
|
||||
struct snd_card *card = pnp_get_card_drvdata(pcard);
|
||||
struct snd_es1688 *chip = card->private_data;
|
||||
|
||||
snd_es1688_reset(chip);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pnp_card_device_id snd_es968_pnpids[] = {
|
||||
{ .id = "ESS0968", .devs = { { "@@@0968" }, } },
|
||||
{ .id = "ESS0968", .devs = { { "ESS0968" }, } },
|
||||
{ .id = "", } /* end */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids);
|
||||
|
||||
static struct pnp_card_driver es968_pnpc_driver = {
|
||||
.flags = PNP_DRIVER_RES_DISABLE,
|
||||
.name = DEV_NAME " PnP",
|
||||
.id_table = snd_es968_pnpids,
|
||||
.probe = snd_es968_pnp_detect,
|
||||
.remove = __devexit_p(snd_es968_pnp_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_es968_pnp_suspend,
|
||||
.resume = snd_es968_pnp_resume,
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init alsa_card_es1688_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PNP
|
||||
pnp_register_card_driver(&es968_pnpc_driver);
|
||||
if (snd_es968_pnp_is_probed)
|
||||
return 0;
|
||||
pnp_unregister_card_driver(&es968_pnpc_driver);
|
||||
#endif
|
||||
return isa_register_driver(&snd_es1688_driver, SNDRV_CARDS);
|
||||
}
|
||||
|
||||
static void __exit alsa_card_es1688_exit(void)
|
||||
{
|
||||
isa_unregister_driver(&snd_es1688_driver);
|
||||
if (!snd_es968_pnp_is_probed) {
|
||||
isa_unregister_driver(&snd_es1688_driver);
|
||||
return;
|
||||
}
|
||||
#ifdef CONFIG_PNP
|
||||
pnp_unregister_card_driver(&es968_pnpc_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(alsa_card_es1688_init);
|
||||
|
@ -99,7 +99,7 @@ static unsigned char snd_es1688_mixer_read(struct snd_es1688 *chip, unsigned cha
|
||||
return result;
|
||||
}
|
||||
|
||||
static int snd_es1688_reset(struct snd_es1688 *chip)
|
||||
int snd_es1688_reset(struct snd_es1688 *chip)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -115,6 +115,7 @@ static int snd_es1688_reset(struct snd_es1688 *chip)
|
||||
snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_es1688_reset);
|
||||
|
||||
static int snd_es1688_probe(struct snd_es1688 *chip)
|
||||
{
|
||||
@ -620,7 +621,6 @@ static int snd_es1688_free(struct snd_es1688 *chip)
|
||||
disable_dma(chip->dma8);
|
||||
free_dma(chip->dma8);
|
||||
}
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -638,23 +638,20 @@ static const char *snd_es1688_chip_id(struct snd_es1688 *chip)
|
||||
}
|
||||
|
||||
int snd_es1688_create(struct snd_card *card,
|
||||
struct snd_es1688 *chip,
|
||||
unsigned long port,
|
||||
unsigned long mpu_port,
|
||||
int irq,
|
||||
int mpu_irq,
|
||||
int dma8,
|
||||
unsigned short hardware,
|
||||
struct snd_es1688 **rchip)
|
||||
unsigned short hardware)
|
||||
{
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_es1688_dev_free,
|
||||
};
|
||||
|
||||
struct snd_es1688 *chip;
|
||||
int err;
|
||||
|
||||
*rchip = NULL;
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL)
|
||||
return -ENOMEM;
|
||||
chip->irq = -1;
|
||||
@ -662,25 +659,21 @@ int snd_es1688_create(struct snd_card *card,
|
||||
|
||||
if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
|
||||
snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
|
||||
snd_es1688_free(chip);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) {
|
||||
snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
|
||||
snd_es1688_free(chip);
|
||||
return -EBUSY;
|
||||
}
|
||||
chip->irq = irq;
|
||||
if (request_dma(dma8, "ES1688")) {
|
||||
snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
|
||||
snd_es1688_free(chip);
|
||||
return -EBUSY;
|
||||
}
|
||||
chip->dma8 = dma8;
|
||||
|
||||
spin_lock_init(&chip->reg_lock);
|
||||
spin_lock_init(&chip->mixer_lock);
|
||||
chip->card = card;
|
||||
chip->port = port;
|
||||
mpu_port &= ~0x000f;
|
||||
if (mpu_port < 0x300 || mpu_port > 0x330)
|
||||
@ -689,23 +682,16 @@ int snd_es1688_create(struct snd_card *card,
|
||||
chip->mpu_irq = mpu_irq;
|
||||
chip->hardware = hardware;
|
||||
|
||||
if ((err = snd_es1688_probe(chip)) < 0) {
|
||||
snd_es1688_free(chip);
|
||||
err = snd_es1688_probe(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if ((err = snd_es1688_init(chip, 1)) < 0) {
|
||||
snd_es1688_free(chip);
|
||||
|
||||
err = snd_es1688_init(chip, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Register device */
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
|
||||
snd_es1688_free(chip);
|
||||
return err;
|
||||
}
|
||||
|
||||
*rchip = chip;
|
||||
return 0;
|
||||
return snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops snd_es1688_playback_ops = {
|
||||
@ -730,12 +716,14 @@ static struct snd_pcm_ops snd_es1688_capture_ops = {
|
||||
.pointer = snd_es1688_capture_pointer,
|
||||
};
|
||||
|
||||
int snd_es1688_pcm(struct snd_es1688 * chip, int device, struct snd_pcm ** rpcm)
|
||||
int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
|
||||
int device, struct snd_pcm **rpcm)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_new(chip->card, "ESx688", device, 1, 1, &pcm)) < 0)
|
||||
err = snd_pcm_new(card, "ESx688", device, 1, 1, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops);
|
||||
@ -1009,18 +997,15 @@ static unsigned char snd_es1688_init_table[][2] = {
|
||||
{ ES1688_REC_DEV, 0x17 }
|
||||
};
|
||||
|
||||
int snd_es1688_mixer(struct snd_es1688 *chip)
|
||||
int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip)
|
||||
{
|
||||
struct snd_card *card;
|
||||
unsigned int idx;
|
||||
int err;
|
||||
unsigned char reg, val;
|
||||
|
||||
if (snd_BUG_ON(!chip || !chip->card))
|
||||
if (snd_BUG_ON(!chip || !card))
|
||||
return -EINVAL;
|
||||
|
||||
card = chip->card;
|
||||
|
||||
strcpy(card->mixername, snd_es1688_chip_id(chip));
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) {
|
||||
|
@ -31,52 +31,21 @@ struct gus_proc_private {
|
||||
struct snd_gus_card * gus;
|
||||
};
|
||||
|
||||
static long snd_gf1_mem_proc_dump(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_gf1_mem_proc_dump(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
long size;
|
||||
struct gus_proc_private *priv = entry->private_data;
|
||||
struct snd_gus_card *gus = priv->gus;
|
||||
int err;
|
||||
|
||||
size = count;
|
||||
if (pos + size > priv->size)
|
||||
size = (long)priv->size - pos;
|
||||
if (size > 0) {
|
||||
if ((err = snd_gus_dram_read(gus, buf, pos, size, priv->rom)) < 0)
|
||||
return err;
|
||||
return size;
|
||||
}
|
||||
return 0;
|
||||
err = snd_gus_dram_read(gus, buf, pos, count, priv->rom);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return count;
|
||||
}
|
||||
|
||||
static long long snd_gf1_mem_proc_llseek(struct snd_info_entry *entry,
|
||||
void *private_file_data,
|
||||
struct file *file,
|
||||
long long offset,
|
||||
int orig)
|
||||
{
|
||||
struct gus_proc_private *priv = entry->private_data;
|
||||
|
||||
switch (orig) {
|
||||
case SEEK_SET:
|
||||
file->f_pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
file->f_pos += offset;
|
||||
break;
|
||||
case SEEK_END: /* offset is negative */
|
||||
file->f_pos = priv->size + offset;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (file->f_pos > priv->size)
|
||||
file->f_pos = priv->size;
|
||||
return file->f_pos;
|
||||
}
|
||||
|
||||
static void snd_gf1_mem_proc_free(struct snd_info_entry *entry)
|
||||
{
|
||||
struct gus_proc_private *priv = entry->private_data;
|
||||
@ -85,7 +54,6 @@ static void snd_gf1_mem_proc_free(struct snd_info_entry *entry)
|
||||
|
||||
static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
|
||||
.read = snd_gf1_mem_proc_dump,
|
||||
.llseek = snd_gf1_mem_proc_llseek,
|
||||
};
|
||||
|
||||
int snd_gf1_mem_proc_init(struct snd_gus_card * gus)
|
||||
|
@ -95,7 +95,7 @@ static int __devinit snd_gusextreme_match(struct device *dev, unsigned int n)
|
||||
}
|
||||
|
||||
static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
|
||||
struct device *dev, unsigned int n, struct snd_es1688 **rchip)
|
||||
struct snd_es1688 *chip, struct device *dev, unsigned int n)
|
||||
{
|
||||
static long possible_ports[] = {0x220, 0x240, 0x260};
|
||||
static int possible_irqs[] = {5, 9, 10, 7, -1};
|
||||
@ -119,14 +119,14 @@ static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
|
||||
}
|
||||
|
||||
if (port[n] != SNDRV_AUTO_PORT)
|
||||
return snd_es1688_create(card, port[n], mpu_port[n], irq[n],
|
||||
mpu_irq[n], dma8[n], ES1688_HW_1688, rchip);
|
||||
return snd_es1688_create(card, chip, port[n], mpu_port[n],
|
||||
irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688);
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
port[n] = possible_ports[i];
|
||||
error = snd_es1688_create(card, port[n], mpu_port[n], irq[n],
|
||||
mpu_irq[n], dma8[n], ES1688_HW_1688, rchip);
|
||||
error = snd_es1688_create(card, chip, port[n], mpu_port[n],
|
||||
irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688);
|
||||
} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
|
||||
|
||||
return error;
|
||||
@ -206,9 +206,8 @@ static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit snd_gusextreme_mixer(struct snd_es1688 *chip)
|
||||
static int __devinit snd_gusextreme_mixer(struct snd_card *card)
|
||||
{
|
||||
struct snd_card *card = chip->card;
|
||||
struct snd_ctl_elem_id id1, id2;
|
||||
int error;
|
||||
|
||||
@ -241,17 +240,20 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
|
||||
struct snd_opl3 *opl3;
|
||||
int error;
|
||||
|
||||
error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
|
||||
error = snd_card_create(index[n], id[n], THIS_MODULE,
|
||||
sizeof(struct snd_es1688), &card);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
es1688 = card->private_data;
|
||||
|
||||
if (mpu_port[n] == SNDRV_AUTO_PORT)
|
||||
mpu_port[n] = 0;
|
||||
|
||||
if (mpu_irq[n] > 15)
|
||||
mpu_irq[n] = -1;
|
||||
|
||||
error = snd_gusextreme_es1688_create(card, dev, n, &es1688);
|
||||
error = snd_gusextreme_es1688_create(card, es1688, dev, n);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
@ -280,11 +282,11 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
|
||||
}
|
||||
gus->codec_flag = 1;
|
||||
|
||||
error = snd_es1688_pcm(es1688, 0, NULL);
|
||||
error = snd_es1688_pcm(card, es1688, 0, NULL);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
error = snd_es1688_mixer(es1688);
|
||||
error = snd_es1688_mixer(card, es1688);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
@ -300,7 +302,7 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
error = snd_gusextreme_mixer(es1688);
|
||||
error = snd_gusextreme_mixer(card);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -11,7 +11,6 @@ snd-sb8-objs := sb8.o
|
||||
snd-sb16-objs := sb16.o
|
||||
snd-sbawe-objs := sbawe.o emu8000.o
|
||||
snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
|
||||
snd-es968-objs := es968.o
|
||||
snd-jazz16-objs := jazz16.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
@ -21,7 +20,6 @@ obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
|
||||
obj-$(CONFIG_SND_SB8) += snd-sb8.o
|
||||
obj-$(CONFIG_SND_SB16) += snd-sb16.o
|
||||
obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
|
||||
obj-$(CONFIG_SND_ES968) += snd-es968.o
|
||||
obj-$(CONFIG_SND_JAZZ16) += snd-jazz16.o
|
||||
ifeq ($(CONFIG_SND_SB16_CSP),y)
|
||||
obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
|
||||
|
@ -1,248 +0,0 @@
|
||||
|
||||
/*
|
||||
card-es968.c - driver for ESS AudioDrive ES968 based soundcards.
|
||||
Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
|
||||
|
||||
Thanks to Pierfrancesco 'qM2' Passerini.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/pnp.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/sb.h>
|
||||
|
||||
#define PFX "es968: "
|
||||
|
||||
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
|
||||
MODULE_DESCRIPTION("ESS AudioDrive ES968");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("{{ESS,AudioDrive ES968}}");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
||||
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
|
||||
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
|
||||
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
|
||||
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for es968 based soundcard.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for es968 based soundcard.");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable es968 based soundcard.");
|
||||
|
||||
struct snd_card_es968 {
|
||||
struct pnp_dev *dev;
|
||||
struct snd_sb *chip;
|
||||
};
|
||||
|
||||
static struct pnp_card_device_id snd_es968_pnpids[] = {
|
||||
{ .id = "ESS0968", .devs = { { "@@@0968" }, } },
|
||||
{ .id = "", } /* end */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids);
|
||||
|
||||
#define DRIVER_NAME "snd-card-es968"
|
||||
|
||||
static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct snd_sb *chip = dev_id;
|
||||
|
||||
if (chip->open & SB_OPEN_PCM) {
|
||||
return snd_sb8dsp_interrupt(chip);
|
||||
} else {
|
||||
return snd_sb8dsp_midi_interrupt(chip);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard,
|
||||
struct pnp_card_link *card,
|
||||
const struct pnp_card_device_id *id)
|
||||
{
|
||||
struct pnp_dev *pdev;
|
||||
int err;
|
||||
|
||||
acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
|
||||
if (acard->dev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
pdev = acard->dev;
|
||||
|
||||
err = pnp_activate_dev(pdev);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
|
||||
return err;
|
||||
}
|
||||
port[dev] = pnp_port_start(pdev, 0);
|
||||
dma8[dev] = pnp_dma(pdev, 0);
|
||||
irq[dev] = pnp_irq(pdev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit snd_card_es968_probe(int dev,
|
||||
struct pnp_card_link *pcard,
|
||||
const struct pnp_card_device_id *pid)
|
||||
{
|
||||
int error;
|
||||
struct snd_sb *chip;
|
||||
struct snd_card *card;
|
||||
struct snd_card_es968 *acard;
|
||||
|
||||
error = snd_card_create(index[dev], id[dev], THIS_MODULE,
|
||||
sizeof(struct snd_card_es968), &card);
|
||||
if (error < 0)
|
||||
return error;
|
||||
acard = card->private_data;
|
||||
if ((error = snd_card_es968_pnp(dev, acard, pcard, pid))) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
snd_card_set_dev(card, &pcard->card->dev);
|
||||
|
||||
if ((error = snd_sbdsp_create(card, port[dev],
|
||||
irq[dev],
|
||||
snd_card_es968_interrupt,
|
||||
dma8[dev],
|
||||
-1,
|
||||
SB_HW_AUTO, &chip)) < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
acard->chip = chip;
|
||||
|
||||
if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = snd_sbmixer_new(chip)) < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
|
||||
strcpy(card->driver, "ES968");
|
||||
strcpy(card->shortname, "ESS ES968");
|
||||
sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
|
||||
card->shortname, chip->name, chip->port, irq[dev], dma8[dev]);
|
||||
|
||||
if ((error = snd_card_register(card)) < 0) {
|
||||
snd_card_free(card);
|
||||
return error;
|
||||
}
|
||||
pnp_set_card_drvdata(pcard, card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int __devinitdata es968_devices;
|
||||
|
||||
static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card,
|
||||
const struct pnp_card_device_id *id)
|
||||
{
|
||||
static int dev;
|
||||
int res;
|
||||
|
||||
for ( ; dev < SNDRV_CARDS; dev++) {
|
||||
if (!enable[dev])
|
||||
continue;
|
||||
res = snd_card_es968_probe(dev, card, id);
|
||||
if (res < 0)
|
||||
return res;
|
||||
dev++;
|
||||
es968_devices++;
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
|
||||
{
|
||||
snd_card_free(pnp_get_card_drvdata(pcard));
|
||||
pnp_set_card_drvdata(pcard, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
|
||||
{
|
||||
struct snd_card *card = pnp_get_card_drvdata(pcard);
|
||||
struct snd_card_es968 *acard = card->private_data;
|
||||
struct snd_sb *chip = acard->chip;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
snd_sbmixer_suspend(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
|
||||
{
|
||||
struct snd_card *card = pnp_get_card_drvdata(pcard);
|
||||
struct snd_card_es968 *acard = card->private_data;
|
||||
struct snd_sb *chip = acard->chip;
|
||||
|
||||
snd_sbdsp_reset(chip);
|
||||
snd_sbmixer_resume(chip);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pnp_card_driver es968_pnpc_driver = {
|
||||
.flags = PNP_DRIVER_RES_DISABLE,
|
||||
.name = "es968",
|
||||
.id_table = snd_es968_pnpids,
|
||||
.probe = snd_es968_pnp_detect,
|
||||
.remove = __devexit_p(snd_es968_pnp_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_es968_pnp_suspend,
|
||||
.resume = snd_es968_pnp_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init alsa_card_es968_init(void)
|
||||
{
|
||||
int err = pnp_register_card_driver(&es968_pnpc_driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!es968_devices) {
|
||||
pnp_unregister_card_driver(&es968_pnpc_driver);
|
||||
#ifdef MODULE
|
||||
snd_printk(KERN_ERR "no ES968 based soundcards found\n");
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_card_es968_exit(void)
|
||||
{
|
||||
pnp_unregister_card_driver(&es968_pnpc_driver);
|
||||
}
|
||||
|
||||
module_init(alsa_card_es968_init)
|
||||
module_exit(alsa_card_es968_exit)
|
@ -58,6 +58,18 @@ config SND_ALI5451
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-ali5451.
|
||||
|
||||
config SND_ASIHPI
|
||||
tristate "AudioScience ASIxxxx"
|
||||
depends on X86
|
||||
select FW_LOADER
|
||||
select SND_PCM
|
||||
select SND_HWDEP
|
||||
help
|
||||
Say Y here to include support for AudioScience ASI sound cards.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-asihpi.
|
||||
|
||||
config SND_ATIIXP
|
||||
tristate "ATI IXP AC97 Controller"
|
||||
select SND_AC97_CODEC
|
||||
@ -501,6 +513,16 @@ config SND_ES1968
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-es1968.
|
||||
|
||||
config SND_ES1968_INPUT
|
||||
bool "Enable input device for es1968 volume buttons"
|
||||
depends on SND_ES1968
|
||||
depends on INPUT=y || INPUT=SND_ES1968
|
||||
help
|
||||
If you say Y here, you will get an input device which reports
|
||||
keypresses for the volume buttons connected to the es1968 chip.
|
||||
If you say N the buttons will directly control the master volume.
|
||||
It is recommended to say Y.
|
||||
|
||||
config SND_FM801
|
||||
tristate "ForteMedia FM801"
|
||||
select SND_OPL3_LIB
|
||||
@ -655,6 +677,16 @@ config SND_MAESTRO3
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-maestro3.
|
||||
|
||||
config SND_MAESTRO3_INPUT
|
||||
bool "Enable input device for maestro3 volume buttons"
|
||||
depends on SND_MAESTRO3
|
||||
depends on INPUT=y || INPUT=SND_MAESTRO3
|
||||
help
|
||||
If you say Y here, you will get an input device which reports
|
||||
keypresses for the volume buttons connected to the maestro3 chip.
|
||||
If you say N the buttons will directly control the master volume.
|
||||
It is recommended to say Y.
|
||||
|
||||
config SND_MIXART
|
||||
tristate "Digigram miXart"
|
||||
select SND_HWDEP
|
||||
|
@ -57,6 +57,7 @@ obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o
|
||||
obj-$(CONFIG_SND) += \
|
||||
ac97/ \
|
||||
ali5451/ \
|
||||
asihpi/ \
|
||||
au88x0/ \
|
||||
aw2/ \
|
||||
ctxfi/ \
|
||||
|
5
sound/pci/asihpi/Makefile
Normal file
5
sound/pci/asihpi/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
|
||||
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
|
||||
hpios.o hpi6000.o hpi6205.o hpimsgx.o
|
||||
|
||||
obj-$(CONFIG_SND_ASIHPI) += snd-asihpi.o
|
3002
sound/pci/asihpi/asihpi.c
Normal file
3002
sound/pci/asihpi/asihpi.c
Normal file
File diff suppressed because it is too large
Load Diff
2001
sound/pci/asihpi/hpi.h
Normal file
2001
sound/pci/asihpi/hpi.h
Normal file
File diff suppressed because it is too large
Load Diff
1840
sound/pci/asihpi/hpi6000.c
Normal file
1840
sound/pci/asihpi/hpi6000.c
Normal file
File diff suppressed because it is too large
Load Diff
70
sound/pci/asihpi/hpi6000.h
Normal file
70
sound/pci/asihpi/hpi6000.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*****************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Public declarations for DSP Proramming Interface to TI C6701
|
||||
|
||||
Shared between hpi6000.c and DSP code
|
||||
|
||||
(C) Copyright AudioScience Inc. 1998-2003
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HPI6000_H_
|
||||
#define _HPI6000_H_
|
||||
|
||||
#define HPI_NMIXER_CONTROLS 200
|
||||
|
||||
/*
|
||||
* Control caching is always supported in the HPI code.
|
||||
* The DSP should make sure that dwControlCacheSizeInBytes is initialized to 0
|
||||
* during boot to make it in-active.
|
||||
*/
|
||||
struct hpi_hif_6000 {
|
||||
u32 host_cmd;
|
||||
u32 dsp_ack;
|
||||
u32 address;
|
||||
u32 length;
|
||||
u32 message_buffer_address;
|
||||
u32 response_buffer_address;
|
||||
u32 dsp_number;
|
||||
u32 adapter_info;
|
||||
u32 control_cache_is_dirty;
|
||||
u32 control_cache_address;
|
||||
u32 control_cache_size_in_bytes;
|
||||
u32 control_cache_count;
|
||||
};
|
||||
|
||||
#define HPI_HIF_PACK_ADAPTER_INFO(adapter, version_major, version_minor) \
|
||||
((adapter << 16) | (version_major << 8) | version_minor)
|
||||
#define HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER(adapterinfo) \
|
||||
((adapterinfo >> 16) & 0xffff)
|
||||
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MAJOR(adapterinfo) \
|
||||
((adapterinfo >> 8) & 0xff)
|
||||
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MINOR(adapterinfo) \
|
||||
(adapterinfo & 0xff)
|
||||
|
||||
/* Command/status exchanged between host and DSP */
|
||||
#define HPI_HIF_IDLE 0
|
||||
#define HPI_HIF_SEND_MSG 1
|
||||
#define HPI_HIF_GET_RESP 2
|
||||
#define HPI_HIF_DATA_MASK 0x10
|
||||
#define HPI_HIF_SEND_DATA 0x13
|
||||
#define HPI_HIF_GET_DATA 0x14
|
||||
#define HPI_HIF_SEND_DONE 5
|
||||
#define HPI_HIF_RESET 9
|
||||
|
||||
#endif /* _HPI6000_H_ */
|
2331
sound/pci/asihpi/hpi6205.c
Normal file
2331
sound/pci/asihpi/hpi6205.c
Normal file
File diff suppressed because it is too large
Load Diff
93
sound/pci/asihpi/hpi6205.h
Normal file
93
sound/pci/asihpi/hpi6205.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*****************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Host Interface module for an ASI6205 based
|
||||
bus mastering PCI adapter.
|
||||
|
||||
Copyright AudioScience, Inc., 2003
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HPI6205_H_
|
||||
#define _HPI6205_H_
|
||||
|
||||
/* transitional conditional compile shared between host and DSP */
|
||||
/* #define HPI6205_NO_HSR_POLL */
|
||||
|
||||
#include "hpi_internal.h"
|
||||
|
||||
/***********************************************************
|
||||
Defines used for basic messaging
|
||||
************************************************************/
|
||||
#define H620_HIF_RESET 0
|
||||
#define H620_HIF_IDLE 1
|
||||
#define H620_HIF_GET_RESP 2
|
||||
#define H620_HIF_DATA_DONE 3
|
||||
#define H620_HIF_DATA_MASK 0x10
|
||||
#define H620_HIF_SEND_DATA 0x14
|
||||
#define H620_HIF_GET_DATA 0x15
|
||||
#define H620_HIF_UNKNOWN 0x0000ffff
|
||||
|
||||
/***********************************************************
|
||||
Types used for mixer control caching
|
||||
************************************************************/
|
||||
|
||||
#define H620_MAX_ISTREAMS 32
|
||||
#define H620_MAX_OSTREAMS 32
|
||||
#define HPI_NMIXER_CONTROLS 2048
|
||||
|
||||
/*********************************************************************
|
||||
This is used for dynamic control cache allocation
|
||||
**********************************************************************/
|
||||
struct controlcache_6205 {
|
||||
u32 number_of_controls;
|
||||
u32 physical_address32;
|
||||
u32 size_in_bytes;
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
This is used for dynamic allocation of async event array
|
||||
**********************************************************************/
|
||||
struct async_event_buffer_6205 {
|
||||
u32 physical_address32;
|
||||
u32 spare;
|
||||
struct hpi_fifo_buffer b;
|
||||
};
|
||||
|
||||
/***********************************************************
|
||||
The Host located memory buffer that the 6205 will bus master
|
||||
in and out of.
|
||||
************************************************************/
|
||||
#define HPI6205_SIZEOF_DATA (16*1024)
|
||||
struct bus_master_interface {
|
||||
u32 host_cmd;
|
||||
u32 dsp_ack;
|
||||
u32 transfer_size_in_bytes;
|
||||
union {
|
||||
struct hpi_message message_buffer;
|
||||
struct hpi_response response_buffer;
|
||||
u8 b_data[HPI6205_SIZEOF_DATA];
|
||||
} u;
|
||||
struct controlcache_6205 control_cache;
|
||||
struct async_event_buffer_6205 async_buffer;
|
||||
struct hpi_hostbuffer_status
|
||||
instream_host_buffer_status[H620_MAX_ISTREAMS];
|
||||
struct hpi_hostbuffer_status
|
||||
outstream_host_buffer_status[H620_MAX_OSTREAMS];
|
||||
};
|
||||
|
||||
#endif
|
1641
sound/pci/asihpi/hpi_internal.h
Normal file
1641
sound/pci/asihpi/hpi_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
643
sound/pci/asihpi/hpicmn.c
Normal file
643
sound/pci/asihpi/hpicmn.c
Normal file
@ -0,0 +1,643 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
\file hpicmn.c
|
||||
|
||||
Common functions used by hpixxxx.c modules
|
||||
|
||||
(C) Copyright AudioScience Inc. 1998-2003
|
||||
*******************************************************************************/
|
||||
#define SOURCEFILE_NAME "hpicmn.c"
|
||||
|
||||
#include "hpi_internal.h"
|
||||
#include "hpidebug.h"
|
||||
#include "hpicmn.h"
|
||||
|
||||
struct hpi_adapters_list {
|
||||
struct hpios_spinlock list_lock;
|
||||
struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS];
|
||||
u16 gw_num_adapters;
|
||||
};
|
||||
|
||||
static struct hpi_adapters_list adapters;
|
||||
|
||||
/**
|
||||
* Given an HPI Message that was sent out and a response that was received,
|
||||
* validate that the response has the correct fields filled in,
|
||||
* i.e ObjectType, Function etc
|
||||
**/
|
||||
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
u16 error = 0;
|
||||
|
||||
if ((phr->type != HPI_TYPE_RESPONSE)
|
||||
|| (phr->object != phm->object)
|
||||
|| (phr->function != phm->function))
|
||||
error = HPI_ERROR_INVALID_RESPONSE;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
|
||||
{
|
||||
u16 retval = 0;
|
||||
/*HPI_ASSERT(pao->wAdapterType); */
|
||||
|
||||
hpios_alistlock_lock(&adapters);
|
||||
|
||||
if (pao->index >= HPI_MAX_ADAPTERS) {
|
||||
retval = HPI_ERROR_BAD_ADAPTER_NUMBER;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (adapters.adapter[pao->index].adapter_type) {
|
||||
{
|
||||
retval = HPI_DUPLICATE_ADAPTER_NUMBER;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
adapters.adapter[pao->index] = *pao;
|
||||
hpios_dsplock_init(&adapters.adapter[pao->index]);
|
||||
adapters.gw_num_adapters++;
|
||||
|
||||
unlock:
|
||||
hpios_alistlock_un_lock(&adapters);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void hpi_delete_adapter(struct hpi_adapter_obj *pao)
|
||||
{
|
||||
memset(pao, 0, sizeof(struct hpi_adapter_obj));
|
||||
|
||||
hpios_alistlock_lock(&adapters);
|
||||
adapters.gw_num_adapters--; /* dec the number of adapters */
|
||||
hpios_alistlock_un_lock(&adapters);
|
||||
}
|
||||
|
||||
/**
|
||||
* FindAdapter returns a pointer to the struct hpi_adapter_obj with
|
||||
* index wAdapterIndex in an HPI_ADAPTERS_LIST structure.
|
||||
*
|
||||
*/
|
||||
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
|
||||
{
|
||||
struct hpi_adapter_obj *pao = NULL;
|
||||
|
||||
if (adapter_index >= HPI_MAX_ADAPTERS) {
|
||||
HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ",
|
||||
adapter_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pao = &adapters.adapter[adapter_index];
|
||||
if (pao->adapter_type != 0) {
|
||||
/*
|
||||
HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n",
|
||||
wAdapterIndex);
|
||||
*/
|
||||
return pao;
|
||||
} else {
|
||||
/*
|
||||
HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n",
|
||||
wAdapterIndex);
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* wipe an HPI_ADAPTERS_LIST structure.
|
||||
*
|
||||
**/
|
||||
static void wipe_adapter_list(void
|
||||
)
|
||||
{
|
||||
memset(&adapters, 0, sizeof(adapters));
|
||||
}
|
||||
|
||||
/**
|
||||
* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure
|
||||
* with all adapters in the given HPI_ADAPTERS_LIST.
|
||||
*
|
||||
*/
|
||||
static void subsys_get_adapters(struct hpi_response *phr)
|
||||
{
|
||||
/* fill in the response adapter array with the position */
|
||||
/* identified by the adapter number/index of the adapters in */
|
||||
/* this HPI */
|
||||
/* i.e. if we have an A120 with it's jumper set to */
|
||||
/* Adapter Number 2 then put an Adapter type A120 in the */
|
||||
/* array in position 1 */
|
||||
/* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */
|
||||
|
||||
/* input: NONE */
|
||||
/* output: wNumAdapters */
|
||||
/* awAdapter[] */
|
||||
/* */
|
||||
|
||||
short i;
|
||||
struct hpi_adapter_obj *pao = NULL;
|
||||
|
||||
HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n");
|
||||
|
||||
/* for each adapter, place it's type in the position of the array */
|
||||
/* corresponding to it's adapter number */
|
||||
for (i = 0; i < adapters.gw_num_adapters; i++) {
|
||||
pao = &adapters.adapter[i];
|
||||
if (phr->u.s.aw_adapter_list[pao->index] != 0) {
|
||||
phr->error = HPI_DUPLICATE_ADAPTER_NUMBER;
|
||||
phr->specific_error = pao->index;
|
||||
return;
|
||||
}
|
||||
phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type;
|
||||
}
|
||||
|
||||
phr->u.s.num_adapters = adapters.gw_num_adapters;
|
||||
phr->error = 0; /* the function completed OK; */
|
||||
}
|
||||
|
||||
static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
|
||||
{
|
||||
unsigned int i;
|
||||
int cached = 0;
|
||||
if (!pC)
|
||||
return 0;
|
||||
if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count)
|
||||
&& (pC->cache_size_in_bytes)
|
||||
) {
|
||||
u32 *p_master_cache;
|
||||
pC->init = 1;
|
||||
|
||||
p_master_cache = (u32 *)pC->p_cache;
|
||||
HPI_DEBUG_LOG(VERBOSE, "check %d controls\n",
|
||||
pC->control_count);
|
||||
for (i = 0; i < pC->control_count; i++) {
|
||||
struct hpi_control_cache_info *info =
|
||||
(struct hpi_control_cache_info *)
|
||||
p_master_cache;
|
||||
|
||||
if (info->control_type) {
|
||||
pC->p_info[i] = info;
|
||||
cached++;
|
||||
} else
|
||||
pC->p_info[i] = NULL;
|
||||
|
||||
if (info->size_in32bit_words)
|
||||
p_master_cache += info->size_in32bit_words;
|
||||
else
|
||||
p_master_cache +=
|
||||
sizeof(struct
|
||||
hpi_control_cache_single) /
|
||||
sizeof(u32);
|
||||
|
||||
HPI_DEBUG_LOG(VERBOSE,
|
||||
"cached %d, pinfo %p index %d type %d\n",
|
||||
cached, pC->p_info[i], info->control_index,
|
||||
info->control_type);
|
||||
}
|
||||
/*
|
||||
We didn't find anything to cache, so try again later !
|
||||
*/
|
||||
if (!cached)
|
||||
pC->init = 0;
|
||||
}
|
||||
return pC->init;
|
||||
}
|
||||
|
||||
/** Find a control.
|
||||
*/
|
||||
static short find_control(struct hpi_message *phm,
|
||||
struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI,
|
||||
u16 *pw_control_index)
|
||||
{
|
||||
*pw_control_index = phm->obj_index;
|
||||
|
||||
if (!control_cache_alloc_check(p_cache)) {
|
||||
HPI_DEBUG_LOG(VERBOSE,
|
||||
"control_cache_alloc_check() failed. adap%d ci%d\n",
|
||||
phm->adapter_index, *pw_control_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pI = p_cache->p_info[*pw_control_index];
|
||||
if (!*pI) {
|
||||
HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n",
|
||||
phm->adapter_index, *pw_control_index);
|
||||
return 0;
|
||||
} else {
|
||||
HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
|
||||
(*pI)->control_type);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Used by the kernel driver to figure out if a buffer needs mapping.
|
||||
*/
|
||||
short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
|
||||
struct hpi_message *phm, void **p, unsigned int *pN)
|
||||
{
|
||||
*pN = 0;
|
||||
*p = NULL;
|
||||
if ((phm->function == HPI_CONTROL_GET_STATE)
|
||||
&& (phm->object == HPI_OBJ_CONTROLEX)
|
||||
) {
|
||||
u16 control_index;
|
||||
struct hpi_control_cache_info *pI;
|
||||
|
||||
if (!find_control(phm, p_cache, &pI, &control_index))
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* allow unified treatment of several string fields within struct */
|
||||
#define HPICMN_PAD_OFS_AND_SIZE(m) {\
|
||||
offsetof(struct hpi_control_cache_pad, m), \
|
||||
sizeof(((struct hpi_control_cache_pad *)(NULL))->m) }
|
||||
|
||||
struct pad_ofs_size {
|
||||
unsigned int offset;
|
||||
unsigned int field_size;
|
||||
};
|
||||
|
||||
static struct pad_ofs_size pad_desc[] = {
|
||||
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
|
||||
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
|
||||
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
|
||||
HPICMN_PAD_OFS_AND_SIZE(c_comment), /* HPI_PAD_COMMENT */
|
||||
};
|
||||
|
||||
/** CheckControlCache checks the cache and fills the struct hpi_response
|
||||
* accordingly. It returns one if a cache hit occurred, zero otherwise.
|
||||
*/
|
||||
short hpi_check_control_cache(struct hpi_control_cache *p_cache,
|
||||
struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
short found = 1;
|
||||
u16 control_index;
|
||||
struct hpi_control_cache_info *pI;
|
||||
struct hpi_control_cache_single *pC;
|
||||
struct hpi_control_cache_pad *p_pad;
|
||||
|
||||
if (!find_control(phm, p_cache, &pI, &control_index))
|
||||
return 0;
|
||||
|
||||
phr->error = 0;
|
||||
|
||||
/* pC is the default cached control strucure. May be cast to
|
||||
something else in the following switch statement.
|
||||
*/
|
||||
pC = (struct hpi_control_cache_single *)pI;
|
||||
p_pad = (struct hpi_control_cache_pad *)pI;
|
||||
|
||||
switch (pI->control_type) {
|
||||
|
||||
case HPI_CONTROL_METER:
|
||||
if (phm->u.c.attribute == HPI_METER_PEAK) {
|
||||
phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0];
|
||||
phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1];
|
||||
} else if (phm->u.c.attribute == HPI_METER_RMS) {
|
||||
phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0];
|
||||
phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1];
|
||||
} else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_VOLUME:
|
||||
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
|
||||
phr->u.c.an_log_value[0] = pC->u.v.an_log[0];
|
||||
phr->u.c.an_log_value[1] = pC->u.v.an_log[1];
|
||||
} else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_MULTIPLEXER:
|
||||
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
|
||||
phr->u.c.param1 = pC->u.x.source_node_type;
|
||||
phr->u.c.param2 = pC->u.x.source_node_index;
|
||||
} else {
|
||||
found = 0;
|
||||
}
|
||||
break;
|
||||
case HPI_CONTROL_CHANNEL_MODE:
|
||||
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
|
||||
phr->u.c.param1 = pC->u.m.mode;
|
||||
else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_LEVEL:
|
||||
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
|
||||
phr->u.c.an_log_value[0] = pC->u.l.an_log[0];
|
||||
phr->u.c.an_log_value[1] = pC->u.l.an_log[1];
|
||||
} else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_TUNER:
|
||||
{
|
||||
struct hpi_control_cache_single *pCT =
|
||||
(struct hpi_control_cache_single *)pI;
|
||||
if (phm->u.c.attribute == HPI_TUNER_FREQ)
|
||||
phr->u.c.param1 = pCT->u.t.freq_ink_hz;
|
||||
else if (phm->u.c.attribute == HPI_TUNER_BAND)
|
||||
phr->u.c.param1 = pCT->u.t.band;
|
||||
else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
|
||||
&& (phm->u.c.param1 ==
|
||||
HPI_TUNER_LEVEL_AVERAGE))
|
||||
phr->u.c.param1 = pCT->u.t.level;
|
||||
else
|
||||
found = 0;
|
||||
}
|
||||
break;
|
||||
case HPI_CONTROL_AESEBU_RECEIVER:
|
||||
if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
|
||||
phr->u.c.param1 = pC->u.aes3rx.error_status;
|
||||
else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
|
||||
phr->u.c.param1 = pC->u.aes3rx.source;
|
||||
else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_AESEBU_TRANSMITTER:
|
||||
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
|
||||
phr->u.c.param1 = pC->u.aes3tx.format;
|
||||
else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_TONEDETECTOR:
|
||||
if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE)
|
||||
phr->u.c.param1 = pC->u.tone.state;
|
||||
else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_SILENCEDETECTOR:
|
||||
if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {
|
||||
phr->u.c.param1 = pC->u.silence.state;
|
||||
phr->u.c.param2 = pC->u.silence.count;
|
||||
} else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_MICROPHONE:
|
||||
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
|
||||
phr->u.c.param1 = pC->u.phantom_power.state;
|
||||
else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_SAMPLECLOCK:
|
||||
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
|
||||
phr->u.c.param1 = pC->u.clk.source;
|
||||
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
|
||||
if (pC->u.clk.source_index ==
|
||||
HPI_ERROR_ILLEGAL_CACHE_VALUE) {
|
||||
phr->u.c.param1 = 0;
|
||||
phr->error = HPI_ERROR_INVALID_OPERATION;
|
||||
} else
|
||||
phr->u.c.param1 = pC->u.clk.source_index;
|
||||
} else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
|
||||
phr->u.c.param1 = pC->u.clk.sample_rate;
|
||||
else
|
||||
found = 0;
|
||||
break;
|
||||
case HPI_CONTROL_PAD:
|
||||
|
||||
if (!(p_pad->field_valid_flags & (1 <<
|
||||
HPI_CTL_ATTR_INDEX(phm->u.c.
|
||||
attribute)))) {
|
||||
phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
|
||||
phr->u.c.param1 = p_pad->pI;
|
||||
else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
|
||||
phr->u.c.param1 = p_pad->pTY;
|
||||
else {
|
||||
unsigned int index =
|
||||
HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1;
|
||||
unsigned int offset = phm->u.c.param1;
|
||||
unsigned int pad_string_len, field_size;
|
||||
char *pad_string;
|
||||
unsigned int tocopy;
|
||||
|
||||
HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n",
|
||||
phm->u.c.attribute);
|
||||
|
||||
if (index > ARRAY_SIZE(pad_desc) - 1) {
|
||||
phr->error =
|
||||
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
pad_string = ((char *)p_pad) + pad_desc[index].offset;
|
||||
field_size = pad_desc[index].field_size;
|
||||
/* Ensure null terminator */
|
||||
pad_string[field_size - 1] = 0;
|
||||
|
||||
pad_string_len = strlen(pad_string) + 1;
|
||||
|
||||
if (offset > pad_string_len) {
|
||||
phr->error = HPI_ERROR_INVALID_CONTROL_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
tocopy = pad_string_len - offset;
|
||||
if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
|
||||
tocopy = sizeof(phr->u.cu.chars8.sz_data);
|
||||
|
||||
HPI_DEBUG_LOG(VERBOSE,
|
||||
"PADS memcpy(%d), offset %d \n", tocopy,
|
||||
offset);
|
||||
memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset],
|
||||
tocopy);
|
||||
|
||||
phr->u.cu.chars8.remaining_chars =
|
||||
pad_string_len - offset - tocopy;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
HPI_DEBUG_LOG(VERBOSE,
|
||||
"cached adap %d, ctl %d, type %d, attr %d\n",
|
||||
phm->adapter_index, pI->control_index,
|
||||
pI->control_type, phm->u.c.attribute);
|
||||
else
|
||||
HPI_DEBUG_LOG(VERBOSE,
|
||||
"uncached adap %d, ctl %d, ctl type %d\n",
|
||||
phm->adapter_index, pI->control_index,
|
||||
pI->control_type);
|
||||
|
||||
if (found)
|
||||
phr->size =
|
||||
sizeof(struct hpi_response_header) +
|
||||
sizeof(struct hpi_control_res);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/** Updates the cache with Set values.
|
||||
|
||||
Only update if no error.
|
||||
Volume and Level return the limited values in the response, so use these
|
||||
Multiplexer does so use sent values
|
||||
*/
|
||||
void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
|
||||
struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
u16 control_index;
|
||||
struct hpi_control_cache_single *pC;
|
||||
struct hpi_control_cache_info *pI;
|
||||
|
||||
if (!find_control(phm, p_cache, &pI, &control_index))
|
||||
return;
|
||||
|
||||
/* pC is the default cached control strucure.
|
||||
May be cast to something else in the following switch statement.
|
||||
*/
|
||||
pC = (struct hpi_control_cache_single *)pI;
|
||||
|
||||
switch (pI->control_type) {
|
||||
case HPI_CONTROL_VOLUME:
|
||||
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
|
||||
pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
|
||||
pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
|
||||
}
|
||||
break;
|
||||
case HPI_CONTROL_MULTIPLEXER:
|
||||
/* mux does not return its setting on Set command. */
|
||||
if (phr->error)
|
||||
return;
|
||||
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
|
||||
pC->u.x.source_node_type = (u16)phm->u.c.param1;
|
||||
pC->u.x.source_node_index = (u16)phm->u.c.param2;
|
||||
}
|
||||
break;
|
||||
case HPI_CONTROL_CHANNEL_MODE:
|
||||
/* mode does not return its setting on Set command. */
|
||||
if (phr->error)
|
||||
return;
|
||||
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
|
||||
pC->u.m.mode = (u16)phm->u.c.param1;
|
||||
break;
|
||||
case HPI_CONTROL_LEVEL:
|
||||
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
|
||||
pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
|
||||
pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
|
||||
}
|
||||
break;
|
||||
case HPI_CONTROL_MICROPHONE:
|
||||
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
|
||||
pC->u.phantom_power.state = (u16)phm->u.c.param1;
|
||||
break;
|
||||
case HPI_CONTROL_AESEBU_TRANSMITTER:
|
||||
if (phr->error)
|
||||
return;
|
||||
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
|
||||
pC->u.aes3tx.format = phm->u.c.param1;
|
||||
break;
|
||||
case HPI_CONTROL_AESEBU_RECEIVER:
|
||||
if (phr->error)
|
||||
return;
|
||||
if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
|
||||
pC->u.aes3rx.source = phm->u.c.param1;
|
||||
break;
|
||||
case HPI_CONTROL_SAMPLECLOCK:
|
||||
if (phr->error)
|
||||
return;
|
||||
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
|
||||
pC->u.clk.source = (u16)phm->u.c.param1;
|
||||
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX)
|
||||
pC->u.clk.source_index = (u16)phm->u.c.param1;
|
||||
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)
|
||||
pC->u.clk.sample_rate = phm->u.c.param1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct hpi_control_cache *hpi_alloc_control_cache(const u32
|
||||
number_of_controls, const u32 size_in_bytes,
|
||||
struct hpi_control_cache_info *pDSP_control_buffer)
|
||||
{
|
||||
struct hpi_control_cache *p_cache =
|
||||
kmalloc(sizeof(*p_cache), GFP_KERNEL);
|
||||
p_cache->cache_size_in_bytes = size_in_bytes;
|
||||
p_cache->control_count = number_of_controls;
|
||||
p_cache->p_cache =
|
||||
(struct hpi_control_cache_single *)pDSP_control_buffer;
|
||||
p_cache->init = 0;
|
||||
p_cache->p_info =
|
||||
kmalloc(sizeof(*p_cache->p_info) * p_cache->control_count,
|
||||
GFP_KERNEL);
|
||||
return p_cache;
|
||||
}
|
||||
|
||||
void hpi_free_control_cache(struct hpi_control_cache *p_cache)
|
||||
{
|
||||
if ((p_cache->init) && (p_cache->p_info)) {
|
||||
kfree(p_cache->p_info);
|
||||
p_cache->p_info = NULL;
|
||||
p_cache->init = 0;
|
||||
kfree(p_cache);
|
||||
}
|
||||
}
|
||||
|
||||
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
|
||||
switch (phm->function) {
|
||||
case HPI_SUBSYS_OPEN:
|
||||
case HPI_SUBSYS_CLOSE:
|
||||
case HPI_SUBSYS_DRIVER_UNLOAD:
|
||||
phr->error = 0;
|
||||
break;
|
||||
case HPI_SUBSYS_DRIVER_LOAD:
|
||||
wipe_adapter_list();
|
||||
hpios_alistlock_init(&adapters);
|
||||
phr->error = 0;
|
||||
break;
|
||||
case HPI_SUBSYS_GET_INFO:
|
||||
subsys_get_adapters(phr);
|
||||
break;
|
||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
phr->error = 0;
|
||||
break;
|
||||
default:
|
||||
phr->error = HPI_ERROR_INVALID_FUNC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
switch (phm->type) {
|
||||
case HPI_TYPE_MESSAGE:
|
||||
switch (phm->object) {
|
||||
case HPI_OBJ_SUBSYSTEM:
|
||||
subsys_message(phm, phr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
phr->error = HPI_ERROR_INVALID_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
64
sound/pci/asihpi/hpicmn.h
Normal file
64
sound/pci/asihpi/hpicmn.h
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
struct hpi_adapter_obj {
|
||||
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
|
||||
u16 adapter_type; /* ASI6701 etc */
|
||||
u16 index; /* */
|
||||
u16 open; /* =1 when adapter open */
|
||||
u16 mixer_open;
|
||||
|
||||
struct hpios_spinlock dsp_lock;
|
||||
|
||||
u16 dsp_crashed;
|
||||
u16 has_control_cache;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct hpi_control_cache {
|
||||
u32 init; /**< indicates whether the
|
||||
structures are initialized */
|
||||
u32 control_count;
|
||||
u32 cache_size_in_bytes;
|
||||
struct hpi_control_cache_info
|
||||
**p_info; /**< pointer to allocated memory of
|
||||
lookup pointers. */
|
||||
struct hpi_control_cache_single
|
||||
*p_cache; /**< pointer to DSP's control cache. */
|
||||
};
|
||||
|
||||
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
|
||||
u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
|
||||
|
||||
void hpi_delete_adapter(struct hpi_adapter_obj *pao);
|
||||
|
||||
short hpi_check_control_cache(struct hpi_control_cache *pC,
|
||||
struct hpi_message *phm, struct hpi_response *phr);
|
||||
struct hpi_control_cache *hpi_alloc_control_cache(const u32
|
||||
number_of_controls, const u32 size_in_bytes,
|
||||
struct hpi_control_cache_info
|
||||
*pDSP_control_buffer);
|
||||
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
|
||||
|
||||
void hpi_sync_control_cache(struct hpi_control_cache *pC,
|
||||
struct hpi_message *phm, struct hpi_response *phr);
|
||||
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
|
||||
short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
|
||||
struct hpi_message *phm, void **p, unsigned int *pN);
|
225
sound/pci/asihpi/hpidebug.c
Normal file
225
sound/pci/asihpi/hpidebug.c
Normal file
@ -0,0 +1,225 @@
|
||||
/************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Debug macro translation.
|
||||
|
||||
************************************************************************/
|
||||
|
||||
#include "hpi_internal.h"
|
||||
#include "hpidebug.h"
|
||||
|
||||
/* Debug level; 0 quiet; 1 informative, 2 debug, 3 verbose debug. */
|
||||
int hpi_debug_level = HPI_DEBUG_LEVEL_DEFAULT;
|
||||
|
||||
void hpi_debug_init(void)
|
||||
{
|
||||
printk(KERN_INFO "debug start\n");
|
||||
}
|
||||
|
||||
int hpi_debug_level_set(int level)
|
||||
{
|
||||
int old_level;
|
||||
|
||||
old_level = hpi_debug_level;
|
||||
hpi_debug_level = level;
|
||||
return old_level;
|
||||
}
|
||||
|
||||
int hpi_debug_level_get(void)
|
||||
{
|
||||
return hpi_debug_level;
|
||||
}
|
||||
|
||||
#ifdef HPIOS_DEBUG_PRINT
|
||||
/* implies OS has no printf-like function */
|
||||
#include <stdarg.h>
|
||||
|
||||
void hpi_debug_printf(char *fmt, ...)
|
||||
{
|
||||
va_list arglist;
|
||||
char buffer[128];
|
||||
|
||||
va_start(arglist, fmt);
|
||||
|
||||
if (buffer[0])
|
||||
HPIOS_DEBUG_PRINT(buffer);
|
||||
va_end(arglist);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct treenode {
|
||||
void *array;
|
||||
unsigned int num_elements;
|
||||
};
|
||||
|
||||
#define make_treenode_from_array(nodename, array) \
|
||||
static void *tmp_strarray_##nodename[] = array; \
|
||||
static struct treenode nodename = { \
|
||||
&tmp_strarray_##nodename, \
|
||||
ARRAY_SIZE(tmp_strarray_##nodename) \
|
||||
};
|
||||
|
||||
#define get_treenode_elem(node_ptr, idx, type) \
|
||||
(&(*((type *)(node_ptr)->array)[idx]))
|
||||
|
||||
make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS)
|
||||
|
||||
make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS)
|
||||
make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS)
|
||||
make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS)
|
||||
make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS)
|
||||
make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS)
|
||||
make_treenode_from_array(hpi_node_strings,
|
||||
{
|
||||
"NODE is invalid object"})
|
||||
|
||||
make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS)
|
||||
make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS)
|
||||
make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS)
|
||||
make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS)
|
||||
make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS)
|
||||
make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS)
|
||||
make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS)
|
||||
#define HPI_FUNCTION_STRINGS \
|
||||
{ \
|
||||
&hpi_subsys_strings,\
|
||||
&hpi_adapter_strings,\
|
||||
&hpi_ostream_strings,\
|
||||
&hpi_istream_strings,\
|
||||
&hpi_mixer_strings,\
|
||||
&hpi_node_strings,\
|
||||
&hpi_control_strings,\
|
||||
&hpi_nvmemory_strings,\
|
||||
&hpi_digitalio_strings,\
|
||||
&hpi_watchdog_strings,\
|
||||
&hpi_clock_strings,\
|
||||
&hpi_profile_strings,\
|
||||
&hpi_control_strings, \
|
||||
&hpi_asyncevent_strings \
|
||||
};
|
||||
make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
|
||||
|
||||
compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
|
||||
|
||||
static char *hpi_function_string(unsigned int function)
|
||||
{
|
||||
unsigned int object;
|
||||
struct treenode *tmp;
|
||||
|
||||
object = function / HPI_OBJ_FUNCTION_SPACING;
|
||||
function = function - object * HPI_OBJ_FUNCTION_SPACING;
|
||||
|
||||
if (object == 0 || object == HPI_OBJ_NODE
|
||||
|| object > hpi_function_strings.num_elements)
|
||||
return "invalid object";
|
||||
|
||||
tmp = get_treenode_elem(&hpi_function_strings, object - 1,
|
||||
struct treenode *);
|
||||
|
||||
if (function == 0 || function > tmp->num_elements)
|
||||
return "invalid function";
|
||||
|
||||
return get_treenode_elem(tmp, function - 1, char *);
|
||||
}
|
||||
|
||||
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
|
||||
{
|
||||
if (phm) {
|
||||
if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) {
|
||||
u16 index = 0;
|
||||
u16 attrib = 0;
|
||||
int is_control = 0;
|
||||
|
||||
index = phm->obj_index;
|
||||
switch (phm->object) {
|
||||
case HPI_OBJ_ADAPTER:
|
||||
case HPI_OBJ_PROFILE:
|
||||
break;
|
||||
case HPI_OBJ_MIXER:
|
||||
if (phm->function ==
|
||||
HPI_MIXER_GET_CONTROL_BY_INDEX)
|
||||
index = phm->u.m.control_index;
|
||||
break;
|
||||
case HPI_OBJ_OSTREAM:
|
||||
case HPI_OBJ_ISTREAM:
|
||||
break;
|
||||
|
||||
case HPI_OBJ_CONTROLEX:
|
||||
case HPI_OBJ_CONTROL:
|
||||
if (phm->version == 1)
|
||||
attrib = HPI_CTL_ATTR(UNIVERSAL, 1);
|
||||
else
|
||||
attrib = phm->u.c.attribute;
|
||||
is_control = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_control && (attrib & 0xFF00)) {
|
||||
int control_type = (attrib & 0xFF00) >> 8;
|
||||
int attr_index = HPI_CTL_ATTR_INDEX(attrib);
|
||||
/* note the KERN facility level
|
||||
is in szFileline already */
|
||||
printk("%s adapter %d %s "
|
||||
"ctrl_index x%04x %s %d\n",
|
||||
sz_fileline, phm->adapter_index,
|
||||
hpi_function_string(phm->function),
|
||||
index,
|
||||
get_treenode_elem
|
||||
(&hpi_control_type_strings,
|
||||
control_type, char *),
|
||||
attr_index);
|
||||
|
||||
} else
|
||||
printk("%s adapter %d %s "
|
||||
"idx x%04x attr x%04x \n",
|
||||
sz_fileline, phm->adapter_index,
|
||||
hpi_function_string(phm->function),
|
||||
index, attrib);
|
||||
} else {
|
||||
printk("adap=%d, invalid obj=%d, func=0x%x\n",
|
||||
phm->adapter_index, phm->object,
|
||||
phm->function);
|
||||
}
|
||||
} else
|
||||
printk(KERN_ERR
|
||||
"NULL message pointer to hpi_debug_message!\n");
|
||||
}
|
||||
|
||||
void hpi_debug_data(u16 *pdata, u32 len)
|
||||
{
|
||||
u32 i;
|
||||
int j;
|
||||
int k;
|
||||
int lines;
|
||||
int cols = 8;
|
||||
|
||||
lines = (len + cols - 1) / cols;
|
||||
if (lines > 8)
|
||||
lines = 8;
|
||||
|
||||
for (i = 0, j = 0; j < lines; j++) {
|
||||
printk(KERN_DEBUG "%p:", (pdata + i));
|
||||
|
||||
for (k = 0; k < cols && i < len; i++, k++)
|
||||
printk("%s%04x", k == 0 ? "" : " ", pdata[i]);
|
||||
|
||||
printk("\n");
|
||||
}
|
||||
}
|
385
sound/pci/asihpi/hpidebug.h
Normal file
385
sound/pci/asihpi/hpidebug.h
Normal file
@ -0,0 +1,385 @@
|
||||
/*****************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Debug macros.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _HPIDEBUG_H
|
||||
#define _HPIDEBUG_H
|
||||
|
||||
#include "hpi_internal.h"
|
||||
|
||||
/* Define debugging levels. */
|
||||
enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
|
||||
HPI_DEBUG_LEVEL_WARNING = 1,
|
||||
HPI_DEBUG_LEVEL_NOTICE = 2,
|
||||
HPI_DEBUG_LEVEL_INFO = 3,
|
||||
HPI_DEBUG_LEVEL_DEBUG = 4,
|
||||
HPI_DEBUG_LEVEL_VERBOSE = 5 /* same printk level as DEBUG */
|
||||
};
|
||||
|
||||
#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
|
||||
|
||||
/* an OS can define an extra flag string that is appended to
|
||||
the start of each message, eg see hpios_linux.h */
|
||||
|
||||
#ifdef SOURCEFILE_NAME
|
||||
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
|
||||
#else
|
||||
#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
|
||||
#endif
|
||||
|
||||
#if defined(HPI_DEBUG) && defined(_WINDOWS)
|
||||
#define HPI_DEBUGBREAK() debug_break()
|
||||
#else
|
||||
#define HPI_DEBUGBREAK()
|
||||
#endif
|
||||
|
||||
#define HPI_DEBUG_ASSERT(expression) \
|
||||
do { \
|
||||
if (!(expression)) {\
|
||||
printk(KERN_ERR FILE_LINE\
|
||||
"ASSERT " __stringify(expression));\
|
||||
HPI_DEBUGBREAK();\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define HPI_DEBUG_LOG(level, ...) \
|
||||
do { \
|
||||
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
|
||||
printk(HPI_DEBUG_FLAG_##level \
|
||||
FILE_LINE __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void hpi_debug_init(void);
|
||||
int hpi_debug_level_set(int level);
|
||||
int hpi_debug_level_get(void);
|
||||
/* needed by Linux driver for dynamic debug level changes */
|
||||
extern int hpi_debug_level;
|
||||
|
||||
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
|
||||
|
||||
void hpi_debug_data(u16 *pdata, u32 len);
|
||||
|
||||
#define HPI_DEBUG_DATA(pdata, len) \
|
||||
do { \
|
||||
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
|
||||
hpi_debug_data(pdata, len); \
|
||||
} while (0)
|
||||
|
||||
#define HPI_DEBUG_MESSAGE(level, phm) \
|
||||
do { \
|
||||
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
|
||||
hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \
|
||||
FILE_LINE __stringify(level));\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define HPI_DEBUG_RESPONSE(phr) \
|
||||
do { \
|
||||
if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\
|
||||
HPI_DEBUG_LOG(ERROR, \
|
||||
"HPI response - error# %d\n", \
|
||||
phr->error); \
|
||||
else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
|
||||
HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\
|
||||
} while (0)
|
||||
|
||||
#ifndef compile_time_assert
|
||||
#define compile_time_assert(cond, msg) \
|
||||
typedef char msg[(cond) ? 1 : -1]
|
||||
#endif
|
||||
|
||||
/* check that size is exactly some number */
|
||||
#define function_count_check(sym, size) \
|
||||
compile_time_assert((sym##_FUNCTION_COUNT) == (size),\
|
||||
strings_match_defs_##sym)
|
||||
|
||||
/* These strings should be generated using a macro which defines
|
||||
the corresponding symbol values. */
|
||||
#define HPI_OBJ_STRINGS \
|
||||
{ \
|
||||
"HPI_OBJ_SUBSYSTEM", \
|
||||
"HPI_OBJ_ADAPTER", \
|
||||
"HPI_OBJ_OSTREAM", \
|
||||
"HPI_OBJ_ISTREAM", \
|
||||
"HPI_OBJ_MIXER", \
|
||||
"HPI_OBJ_NODE", \
|
||||
"HPI_OBJ_CONTROL", \
|
||||
"HPI_OBJ_NVMEMORY", \
|
||||
"HPI_OBJ_DIGITALIO", \
|
||||
"HPI_OBJ_WATCHDOG", \
|
||||
"HPI_OBJ_CLOCK", \
|
||||
"HPI_OBJ_PROFILE", \
|
||||
"HPI_OBJ_CONTROLEX" \
|
||||
}
|
||||
|
||||
#define HPI_SUBSYS_STRINGS \
|
||||
{ \
|
||||
"HPI_SUBSYS_OPEN", \
|
||||
"HPI_SUBSYS_GET_VERSION", \
|
||||
"HPI_SUBSYS_GET_INFO", \
|
||||
"HPI_SUBSYS_FIND_ADAPTERS", \
|
||||
"HPI_SUBSYS_CREATE_ADAPTER",\
|
||||
"HPI_SUBSYS_CLOSE", \
|
||||
"HPI_SUBSYS_DELETE_ADAPTER", \
|
||||
"HPI_SUBSYS_DRIVER_LOAD", \
|
||||
"HPI_SUBSYS_DRIVER_UNLOAD", \
|
||||
"HPI_SUBSYS_READ_PORT_8", \
|
||||
"HPI_SUBSYS_WRITE_PORT_8", \
|
||||
"HPI_SUBSYS_GET_NUM_ADAPTERS",\
|
||||
"HPI_SUBSYS_GET_ADAPTER", \
|
||||
"HPI_SUBSYS_SET_NETWORK_INTERFACE"\
|
||||
}
|
||||
function_count_check(HPI_SUBSYS, 14);
|
||||
|
||||
#define HPI_ADAPTER_STRINGS \
|
||||
{ \
|
||||
"HPI_ADAPTER_OPEN", \
|
||||
"HPI_ADAPTER_CLOSE", \
|
||||
"HPI_ADAPTER_GET_INFO", \
|
||||
"HPI_ADAPTER_GET_ASSERT", \
|
||||
"HPI_ADAPTER_TEST_ASSERT", \
|
||||
"HPI_ADAPTER_SET_MODE", \
|
||||
"HPI_ADAPTER_GET_MODE", \
|
||||
"HPI_ADAPTER_ENABLE_CAPABILITY",\
|
||||
"HPI_ADAPTER_SELFTEST", \
|
||||
"HPI_ADAPTER_FIND_OBJECT", \
|
||||
"HPI_ADAPTER_QUERY_FLASH", \
|
||||
"HPI_ADAPTER_START_FLASH", \
|
||||
"HPI_ADAPTER_PROGRAM_FLASH", \
|
||||
"HPI_ADAPTER_SET_PROPERTY", \
|
||||
"HPI_ADAPTER_GET_PROPERTY", \
|
||||
"HPI_ADAPTER_ENUM_PROPERTY", \
|
||||
"HPI_ADAPTER_MODULE_INFO", \
|
||||
"HPI_ADAPTER_DEBUG_READ" \
|
||||
}
|
||||
|
||||
function_count_check(HPI_ADAPTER, 18);
|
||||
|
||||
#define HPI_OSTREAM_STRINGS \
|
||||
{ \
|
||||
"HPI_OSTREAM_OPEN", \
|
||||
"HPI_OSTREAM_CLOSE", \
|
||||
"HPI_OSTREAM_WRITE", \
|
||||
"HPI_OSTREAM_START", \
|
||||
"HPI_OSTREAM_STOP", \
|
||||
"HPI_OSTREAM_RESET", \
|
||||
"HPI_OSTREAM_GET_INFO", \
|
||||
"HPI_OSTREAM_QUERY_FORMAT", \
|
||||
"HPI_OSTREAM_DATA", \
|
||||
"HPI_OSTREAM_SET_VELOCITY", \
|
||||
"HPI_OSTREAM_SET_PUNCHINOUT", \
|
||||
"HPI_OSTREAM_SINEGEN", \
|
||||
"HPI_OSTREAM_ANC_RESET", \
|
||||
"HPI_OSTREAM_ANC_GET_INFO", \
|
||||
"HPI_OSTREAM_ANC_READ", \
|
||||
"HPI_OSTREAM_SET_TIMESCALE",\
|
||||
"HPI_OSTREAM_SET_FORMAT", \
|
||||
"HPI_OSTREAM_HOSTBUFFER_ALLOC", \
|
||||
"HPI_OSTREAM_HOSTBUFFER_FREE", \
|
||||
"HPI_OSTREAM_GROUP_ADD",\
|
||||
"HPI_OSTREAM_GROUP_GETMAP", \
|
||||
"HPI_OSTREAM_GROUP_RESET", \
|
||||
"HPI_OSTREAM_HOSTBUFFER_GET_INFO", \
|
||||
"HPI_OSTREAM_WAIT_START", \
|
||||
}
|
||||
function_count_check(HPI_OSTREAM, 24);
|
||||
|
||||
#define HPI_ISTREAM_STRINGS \
|
||||
{ \
|
||||
"HPI_ISTREAM_OPEN", \
|
||||
"HPI_ISTREAM_CLOSE", \
|
||||
"HPI_ISTREAM_SET_FORMAT", \
|
||||
"HPI_ISTREAM_READ", \
|
||||
"HPI_ISTREAM_START", \
|
||||
"HPI_ISTREAM_STOP", \
|
||||
"HPI_ISTREAM_RESET", \
|
||||
"HPI_ISTREAM_GET_INFO", \
|
||||
"HPI_ISTREAM_QUERY_FORMAT", \
|
||||
"HPI_ISTREAM_ANC_RESET", \
|
||||
"HPI_ISTREAM_ANC_GET_INFO", \
|
||||
"HPI_ISTREAM_ANC_WRITE", \
|
||||
"HPI_ISTREAM_HOSTBUFFER_ALLOC",\
|
||||
"HPI_ISTREAM_HOSTBUFFER_FREE", \
|
||||
"HPI_ISTREAM_GROUP_ADD", \
|
||||
"HPI_ISTREAM_GROUP_GETMAP", \
|
||||
"HPI_ISTREAM_GROUP_RESET", \
|
||||
"HPI_ISTREAM_HOSTBUFFER_GET_INFO", \
|
||||
"HPI_ISTREAM_WAIT_START", \
|
||||
}
|
||||
function_count_check(HPI_ISTREAM, 19);
|
||||
|
||||
#define HPI_MIXER_STRINGS \
|
||||
{ \
|
||||
"HPI_MIXER_OPEN", \
|
||||
"HPI_MIXER_CLOSE", \
|
||||
"HPI_MIXER_GET_INFO", \
|
||||
"HPI_MIXER_GET_NODE_INFO", \
|
||||
"HPI_MIXER_GET_CONTROL", \
|
||||
"HPI_MIXER_SET_CONNECTION", \
|
||||
"HPI_MIXER_GET_CONNECTIONS", \
|
||||
"HPI_MIXER_GET_CONTROL_BY_INDEX", \
|
||||
"HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \
|
||||
"HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \
|
||||
"HPI_MIXER_STORE", \
|
||||
}
|
||||
function_count_check(HPI_MIXER, 11);
|
||||
|
||||
#define HPI_CONTROL_STRINGS \
|
||||
{ \
|
||||
"HPI_CONTROL_GET_INFO", \
|
||||
"HPI_CONTROL_GET_STATE", \
|
||||
"HPI_CONTROL_SET_STATE" \
|
||||
}
|
||||
function_count_check(HPI_CONTROL, 3);
|
||||
|
||||
#define HPI_NVMEMORY_STRINGS \
|
||||
{ \
|
||||
"HPI_NVMEMORY_OPEN", \
|
||||
"HPI_NVMEMORY_READ_BYTE", \
|
||||
"HPI_NVMEMORY_WRITE_BYTE" \
|
||||
}
|
||||
function_count_check(HPI_NVMEMORY, 3);
|
||||
|
||||
#define HPI_DIGITALIO_STRINGS \
|
||||
{ \
|
||||
"HPI_GPIO_OPEN", \
|
||||
"HPI_GPIO_READ_BIT", \
|
||||
"HPI_GPIO_WRITE_BIT", \
|
||||
"HPI_GPIO_READ_ALL", \
|
||||
"HPI_GPIO_WRITE_STATUS"\
|
||||
}
|
||||
function_count_check(HPI_GPIO, 5);
|
||||
|
||||
#define HPI_WATCHDOG_STRINGS \
|
||||
{ \
|
||||
"HPI_WATCHDOG_OPEN", \
|
||||
"HPI_WATCHDOG_SET_TIME", \
|
||||
"HPI_WATCHDOG_PING" \
|
||||
}
|
||||
|
||||
#define HPI_CLOCK_STRINGS \
|
||||
{ \
|
||||
"HPI_CLOCK_OPEN", \
|
||||
"HPI_CLOCK_SET_TIME", \
|
||||
"HPI_CLOCK_GET_TIME" \
|
||||
}
|
||||
|
||||
#define HPI_PROFILE_STRINGS \
|
||||
{ \
|
||||
"HPI_PROFILE_OPEN_ALL", \
|
||||
"HPI_PROFILE_START_ALL", \
|
||||
"HPI_PROFILE_STOP_ALL", \
|
||||
"HPI_PROFILE_GET", \
|
||||
"HPI_PROFILE_GET_IDLECOUNT", \
|
||||
"HPI_PROFILE_GET_NAME", \
|
||||
"HPI_PROFILE_GET_UTILIZATION" \
|
||||
}
|
||||
function_count_check(HPI_PROFILE, 7);
|
||||
|
||||
#define HPI_ASYNCEVENT_STRINGS \
|
||||
{ \
|
||||
"HPI_ASYNCEVENT_OPEN",\
|
||||
"HPI_ASYNCEVENT_CLOSE ",\
|
||||
"HPI_ASYNCEVENT_WAIT",\
|
||||
"HPI_ASYNCEVENT_GETCOUNT",\
|
||||
"HPI_ASYNCEVENT_GET",\
|
||||
"HPI_ASYNCEVENT_SENDEVENTS"\
|
||||
}
|
||||
function_count_check(HPI_ASYNCEVENT, 6);
|
||||
|
||||
#define HPI_CONTROL_TYPE_STRINGS \
|
||||
{ \
|
||||
"null control", \
|
||||
"HPI_CONTROL_CONNECTION", \
|
||||
"HPI_CONTROL_VOLUME", \
|
||||
"HPI_CONTROL_METER", \
|
||||
"HPI_CONTROL_MUTE", \
|
||||
"HPI_CONTROL_MULTIPLEXER", \
|
||||
"HPI_CONTROL_AESEBU_TRANSMITTER", \
|
||||
"HPI_CONTROL_AESEBU_RECEIVER", \
|
||||
"HPI_CONTROL_LEVEL", \
|
||||
"HPI_CONTROL_TUNER", \
|
||||
"HPI_CONTROL_ONOFFSWITCH", \
|
||||
"HPI_CONTROL_VOX", \
|
||||
"HPI_CONTROL_AES18_TRANSMITTER", \
|
||||
"HPI_CONTROL_AES18_RECEIVER", \
|
||||
"HPI_CONTROL_AES18_BLOCKGENERATOR", \
|
||||
"HPI_CONTROL_CHANNEL_MODE", \
|
||||
"HPI_CONTROL_BITSTREAM", \
|
||||
"HPI_CONTROL_SAMPLECLOCK", \
|
||||
"HPI_CONTROL_MICROPHONE", \
|
||||
"HPI_CONTROL_PARAMETRIC_EQ", \
|
||||
"HPI_CONTROL_COMPANDER", \
|
||||
"HPI_CONTROL_COBRANET", \
|
||||
"HPI_CONTROL_TONE_DETECT", \
|
||||
"HPI_CONTROL_SILENCE_DETECT", \
|
||||
"HPI_CONTROL_PAD", \
|
||||
"HPI_CONTROL_SRC" ,\
|
||||
"HPI_CONTROL_UNIVERSAL" \
|
||||
}
|
||||
|
||||
compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27),
|
||||
controltype_strings_match_defs);
|
||||
|
||||
#define HPI_SOURCENODE_STRINGS \
|
||||
{ \
|
||||
"no source", \
|
||||
"HPI_SOURCENODE_OSTREAM", \
|
||||
"HPI_SOURCENODE_LINEIN", \
|
||||
"HPI_SOURCENODE_AESEBU_IN", \
|
||||
"HPI_SOURCENODE_TUNER", \
|
||||
"HPI_SOURCENODE_RF", \
|
||||
"HPI_SOURCENODE_CLOCK_SOURCE", \
|
||||
"HPI_SOURCENODE_RAW_BITSTREAM", \
|
||||
"HPI_SOURCENODE_MICROPHONE", \
|
||||
"HPI_SOURCENODE_COBRANET", \
|
||||
"HPI_SOURCENODE_ANALOG", \
|
||||
"HPI_SOURCENODE_ADAPTER" \
|
||||
}
|
||||
|
||||
compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_BASE + 1) ==
|
||||
(12), sourcenode_strings_match_defs);
|
||||
|
||||
#define HPI_DESTNODE_STRINGS \
|
||||
{ \
|
||||
"no destination", \
|
||||
"HPI_DESTNODE_ISTREAM", \
|
||||
"HPI_DESTNODE_LINEOUT", \
|
||||
"HPI_DESTNODE_AESEBU_OUT", \
|
||||
"HPI_DESTNODE_RF", \
|
||||
"HPI_DESTNODE_SPEAKER", \
|
||||
"HPI_DESTNODE_COBRANET", \
|
||||
"HPI_DESTNODE_ANALOG" \
|
||||
}
|
||||
compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_BASE + 1) == (8),
|
||||
destnode_strings_match_defs);
|
||||
|
||||
#define HPI_CONTROL_CHANNEL_MODE_STRINGS \
|
||||
{ \
|
||||
"XXX HPI_CHANNEL_MODE_ERROR XXX", \
|
||||
"HPI_CHANNEL_MODE_NORMAL", \
|
||||
"HPI_CHANNEL_MODE_SWAP", \
|
||||
"HPI_CHANNEL_MODE_LEFT_ONLY", \
|
||||
"HPI_CHANNEL_MODE_RIGHT_ONLY" \
|
||||
}
|
||||
|
||||
#endif /* _HPIDEBUG_H */
|
172
sound/pci/asihpi/hpidspcd.c
Normal file
172
sound/pci/asihpi/hpidspcd.c
Normal file
@ -0,0 +1,172 @@
|
||||
/***********************************************************************/
|
||||
/*!
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
\file
|
||||
Functions for reading DSP code to load into DSP
|
||||
|
||||
(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using
|
||||
hotplug firmware loader from individual dsp code files
|
||||
|
||||
If neither of the above is defined, code is read from linked arrays.
|
||||
DSPCODE_ARRAY is defined.
|
||||
|
||||
HPI_INCLUDE_**** must be defined
|
||||
and the appropriate hzz?????.c or hex?????.c linked in
|
||||
|
||||
*/
|
||||
/***********************************************************************/
|
||||
#define SOURCEFILE_NAME "hpidspcd.c"
|
||||
#include "hpidspcd.h"
|
||||
#include "hpidebug.h"
|
||||
|
||||
/**
|
||||
Header structure for binary dsp code file (see asidsp.doc)
|
||||
This structure must match that used in s2bin.c for generation of asidsp.bin
|
||||
*/
|
||||
|
||||
#ifndef DISABLE_PRAGMA_PACK1
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
struct code_header {
|
||||
u32 size;
|
||||
char type[4];
|
||||
u32 adapter;
|
||||
u32 version;
|
||||
u32 crc;
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PRAGMA_PACK1
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
|
||||
HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
|
||||
|
||||
/***********************************************************************/
|
||||
#include "linux/pci.h"
|
||||
/*-------------------------------------------------------------------*/
|
||||
short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
|
||||
u32 *pos_error_code)
|
||||
{
|
||||
const struct firmware *ps_firmware = ps_dsp_code->ps_firmware;
|
||||
struct code_header header;
|
||||
char fw_name[20];
|
||||
int err;
|
||||
|
||||
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
|
||||
HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name);
|
||||
|
||||
err = request_firmware(&ps_firmware, fw_name,
|
||||
&ps_dsp_code->ps_dev->dev);
|
||||
if (err != 0) {
|
||||
HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n",
|
||||
err, fw_name);
|
||||
goto error1;
|
||||
}
|
||||
if (ps_firmware->size < sizeof(header)) {
|
||||
HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name);
|
||||
goto error2;
|
||||
}
|
||||
memcpy(&header, ps_firmware->data, sizeof(header));
|
||||
if (header.adapter != adapter) {
|
||||
HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n",
|
||||
header.adapter, adapter);
|
||||
goto error2;
|
||||
}
|
||||
if (header.size != ps_firmware->size) {
|
||||
HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n",
|
||||
header.size, (unsigned long)ps_firmware->size);
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (header.version / 10000 != HPI_VER_DECIMAL / 10000) {
|
||||
HPI_DEBUG_LOG(ERROR,
|
||||
"firmware major version mismatch "
|
||||
"DSP image %d != driver %d\n", header.version,
|
||||
HPI_VER_DECIMAL);
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (header.version != HPI_VER_DECIMAL) {
|
||||
HPI_DEBUG_LOG(WARNING,
|
||||
"version mismatch DSP image %d != driver %d\n",
|
||||
header.version, HPI_VER_DECIMAL);
|
||||
/* goto error2; still allow driver to load */
|
||||
}
|
||||
|
||||
HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name);
|
||||
ps_dsp_code->ps_firmware = ps_firmware;
|
||||
ps_dsp_code->block_length = header.size / sizeof(u32);
|
||||
ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
|
||||
ps_dsp_code->version = header.version;
|
||||
ps_dsp_code->crc = header.crc;
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
release_firmware(ps_firmware);
|
||||
error1:
|
||||
ps_dsp_code->ps_firmware = NULL;
|
||||
ps_dsp_code->block_length = 0;
|
||||
return HPI_ERROR_DSP_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code)
|
||||
{
|
||||
if (ps_dsp_code->ps_firmware != NULL) {
|
||||
HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
|
||||
release_firmware(ps_dsp_code->ps_firmware);
|
||||
ps_dsp_code->ps_firmware = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
|
||||
{
|
||||
/* Go back to start of data, after header */
|
||||
ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
|
||||
{
|
||||
if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
|
||||
return (HPI_ERROR_DSP_FILE_FORMAT);
|
||||
|
||||
*pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
|
||||
word_count];
|
||||
ps_dsp_code->word_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
short hpi_dsp_code_read_block(size_t words_requested,
|
||||
struct dsp_code *ps_dsp_code, u32 **ppblock)
|
||||
{
|
||||
if (ps_dsp_code->word_count + words_requested >
|
||||
ps_dsp_code->block_length)
|
||||
return HPI_ERROR_DSP_FILE_FORMAT;
|
||||
|
||||
*ppblock =
|
||||
((u32 *)(ps_dsp_code->ps_firmware->data)) +
|
||||
ps_dsp_code->word_count;
|
||||
ps_dsp_code->word_count += words_requested;
|
||||
return 0;
|
||||
}
|
104
sound/pci/asihpi/hpidspcd.h
Normal file
104
sound/pci/asihpi/hpidspcd.h
Normal file
@ -0,0 +1,104 @@
|
||||
/***********************************************************************/
|
||||
/**
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
\file
|
||||
Functions for reading DSP code to load into DSP
|
||||
|
||||
hpi_dspcode_defines HPI DSP code loading method
|
||||
Define exactly one of these to select how the DSP code is supplied to
|
||||
the adapter.
|
||||
|
||||
End users writing applications that use the HPI interface do not have to
|
||||
use any of the below defines; they are only necessary for building drivers
|
||||
|
||||
HPI_DSPCODE_FILE:
|
||||
DSP code is supplied as a file that is opened and read from by the driver.
|
||||
|
||||
HPI_DSPCODE_FIRMWARE:
|
||||
DSP code is read using the hotplug firmware loader module.
|
||||
Only valid when compiling the HPI kernel driver under Linux.
|
||||
*/
|
||||
/***********************************************************************/
|
||||
#ifndef _HPIDSPCD_H_
|
||||
#define _HPIDSPCD_H_
|
||||
|
||||
#include "hpi_internal.h"
|
||||
|
||||
#ifndef DISABLE_PRAGMA_PACK1
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
/** Descriptor for dspcode from firmware loader */
|
||||
struct dsp_code {
|
||||
/** Firmware descriptor */
|
||||
const struct firmware *ps_firmware;
|
||||
struct pci_dev *ps_dev;
|
||||
/** Expected number of words in the whole dsp code,INCL header */
|
||||
long int block_length;
|
||||
/** Number of words read so far */
|
||||
long int word_count;
|
||||
/** Version read from dsp code file */
|
||||
u32 version;
|
||||
/** CRC read from dsp code file */
|
||||
u32 crc;
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PRAGMA_PACK1
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/** Prepare *psDspCode to refer to the requuested adapter.
|
||||
Searches the file, or selects the appropriate linked array
|
||||
|
||||
\return 0 for success, or error code if requested code is not available
|
||||
*/
|
||||
short hpi_dsp_code_open(
|
||||
/** Code identifier, usually adapter family */
|
||||
u32 adapter,
|
||||
/** Pointer to DSP code control structure */
|
||||
struct dsp_code *ps_dsp_code,
|
||||
/** Pointer to dword to receive OS specific error code */
|
||||
u32 *pos_error_code);
|
||||
|
||||
/** Close the DSP code file */
|
||||
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code);
|
||||
|
||||
/** Rewind to the beginning of the DSP code file (for verify) */
|
||||
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
|
||||
|
||||
/** Read one word from the dsp code file
|
||||
\return 0 for success, or error code if eof, or block length exceeded
|
||||
*/
|
||||
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
|
||||
/**< DSP code descriptor */
|
||||
u32 *pword /**< where to store the read word */
|
||||
);
|
||||
|
||||
/** Get a block of dsp code into an internal buffer, and provide a pointer to
|
||||
that buffer. (If dsp code is already an array in memory, it is referenced,
|
||||
not copied.)
|
||||
|
||||
\return Error if requested number of words are not available
|
||||
*/
|
||||
short hpi_dsp_code_read_block(size_t words_requested,
|
||||
struct dsp_code *ps_dsp_code,
|
||||
/* Pointer to store (Pointer to code buffer) */
|
||||
u32 **ppblock);
|
||||
|
||||
#endif
|
3864
sound/pci/asihpi/hpifunc.c
Normal file
3864
sound/pci/asihpi/hpifunc.c
Normal file
File diff suppressed because it is too large
Load Diff
130
sound/pci/asihpi/hpimsginit.c
Normal file
130
sound/pci/asihpi/hpimsginit.c
Normal file
@ -0,0 +1,130 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Hardware Programming Interface (HPI) Utility functions.
|
||||
|
||||
(C) Copyright AudioScience Inc. 2007
|
||||
*******************************************************************************/
|
||||
|
||||
#include "hpi_internal.h"
|
||||
#include "hpimsginit.h"
|
||||
|
||||
/* The actual message size for each object type */
|
||||
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
|
||||
/* The actual response size for each object type */
|
||||
static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
|
||||
/* Flag to enable alternate message type for SSX2 bypass. */
|
||||
static u16 gwSSX2_bypass;
|
||||
|
||||
/** \internal
|
||||
* Used by ASIO driver to disable SSX2 for a single process
|
||||
* \param phSubSys Pointer to HPI subsystem handle.
|
||||
* \param wBypass New bypass setting 0 = off, nonzero = on
|
||||
* \return Previous bypass setting.
|
||||
*/
|
||||
u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass)
|
||||
{
|
||||
u16 old_value = gwSSX2_bypass;
|
||||
|
||||
gwSSX2_bypass = bypass;
|
||||
|
||||
return old_value;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* initialize the HPI message structure
|
||||
*/
|
||||
static void hpi_init_message(struct hpi_message *phm, u16 object,
|
||||
u16 function)
|
||||
{
|
||||
memset(phm, 0, sizeof(*phm));
|
||||
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
|
||||
phm->size = msg_size[object];
|
||||
else
|
||||
phm->size = sizeof(*phm);
|
||||
|
||||
if (gwSSX2_bypass)
|
||||
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
|
||||
else
|
||||
phm->type = HPI_TYPE_MESSAGE;
|
||||
phm->object = object;
|
||||
phm->function = function;
|
||||
phm->version = 0;
|
||||
/* Expect adapter index to be set by caller */
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* initialize the HPI response structure
|
||||
*/
|
||||
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
|
||||
u16 error)
|
||||
{
|
||||
memset(phr, 0, sizeof(*phr));
|
||||
phr->type = HPI_TYPE_RESPONSE;
|
||||
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
|
||||
phr->size = res_size[object];
|
||||
else
|
||||
phr->size = sizeof(*phr);
|
||||
phr->object = object;
|
||||
phr->function = function;
|
||||
phr->error = error;
|
||||
phr->specific_error = 0;
|
||||
phr->version = 0;
|
||||
}
|
||||
|
||||
void hpi_init_message_response(struct hpi_message *phm,
|
||||
struct hpi_response *phr, u16 object, u16 function)
|
||||
{
|
||||
hpi_init_message(phm, object, function);
|
||||
/* default error return if the response is
|
||||
not filled in by the callee */
|
||||
hpi_init_response(phr, object, function,
|
||||
HPI_ERROR_PROCESSING_MESSAGE);
|
||||
}
|
||||
|
||||
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
|
||||
u16 object, u16 function)
|
||||
{
|
||||
memset(phm, 0, sizeof(*phm));
|
||||
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
|
||||
phm->size = size;
|
||||
phm->type = HPI_TYPE_MESSAGE;
|
||||
phm->object = object;
|
||||
phm->function = function;
|
||||
phm->version = 1;
|
||||
/* Expect adapter index to be set by caller */
|
||||
}
|
||||
}
|
||||
|
||||
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
|
||||
u16 object, u16 function)
|
||||
{
|
||||
memset(phr, 0, sizeof(*phr));
|
||||
phr->size = size;
|
||||
phr->version = 1;
|
||||
phr->type = HPI_TYPE_RESPONSE;
|
||||
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
|
||||
}
|
||||
|
||||
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
|
||||
struct hpi_response_header *phr, u16 res_size, u16 object,
|
||||
u16 function)
|
||||
{
|
||||
hpi_init_messageV1(phm, msg_size, object, function);
|
||||
hpi_init_responseV1(phr, res_size, object, function);
|
||||
}
|
40
sound/pci/asihpi/hpimsginit.h
Normal file
40
sound/pci/asihpi/hpimsginit.h
Normal file
@ -0,0 +1,40 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Hardware Programming Interface (HPI) Utility functions
|
||||
|
||||
(C) Copyright AudioScience Inc. 2007
|
||||
*******************************************************************************/
|
||||
/* Initialise response headers, or msg/response pairs.
|
||||
Note that it is valid to just init a response e.g. when a lower level is preparing
|
||||
a response to a message.
|
||||
However, when sending a message, a matching response buffer always must be prepared
|
||||
*/
|
||||
|
||||
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
|
||||
u16 error);
|
||||
|
||||
void hpi_init_message_response(struct hpi_message *phm,
|
||||
struct hpi_response *phr, u16 object, u16 function);
|
||||
|
||||
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
|
||||
u16 object, u16 function);
|
||||
|
||||
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
|
||||
struct hpi_response_header *phr, u16 res_size, u16 object,
|
||||
u16 function);
|
907
sound/pci/asihpi/hpimsgx.c
Normal file
907
sound/pci/asihpi/hpimsgx.c
Normal file
@ -0,0 +1,907 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Extended Message Function With Response Cacheing
|
||||
|
||||
(C) Copyright AudioScience Inc. 2002
|
||||
*****************************************************************************/
|
||||
#define SOURCEFILE_NAME "hpimsgx.c"
|
||||
#include "hpi_internal.h"
|
||||
#include "hpimsginit.h"
|
||||
#include "hpimsgx.h"
|
||||
#include "hpidebug.h"
|
||||
|
||||
static struct pci_device_id asihpi_pci_tbl[] = {
|
||||
#include "hpipcida.h"
|
||||
};
|
||||
|
||||
static struct hpios_spinlock msgx_lock;
|
||||
|
||||
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
|
||||
|
||||
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
|
||||
*pci_info)
|
||||
{
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
|
||||
if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
|
||||
&& asihpi_pci_tbl[i].vendor != pci_info->vendor_id)
|
||||
continue;
|
||||
if (asihpi_pci_tbl[i].device != PCI_ANY_ID
|
||||
&& asihpi_pci_tbl[i].device != pci_info->device_id)
|
||||
continue;
|
||||
if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
|
||||
&& asihpi_pci_tbl[i].subvendor !=
|
||||
pci_info->subsys_vendor_id)
|
||||
continue;
|
||||
if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
|
||||
&& asihpi_pci_tbl[i].subdevice !=
|
||||
pci_info->subsys_device_id)
|
||||
continue;
|
||||
|
||||
HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i,
|
||||
asihpi_pci_tbl[i].driver_data);
|
||||
return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void hw_entry_point(struct hpi_message *phm,
|
||||
struct hpi_response *phr)
|
||||
{
|
||||
|
||||
hpi_handler_func *ep;
|
||||
|
||||
if (phm->adapter_index < HPI_MAX_ADAPTERS) {
|
||||
ep = (hpi_handler_func *) hpi_entry_points[phm->
|
||||
adapter_index];
|
||||
if (ep) {
|
||||
HPI_DEBUG_MESSAGE(DEBUG, phm);
|
||||
ep(phm, phr);
|
||||
HPI_DEBUG_RESPONSE(phr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
hpi_init_response(phr, phm->object, phm->function,
|
||||
HPI_ERROR_PROCESSING_MESSAGE);
|
||||
}
|
||||
|
||||
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
|
||||
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr);
|
||||
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner);
|
||||
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner);
|
||||
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner);
|
||||
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner);
|
||||
|
||||
static void HPIMSGX__reset(u16 adapter_index);
|
||||
static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
|
||||
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
|
||||
|
||||
#ifndef DISABLE_PRAGMA_PACK1
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
struct hpi_subsys_response {
|
||||
struct hpi_response_header h;
|
||||
struct hpi_subsys_res s;
|
||||
};
|
||||
|
||||
struct hpi_adapter_response {
|
||||
struct hpi_response_header h;
|
||||
struct hpi_adapter_res a;
|
||||
};
|
||||
|
||||
struct hpi_mixer_response {
|
||||
struct hpi_response_header h;
|
||||
struct hpi_mixer_res m;
|
||||
};
|
||||
|
||||
struct hpi_stream_response {
|
||||
struct hpi_response_header h;
|
||||
struct hpi_stream_res d;
|
||||
};
|
||||
|
||||
struct adapter_info {
|
||||
u16 type;
|
||||
u16 num_instreams;
|
||||
u16 num_outstreams;
|
||||
};
|
||||
|
||||
struct asi_open_state {
|
||||
int open_flag;
|
||||
void *h_owner;
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PRAGMA_PACK1
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Globals */
|
||||
static struct hpi_adapter_response rESP_HPI_ADAPTER_OPEN[HPI_MAX_ADAPTERS];
|
||||
|
||||
static struct hpi_stream_response
|
||||
rESP_HPI_OSTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
|
||||
|
||||
static struct hpi_stream_response
|
||||
rESP_HPI_ISTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
|
||||
|
||||
static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
|
||||
|
||||
static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS;
|
||||
|
||||
static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
|
||||
|
||||
/* use these to keep track of opens from user mode apps/DLLs */
|
||||
static struct asi_open_state
|
||||
outstream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
|
||||
|
||||
static struct asi_open_state
|
||||
instream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
|
||||
|
||||
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
switch (phm->function) {
|
||||
case HPI_SUBSYS_GET_VERSION:
|
||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_GET_VERSION, 0);
|
||||
phr->u.s.version = HPI_VER >> 8; /* return major.minor */
|
||||
phr->u.s.data = HPI_VER; /* return major.minor.release */
|
||||
break;
|
||||
case HPI_SUBSYS_OPEN:
|
||||
/*do not propagate the message down the chain */
|
||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPEN, 0);
|
||||
break;
|
||||
case HPI_SUBSYS_CLOSE:
|
||||
/*do not propagate the message down the chain */
|
||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CLOSE,
|
||||
0);
|
||||
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
|
||||
break;
|
||||
case HPI_SUBSYS_DRIVER_LOAD:
|
||||
/* Initialize this module's internal state */
|
||||
hpios_msgxlock_init(&msgx_lock);
|
||||
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
|
||||
hpios_locked_mem_init();
|
||||
/* Init subsys_findadapters response to no-adapters */
|
||||
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
|
||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_DRIVER_LOAD, 0);
|
||||
/* individual HPIs dont implement driver load */
|
||||
HPI_COMMON(phm, phr);
|
||||
break;
|
||||
case HPI_SUBSYS_DRIVER_UNLOAD:
|
||||
HPI_COMMON(phm, phr);
|
||||
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
|
||||
hpios_locked_mem_free_all();
|
||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_DRIVER_UNLOAD, 0);
|
||||
return;
|
||||
|
||||
case HPI_SUBSYS_GET_INFO:
|
||||
HPI_COMMON(phm, phr);
|
||||
break;
|
||||
|
||||
case HPI_SUBSYS_FIND_ADAPTERS:
|
||||
memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
|
||||
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
|
||||
break;
|
||||
case HPI_SUBSYS_GET_NUM_ADAPTERS:
|
||||
memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
|
||||
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
|
||||
phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS;
|
||||
break;
|
||||
case HPI_SUBSYS_GET_ADAPTER:
|
||||
{
|
||||
int count = phm->adapter_index;
|
||||
int index = 0;
|
||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_GET_ADAPTER, 0);
|
||||
|
||||
/* This is complicated by the fact that we want to
|
||||
* "skip" 0's in the adapter list.
|
||||
* First, make sure we are pointing to a
|
||||
* non-zero adapter type.
|
||||
*/
|
||||
while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
|
||||
s.aw_adapter_list[index] == 0) {
|
||||
index++;
|
||||
if (index >= HPI_MAX_ADAPTERS)
|
||||
break;
|
||||
}
|
||||
while (count) {
|
||||
/* move on to the next adapter */
|
||||
index++;
|
||||
if (index >= HPI_MAX_ADAPTERS)
|
||||
break;
|
||||
while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
|
||||
s.aw_adapter_list[index] == 0) {
|
||||
index++;
|
||||
if (index >= HPI_MAX_ADAPTERS)
|
||||
break;
|
||||
}
|
||||
count--;
|
||||
}
|
||||
|
||||
if (index < HPI_MAX_ADAPTERS) {
|
||||
phr->u.s.adapter_index = (u16)index;
|
||||
phr->u.s.aw_adapter_list[0] =
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.
|
||||
s.aw_adapter_list[index];
|
||||
} else {
|
||||
phr->u.s.adapter_index = 0;
|
||||
phr->u.s.aw_adapter_list[0] = 0;
|
||||
phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||
HPIMSGX__init(phm, phr);
|
||||
break;
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
HPIMSGX__cleanup(phm->adapter_index, h_owner);
|
||||
{
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
/* call to HPI_ADAPTER_CLOSE */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_CLOSE);
|
||||
hm.adapter_index = phm->adapter_index;
|
||||
hw_entry_point(&hm, &hr);
|
||||
}
|
||||
hw_entry_point(phm, phr);
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.
|
||||
aw_adapter_list[phm->adapter_index]
|
||||
= 0;
|
||||
hpi_entry_points[phm->adapter_index] = NULL;
|
||||
break;
|
||||
default:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
switch (phm->function) {
|
||||
case HPI_ADAPTER_OPEN:
|
||||
adapter_open(phm, phr);
|
||||
break;
|
||||
case HPI_ADAPTER_CLOSE:
|
||||
adapter_close(phm, phr);
|
||||
break;
|
||||
default:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mixer_message(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
switch (phm->function) {
|
||||
case HPI_MIXER_OPEN:
|
||||
mixer_open(phm, phr);
|
||||
break;
|
||||
case HPI_MIXER_CLOSE:
|
||||
mixer_close(phm, phr);
|
||||
break;
|
||||
default:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void outstream_message(struct hpi_message *phm,
|
||||
struct hpi_response *phr, void *h_owner)
|
||||
{
|
||||
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_outstreams) {
|
||||
hpi_init_response(phr, HPI_OBJ_OSTREAM, phm->function,
|
||||
HPI_ERROR_INVALID_OBJ_INDEX);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phm->function) {
|
||||
case HPI_OSTREAM_OPEN:
|
||||
outstream_open(phm, phr, h_owner);
|
||||
break;
|
||||
case HPI_OSTREAM_CLOSE:
|
||||
outstream_close(phm, phr, h_owner);
|
||||
break;
|
||||
default:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void instream_message(struct hpi_message *phm,
|
||||
struct hpi_response *phr, void *h_owner)
|
||||
{
|
||||
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_instreams) {
|
||||
hpi_init_response(phr, HPI_OBJ_ISTREAM, phm->function,
|
||||
HPI_ERROR_INVALID_OBJ_INDEX);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phm->function) {
|
||||
case HPI_ISTREAM_OPEN:
|
||||
instream_open(phm, phr, h_owner);
|
||||
break;
|
||||
case HPI_ISTREAM_CLOSE:
|
||||
instream_close(phm, phr, h_owner);
|
||||
break;
|
||||
default:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: HPI_Message() must be defined in the driver as a wrapper for
|
||||
* HPI_MessageEx so that functions in hpifunc.c compile.
|
||||
*/
|
||||
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
HPI_DEBUG_MESSAGE(DEBUG, phm);
|
||||
|
||||
if (phm->type != HPI_TYPE_MESSAGE) {
|
||||
hpi_init_response(phr, phm->object, phm->function,
|
||||
HPI_ERROR_INVALID_TYPE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (phm->adapter_index >= HPI_MAX_ADAPTERS
|
||||
&& phm->adapter_index != HPIMSGX_ALLADAPTERS) {
|
||||
hpi_init_response(phr, phm->object, phm->function,
|
||||
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phm->object) {
|
||||
case HPI_OBJ_SUBSYSTEM:
|
||||
subsys_message(phm, phr, h_owner);
|
||||
break;
|
||||
|
||||
case HPI_OBJ_ADAPTER:
|
||||
adapter_message(phm, phr, h_owner);
|
||||
break;
|
||||
|
||||
case HPI_OBJ_MIXER:
|
||||
mixer_message(phm, phr);
|
||||
break;
|
||||
|
||||
case HPI_OBJ_OSTREAM:
|
||||
outstream_message(phm, phr, h_owner);
|
||||
break;
|
||||
|
||||
case HPI_OBJ_ISTREAM:
|
||||
instream_message(phm, phr, h_owner);
|
||||
break;
|
||||
|
||||
default:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
}
|
||||
HPI_DEBUG_RESPONSE(phr);
|
||||
#if 1
|
||||
if (phr->error >= HPI_ERROR_BACKEND_BASE) {
|
||||
void *ep = NULL;
|
||||
char *ep_name;
|
||||
|
||||
HPI_DEBUG_MESSAGE(ERROR, phm);
|
||||
|
||||
if (phm->adapter_index < HPI_MAX_ADAPTERS)
|
||||
ep = hpi_entry_points[phm->adapter_index];
|
||||
|
||||
/* Don't need this? Have adapter index in debug info
|
||||
Know at driver load time index->backend mapping */
|
||||
if (ep == HPI_6000)
|
||||
ep_name = "HPI_6000";
|
||||
else if (ep == HPI_6205)
|
||||
ep_name = "HPI_6205";
|
||||
else
|
||||
ep_name = "unknown";
|
||||
|
||||
HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name,
|
||||
phr->error);
|
||||
|
||||
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)
|
||||
hpi_debug_data((u16 *)phm,
|
||||
sizeof(*phm) / sizeof(u16));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
HPI_DEBUG_LOG(VERBOSE, "adapter_open\n");
|
||||
memcpy(phr, &rESP_HPI_ADAPTER_OPEN[phm->adapter_index],
|
||||
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
|
||||
}
|
||||
|
||||
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
HPI_DEBUG_LOG(VERBOSE, "adapter_close\n");
|
||||
hpi_init_response(phr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE, 0);
|
||||
}
|
||||
|
||||
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
memcpy(phr, &rESP_HPI_MIXER_OPEN[phm->adapter_index],
|
||||
sizeof(rESP_HPI_MIXER_OPEN[0]));
|
||||
}
|
||||
|
||||
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
hpi_init_response(phr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE, 0);
|
||||
}
|
||||
|
||||
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_OPEN, 0);
|
||||
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
|
||||
if (instream_user_open[phm->adapter_index][phm->obj_index].open_flag)
|
||||
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
|
||||
else if (rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
|
||||
[phm->obj_index].h.error)
|
||||
memcpy(phr,
|
||||
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index][phm->
|
||||
obj_index],
|
||||
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
|
||||
else {
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 1;
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
|
||||
/* issue a reset */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
|
||||
HPI_ISTREAM_RESET);
|
||||
hm.adapter_index = phm->adapter_index;
|
||||
hm.obj_index = phm->obj_index;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
if (hr.error) {
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 0;
|
||||
phr->error = hr.error;
|
||||
} else {
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 1;
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = h_owner;
|
||||
memcpy(phr,
|
||||
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
|
||||
[phm->obj_index],
|
||||
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
|
||||
}
|
||||
}
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
}
|
||||
|
||||
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE, 0);
|
||||
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
if (h_owner ==
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner) {
|
||||
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
|
||||
"instream %d owned by %p\n",
|
||||
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = NULL;
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
/* issue a reset */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
|
||||
HPI_ISTREAM_RESET);
|
||||
hm.adapter_index = phm->adapter_index;
|
||||
hm.obj_index = phm->obj_index;
|
||||
hw_entry_point(&hm, &hr);
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
if (hr.error) {
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = h_owner;
|
||||
phr->error = hr.error;
|
||||
} else {
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 0;
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = NULL;
|
||||
}
|
||||
} else {
|
||||
HPI_DEBUG_LOG(WARNING,
|
||||
"%p trying to close %d instream %d owned by %p\n",
|
||||
h_owner, phm->adapter_index, phm->obj_index,
|
||||
instream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner);
|
||||
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
|
||||
}
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
}
|
||||
|
||||
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_OPEN, 0);
|
||||
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
|
||||
if (outstream_user_open[phm->adapter_index][phm->obj_index].open_flag)
|
||||
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
|
||||
else if (rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
|
||||
[phm->obj_index].h.error)
|
||||
memcpy(phr,
|
||||
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index][phm->
|
||||
obj_index],
|
||||
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
|
||||
else {
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 1;
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
|
||||
/* issue a reset */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
|
||||
HPI_OSTREAM_RESET);
|
||||
hm.adapter_index = phm->adapter_index;
|
||||
hm.obj_index = phm->obj_index;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
if (hr.error) {
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 0;
|
||||
phr->error = hr.error;
|
||||
} else {
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 1;
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = h_owner;
|
||||
memcpy(phr,
|
||||
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
|
||||
[phm->obj_index],
|
||||
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
|
||||
}
|
||||
}
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
}
|
||||
|
||||
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner)
|
||||
{
|
||||
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE, 0);
|
||||
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
|
||||
if (h_owner ==
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner) {
|
||||
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
|
||||
"outstream %d owned by %p\n",
|
||||
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = NULL;
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
/* issue a reset */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
|
||||
HPI_OSTREAM_RESET);
|
||||
hm.adapter_index = phm->adapter_index;
|
||||
hm.obj_index = phm->obj_index;
|
||||
hw_entry_point(&hm, &hr);
|
||||
hpios_msgxlock_lock(&msgx_lock);
|
||||
if (hr.error) {
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = h_owner;
|
||||
phr->error = hr.error;
|
||||
} else {
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].open_flag = 0;
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner = NULL;
|
||||
}
|
||||
} else {
|
||||
HPI_DEBUG_LOG(WARNING,
|
||||
"%p trying to close %d outstream %d owned by %p\n",
|
||||
h_owner, phm->adapter_index, phm->obj_index,
|
||||
outstream_user_open[phm->adapter_index][phm->
|
||||
obj_index].h_owner);
|
||||
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
|
||||
}
|
||||
hpios_msgxlock_un_lock(&msgx_lock);
|
||||
}
|
||||
|
||||
static u16 adapter_prepare(u16 adapter)
|
||||
{
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
/* Open the adapter and streams */
|
||||
u16 i;
|
||||
|
||||
/* call to HPI_ADAPTER_OPEN */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_OPEN);
|
||||
hm.adapter_index = adapter;
|
||||
hw_entry_point(&hm, &hr);
|
||||
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
|
||||
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
|
||||
if (hr.error)
|
||||
return hr.error;
|
||||
|
||||
/* call to HPI_ADAPTER_GET_INFO */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_GET_INFO);
|
||||
hm.adapter_index = adapter;
|
||||
hw_entry_point(&hm, &hr);
|
||||
if (hr.error)
|
||||
return hr.error;
|
||||
|
||||
aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams;
|
||||
aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams;
|
||||
aDAPTER_INFO[adapter].type = hr.u.a.adapter_type;
|
||||
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] =
|
||||
hr.u.a.adapter_type;
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++;
|
||||
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS)
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters =
|
||||
HPI_MAX_ADAPTERS;
|
||||
|
||||
/* call to HPI_OSTREAM_OPEN */
|
||||
for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
|
||||
HPI_OSTREAM_OPEN);
|
||||
hm.adapter_index = adapter;
|
||||
hm.obj_index = i;
|
||||
hw_entry_point(&hm, &hr);
|
||||
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i], &hr,
|
||||
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
|
||||
outstream_user_open[adapter][i].open_flag = 0;
|
||||
outstream_user_open[adapter][i].h_owner = NULL;
|
||||
}
|
||||
|
||||
/* call to HPI_ISTREAM_OPEN */
|
||||
for (i = 0; i < aDAPTER_INFO[adapter].num_instreams; i++) {
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
|
||||
HPI_ISTREAM_OPEN);
|
||||
hm.adapter_index = adapter;
|
||||
hm.obj_index = i;
|
||||
hw_entry_point(&hm, &hr);
|
||||
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i], &hr,
|
||||
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
|
||||
instream_user_open[adapter][i].open_flag = 0;
|
||||
instream_user_open[adapter][i].h_owner = NULL;
|
||||
}
|
||||
|
||||
/* call to HPI_MIXER_OPEN */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN);
|
||||
hm.adapter_index = adapter;
|
||||
hw_entry_point(&hm, &hr);
|
||||
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
|
||||
sizeof(rESP_HPI_MIXER_OPEN[0]));
|
||||
|
||||
return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error;
|
||||
}
|
||||
|
||||
static void HPIMSGX__reset(u16 adapter_index)
|
||||
{
|
||||
int i;
|
||||
u16 adapter;
|
||||
struct hpi_response hr;
|
||||
|
||||
if (adapter_index == HPIMSGX_ALLADAPTERS) {
|
||||
/* reset all responses to contain errors */
|
||||
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_FIND_ADAPTERS, 0);
|
||||
memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
|
||||
sizeof(&gRESP_HPI_SUBSYS_FIND_ADAPTERS));
|
||||
|
||||
for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
|
||||
|
||||
hpi_init_response(&hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER);
|
||||
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
|
||||
sizeof(rESP_HPI_ADAPTER_OPEN[adapter]));
|
||||
|
||||
hpi_init_response(&hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN,
|
||||
HPI_ERROR_INVALID_OBJ);
|
||||
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
|
||||
sizeof(rESP_HPI_MIXER_OPEN[adapter]));
|
||||
|
||||
for (i = 0; i < HPI_MAX_STREAMS; i++) {
|
||||
hpi_init_response(&hr, HPI_OBJ_OSTREAM,
|
||||
HPI_OSTREAM_OPEN,
|
||||
HPI_ERROR_INVALID_OBJ);
|
||||
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i],
|
||||
&hr,
|
||||
sizeof(rESP_HPI_OSTREAM_OPEN[adapter]
|
||||
[i]));
|
||||
hpi_init_response(&hr, HPI_OBJ_ISTREAM,
|
||||
HPI_ISTREAM_OPEN,
|
||||
HPI_ERROR_INVALID_OBJ);
|
||||
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i],
|
||||
&hr,
|
||||
sizeof(rESP_HPI_ISTREAM_OPEN[adapter]
|
||||
[i]));
|
||||
}
|
||||
}
|
||||
} else if (adapter_index < HPI_MAX_ADAPTERS) {
|
||||
rESP_HPI_ADAPTER_OPEN[adapter_index].h.error =
|
||||
HPI_ERROR_BAD_ADAPTER;
|
||||
rESP_HPI_MIXER_OPEN[adapter_index].h.error =
|
||||
HPI_ERROR_INVALID_OBJ;
|
||||
for (i = 0; i < HPI_MAX_STREAMS; i++) {
|
||||
rESP_HPI_OSTREAM_OPEN[adapter_index][i].h.error =
|
||||
HPI_ERROR_INVALID_OBJ;
|
||||
rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
|
||||
HPI_ERROR_INVALID_OBJ;
|
||||
}
|
||||
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
|
||||
s.aw_adapter_list[adapter_index]) {
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.
|
||||
s.aw_adapter_list[adapter_index] = 0;
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u16 HPIMSGX__init(struct hpi_message *phm,
|
||||
/* HPI_SUBSYS_CREATE_ADAPTER structure with */
|
||||
/* resource list or NULL=find all */
|
||||
struct hpi_response *phr
|
||||
/* response from HPI_ADAPTER_GET_INFO */
|
||||
)
|
||||
{
|
||||
hpi_handler_func *entry_point_func;
|
||||
struct hpi_response hr;
|
||||
|
||||
if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS)
|
||||
return HPI_ERROR_BAD_ADAPTER_NUMBER;
|
||||
|
||||
/* Init response here so we can pass in previous adapter list */
|
||||
hpi_init_response(&hr, phm->object, phm->function,
|
||||
HPI_ERROR_INVALID_OBJ);
|
||||
memcpy(hr.u.s.aw_adapter_list,
|
||||
gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list,
|
||||
sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list));
|
||||
|
||||
entry_point_func =
|
||||
hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
|
||||
|
||||
if (entry_point_func) {
|
||||
HPI_DEBUG_MESSAGE(DEBUG, phm);
|
||||
entry_point_func(phm, &hr);
|
||||
} else {
|
||||
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
|
||||
return phr->error;
|
||||
}
|
||||
if (hr.error == 0) {
|
||||
/* the adapter was created succesfully
|
||||
save the mapping for future use */
|
||||
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
|
||||
/* prepare adapter (pre-open streams etc.) */
|
||||
HPI_DEBUG_LOG(DEBUG,
|
||||
"HPI_SUBSYS_CREATE_ADAPTER successful,"
|
||||
" preparing adapter\n");
|
||||
adapter_prepare(hr.u.s.adapter_index);
|
||||
}
|
||||
memcpy(phr, &hr, hr.size);
|
||||
return phr->error;
|
||||
}
|
||||
|
||||
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
|
||||
{
|
||||
int i, adapter, adapter_limit;
|
||||
|
||||
if (!h_owner)
|
||||
return;
|
||||
|
||||
if (adapter_index == HPIMSGX_ALLADAPTERS) {
|
||||
adapter = 0;
|
||||
adapter_limit = HPI_MAX_ADAPTERS;
|
||||
} else {
|
||||
adapter = adapter_index;
|
||||
adapter_limit = adapter + 1;
|
||||
}
|
||||
|
||||
for (; adapter < adapter_limit; adapter++) {
|
||||
/* printk(KERN_INFO "Cleanup adapter #%d\n",wAdapter); */
|
||||
for (i = 0; i < HPI_MAX_STREAMS; i++) {
|
||||
if (h_owner ==
|
||||
outstream_user_open[adapter][i].h_owner) {
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
HPI_DEBUG_LOG(DEBUG,
|
||||
"close adapter %d ostream %d\n",
|
||||
adapter, i);
|
||||
|
||||
hpi_init_message_response(&hm, &hr,
|
||||
HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET);
|
||||
hm.adapter_index = (u16)adapter;
|
||||
hm.obj_index = (u16)i;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
hm.function = HPI_OSTREAM_HOSTBUFFER_FREE;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
hm.function = HPI_OSTREAM_GROUP_RESET;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
outstream_user_open[adapter][i].open_flag = 0;
|
||||
outstream_user_open[adapter][i].h_owner =
|
||||
NULL;
|
||||
}
|
||||
if (h_owner == instream_user_open[adapter][i].h_owner) {
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
HPI_DEBUG_LOG(DEBUG,
|
||||
"close adapter %d istream %d\n",
|
||||
adapter, i);
|
||||
|
||||
hpi_init_message_response(&hm, &hr,
|
||||
HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET);
|
||||
hm.adapter_index = (u16)adapter;
|
||||
hm.obj_index = (u16)i;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
hm.function = HPI_ISTREAM_HOSTBUFFER_FREE;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
hm.function = HPI_ISTREAM_GROUP_RESET;
|
||||
hw_entry_point(&hm, &hr);
|
||||
|
||||
instream_user_open[adapter][i].open_flag = 0;
|
||||
instream_user_open[adapter][i].h_owner = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
sound/pci/asihpi/hpimsgx.h
Normal file
36
sound/pci/asihpi/hpimsgx.h
Normal file
@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
HPI Extended Message Handler Functions
|
||||
|
||||
(C) Copyright AudioScience Inc. 1997-2003
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HPIMSGX_H_
|
||||
#define _HPIMSGX_H_
|
||||
|
||||
#include "hpi_internal.h"
|
||||
|
||||
#define HPIMSGX_ALLADAPTERS (0xFFFF)
|
||||
|
||||
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
|
||||
void *h_owner);
|
||||
|
||||
#define HPI_MESSAGE_LOWER_LAYER hpi_send_recv_ex
|
||||
|
||||
#endif /* _HPIMSGX_H_ */
|
484
sound/pci/asihpi/hpioctl.c
Normal file
484
sound/pci/asihpi/hpioctl.c
Normal file
@ -0,0 +1,484 @@
|
||||
/*******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Common Linux HPI ioctl and module probe/remove functions
|
||||
*******************************************************************************/
|
||||
#define SOURCEFILE_NAME "hpioctl.c"
|
||||
|
||||
#include "hpi_internal.h"
|
||||
#include "hpimsginit.h"
|
||||
#include "hpidebug.h"
|
||||
#include "hpimsgx.h"
|
||||
#include "hpioctl.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#ifdef MODULE_FIRMWARE
|
||||
MODULE_FIRMWARE("asihpi/dsp5000.bin");
|
||||
MODULE_FIRMWARE("asihpi/dsp6200.bin");
|
||||
MODULE_FIRMWARE("asihpi/dsp6205.bin");
|
||||
MODULE_FIRMWARE("asihpi/dsp6400.bin");
|
||||
MODULE_FIRMWARE("asihpi/dsp6600.bin");
|
||||
MODULE_FIRMWARE("asihpi/dsp8700.bin");
|
||||
MODULE_FIRMWARE("asihpi/dsp8900.bin");
|
||||
#endif
|
||||
|
||||
static int prealloc_stream_buf;
|
||||
module_param(prealloc_stream_buf, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(prealloc_stream_buf,
|
||||
"preallocate size for per-adapter stream buffer");
|
||||
|
||||
/* Allow the debug level to be changed after module load.
|
||||
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
|
||||
*/
|
||||
module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
|
||||
|
||||
/* List of adapters found */
|
||||
static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];
|
||||
|
||||
/* Wrapper function to HPI_Message to enable dumping of the
|
||||
message and response types.
|
||||
*/
|
||||
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
|
||||
struct file *file)
|
||||
{
|
||||
int adapter = phm->adapter_index;
|
||||
|
||||
if ((adapter >= HPI_MAX_ADAPTERS || adapter < 0)
|
||||
&& (phm->object != HPI_OBJ_SUBSYSTEM))
|
||||
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
|
||||
else
|
||||
hpi_send_recv_ex(phm, phr, file);
|
||||
}
|
||||
|
||||
/* This is called from hpifunc.c functions, called by ALSA
|
||||
* (or other kernel process) In this case there is no file descriptor
|
||||
* available for the message cache code
|
||||
*/
|
||||
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
hpi_send_recv_f(phm, phr, HOWNER_KERNEL);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(hpi_send_recv);
|
||||
/* for radio-asihpi */
|
||||
|
||||
int asihpi_hpi_release(struct file *file)
|
||||
{
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */
|
||||
/* close the subsystem just in case the application forgot to. */
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_CLOSE);
|
||||
hpi_send_recv_ex(&hm, &hr, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hpi_ioctl_linux __user *phpi_ioctl_data;
|
||||
void __user *puhm;
|
||||
void __user *puhr;
|
||||
union hpi_message_buffer_v1 *hm;
|
||||
union hpi_response_buffer_v1 *hr;
|
||||
u16 res_max_size;
|
||||
u32 uncopied_bytes;
|
||||
struct hpi_adapter *pa = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (cmd != HPI_IOCTL_LINUX)
|
||||
return -EINVAL;
|
||||
|
||||
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
|
||||
hr = kmalloc(sizeof(*hr), GFP_KERNEL);
|
||||
if (!hm || !hr) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
|
||||
|
||||
/* Read the message and response pointers from user space. */
|
||||
get_user(puhm, &phpi_ioctl_data->phm);
|
||||
get_user(puhr, &phpi_ioctl_data->phr);
|
||||
|
||||
/* Now read the message size and data from user space. */
|
||||
get_user(hm->h.size, (u16 __user *)puhm);
|
||||
if (hm->h.size > sizeof(*hm))
|
||||
hm->h.size = sizeof(*hm);
|
||||
|
||||
/*printk(KERN_INFO "message size %d\n", hm->h.wSize); */
|
||||
|
||||
uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
|
||||
if (uncopied_bytes) {
|
||||
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
get_user(res_max_size, (u16 __user *)puhr);
|
||||
/* printk(KERN_INFO "user response size %d\n", res_max_size); */
|
||||
if (res_max_size < sizeof(struct hpi_response_header)) {
|
||||
HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size);
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pa = &adapters[hm->h.adapter_index];
|
||||
hr->h.size = 0;
|
||||
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
|
||||
switch (hm->h.function) {
|
||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
/* Application must not use these functions! */
|
||||
hr->h.size = sizeof(hr->h);
|
||||
hr->h.error = HPI_ERROR_INVALID_OPERATION;
|
||||
hr->h.function = hm->h.function;
|
||||
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
|
||||
if (uncopied_bytes)
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = 0;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
hpi_send_recv_f(&hm->m0, &hr->r0, file);
|
||||
}
|
||||
} else {
|
||||
u16 __user *ptr = NULL;
|
||||
u32 size = 0;
|
||||
|
||||
/* -1=no data 0=read from user mem, 1=write to user mem */
|
||||
int wrflag = -1;
|
||||
u32 adapter = hm->h.adapter_index;
|
||||
|
||||
if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
|
||||
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_OPEN,
|
||||
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||
|
||||
uncopied_bytes =
|
||||
copy_to_user(puhr, hr, sizeof(hr->h));
|
||||
if (uncopied_bytes)
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
|
||||
err = -EINTR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Dig out any pointers embedded in the message. */
|
||||
switch (hm->h.function) {
|
||||
case HPI_OSTREAM_WRITE:
|
||||
case HPI_ISTREAM_READ:{
|
||||
/* Yes, sparse, this is correct. */
|
||||
ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data;
|
||||
size = hm->m0.u.d.u.data.data_size;
|
||||
|
||||
/* Allocate buffer according to application request.
|
||||
?Is it better to alloc/free for the duration
|
||||
of the transaction?
|
||||
*/
|
||||
if (pa->buffer_size < size) {
|
||||
HPI_DEBUG_LOG(DEBUG,
|
||||
"realloc adapter %d stream "
|
||||
"buffer from %zd to %d\n",
|
||||
hm->h.adapter_index,
|
||||
pa->buffer_size, size);
|
||||
if (pa->p_buffer) {
|
||||
pa->buffer_size = 0;
|
||||
vfree(pa->p_buffer);
|
||||
}
|
||||
pa->p_buffer = vmalloc(size);
|
||||
if (pa->p_buffer)
|
||||
pa->buffer_size = size;
|
||||
else {
|
||||
HPI_DEBUG_LOG(ERROR,
|
||||
"HPI could not allocate "
|
||||
"stream buffer size %d\n",
|
||||
size);
|
||||
|
||||
mutex_unlock(&adapters
|
||||
[adapter].mutex);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
hm->m0.u.d.u.data.pb_data = pa->p_buffer;
|
||||
if (hm->h.function == HPI_ISTREAM_READ)
|
||||
/* from card, WRITE to user mem */
|
||||
wrflag = 1;
|
||||
else
|
||||
wrflag = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (size && (wrflag == 0)) {
|
||||
uncopied_bytes =
|
||||
copy_from_user(pa->p_buffer, ptr, size);
|
||||
if (uncopied_bytes)
|
||||
HPI_DEBUG_LOG(WARNING,
|
||||
"missed %d of %d "
|
||||
"bytes from user\n", uncopied_bytes,
|
||||
size);
|
||||
}
|
||||
|
||||
hpi_send_recv_f(&hm->m0, &hr->r0, file);
|
||||
|
||||
if (size && (wrflag == 1)) {
|
||||
uncopied_bytes =
|
||||
copy_to_user(ptr, pa->p_buffer, size);
|
||||
if (uncopied_bytes)
|
||||
HPI_DEBUG_LOG(WARNING,
|
||||
"missed %d of %d " "bytes to user\n",
|
||||
uncopied_bytes, size);
|
||||
}
|
||||
|
||||
mutex_unlock(&adapters[adapter].mutex);
|
||||
}
|
||||
|
||||
/* on return response size must be set */
|
||||
/*printk(KERN_INFO "response size %d\n", hr->h.wSize); */
|
||||
|
||||
if (!hr->h.size) {
|
||||
HPI_DEBUG_LOG(ERROR, "response zero size\n");
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hr->h.size > res_max_size) {
|
||||
HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
|
||||
res_max_size);
|
||||
/*HPI_DEBUG_MESSAGE(ERROR, hm); */
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
|
||||
if (uncopied_bytes) {
|
||||
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(hm);
|
||||
kfree(hr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
int err, idx, nm;
|
||||
unsigned int memlen;
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
struct hpi_adapter adapter;
|
||||
struct hpi_pci pci;
|
||||
|
||||
memset(&adapter, 0, sizeof(adapter));
|
||||
|
||||
printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n",
|
||||
pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor,
|
||||
pci_dev->subsystem_device, pci_dev->devfn);
|
||||
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_CREATE_ADAPTER);
|
||||
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
|
||||
HPI_ERROR_PROCESSING_MESSAGE);
|
||||
|
||||
hm.adapter_index = -1; /* an invalid index */
|
||||
|
||||
/* fill in HPI_PCI information from kernel provided information */
|
||||
adapter.pci = pci_dev;
|
||||
|
||||
nm = HPI_MAX_ADAPTER_MEM_SPACES;
|
||||
|
||||
for (idx = 0; idx < nm; idx++) {
|
||||
HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
|
||||
idx, pci_dev->resource[idx].name,
|
||||
(unsigned long long)pci_resource_start(pci_dev, idx),
|
||||
(unsigned long long)pci_resource_end(pci_dev, idx),
|
||||
(unsigned long long)pci_resource_flags(pci_dev, idx));
|
||||
|
||||
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
|
||||
memlen = pci_resource_len(pci_dev, idx);
|
||||
adapter.ap_remapped_mem_base[idx] =
|
||||
ioremap(pci_resource_start(pci_dev, idx),
|
||||
memlen);
|
||||
if (!adapter.ap_remapped_mem_base[idx]) {
|
||||
HPI_DEBUG_LOG(ERROR,
|
||||
"ioremap failed, aborting\n");
|
||||
/* unmap previously mapped pci mem space */
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx];
|
||||
}
|
||||
|
||||
/* could replace Pci with direct pointer to pci_dev for linux
|
||||
Instead wrap accessor functions for IDs etc.
|
||||
Would it work for windows?
|
||||
*/
|
||||
pci.bus_number = pci_dev->bus->number;
|
||||
pci.vendor_id = (u16)pci_dev->vendor;
|
||||
pci.device_id = (u16)pci_dev->device;
|
||||
pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff);
|
||||
pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff);
|
||||
pci.device_number = pci_dev->devfn;
|
||||
pci.interrupt = pci_dev->irq;
|
||||
pci.p_os_data = pci_dev;
|
||||
|
||||
hm.u.s.resource.bus_type = HPI_BUS_PCI;
|
||||
hm.u.s.resource.r.pci = &pci;
|
||||
|
||||
/* call CreateAdapterObject on the relevant hpi module */
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
if (hr.error)
|
||||
goto err;
|
||||
|
||||
if (prealloc_stream_buf) {
|
||||
adapter.p_buffer = vmalloc(prealloc_stream_buf);
|
||||
if (!adapter.p_buffer) {
|
||||
HPI_DEBUG_LOG(ERROR,
|
||||
"HPI could not allocate "
|
||||
"kernel buffer size %d\n",
|
||||
prealloc_stream_buf);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
adapter.index = hr.u.s.adapter_index;
|
||||
adapter.type = hr.u.s.aw_adapter_list[adapter.index];
|
||||
hm.adapter_index = adapter.index;
|
||||
|
||||
err = hpi_adapter_open(NULL, adapter.index);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
adapter.snd_card_asihpi = NULL;
|
||||
/* WARNING can't init mutex in 'adapter'
|
||||
* and then copy it to adapters[] ?!?!
|
||||
*/
|
||||
adapters[hr.u.s.adapter_index] = adapter;
|
||||
mutex_init(&adapters[adapter.index].mutex);
|
||||
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
|
||||
|
||||
printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n",
|
||||
adapter.type, adapter.index);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
|
||||
if (adapter.ap_remapped_mem_base[idx]) {
|
||||
iounmap(adapter.ap_remapped_mem_base[idx]);
|
||||
adapter.ap_remapped_mem_base[idx] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.p_buffer) {
|
||||
adapter.buffer_size = 0;
|
||||
vfree(adapter.p_buffer);
|
||||
}
|
||||
|
||||
HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
|
||||
{
|
||||
int idx;
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
struct hpi_adapter *pa;
|
||||
pa = (struct hpi_adapter *)pci_get_drvdata(pci_dev);
|
||||
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_DELETE_ADAPTER);
|
||||
hm.adapter_index = pa->index;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
|
||||
/* unmap PCI memory space, mapped during device init. */
|
||||
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
|
||||
if (pa->ap_remapped_mem_base[idx]) {
|
||||
iounmap(pa->ap_remapped_mem_base[idx]);
|
||||
pa->ap_remapped_mem_base[idx] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pa->p_buffer) {
|
||||
pa->buffer_size = 0;
|
||||
vfree(pa->p_buffer);
|
||||
}
|
||||
|
||||
pci_set_drvdata(pci_dev, NULL);
|
||||
/*
|
||||
printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x),"
|
||||
" HPI index # %d, removed.\n",
|
||||
pci_dev->vendor, pci_dev->device,
|
||||
pci_dev->subsystem_vendor,
|
||||
pci_dev->subsystem_device, pci_dev->devfn,
|
||||
pa->index);
|
||||
*/
|
||||
}
|
||||
|
||||
void __init asihpi_init(void)
|
||||
{
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
memset(adapters, 0, sizeof(adapters));
|
||||
|
||||
printk(KERN_INFO "ASIHPI driver %d.%02d.%02d\n",
|
||||
HPI_VER_MAJOR(HPI_VER), HPI_VER_MINOR(HPI_VER),
|
||||
HPI_VER_RELEASE(HPI_VER));
|
||||
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_DRIVER_LOAD);
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
}
|
||||
|
||||
void asihpi_exit(void)
|
||||
{
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_DRIVER_UNLOAD);
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
}
|
38
sound/pci/asihpi/hpioctl.h
Normal file
38
sound/pci/asihpi/hpioctl.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Linux HPI ioctl, and shared module init functions
|
||||
*******************************************************************************/
|
||||
|
||||
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *pci_id);
|
||||
void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev);
|
||||
void __init asihpi_init(void);
|
||||
void __exit asihpi_exit(void);
|
||||
|
||||
int asihpi_hpi_release(struct file *file);
|
||||
|
||||
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
/* This is called from hpifunc.c functions, called by ALSA
|
||||
* (or other kernel process) In this case there is no file descriptor
|
||||
* available for the message cache code
|
||||
*/
|
||||
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
#define HOWNER_KERNEL ((void *)-1)
|
114
sound/pci/asihpi/hpios.c
Normal file
114
sound/pci/asihpi/hpios.c
Normal file
@ -0,0 +1,114 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
HPI Operating System function implementation for Linux
|
||||
|
||||
(C) Copyright AudioScience Inc. 1997-2003
|
||||
******************************************************************************/
|
||||
#define SOURCEFILE_NAME "hpios.c"
|
||||
#include "hpi_internal.h"
|
||||
#include "hpidebug.h"
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
void hpios_delay_micro_seconds(u32 num_micro_sec)
|
||||
{
|
||||
if ((usecs_to_jiffies(num_micro_sec) > 1) && !in_interrupt()) {
|
||||
/* MUST NOT SCHEDULE IN INTERRUPT CONTEXT! */
|
||||
schedule_timeout_uninterruptible(usecs_to_jiffies
|
||||
(num_micro_sec));
|
||||
} else if (num_micro_sec <= 2000)
|
||||
udelay(num_micro_sec);
|
||||
else
|
||||
mdelay(num_micro_sec / 1000);
|
||||
|
||||
}
|
||||
|
||||
void hpios_locked_mem_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
/** Allocated an area of locked memory for bus master DMA operations.
|
||||
|
||||
On error, return -ENOMEM, and *pMemArea.size = 0
|
||||
*/
|
||||
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
|
||||
struct pci_dev *pdev)
|
||||
{
|
||||
/*?? any benefit in using managed dmam_alloc_coherent? */
|
||||
p_mem_area->vaddr =
|
||||
dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle,
|
||||
GFP_DMA32 | GFP_KERNEL);
|
||||
|
||||
if (p_mem_area->vaddr) {
|
||||
HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n",
|
||||
size, (unsigned int)p_mem_area->dma_handle,
|
||||
p_mem_area->vaddr);
|
||||
p_mem_area->pdev = &pdev->dev;
|
||||
p_mem_area->size = size;
|
||||
return 0;
|
||||
} else {
|
||||
HPI_DEBUG_LOG(WARNING,
|
||||
"failed to allocate %d bytes locked memory\n", size);
|
||||
p_mem_area->size = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
|
||||
{
|
||||
if (p_mem_area->size) {
|
||||
dma_free_coherent(p_mem_area->pdev, p_mem_area->size,
|
||||
p_mem_area->vaddr, p_mem_area->dma_handle);
|
||||
HPI_DEBUG_LOG(DEBUG, "freed %lu bytes, dma 0x%x vma %p\n",
|
||||
(unsigned long)p_mem_area->size,
|
||||
(unsigned int)p_mem_area->dma_handle,
|
||||
p_mem_area->vaddr);
|
||||
p_mem_area->size = 0;
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void hpios_locked_mem_free_all(void)
|
||||
{
|
||||
}
|
||||
|
||||
void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
|
||||
unsigned int length)
|
||||
{
|
||||
HPI_DEBUG_LOG(DEBUG, "mapping %d %s %08llx-%08llx %04llx len 0x%x\n",
|
||||
idx, pci_dev->resource[idx].name,
|
||||
(unsigned long long)pci_resource_start(pci_dev, idx),
|
||||
(unsigned long long)pci_resource_end(pci_dev, idx),
|
||||
(unsigned long long)pci_resource_flags(pci_dev, idx), length);
|
||||
|
||||
if (!(pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM)) {
|
||||
HPI_DEBUG_LOG(ERROR, "not an io memory resource\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (length > pci_resource_len(pci_dev, idx)) {
|
||||
HPI_DEBUG_LOG(ERROR, "resource too small for requested %d \n",
|
||||
length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ioremap(pci_resource_start(pci_dev, idx), length);
|
||||
}
|
178
sound/pci/asihpi/hpios.h
Normal file
178
sound/pci/asihpi/hpios.h
Normal file
@ -0,0 +1,178 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
HPI Operating System Specific macros for Linux Kernel driver
|
||||
|
||||
(C) Copyright AudioScience Inc. 1997-2003
|
||||
******************************************************************************/
|
||||
#ifndef _HPIOS_H_
|
||||
#define _HPIOS_H_
|
||||
|
||||
#undef HPI_OS_LINUX_KERNEL
|
||||
#define HPI_OS_LINUX_KERNEL
|
||||
|
||||
#define HPI_OS_DEFINED
|
||||
#define HPI_KERNEL_MODE
|
||||
|
||||
#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#define HPI_NO_OS_FILE_OPS
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#define HPI64BIT
|
||||
#endif
|
||||
|
||||
/** Details of a memory area allocated with pci_alloc_consistent
|
||||
Need all info for parameters to pci_free_consistent
|
||||
*/
|
||||
struct consistent_dma_area {
|
||||
struct device *pdev;
|
||||
/* looks like dma-mapping dma_devres ?! */
|
||||
size_t size;
|
||||
void *vaddr;
|
||||
dma_addr_t dma_handle;
|
||||
};
|
||||
|
||||
static inline u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
|
||||
*locked_mem_handle, u32 *p_physical_addr)
|
||||
{
|
||||
*p_physical_addr = locked_mem_handle->dma_handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area
|
||||
*locked_mem_handle, void **pp_virtual_addr)
|
||||
{
|
||||
*pp_virtual_addr = locked_mem_handle->vaddr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u16 hpios_locked_mem_valid(struct consistent_dma_area
|
||||
*locked_mem_handle)
|
||||
{
|
||||
return locked_mem_handle->size != 0;
|
||||
}
|
||||
|
||||
struct hpi_ioctl_linux {
|
||||
void __user *phm;
|
||||
void __user *phr;
|
||||
};
|
||||
|
||||
/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
|
||||
and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
|
||||
*/
|
||||
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)
|
||||
|
||||
#define HPI_DEBUG_FLAG_ERROR KERN_ERR
|
||||
#define HPI_DEBUG_FLAG_WARNING KERN_WARNING
|
||||
#define HPI_DEBUG_FLAG_NOTICE KERN_NOTICE
|
||||
#define HPI_DEBUG_FLAG_INFO KERN_INFO
|
||||
#define HPI_DEBUG_FLAG_DEBUG KERN_DEBUG
|
||||
#define HPI_DEBUG_FLAG_VERBOSE KERN_DEBUG /* kernel has no verbose */
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define HPI_LOCKING
|
||||
|
||||
struct hpios_spinlock {
|
||||
spinlock_t lock; /* SEE hpios_spinlock */
|
||||
int lock_context;
|
||||
};
|
||||
|
||||
/* The reason for all this evilness is that ALSA calls some of a drivers
|
||||
* operators in atomic context, and some not. But all our functions channel
|
||||
* through the HPI_Message conduit, so we can't handle the different context
|
||||
* per function
|
||||
*/
|
||||
#define IN_LOCK_BH 1
|
||||
#define IN_LOCK_IRQ 0
|
||||
static inline void cond_lock(struct hpios_spinlock *l)
|
||||
{
|
||||
if (irqs_disabled()) {
|
||||
/* NO bh or isr can execute on this processor,
|
||||
so ordinary lock will do
|
||||
*/
|
||||
spin_lock(&((l)->lock));
|
||||
l->lock_context = IN_LOCK_IRQ;
|
||||
} else {
|
||||
spin_lock_bh(&((l)->lock));
|
||||
l->lock_context = IN_LOCK_BH;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cond_unlock(struct hpios_spinlock *l)
|
||||
{
|
||||
if (l->lock_context == IN_LOCK_BH)
|
||||
spin_unlock_bh(&((l)->lock));
|
||||
else
|
||||
spin_unlock(&((l)->lock));
|
||||
}
|
||||
|
||||
#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock)
|
||||
#define hpios_msgxlock_lock(obj) cond_lock(obj)
|
||||
#define hpios_msgxlock_un_lock(obj) cond_unlock(obj)
|
||||
|
||||
#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock)
|
||||
#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock)
|
||||
#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock)
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
#define HPI_DEBUG
|
||||
#endif
|
||||
|
||||
#define HPI_ALIST_LOCKING
|
||||
#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock))
|
||||
#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
|
||||
#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock))
|
||||
|
||||
struct hpi_adapter {
|
||||
/* mutex prevents contention for one card
|
||||
between multiple user programs (via ioctl) */
|
||||
struct mutex mutex;
|
||||
u16 index;
|
||||
u16 type;
|
||||
|
||||
/* ALSA card structure */
|
||||
void *snd_card_asihpi;
|
||||
|
||||
char *p_buffer;
|
||||
size_t buffer_size;
|
||||
struct pci_dev *pci;
|
||||
void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
|
||||
};
|
||||
|
||||
static inline void hpios_unmap_io(void __iomem *addr,
|
||||
unsigned long size)
|
||||
{
|
||||
iounmap(addr);
|
||||
}
|
||||
|
||||
void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
|
||||
unsigned int length);
|
||||
|
||||
#endif
|
37
sound/pci/asihpi/hpipcida.h
Normal file
37
sound/pci/asihpi/hpipcida.h
Normal file
@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
|
||||
AudioScience HPI driver
|
||||
Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of version 2 of the GNU General Public License as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Array initializer for PCI card IDs
|
||||
|
||||
(C) Copyright AudioScience Inc. 1998-2003
|
||||
*******************************************************************************/
|
||||
|
||||
/*NOTE: when adding new lines to this header file
|
||||
they MUST be grouped by HPI entry point.
|
||||
*/
|
||||
|
||||
{
|
||||
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
|
||||
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
|
||||
(kernel_ulong_t) HPI_6205}
|
||||
, {
|
||||
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
|
||||
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
|
||||
(kernel_ulong_t) HPI_6000}
|
||||
, {
|
||||
0}
|
@ -1139,40 +1139,28 @@ static void snd_cs4281_proc_read(struct snd_info_entry *entry,
|
||||
snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq);
|
||||
}
|
||||
|
||||
static long snd_cs4281_BA0_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_cs4281_BA0_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
long size;
|
||||
struct cs4281 *chip = entry->private_data;
|
||||
|
||||
size = count;
|
||||
if (pos + size > CS4281_BA0_SIZE)
|
||||
size = (long)CS4281_BA0_SIZE - pos;
|
||||
if (size > 0) {
|
||||
if (copy_to_user_fromio(buf, chip->ba0 + pos, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return size;
|
||||
if (copy_to_user_fromio(buf, chip->ba0 + pos, count))
|
||||
return -EFAULT;
|
||||
return count;
|
||||
}
|
||||
|
||||
static long snd_cs4281_BA1_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
long size;
|
||||
struct cs4281 *chip = entry->private_data;
|
||||
|
||||
size = count;
|
||||
if (pos + size > CS4281_BA1_SIZE)
|
||||
size = (long)CS4281_BA1_SIZE - pos;
|
||||
if (size > 0) {
|
||||
if (copy_to_user_fromio(buf, chip->ba1 + pos, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return size;
|
||||
if (copy_to_user_fromio(buf, chip->ba1 + pos, count))
|
||||
return -EFAULT;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
|
||||
|
@ -2657,21 +2657,16 @@ static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
|
||||
* proc interface
|
||||
*/
|
||||
|
||||
static long snd_cs46xx_io_read(struct snd_info_entry *entry, void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
long size;
|
||||
struct snd_cs46xx_region *region = entry->private_data;
|
||||
|
||||
size = count;
|
||||
if (pos + (size_t)size > region->size)
|
||||
size = region->size - pos;
|
||||
if (size > 0) {
|
||||
if (copy_to_user_fromio(buf, region->remap_addr + pos, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return size;
|
||||
if (copy_to_user_fromio(buf, region->remap_addr + pos, count))
|
||||
return -EFAULT;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
|
||||
|
@ -341,15 +341,17 @@ static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
|
||||
#define TOTAL_SIZE_CODE (0x200*8)
|
||||
#define A_TOTAL_SIZE_CODE (0x400*8)
|
||||
|
||||
static long snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
unsigned long count, unsigned long pos)
|
||||
static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
long size;
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned int offset;
|
||||
int tram_addr = 0;
|
||||
unsigned int *tmp;
|
||||
long res;
|
||||
unsigned int idx;
|
||||
|
||||
if (!strcmp(entry->name, "fx8010_tram_addr")) {
|
||||
offset = TANKMEMADDRREGBASE;
|
||||
@ -361,30 +363,25 @@ static long snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
|
||||
} else {
|
||||
offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
|
||||
}
|
||||
size = count;
|
||||
if (pos + size > entry->size)
|
||||
size = (long)entry->size - pos;
|
||||
if (size > 0) {
|
||||
unsigned int *tmp;
|
||||
long res;
|
||||
unsigned int idx;
|
||||
if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
for (idx = 0; idx < ((pos & 3) + size + 3) >> 2; idx++)
|
||||
if (tram_addr && emu->audigy) {
|
||||
tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0) >> 11;
|
||||
tmp[idx] |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20;
|
||||
} else
|
||||
tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
|
||||
if (copy_to_user(buf, ((char *)tmp) + (pos & 3), size))
|
||||
res = -EFAULT;
|
||||
else {
|
||||
res = size;
|
||||
|
||||
tmp = kmalloc(count + 8, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
for (idx = 0; idx < ((pos & 3) + count + 3) >> 2; idx++) {
|
||||
unsigned int val;
|
||||
val = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
|
||||
if (tram_addr && emu->audigy) {
|
||||
val >>= 11;
|
||||
val |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20;
|
||||
}
|
||||
kfree(tmp);
|
||||
return res;
|
||||
tmp[idx] = val;
|
||||
}
|
||||
return 0;
|
||||
if (copy_to_user(buf, ((char *)tmp) + (pos & 3), count))
|
||||
res = -EFAULT;
|
||||
else
|
||||
res = count;
|
||||
kfree(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry,
|
||||
|
@ -104,6 +104,7 @@
|
||||
#include <linux/gameport.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
@ -517,14 +518,9 @@ struct es1968 {
|
||||
|
||||
/* ALSA Stuff */
|
||||
struct snd_ac97 *ac97;
|
||||
struct snd_kcontrol *master_switch; /* for h/w volume control */
|
||||
struct snd_kcontrol *master_volume;
|
||||
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
||||
spinlock_t reg_lock;
|
||||
spinlock_t ac97_lock;
|
||||
struct tasklet_struct hwvol_tq;
|
||||
unsigned int in_suspend;
|
||||
|
||||
/* Maestro Stuff */
|
||||
@ -547,6 +543,16 @@ struct es1968 {
|
||||
#ifdef SUPPORT_JOYSTICK
|
||||
struct gameport *gameport;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_ES1968_INPUT
|
||||
struct input_dev *input_dev;
|
||||
char phys[64]; /* physical device path */
|
||||
#else
|
||||
struct snd_kcontrol *master_switch; /* for h/w volume control */
|
||||
struct snd_kcontrol *master_volume;
|
||||
spinlock_t ac97_lock;
|
||||
struct tasklet_struct hwvol_tq;
|
||||
#endif
|
||||
};
|
||||
|
||||
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
|
||||
@ -632,28 +638,38 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip)
|
||||
static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
|
||||
{
|
||||
struct es1968 *chip = ac97->private_data;
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
|
||||
snd_es1968_ac97_wait(chip);
|
||||
|
||||
/* Write the bus */
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
spin_lock_irqsave(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
outw(val, chip->io_port + ESM_AC97_DATA);
|
||||
/*msleep(1);*/
|
||||
outb(reg, chip->io_port + ESM_AC97_INDEX);
|
||||
/*msleep(1);*/
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
spin_unlock_irqrestore(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
|
||||
{
|
||||
u16 data = 0;
|
||||
struct es1968 *chip = ac97->private_data;
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
|
||||
snd_es1968_ac97_wait(chip);
|
||||
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
spin_lock_irqsave(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
|
||||
/*msleep(1);*/
|
||||
|
||||
@ -661,7 +677,9 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short
|
||||
data = inw(chip->io_port + ESM_AC97_DATA);
|
||||
/*msleep(1);*/
|
||||
}
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
spin_unlock_irqrestore(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -1874,13 +1892,17 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
/* The hardware volume works by incrementing / decrementing 2 counters
|
||||
(without wrap around) in response to volume button presses and then
|
||||
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
|
||||
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
|
||||
static void es1968_update_hw_volume(unsigned long private_data)
|
||||
{
|
||||
struct es1968 *chip = (struct es1968 *) private_data;
|
||||
int x, val;
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
|
||||
/* Figure out which volume control button was pushed,
|
||||
based on differences from the default register
|
||||
@ -1895,6 +1917,7 @@ static void es1968_update_hw_volume(unsigned long private_data)
|
||||
if (chip->in_suspend)
|
||||
return;
|
||||
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
if (! chip->master_switch || ! chip->master_volume)
|
||||
return;
|
||||
|
||||
@ -1937,6 +1960,35 @@ static void es1968_update_hw_volume(unsigned long private_data)
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->ac97_lock, flags);
|
||||
#else
|
||||
if (!chip->input_dev)
|
||||
return;
|
||||
|
||||
val = 0;
|
||||
switch (x) {
|
||||
case 0x88:
|
||||
/* The counters have not changed, yet we've received a HV
|
||||
interrupt. According to tests run by various people this
|
||||
happens when pressing the mute button. */
|
||||
val = KEY_MUTE;
|
||||
break;
|
||||
case 0xaa:
|
||||
/* counters increased by 1 -> volume up */
|
||||
val = KEY_VOLUMEUP;
|
||||
break;
|
||||
case 0x66:
|
||||
/* counters decreased by 1 -> volume down */
|
||||
val = KEY_VOLUMEDOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
input_report_key(chip->input_dev, val, 1);
|
||||
input_sync(chip->input_dev);
|
||||
input_report_key(chip->input_dev, val, 0);
|
||||
input_sync(chip->input_dev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1953,7 +2005,11 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
|
||||
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
|
||||
|
||||
if (event & ESM_HWVOL_IRQ)
|
||||
#ifdef CONFIG_SND_ES1968_INPUT
|
||||
es1968_update_hw_volume((unsigned long)chip);
|
||||
#else
|
||||
tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
|
||||
#endif
|
||||
|
||||
/* else ack 'em all, i imagine */
|
||||
outb(0xFF, chip->io_port + 0x1A);
|
||||
@ -1993,7 +2049,9 @@ snd_es1968_mixer(struct es1968 *chip)
|
||||
{
|
||||
struct snd_ac97_bus *pbus;
|
||||
struct snd_ac97_template ac97;
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
struct snd_ctl_elem_id elem_id;
|
||||
#endif
|
||||
int err;
|
||||
static struct snd_ac97_bus_ops ops = {
|
||||
.write = snd_es1968_ac97_write,
|
||||
@ -2009,6 +2067,7 @@ snd_es1968_mixer(struct es1968 *chip)
|
||||
if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
|
||||
return err;
|
||||
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
/* attach master switch / volumes for h/w volume control */
|
||||
memset(&elem_id, 0, sizeof(elem_id));
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
@ -2018,6 +2077,7 @@ snd_es1968_mixer(struct es1968 *chip)
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(elem_id.name, "Master Playback Volume");
|
||||
chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2341,6 +2401,7 @@ static void snd_es1968_start_irq(struct es1968 *chip)
|
||||
w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME;
|
||||
if (chip->rmidi)
|
||||
w |= ESM_HIRQ_MPU401;
|
||||
outb(w, chip->io_port + 0x1A);
|
||||
outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
|
||||
}
|
||||
|
||||
@ -2474,8 +2535,49 @@ static inline int snd_es1968_create_gameport(struct es1968 *chip, int dev) { ret
|
||||
static inline void snd_es1968_free_gameport(struct es1968 *chip) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_ES1968_INPUT
|
||||
static int __devinit snd_es1968_input_register(struct es1968 *chip)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0",
|
||||
pci_name(chip->pci));
|
||||
|
||||
input_dev->name = chip->card->driver;
|
||||
input_dev->phys = chip->phys;
|
||||
input_dev->id.bustype = BUS_PCI;
|
||||
input_dev->id.vendor = chip->pci->vendor;
|
||||
input_dev->id.product = chip->pci->device;
|
||||
input_dev->dev.parent = &chip->pci->dev;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(KEY_MUTE, input_dev->keybit);
|
||||
__set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
|
||||
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err) {
|
||||
input_free_device(input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->input_dev = input_dev;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SND_ES1968_INPUT */
|
||||
|
||||
static int snd_es1968_free(struct es1968 *chip)
|
||||
{
|
||||
#ifdef CONFIG_SND_ES1968_INPUT
|
||||
if (chip->input_dev)
|
||||
input_unregister_device(chip->input_dev);
|
||||
#endif
|
||||
|
||||
if (chip->io_port) {
|
||||
if (chip->irq >= 0)
|
||||
synchronize_irq(chip->irq);
|
||||
@ -2486,8 +2588,6 @@ static int snd_es1968_free(struct es1968 *chip)
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
snd_es1968_free_gameport(chip);
|
||||
chip->master_switch = NULL;
|
||||
chip->master_volume = NULL;
|
||||
pci_release_regions(chip->pci);
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
@ -2558,9 +2658,11 @@ static int __devinit snd_es1968_create(struct snd_card *card,
|
||||
spin_lock_init(&chip->substream_lock);
|
||||
INIT_LIST_HEAD(&chip->buf_list);
|
||||
INIT_LIST_HEAD(&chip->substream_list);
|
||||
spin_lock_init(&chip->ac97_lock);
|
||||
mutex_init(&chip->memory_mutex);
|
||||
#ifndef CONFIG_SND_ES1968_INPUT
|
||||
spin_lock_init(&chip->ac97_lock);
|
||||
tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
|
||||
#endif
|
||||
chip->card = card;
|
||||
chip->pci = pci;
|
||||
chip->irq = -1;
|
||||
@ -2713,6 +2815,13 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
|
||||
|
||||
snd_es1968_create_gameport(chip, dev);
|
||||
|
||||
#ifdef CONFIG_SND_ES1968_INPUT
|
||||
err = snd_es1968_input_register(chip);
|
||||
if (err)
|
||||
snd_printk(KERN_WARNING "Input device registration "
|
||||
"failed with error %i", err);
|
||||
#endif
|
||||
|
||||
snd_es1968_start_irq(chip);
|
||||
|
||||
chip->clock = clock[dev];
|
||||
|
@ -145,6 +145,7 @@ config SND_HDA_CODEC_NVHDMI
|
||||
|
||||
config SND_HDA_CODEC_INTELHDMI
|
||||
bool "Build INTEL HDMI HD-audio codec support"
|
||||
select SND_DYNAMIC_MINORS
|
||||
default y
|
||||
help
|
||||
Say Y here to include INTEL HDMI HD-audio codec support in
|
||||
|
@ -1209,8 +1209,7 @@ static void free_hda_cache(struct hda_cache_rec *cache)
|
||||
}
|
||||
|
||||
/* query the hash. allocate an entry if not found. */
|
||||
static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
|
||||
u32 key)
|
||||
static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
|
||||
{
|
||||
u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
|
||||
u16 cur = cache->hash[idx];
|
||||
@ -1222,17 +1221,27 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
|
||||
return info;
|
||||
cur = info->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* add a new hash entry */
|
||||
info = snd_array_new(&cache->buf);
|
||||
if (!info)
|
||||
return NULL;
|
||||
cur = snd_array_index(&cache->buf, info);
|
||||
info->key = key;
|
||||
info->val = 0;
|
||||
info->next = cache->hash[idx];
|
||||
cache->hash[idx] = cur;
|
||||
|
||||
/* query the hash. allocate an entry if not found. */
|
||||
static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
|
||||
u32 key)
|
||||
{
|
||||
struct hda_cache_head *info = get_hash(cache, key);
|
||||
if (!info) {
|
||||
u16 idx, cur;
|
||||
/* add a new hash entry */
|
||||
info = snd_array_new(&cache->buf);
|
||||
if (!info)
|
||||
return NULL;
|
||||
cur = snd_array_index(&cache->buf, info);
|
||||
info->key = key;
|
||||
info->val = 0;
|
||||
idx = key % (u16)ARRAY_SIZE(cache->hash);
|
||||
info->next = cache->hash[idx];
|
||||
cache->hash[idx] = cur;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@ -1461,6 +1470,8 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
|
||||
info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
|
||||
if (!info)
|
||||
return 0;
|
||||
if (snd_BUG_ON(mask & ~0xff))
|
||||
mask &= 0xff;
|
||||
val &= mask;
|
||||
val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
|
||||
if (info->vol[ch] == val)
|
||||
@ -1486,6 +1497,9 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direction, int idx, int mask, int val)
|
||||
{
|
||||
int ch, ret = 0;
|
||||
|
||||
if (snd_BUG_ON(mask & ~0xff))
|
||||
mask &= 0xff;
|
||||
for (ch = 0; ch < 2; ch++)
|
||||
ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
|
||||
idx, mask, val);
|
||||
@ -2716,6 +2730,41 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
|
||||
* @codec: the HDA codec
|
||||
* @nid: NID to send the command
|
||||
* @direct: direct flag
|
||||
* @verb: the verb to send
|
||||
* @parm: the parameter for the verb
|
||||
*
|
||||
* This function works like snd_hda_codec_write_cache(), but it doesn't send
|
||||
* command if the parameter is already identical with the cached value.
|
||||
* If not, it sends the command and refreshes the cache.
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
struct hda_cache_head *c;
|
||||
u32 key;
|
||||
|
||||
/* parm may contain the verb stuff for get/set amp */
|
||||
verb = verb | (parm >> 8);
|
||||
parm &= 0xff;
|
||||
key = build_cmd_cache_key(nid, verb);
|
||||
mutex_lock(&codec->bus->cmd_mutex);
|
||||
c = get_hash(&codec->cmd_cache, key);
|
||||
if (c && c->val == parm) {
|
||||
mutex_unlock(&codec->bus->cmd_mutex);
|
||||
return 0;
|
||||
}
|
||||
mutex_unlock(&codec->bus->cmd_mutex);
|
||||
return snd_hda_codec_write_cache(codec, nid, direct, verb, parm);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
|
||||
|
||||
/**
|
||||
* snd_hda_codec_resume_cache - Resume the all commands from the cache
|
||||
* @codec: HD-audio codec
|
||||
@ -4218,7 +4267,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
||||
break;
|
||||
case AC_JACK_MIC_IN: {
|
||||
int preferred, alt;
|
||||
if (loc == AC_JACK_LOC_FRONT) {
|
||||
if (loc == AC_JACK_LOC_FRONT ||
|
||||
(loc & 0x30) == AC_JACK_LOC_INTERNAL) {
|
||||
preferred = AUTO_PIN_FRONT_MIC;
|
||||
alt = AUTO_PIN_MIC;
|
||||
} else {
|
||||
|
@ -885,9 +885,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm);
|
||||
void snd_hda_sequence_write_cache(struct hda_codec *codec,
|
||||
const struct hda_verb *seq);
|
||||
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm);
|
||||
void snd_hda_codec_resume_cache(struct hda_codec *codec);
|
||||
#else
|
||||
#define snd_hda_codec_write_cache snd_hda_codec_write
|
||||
#define snd_hda_codec_update_cache snd_hda_codec_write
|
||||
#define snd_hda_sequence_write_cache snd_hda_sequence_write
|
||||
#endif
|
||||
|
||||
|
@ -84,7 +84,7 @@ module_param_array(bdl_pos_adj, int, NULL, 0644);
|
||||
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
|
||||
module_param_array(probe_mask, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
|
||||
module_param_array(probe_only, bool, NULL, 0444);
|
||||
module_param_array(probe_only, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
|
||||
module_param(single_cmd, bool, 0444);
|
||||
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
|
||||
@ -174,7 +174,7 @@ MODULE_DESCRIPTION("Intel HDA driver");
|
||||
#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
|
||||
#define ICH6_REG_INTCTL 0x20
|
||||
#define ICH6_REG_INTSTS 0x24
|
||||
#define ICH6_REG_WALCLK 0x30
|
||||
#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
|
||||
#define ICH6_REG_SYNC 0x34
|
||||
#define ICH6_REG_CORBLBASE 0x40
|
||||
#define ICH6_REG_CORBUBASE 0x44
|
||||
@ -340,8 +340,8 @@ struct azx_dev {
|
||||
unsigned int period_bytes; /* size of the period in bytes */
|
||||
unsigned int frags; /* number for period in the play buffer */
|
||||
unsigned int fifo_size; /* FIFO size */
|
||||
unsigned long start_jiffies; /* start + minimum jiffies */
|
||||
unsigned long min_jiffies; /* minimum jiffies before position is valid */
|
||||
unsigned long start_wallclk; /* start + minimum wallclk */
|
||||
unsigned long period_wallclk; /* wallclk for period */
|
||||
|
||||
void __iomem *sd_addr; /* stream descriptor pointer */
|
||||
|
||||
@ -361,7 +361,6 @@ struct azx_dev {
|
||||
unsigned int opened :1;
|
||||
unsigned int running :1;
|
||||
unsigned int irq_pending :1;
|
||||
unsigned int start_flag: 1; /* stream full start flag */
|
||||
/*
|
||||
* For VIA:
|
||||
* A flag to ensure DMA position is 0
|
||||
@ -425,7 +424,7 @@ struct azx {
|
||||
struct snd_dma_buffer posbuf;
|
||||
|
||||
/* flags */
|
||||
int position_fix;
|
||||
int position_fix[2]; /* for both playback/capture streams */
|
||||
int poll_count;
|
||||
unsigned int running :1;
|
||||
unsigned int initialized :1;
|
||||
@ -858,10 +857,13 @@ static void azx_power_notify(struct hda_bus *bus);
|
||||
#endif
|
||||
|
||||
/* reset codec link */
|
||||
static int azx_reset(struct azx *chip)
|
||||
static int azx_reset(struct azx *chip, int full_reset)
|
||||
{
|
||||
int count;
|
||||
|
||||
if (!full_reset)
|
||||
goto __skip;
|
||||
|
||||
/* clear STATESTS */
|
||||
azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
|
||||
|
||||
@ -887,6 +889,7 @@ static int azx_reset(struct azx *chip)
|
||||
/* Brent Chartrand said to wait >= 540us for codecs to initialize */
|
||||
msleep(1);
|
||||
|
||||
__skip:
|
||||
/* check to see if controller is ready */
|
||||
if (!azx_readb(chip, GCTL)) {
|
||||
snd_printd(SFX "azx_reset: controller not ready!\n");
|
||||
@ -998,13 +1001,13 @@ static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
|
||||
/*
|
||||
* reset and start the controller registers
|
||||
*/
|
||||
static void azx_init_chip(struct azx *chip)
|
||||
static void azx_init_chip(struct azx *chip, int full_reset)
|
||||
{
|
||||
if (chip->initialized)
|
||||
return;
|
||||
|
||||
/* reset controller */
|
||||
azx_reset(chip);
|
||||
azx_reset(chip, full_reset);
|
||||
|
||||
/* initialize interrupts */
|
||||
azx_int_clear(chip);
|
||||
@ -1302,8 +1305,10 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
|
||||
azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
|
||||
|
||||
/* enable the position buffer */
|
||||
if (chip->position_fix == POS_FIX_POSBUF ||
|
||||
chip->position_fix == POS_FIX_AUTO ||
|
||||
if (chip->position_fix[0] == POS_FIX_POSBUF ||
|
||||
chip->position_fix[0] == POS_FIX_AUTO ||
|
||||
chip->position_fix[1] == POS_FIX_POSBUF ||
|
||||
chip->position_fix[1] == POS_FIX_AUTO ||
|
||||
chip->via_dmapos_patch) {
|
||||
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
|
||||
azx_writel(chip, DPLBASE,
|
||||
@ -1348,7 +1353,7 @@ static void azx_bus_reset(struct hda_bus *bus)
|
||||
|
||||
bus->in_reset = 1;
|
||||
azx_stop_chip(chip);
|
||||
azx_init_chip(chip);
|
||||
azx_init_chip(chip, 1);
|
||||
#ifdef CONFIG_PM
|
||||
if (chip->initialized) {
|
||||
int i;
|
||||
@ -1422,7 +1427,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
|
||||
* get back to the sanity state.
|
||||
*/
|
||||
azx_stop_chip(chip);
|
||||
azx_init_chip(chip);
|
||||
azx_init_chip(chip, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1670,8 +1675,9 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
return err;
|
||||
}
|
||||
|
||||
azx_dev->min_jiffies = (runtime->period_size * HZ) /
|
||||
(runtime->rate * 2);
|
||||
/* wallclk has 24Mhz clock source */
|
||||
azx_dev->period_wallclk = (((runtime->period_size * 24000) /
|
||||
runtime->rate) * 1000);
|
||||
azx_setup_controller(chip, azx_dev);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
|
||||
@ -1725,14 +1731,15 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
if (s->pcm->card != substream->pcm->card)
|
||||
continue;
|
||||
azx_dev = get_azx_dev(s);
|
||||
if (rstart) {
|
||||
azx_dev->start_flag = 1;
|
||||
azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies;
|
||||
}
|
||||
if (start)
|
||||
if (start) {
|
||||
azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
|
||||
if (!rstart)
|
||||
azx_dev->start_wallclk -=
|
||||
azx_dev->period_wallclk;
|
||||
azx_stream_start(chip, azx_dev);
|
||||
else
|
||||
} else {
|
||||
azx_stream_stop(chip, azx_dev);
|
||||
}
|
||||
azx_dev->running = start;
|
||||
}
|
||||
spin_unlock(&chip->reg_lock);
|
||||
@ -1843,13 +1850,16 @@ static unsigned int azx_get_position(struct azx *chip,
|
||||
|
||||
if (chip->via_dmapos_patch)
|
||||
pos = azx_via_get_position(chip, azx_dev);
|
||||
else if (chip->position_fix == POS_FIX_POSBUF ||
|
||||
chip->position_fix == POS_FIX_AUTO) {
|
||||
/* use the position buffer */
|
||||
pos = le32_to_cpu(*azx_dev->posbuf);
|
||||
} else {
|
||||
/* read LPIB */
|
||||
pos = azx_sd_readl(azx_dev, SD_LPIB);
|
||||
else {
|
||||
int stream = azx_dev->substream->stream;
|
||||
if (chip->position_fix[stream] == POS_FIX_POSBUF ||
|
||||
chip->position_fix[stream] == POS_FIX_AUTO) {
|
||||
/* use the position buffer */
|
||||
pos = le32_to_cpu(*azx_dev->posbuf);
|
||||
} else {
|
||||
/* read LPIB */
|
||||
pos = azx_sd_readl(azx_dev, SD_LPIB);
|
||||
}
|
||||
}
|
||||
if (pos >= azx_dev->bufsize)
|
||||
pos = 0;
|
||||
@ -1876,32 +1886,35 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
*/
|
||||
static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
|
||||
{
|
||||
u32 wallclk;
|
||||
unsigned int pos;
|
||||
int stream;
|
||||
|
||||
if (azx_dev->start_flag &&
|
||||
time_before_eq(jiffies, azx_dev->start_jiffies))
|
||||
wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
|
||||
if (wallclk < (azx_dev->period_wallclk * 2) / 3)
|
||||
return -1; /* bogus (too early) interrupt */
|
||||
azx_dev->start_flag = 0;
|
||||
|
||||
stream = azx_dev->substream->stream;
|
||||
pos = azx_get_position(chip, azx_dev);
|
||||
if (chip->position_fix == POS_FIX_AUTO) {
|
||||
if (chip->position_fix[stream] == POS_FIX_AUTO) {
|
||||
if (!pos) {
|
||||
printk(KERN_WARNING
|
||||
"hda-intel: Invalid position buffer, "
|
||||
"using LPIB read method instead.\n");
|
||||
chip->position_fix = POS_FIX_LPIB;
|
||||
chip->position_fix[stream] = POS_FIX_LPIB;
|
||||
pos = azx_get_position(chip, azx_dev);
|
||||
} else
|
||||
chip->position_fix = POS_FIX_POSBUF;
|
||||
chip->position_fix[stream] = POS_FIX_POSBUF;
|
||||
}
|
||||
|
||||
if (!bdl_pos_adj[chip->dev_index])
|
||||
return 1; /* no delayed ack */
|
||||
if (WARN_ONCE(!azx_dev->period_bytes,
|
||||
"hda-intel: zero azx_dev->period_bytes"))
|
||||
return 0; /* this shouldn't happen! */
|
||||
if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
|
||||
return 0; /* NG - it's below the period boundary */
|
||||
return -1; /* this shouldn't happen! */
|
||||
if (wallclk <= azx_dev->period_wallclk &&
|
||||
pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
|
||||
/* NG - it's below the first next period boundary */
|
||||
return bdl_pos_adj[chip->dev_index] ? 0 : -1;
|
||||
azx_dev->start_wallclk = wallclk;
|
||||
return 1; /* OK, it's fine */
|
||||
}
|
||||
|
||||
@ -1911,7 +1924,7 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
|
||||
static void azx_irq_pending_work(struct work_struct *work)
|
||||
{
|
||||
struct azx *chip = container_of(work, struct azx, irq_pending_work);
|
||||
int i, pending;
|
||||
int i, pending, ok;
|
||||
|
||||
if (!chip->irq_pending_warned) {
|
||||
printk(KERN_WARNING
|
||||
@ -1930,11 +1943,14 @@ static void azx_irq_pending_work(struct work_struct *work)
|
||||
!azx_dev->substream ||
|
||||
!azx_dev->running)
|
||||
continue;
|
||||
if (azx_position_ok(chip, azx_dev)) {
|
||||
ok = azx_position_ok(chip, azx_dev);
|
||||
if (ok > 0) {
|
||||
azx_dev->irq_pending = 0;
|
||||
spin_unlock(&chip->reg_lock);
|
||||
snd_pcm_period_elapsed(azx_dev->substream);
|
||||
spin_lock(&chip->reg_lock);
|
||||
} else if (ok < 0) {
|
||||
pending = 0; /* too early */
|
||||
} else
|
||||
pending++;
|
||||
}
|
||||
@ -2112,7 +2128,7 @@ static void azx_power_notify(struct hda_bus *bus)
|
||||
}
|
||||
}
|
||||
if (power_on)
|
||||
azx_init_chip(chip);
|
||||
azx_init_chip(chip, 1);
|
||||
else if (chip->running && power_save_controller &&
|
||||
!bus->power_keep_link_on)
|
||||
azx_stop_chip(chip);
|
||||
@ -2182,7 +2198,7 @@ static int azx_resume(struct pci_dev *pci)
|
||||
azx_init_pci(chip);
|
||||
|
||||
if (snd_hda_codecs_inuse(chip->bus))
|
||||
azx_init_chip(chip);
|
||||
azx_init_chip(chip, 1);
|
||||
|
||||
snd_hda_resume(chip->bus);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
@ -2431,7 +2447,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
||||
chip->dev_index = dev;
|
||||
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
|
||||
|
||||
chip->position_fix = check_position_fix(chip, position_fix[dev]);
|
||||
chip->position_fix[0] = chip->position_fix[1] =
|
||||
check_position_fix(chip, position_fix[dev]);
|
||||
check_probe_mask(chip, dev);
|
||||
|
||||
chip->single_cmd = single_cmd;
|
||||
@ -2577,7 +2594,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
||||
|
||||
/* initialize chip */
|
||||
azx_init_pci(chip);
|
||||
azx_init_chip(chip);
|
||||
azx_init_chip(chip, (probe_only[dev] & 2) == 0);
|
||||
|
||||
/* codec detection */
|
||||
if (!chip->codec_mask) {
|
||||
@ -2666,7 +2683,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
|
||||
goto out_free;
|
||||
}
|
||||
#endif
|
||||
if (!probe_only[dev]) {
|
||||
if ((probe_only[dev] & 1) == 0) {
|
||||
err = azx_codec_configure(chip);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
@ -71,9 +71,10 @@ struct ad198x_spec {
|
||||
struct hda_input_mux private_imux;
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
|
||||
unsigned int jack_present :1;
|
||||
unsigned int inv_jack_detect:1; /* inverted jack-detection */
|
||||
unsigned int inv_eapd:1; /* inverted EAPD implementation */
|
||||
unsigned int jack_present: 1;
|
||||
unsigned int inv_jack_detect: 1;/* inverted jack-detection */
|
||||
unsigned int inv_eapd: 1; /* inverted EAPD implementation */
|
||||
unsigned int analog_beep: 1; /* analog beep input present */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
struct hda_loopback_check loopback;
|
||||
@ -165,6 +166,12 @@ static struct snd_kcontrol_new ad_beep_mixer[] = {
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad_beep2_mixer[] = {
|
||||
HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
#define set_beep_amp(spec, nid, idx, dir) \
|
||||
((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
|
||||
#else
|
||||
@ -203,7 +210,8 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
if (spec->beep_amp) {
|
||||
struct snd_kcontrol_new *knew;
|
||||
for (knew = ad_beep_mixer; knew->name; knew++) {
|
||||
knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
|
||||
for ( ; knew->name; knew++) {
|
||||
struct snd_kcontrol *kctl;
|
||||
kctl = snd_ctl_new1(knew, codec);
|
||||
if (!kctl)
|
||||
@ -3481,6 +3489,8 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
|
||||
@ -3522,6 +3532,8 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
|
||||
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* docking mic boost */
|
||||
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
|
||||
/* Analog PC Beeper - allow firmware/ACPI beeps */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
|
||||
/* Analog mixer - docking mic; mute as default */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
||||
/* enable EAPD bit */
|
||||
@ -3654,6 +3666,7 @@ static int patch_ad1984(struct hda_codec *codec)
|
||||
spec->input_mux = &ad1984_thinkpad_capture_source;
|
||||
spec->mixers[0] = ad1984_thinkpad_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
|
||||
spec->analog_beep = 1;
|
||||
break;
|
||||
case AD1984_DELL_DESKTOP:
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
@ -115,6 +115,7 @@ struct conexant_spec {
|
||||
unsigned int port_d_mode;
|
||||
unsigned int dell_vostro:1;
|
||||
unsigned int ideapad:1;
|
||||
unsigned int thinkpad:1;
|
||||
|
||||
unsigned int ext_mic_present;
|
||||
unsigned int recording;
|
||||
@ -1784,6 +1785,7 @@ static struct hda_verb cxt5051_init_verbs[] = {
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
|
||||
/* SPDIF route: PCM */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
/* EAPD */
|
||||
{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
@ -1840,6 +1842,7 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
|
||||
/* SPDIF route: PCM */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
|
||||
{0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
/* EAPD */
|
||||
{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
@ -1911,7 +1914,7 @@ enum {
|
||||
CXT5051_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5051_HP, /* no docking */
|
||||
CXT5051_HP_DV6736, /* HP without mic switch */
|
||||
CXT5051_LENOVO_X200, /* Lenovo X200 laptop */
|
||||
CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
|
||||
CXT5051_F700, /* HP Compaq Presario F700 */
|
||||
CXT5051_TOSHIBA, /* Toshiba M300 & co */
|
||||
CXT5051_MODELS
|
||||
@ -2033,6 +2036,9 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
|
||||
/* Port D (HP/LO) */
|
||||
pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
|
||||
? spec->port_d_mode : 0;
|
||||
/* Mute if Port A is connected on Thinkpad */
|
||||
if (spec->thinkpad && (spec->hp_present & 1))
|
||||
pinctl = 0;
|
||||
snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinctl);
|
||||
|
||||
@ -2213,6 +2219,50 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec)
|
||||
}
|
||||
}
|
||||
|
||||
/* toggle input of built-in digital mic and mic jack appropriately
|
||||
order is: external mic -> dock mic -> interal mic */
|
||||
static void cxt5066_thinkpad_automic(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int ext_present, dock_present;
|
||||
|
||||
static struct hda_verb ext_mic_present[] = {
|
||||
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 1},
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
static struct hda_verb dock_mic_present[] = {
|
||||
{0x14, AC_VERB_SET_CONNECT_SEL, 0},
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 0},
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
static struct hda_verb ext_mic_absent[] = {
|
||||
{0x14, AC_VERB_SET_CONNECT_SEL, 2},
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{}
|
||||
};
|
||||
|
||||
ext_present = snd_hda_jack_detect(codec, 0x1b);
|
||||
dock_present = snd_hda_jack_detect(codec, 0x1a);
|
||||
if (ext_present) {
|
||||
snd_printdd("CXT5066: external microphone detected\n");
|
||||
snd_hda_sequence_write(codec, ext_mic_present);
|
||||
} else if (dock_present) {
|
||||
snd_printdd("CXT5066: dock microphone detected\n");
|
||||
snd_hda_sequence_write(codec, dock_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)
|
||||
{
|
||||
@ -2225,7 +2275,8 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
|
||||
/* Port D */
|
||||
portD = snd_hda_jack_detect(codec, 0x1c);
|
||||
|
||||
spec->hp_present = !!(portA | portD);
|
||||
spec->hp_present = !!(portA);
|
||||
spec->hp_present |= portD ? 2 : 0;
|
||||
snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
|
||||
portA, portD, spec->hp_present);
|
||||
cxt5066_update_speaker(codec);
|
||||
@ -2276,6 +2327,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
|
||||
}
|
||||
}
|
||||
|
||||
/* unsolicited event for jack sensing */
|
||||
static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
snd_printdd("CXT5066_thinkpad: 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_thinkpad_automic(codec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct hda_input_mux cxt5066_analog_mic_boost = {
|
||||
.num_items = 5,
|
||||
.items = {
|
||||
@ -2294,7 +2359,7 @@ static void cxt5066_set_mic_boost(struct hda_codec *codec)
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
|
||||
cxt5066_analog_mic_boost.items[spec->mic_boost].index);
|
||||
if (spec->ideapad) {
|
||||
if (spec->ideapad || spec->thinkpad) {
|
||||
/* adjust the internal mic as well...it is not through 0x17 */
|
||||
snd_hda_codec_write_cache(codec, 0x23, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
@ -2782,6 +2847,64 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = {
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
|
||||
{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
|
||||
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
|
||||
|
||||
/* Port G: internal speakers */
|
||||
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
|
||||
{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* Port A: HP, Amp */
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
|
||||
|
||||
/* Port B: Mic Dock */
|
||||
{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port C: Mic */
|
||||
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
|
||||
/* Port D: HP Dock, Amp */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
|
||||
{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)},
|
||||
{0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
|
||||
|
||||
/* Audio input selector */
|
||||
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
|
||||
{0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
|
||||
|
||||
/* 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},
|
||||
|
||||
/* internal microphone */
|
||||
{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
|
||||
|
||||
/* EAPD */
|
||||
{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
|
||||
|
||||
/* enable unsolicited events for Port A, B, C and D */
|
||||
{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
|
||||
{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
|
||||
{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
|
||||
{0x1b, 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 */
|
||||
@ -2800,6 +2923,8 @@ static int cxt5066_init(struct hda_codec *codec)
|
||||
cxt5066_vostro_automic(codec);
|
||||
else if (spec->ideapad)
|
||||
cxt5066_ideapad_automic(codec);
|
||||
else if (spec->thinkpad)
|
||||
cxt5066_thinkpad_automic(codec);
|
||||
}
|
||||
cxt5066_set_mic_boost(codec);
|
||||
return 0;
|
||||
@ -2821,20 +2946,22 @@ static int cxt5066_olpc_init(struct hda_codec *codec)
|
||||
}
|
||||
|
||||
enum {
|
||||
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
|
||||
CXT5066_DELL_LAPTOP, /* Dell Laptop */
|
||||
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
|
||||
CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
|
||||
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
|
||||
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
|
||||
CXT5066_MODELS
|
||||
};
|
||||
|
||||
static const char *cxt5066_models[CXT5066_MODELS] = {
|
||||
[CXT5066_LAPTOP] = "laptop",
|
||||
[CXT5066_LAPTOP] = "laptop",
|
||||
[CXT5066_DELL_LAPTOP] = "dell-laptop",
|
||||
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
|
||||
[CXT5066_DELL_VOSTO] = "dell-vostro",
|
||||
[CXT5066_IDEAPAD] = "ideapad",
|
||||
[CXT5066_THINKPAD] = "thinkpad",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
||||
@ -2849,6 +2976,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
|
||||
SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
|
||||
{}
|
||||
};
|
||||
|
||||
@ -2953,6 +3081,22 @@ static int patch_cxt5066(struct hda_codec *codec)
|
||||
/* no S/PDIF out */
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
||||
/* input source automatically selected */
|
||||
spec->input_mux = NULL;
|
||||
break;
|
||||
case CXT5066_THINKPAD:
|
||||
codec->patch_ops.init = cxt5066_init;
|
||||
codec->patch_ops.unsol_event = cxt5066_thinkpad_event;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
|
||||
spec->mixers[spec->num_mixers++] = cxt5066_mixers;
|
||||
spec->init_verbs[0] = cxt5066_init_verbs_thinkpad;
|
||||
spec->thinkpad = 1;
|
||||
spec->port_d_mode = PIN_OUT;
|
||||
spec->mic_boost = 2; /* default 20dB gain */
|
||||
|
||||
/* no S/PDIF out */
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
|
||||
/* input source automatically selected */
|
||||
spec->input_mux = NULL;
|
||||
break;
|
||||
@ -2975,6 +3119,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
|
||||
.patch = patch_cxt5066 },
|
||||
{ .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
|
||||
.patch = patch_cxt5066 },
|
||||
{ .id = 0x14f15069, .name = "CX20585",
|
||||
.patch = patch_cxt5066 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
@ -2983,6 +3129,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15051");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15066");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15067");
|
||||
MODULE_ALIAS("snd-hda-codec-id:14f15069");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Conexant HD-audio codec");
|
||||
|
@ -766,7 +766,7 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
if (spec->num_pins >= MAX_HDMI_PINS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for pin %d\n", pin_nid);
|
||||
return -EINVAL;
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
|
||||
@ -788,7 +788,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
|
||||
if (spec->num_cvts >= MAX_HDMI_CVTS) {
|
||||
snd_printk(KERN_WARNING
|
||||
"HDMI: no space for converter %d\n", nid);
|
||||
return -EINVAL;
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
spec->cvt[spec->num_cvts] = nid;
|
||||
@ -820,15 +820,13 @@ static int hdmi_parse_codec(struct hda_codec *codec)
|
||||
|
||||
switch (type) {
|
||||
case AC_WID_AUD_OUT:
|
||||
if (hdmi_add_cvt(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
hdmi_add_cvt(codec, nid);
|
||||
break;
|
||||
case AC_WID_PIN:
|
||||
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
|
||||
continue;
|
||||
if (hdmi_add_pin(codec, nid) < 0)
|
||||
return -EINVAL;
|
||||
hdmi_add_pin(codec, nid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
||||
*
|
||||
* The HDA correspondence of pipes/ports are converter/pin nodes.
|
||||
*/
|
||||
#define MAX_HDMI_CVTS 2
|
||||
#define MAX_HDMI_CVTS 3
|
||||
#define MAX_HDMI_PINS 3
|
||||
|
||||
#include "patch_hdmi.c"
|
||||
@ -48,6 +48,7 @@
|
||||
static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
|
||||
"INTEL HDMI 0",
|
||||
"INTEL HDMI 1",
|
||||
"INTEL HDMI 2",
|
||||
};
|
||||
|
||||
/*
|
||||
@ -185,14 +186,15 @@ static int patch_intel_hdmi(struct hda_codec *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 },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
|
||||
{} /* terminator */
|
||||
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:808629fb");
|
||||
@ -200,6 +202,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:80862805");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80860054");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10951392");
|
||||
|
||||
|
@ -276,6 +276,18 @@ struct alc_mic_route {
|
||||
|
||||
#define MUX_IDX_UNDEF ((unsigned char)-1)
|
||||
|
||||
struct alc_customize_define {
|
||||
unsigned int sku_cfg;
|
||||
unsigned char port_connectivity;
|
||||
unsigned char check_sum;
|
||||
unsigned char customization;
|
||||
unsigned char external_amp;
|
||||
unsigned int enable_pcbeep:1;
|
||||
unsigned int platform_type:1;
|
||||
unsigned int swap:1;
|
||||
unsigned int override:1;
|
||||
};
|
||||
|
||||
struct alc_spec {
|
||||
/* codec parameterization */
|
||||
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
|
||||
@ -333,6 +345,7 @@ struct alc_spec {
|
||||
|
||||
/* dynamic controls, init_verbs and input_mux */
|
||||
struct auto_pin_cfg autocfg;
|
||||
struct alc_customize_define cdefine;
|
||||
struct snd_array kctls;
|
||||
struct hda_input_mux private_imux[3];
|
||||
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
|
||||
@ -1248,6 +1261,62 @@ static void alc_init_auto_mic(struct hda_codec *codec)
|
||||
spec->unsol_event = alc_sku_unsol_event;
|
||||
}
|
||||
|
||||
static int alc_auto_parse_customize_define(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int ass, tmp, i;
|
||||
unsigned nid = 0;
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
ass = codec->subsystem_id & 0xffff;
|
||||
if (ass != codec->bus->pci->subsystem_device && (ass & 1))
|
||||
goto do_sku;
|
||||
|
||||
nid = 0x1d;
|
||||
if (codec->vendor_id == 0x10ec0260)
|
||||
nid = 0x17;
|
||||
ass = snd_hda_codec_get_pincfg(codec, nid);
|
||||
|
||||
if (!(ass & 1)) {
|
||||
printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
|
||||
codec->chip_name, ass);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check sum */
|
||||
tmp = 0;
|
||||
for (i = 1; i < 16; i++) {
|
||||
if ((ass >> i) & 1)
|
||||
tmp++;
|
||||
}
|
||||
if (((ass >> 16) & 0xf) != tmp)
|
||||
return -1;
|
||||
|
||||
spec->cdefine.port_connectivity = ass >> 30;
|
||||
spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
|
||||
spec->cdefine.check_sum = (ass >> 16) & 0xf;
|
||||
spec->cdefine.customization = ass >> 8;
|
||||
do_sku:
|
||||
spec->cdefine.sku_cfg = ass;
|
||||
spec->cdefine.external_amp = (ass & 0x38) >> 3;
|
||||
spec->cdefine.platform_type = (ass & 0x4) >> 2;
|
||||
spec->cdefine.swap = (ass & 0x2) >> 1;
|
||||
spec->cdefine.override = ass & 0x1;
|
||||
|
||||
snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
|
||||
nid, spec->cdefine.sku_cfg);
|
||||
snd_printd("SKU: port_connectivity=0x%x\n",
|
||||
spec->cdefine.port_connectivity);
|
||||
snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
|
||||
snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
|
||||
snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
|
||||
snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
|
||||
snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
|
||||
snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
|
||||
snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check subsystem ID and set up device-specific initialization;
|
||||
* return 1 if initialized, 0 if invalid SSID
|
||||
*/
|
||||
@ -3415,6 +3484,10 @@ static int alc_init(struct hda_codec *codec)
|
||||
if (spec->init_hook)
|
||||
spec->init_hook(codec);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (codec->patch_ops.check_power_status)
|
||||
codec->patch_ops.check_power_status(codec, 0x01);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3775,6 +3848,10 @@ static int alc_resume(struct hda_codec *codec)
|
||||
codec->patch_ops.init(codec);
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (codec->patch_ops.check_power_status)
|
||||
codec->patch_ops.check_power_status(codec, 0x01);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -3797,6 +3874,17 @@ static struct hda_codec_ops alc_patch_ops = {
|
||||
.reboot_notify = alc_shutup,
|
||||
};
|
||||
|
||||
/* replace the codec chip_name with the given string */
|
||||
static int alc_codec_rename(struct hda_codec *codec, const char *name)
|
||||
{
|
||||
kfree(codec->chip_name);
|
||||
codec->chip_name = kstrdup(name, GFP_KERNEL);
|
||||
if (!codec->chip_name) {
|
||||
alc_free(codec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test configuration for debugging
|
||||
@ -10189,21 +10277,20 @@ static int alc882_auto_create_input_ctls(struct hda_codec *codec,
|
||||
|
||||
static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
|
||||
hda_nid_t nid, int pin_type,
|
||||
int dac_idx)
|
||||
hda_nid_t dac)
|
||||
{
|
||||
/* set as output */
|
||||
struct alc_spec *spec = codec->spec;
|
||||
int idx;
|
||||
|
||||
/* set as output */
|
||||
alc_set_pin_output(codec, nid, pin_type);
|
||||
if (dac_idx >= spec->multiout.num_dacs)
|
||||
return;
|
||||
if (spec->multiout.dac_nids[dac_idx] == 0x25)
|
||||
idx = 4;
|
||||
else
|
||||
idx = spec->multiout.dac_nids[dac_idx] - 2;
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
|
||||
|
||||
if (dac == 0x25)
|
||||
idx = 4;
|
||||
else if (dac >= 0x02 && dac <= 0x05)
|
||||
idx = dac - 2;
|
||||
else
|
||||
return;
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
|
||||
}
|
||||
|
||||
static void alc882_auto_init_multi_out(struct hda_codec *codec)
|
||||
@ -10216,22 +10303,29 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec)
|
||||
int pin_type = get_pin_type(spec->autocfg.line_out_type);
|
||||
if (nid)
|
||||
alc882_auto_set_output_and_unmute(codec, nid, pin_type,
|
||||
i);
|
||||
spec->multiout.dac_nids[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc882_auto_init_hp_out(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
hda_nid_t pin;
|
||||
hda_nid_t pin, dac;
|
||||
|
||||
pin = spec->autocfg.hp_pins[0];
|
||||
if (pin) /* connect to front */
|
||||
/* use dac 0 */
|
||||
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
|
||||
if (pin) {
|
||||
dac = spec->multiout.hp_nid;
|
||||
if (!dac)
|
||||
dac = spec->multiout.dac_nids[0]; /* to front */
|
||||
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
|
||||
}
|
||||
pin = spec->autocfg.speaker_pins[0];
|
||||
if (pin)
|
||||
alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
|
||||
if (pin) {
|
||||
dac = spec->multiout.extra_out_nid[0];
|
||||
if (!dac)
|
||||
dac = spec->multiout.dac_nids[0]; /* to front */
|
||||
alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
|
||||
}
|
||||
}
|
||||
|
||||
static void alc882_auto_init_analog_input(struct hda_codec *codec)
|
||||
@ -10345,6 +10439,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
|
||||
"Headphone");
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = alc880_auto_create_extra_out(spec,
|
||||
@ -10352,10 +10450,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
|
||||
"Speaker");
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
|
||||
"Headphone");
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@ -10425,6 +10519,8 @@ static int patch_alc882(struct hda_codec *codec)
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x10ec0882:
|
||||
case 0x10ec0885:
|
||||
@ -10484,9 +10580,6 @@ static int patch_alc882(struct hda_codec *codec)
|
||||
spec->stream_digital_playback = &alc882_pcm_digital_playback;
|
||||
spec->stream_digital_capture = &alc882_pcm_digital_capture;
|
||||
|
||||
if (codec->vendor_id == 0x10ec0888)
|
||||
spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
|
||||
|
||||
if (!spec->adc_nids && spec->input_mux) {
|
||||
int i, j;
|
||||
spec->num_adc_nids = 0;
|
||||
@ -10521,7 +10614,9 @@ static int patch_alc882(struct hda_codec *codec)
|
||||
}
|
||||
|
||||
set_capture_mixer(codec);
|
||||
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
|
||||
|
||||
if (spec->cdefine.enable_pcbeep)
|
||||
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
|
||||
|
||||
if (board_config == ALC882_AUTO)
|
||||
alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 0);
|
||||
@ -12308,6 +12403,7 @@ static int patch_alc262(struct hda_codec *codec)
|
||||
snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
|
||||
}
|
||||
#endif
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
||||
alc_fix_pll_init(codec, 0x20, 0x0a, 10);
|
||||
|
||||
@ -12386,7 +12482,7 @@ static int patch_alc262(struct hda_codec *codec)
|
||||
}
|
||||
if (!spec->cap_mixer && !spec->no_analog)
|
||||
set_capture_mixer(codec);
|
||||
if (!spec->no_analog)
|
||||
if (!spec->no_analog && spec->cdefine.enable_pcbeep)
|
||||
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
|
||||
|
||||
spec->vmaster_nid = 0x0c;
|
||||
@ -14005,6 +14101,35 @@ static struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
|
||||
/* NID is set in alc_build_pcms */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static int alc269_mic2_for_mute_led(struct hda_codec *codec)
|
||||
{
|
||||
switch (codec->subsystem_id) {
|
||||
case 0x103c1586:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
/* update mute-LED according to the speaker mute state */
|
||||
if (nid == 0x01 || nid == 0x14) {
|
||||
int pinval;
|
||||
if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) &
|
||||
HDA_AMP_MUTE)
|
||||
pinval = 0x24;
|
||||
else
|
||||
pinval = 0x20;
|
||||
/* mic2 vref pin is used for mute LED control */
|
||||
snd_hda_codec_update_cache(codec, 0x19, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinval);
|
||||
}
|
||||
return alc_check_power_status(codec, nid);
|
||||
}
|
||||
#endif /* CONFIG_SND_HDA_POWER_SAVE */
|
||||
|
||||
/*
|
||||
* BIOS auto configuration
|
||||
*/
|
||||
@ -14082,7 +14207,7 @@ enum {
|
||||
ALC269_FIXUP_SONY_VAIO,
|
||||
};
|
||||
|
||||
const static struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
|
||||
static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
|
||||
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
|
||||
{}
|
||||
};
|
||||
@ -14290,17 +14415,17 @@ static int patch_alc269(struct hda_codec *codec)
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
alc_fix_pll_init(codec, 0x20, 0x04, 15);
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
||||
if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
|
||||
kfree(codec->chip_name);
|
||||
codec->chip_name = kstrdup("ALC259", GFP_KERNEL);
|
||||
if (!codec->chip_name) {
|
||||
alc_free(codec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (codec->bus->pci->subsystem_vendor == 0x1025 &&
|
||||
spec->cdefine.platform_type == 1)
|
||||
alc_codec_rename(codec, "ALC271X");
|
||||
else
|
||||
alc_codec_rename(codec, "ALC259");
|
||||
is_alc269vb = 1;
|
||||
}
|
||||
} else
|
||||
alc_fix_pll_init(codec, 0x20, 0x04, 15);
|
||||
|
||||
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
|
||||
alc269_models,
|
||||
@ -14365,7 +14490,8 @@ static int patch_alc269(struct hda_codec *codec)
|
||||
|
||||
if (!spec->cap_mixer)
|
||||
set_capture_mixer(codec);
|
||||
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
|
||||
if (spec->cdefine.enable_pcbeep)
|
||||
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
|
||||
|
||||
if (board_config == ALC269_AUTO)
|
||||
alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 0);
|
||||
@ -14378,6 +14504,8 @@ static int patch_alc269(struct hda_codec *codec)
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (!spec->loopback.amplist)
|
||||
spec->loopback.amplist = alc269_loopbacks;
|
||||
if (alc269_mic2_for_mute_led(codec))
|
||||
codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@ -18525,16 +18653,16 @@ static int patch_alc662(struct hda_codec *codec)
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
|
||||
alc_fix_pll_init(codec, 0x20, 0x04, 15);
|
||||
|
||||
if (alc_read_coef_idx(codec, 0)==0x8020){
|
||||
kfree(codec->chip_name);
|
||||
codec->chip_name = kstrdup("ALC661", GFP_KERNEL);
|
||||
if (!codec->chip_name) {
|
||||
alc_free(codec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (alc_read_coef_idx(codec, 0) == 0x8020)
|
||||
alc_codec_rename(codec, "ALC661");
|
||||
else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) &&
|
||||
codec->bus->pci->subsystem_vendor == 0x1025 &&
|
||||
spec->cdefine.platform_type == 1)
|
||||
alc_codec_rename(codec, "ALC272X");
|
||||
|
||||
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
|
||||
alc662_models,
|
||||
@ -18584,18 +18712,20 @@ static int patch_alc662(struct hda_codec *codec)
|
||||
if (!spec->cap_mixer)
|
||||
set_capture_mixer(codec);
|
||||
|
||||
switch (codec->vendor_id) {
|
||||
case 0x10ec0662:
|
||||
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
|
||||
break;
|
||||
case 0x10ec0272:
|
||||
case 0x10ec0663:
|
||||
case 0x10ec0665:
|
||||
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
|
||||
break;
|
||||
case 0x10ec0273:
|
||||
set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
|
||||
break;
|
||||
if (spec->cdefine.enable_pcbeep) {
|
||||
switch (codec->vendor_id) {
|
||||
case 0x10ec0662:
|
||||
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
|
||||
break;
|
||||
case 0x10ec0272:
|
||||
case 0x10ec0663:
|
||||
case 0x10ec0665:
|
||||
set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
|
||||
break;
|
||||
case 0x10ec0273:
|
||||
set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spec->vmaster_nid = 0x02;
|
||||
|
||||
|
@ -1956,11 +1956,10 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* initialize the chip
|
||||
* reset the chip
|
||||
*/
|
||||
static int __devinit aureon_init(struct snd_ice1712 *ice)
|
||||
static int aureon_reset(struct snd_ice1712 *ice)
|
||||
{
|
||||
static const unsigned short wm_inits_aureon[] = {
|
||||
/* These come first to reduce init pop noise */
|
||||
@ -2047,30 +2046,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
|
||||
0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */
|
||||
(unsigned short)-1
|
||||
};
|
||||
struct aureon_spec *spec;
|
||||
unsigned int tmp;
|
||||
const unsigned short *p;
|
||||
int err, i;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
ice->spec = spec;
|
||||
|
||||
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
|
||||
ice->num_total_dacs = 6;
|
||||
ice->num_total_adcs = 2;
|
||||
} else {
|
||||
/* aureon 7.1 and prodigy 7.1 */
|
||||
ice->num_total_dacs = 8;
|
||||
ice->num_total_adcs = 2;
|
||||
}
|
||||
|
||||
/* to remeber the register values of CS8415 */
|
||||
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
|
||||
if (!ice->akm)
|
||||
return -ENOMEM;
|
||||
ice->akm_codecs = 1;
|
||||
int err;
|
||||
struct aureon_spec *spec = ice->spec;
|
||||
|
||||
err = aureon_ac97_init(ice);
|
||||
if (err != 0)
|
||||
@ -2118,6 +2097,61 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
|
||||
/* initialize PCA9554 pin directions & set default input */
|
||||
aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
|
||||
aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* suspend/resume
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
static int aureon_resume(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct aureon_spec *spec = ice->spec;
|
||||
int err, i;
|
||||
|
||||
err = aureon_reset(ice);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
/* workaround for poking volume with alsamixer after resume:
|
||||
* just set stored volume again */
|
||||
for (i = 0; i < ice->num_total_dacs; i++)
|
||||
wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* initialize the chip
|
||||
*/
|
||||
static int __devinit aureon_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct aureon_spec *spec;
|
||||
int i, err;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
ice->spec = spec;
|
||||
|
||||
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) {
|
||||
ice->num_total_dacs = 6;
|
||||
ice->num_total_adcs = 2;
|
||||
} else {
|
||||
/* aureon 7.1 and prodigy 7.1 */
|
||||
ice->num_total_dacs = 8;
|
||||
ice->num_total_adcs = 2;
|
||||
}
|
||||
|
||||
/* to remeber the register values of CS8415 */
|
||||
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
|
||||
if (!ice->akm)
|
||||
return -ENOMEM;
|
||||
ice->akm_codecs = 1;
|
||||
|
||||
err = aureon_reset(ice);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
spec->master[0] = WM_VOL_MUTE;
|
||||
spec->master[1] = WM_VOL_MUTE;
|
||||
@ -2126,6 +2160,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
|
||||
wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
ice->pm_resume = aureon_resume;
|
||||
ice->pm_suspend_enabled = 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/input.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/control.h>
|
||||
@ -844,11 +845,17 @@ struct snd_m3 {
|
||||
struct m3_dma *substreams;
|
||||
|
||||
spinlock_t reg_lock;
|
||||
spinlock_t ac97_lock;
|
||||
|
||||
#ifdef CONFIG_SND_MAESTRO3_INPUT
|
||||
struct input_dev *input_dev;
|
||||
char phys[64]; /* physical device path */
|
||||
#else
|
||||
spinlock_t ac97_lock;
|
||||
struct snd_kcontrol *master_switch;
|
||||
struct snd_kcontrol *master_volume;
|
||||
struct tasklet_struct hwvol_tq;
|
||||
#endif
|
||||
|
||||
unsigned int in_suspend;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -1598,18 +1605,32 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)
|
||||
}
|
||||
}
|
||||
|
||||
/* The m3's hardware volume works by incrementing / decrementing 2 counters
|
||||
(without wrap around) in response to volume button presses and then
|
||||
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
|
||||
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
|
||||
static void snd_m3_update_hw_volume(unsigned long private_data)
|
||||
{
|
||||
struct snd_m3 *chip = (struct snd_m3 *) private_data;
|
||||
int x, val;
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
|
||||
/* Figure out which volume control button was pushed,
|
||||
based on differences from the default register
|
||||
values. */
|
||||
x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee;
|
||||
|
||||
/* Reset the volume control registers. */
|
||||
/* Reset the volume counters to 4. Tests on the allegro integrated
|
||||
into a Compaq N600C laptop, have revealed that:
|
||||
1) Writing any value will result in the 2 counters being reset to
|
||||
4 so writing 0x88 is not strictly necessary
|
||||
2) Writing to any of the 4 involved registers will reset all 4
|
||||
of them (and reading them always returns the same value for all
|
||||
of them)
|
||||
It could be that a maestro deviates from this, so leave the code
|
||||
as is. */
|
||||
outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE);
|
||||
outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE);
|
||||
outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER);
|
||||
@ -1620,6 +1641,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
|
||||
if (chip->in_suspend)
|
||||
return;
|
||||
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
if (!chip->master_switch || !chip->master_volume)
|
||||
return;
|
||||
|
||||
@ -1629,7 +1651,9 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
|
||||
val = chip->ac97->regs[AC97_MASTER_VOL];
|
||||
switch (x) {
|
||||
case 0x88:
|
||||
/* mute */
|
||||
/* The counters have not changed, yet we've received a HV
|
||||
interrupt. According to tests run by various people this
|
||||
happens when pressing the mute button. */
|
||||
val ^= 0x8000;
|
||||
chip->ac97->regs[AC97_MASTER_VOL] = val;
|
||||
outw(val, chip->iobase + CODEC_DATA);
|
||||
@ -1638,7 +1662,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
|
||||
&chip->master_switch->id);
|
||||
break;
|
||||
case 0xaa:
|
||||
/* volume up */
|
||||
/* counters increased by 1 -> volume up */
|
||||
if ((val & 0x7f) > 0)
|
||||
val--;
|
||||
if ((val & 0x7f00) > 0)
|
||||
@ -1650,7 +1674,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
|
||||
&chip->master_volume->id);
|
||||
break;
|
||||
case 0x66:
|
||||
/* volume down */
|
||||
/* counters decreased by 1 -> volume down */
|
||||
if ((val & 0x7f) < 0x1f)
|
||||
val++;
|
||||
if ((val & 0x7f00) < 0x1f00)
|
||||
@ -1663,6 +1687,35 @@ static void snd_m3_update_hw_volume(unsigned long private_data)
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->ac97_lock, flags);
|
||||
#else
|
||||
if (!chip->input_dev)
|
||||
return;
|
||||
|
||||
val = 0;
|
||||
switch (x) {
|
||||
case 0x88:
|
||||
/* The counters have not changed, yet we've received a HV
|
||||
interrupt. According to tests run by various people this
|
||||
happens when pressing the mute button. */
|
||||
val = KEY_MUTE;
|
||||
break;
|
||||
case 0xaa:
|
||||
/* counters increased by 1 -> volume up */
|
||||
val = KEY_VOLUMEUP;
|
||||
break;
|
||||
case 0x66:
|
||||
/* counters decreased by 1 -> volume down */
|
||||
val = KEY_VOLUMEDOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
input_report_key(chip->input_dev, val, 1);
|
||||
input_sync(chip->input_dev);
|
||||
input_report_key(chip->input_dev, val, 0);
|
||||
input_sync(chip->input_dev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
|
||||
@ -1677,7 +1730,11 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (status & HV_INT_PENDING)
|
||||
#ifdef CONFIG_SND_MAESTRO3_INPUT
|
||||
snd_m3_update_hw_volume((unsigned long)chip);
|
||||
#else
|
||||
tasklet_schedule(&chip->hwvol_tq);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ack an assp int if its running
|
||||
@ -1943,18 +2000,24 @@ static unsigned short
|
||||
snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
|
||||
{
|
||||
struct snd_m3 *chip = ac97->private_data;
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
unsigned short data = 0xffff;
|
||||
|
||||
if (snd_m3_ac97_wait(chip))
|
||||
goto fail;
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
spin_lock_irqsave(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
|
||||
if (snd_m3_ac97_wait(chip))
|
||||
goto fail_unlock;
|
||||
data = snd_m3_inw(chip, CODEC_DATA);
|
||||
fail_unlock:
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
spin_unlock_irqrestore(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
fail:
|
||||
return data;
|
||||
}
|
||||
@ -1963,14 +2026,20 @@ static void
|
||||
snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
|
||||
{
|
||||
struct snd_m3 *chip = ac97->private_data;
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
unsigned long flags;
|
||||
#endif
|
||||
|
||||
if (snd_m3_ac97_wait(chip))
|
||||
return;
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
spin_lock_irqsave(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
snd_m3_outw(chip, val, CODEC_DATA);
|
||||
snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
spin_unlock_irqrestore(&chip->ac97_lock, flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -2077,7 +2146,9 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
|
||||
{
|
||||
struct snd_ac97_bus *pbus;
|
||||
struct snd_ac97_template ac97;
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
struct snd_ctl_elem_id elem_id;
|
||||
#endif
|
||||
int err;
|
||||
static struct snd_ac97_bus_ops ops = {
|
||||
.write = snd_m3_ac97_write,
|
||||
@ -2097,6 +2168,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
|
||||
schedule_timeout_uninterruptible(msecs_to_jiffies(100));
|
||||
snd_ac97_write(chip->ac97, AC97_PCM, 0);
|
||||
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
memset(&elem_id, 0, sizeof(elem_id));
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(elem_id.name, "Master Playback Switch");
|
||||
@ -2105,6 +2177,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(elem_id.name, "Master Playback Volume");
|
||||
chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2370,6 +2443,7 @@ snd_m3_enable_ints(struct snd_m3 *chip)
|
||||
val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/;
|
||||
if (chip->hv_config & HV_CTRL_ENABLE)
|
||||
val |= HV_INT_ENABLE;
|
||||
outb(val, chip->iobase + HOST_INT_STATUS);
|
||||
outw(val, io + HOST_INT_CTRL);
|
||||
outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
|
||||
io + ASSP_CONTROL_C);
|
||||
@ -2384,6 +2458,11 @@ static int snd_m3_free(struct snd_m3 *chip)
|
||||
struct m3_dma *s;
|
||||
int i;
|
||||
|
||||
#ifdef CONFIG_SND_MAESTRO3_INPUT
|
||||
if (chip->input_dev)
|
||||
input_unregister_device(chip->input_dev);
|
||||
#endif
|
||||
|
||||
if (chip->substreams) {
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
for (i = 0; i < chip->num_substreams; i++) {
|
||||
@ -2510,6 +2589,41 @@ static int m3_resume(struct pci_dev *pci)
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_SND_MAESTRO3_INPUT
|
||||
static int __devinit snd_m3_input_register(struct snd_m3 *chip)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0",
|
||||
pci_name(chip->pci));
|
||||
|
||||
input_dev->name = chip->card->driver;
|
||||
input_dev->phys = chip->phys;
|
||||
input_dev->id.bustype = BUS_PCI;
|
||||
input_dev->id.vendor = chip->pci->vendor;
|
||||
input_dev->id.product = chip->pci->device;
|
||||
input_dev->dev.parent = &chip->pci->dev;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(KEY_MUTE, input_dev->keybit);
|
||||
__set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
|
||||
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err) {
|
||||
input_free_device(input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->input_dev = input_dev;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_INPUT */
|
||||
|
||||
/*
|
||||
*/
|
||||
@ -2553,7 +2667,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
|
||||
}
|
||||
|
||||
spin_lock_init(&chip->reg_lock);
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
spin_lock_init(&chip->ac97_lock);
|
||||
#endif
|
||||
|
||||
switch (pci->device) {
|
||||
case PCI_DEVICE_ID_ESS_ALLEGRO:
|
||||
@ -2636,7 +2752,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
|
||||
|
||||
snd_m3_hv_init(chip);
|
||||
|
||||
#ifndef CONFIG_SND_MAESTRO3_INPUT
|
||||
tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
|
||||
#endif
|
||||
|
||||
if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED,
|
||||
card->driver, chip)) {
|
||||
@ -2668,7 +2786,16 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
|
||||
|
||||
if ((err = snd_m3_pcm(chip, 0)) < 0)
|
||||
return err;
|
||||
|
||||
|
||||
#ifdef CONFIG_SND_MAESTRO3_INPUT
|
||||
if (chip->hv_config & HV_CTRL_ENABLE) {
|
||||
err = snd_m3_input_register(chip);
|
||||
if (err)
|
||||
snd_printk(KERN_WARNING "Input device registration "
|
||||
"failed with error %i", err);
|
||||
}
|
||||
#endif
|
||||
|
||||
snd_m3_enable_ints(chip);
|
||||
snd_m3_assp_continue(chip);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user