Merge branch 'topic/misc' into for-linus
This commit is contained in:
commit
02e5fbf622
|
@ -4264,6 +4264,13 @@ M: Tim Hockin <thockin@hockin.org>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/net/natsemi.c
|
F: drivers/net/natsemi.c
|
||||||
|
|
||||||
|
NATIVE INSTRUMENTS USB SOUND INTERFACE DRIVER
|
||||||
|
M: Daniel Mack <zonque@gmail.com>
|
||||||
|
S: Maintained
|
||||||
|
L: alsa-devel@alsa-project.org
|
||||||
|
W: http://www.native-instruments.com
|
||||||
|
F: sound/usb/caiaq/
|
||||||
|
|
||||||
NCP FILESYSTEM
|
NCP FILESYSTEM
|
||||||
M: Petr Vandrovec <petr@vandrovec.name>
|
M: Petr Vandrovec <petr@vandrovec.name>
|
||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
|
|
|
@ -166,21 +166,6 @@ config RADIO_MAXIRADIO
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called radio-maxiradio.
|
module will be called radio-maxiradio.
|
||||||
|
|
||||||
config RADIO_MAESTRO
|
|
||||||
tristate "Maestro on board radio"
|
|
||||||
depends on VIDEO_V4L2 && PCI
|
|
||||||
---help---
|
|
||||||
Say Y here to directly support the on-board radio tuner on the
|
|
||||||
Maestro 2 or 2E sound card.
|
|
||||||
|
|
||||||
In order to control your radio card, you will need to use programs
|
|
||||||
that are compatible with the Video For Linux API. Information on
|
|
||||||
this API and pointers to "v4l" programs may be found at
|
|
||||||
<file:Documentation/video4linux/API.html>.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called radio-maestro.
|
|
||||||
|
|
||||||
config RADIO_MIROPCM20
|
config RADIO_MIROPCM20
|
||||||
tristate "miroSOUND PCM20 radio"
|
tristate "miroSOUND PCM20 radio"
|
||||||
depends on ISA && VIDEO_V4L2 && SND
|
depends on ISA && VIDEO_V4L2 && SND
|
||||||
|
|
|
@ -16,7 +16,6 @@ obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
|
||||||
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
|
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
|
||||||
obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
|
obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
|
||||||
obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
|
obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
|
||||||
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
|
|
||||||
obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
|
obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
|
||||||
obj-$(CONFIG_USB_DSBR) += dsbr100.o
|
obj-$(CONFIG_USB_DSBR) += dsbr100.o
|
||||||
obj-$(CONFIG_RADIO_SI470X) += si470x/
|
obj-$(CONFIG_RADIO_SI470X) += si470x/
|
||||||
|
|
|
@ -1,452 +0,0 @@
|
||||||
/* Maestro PCI sound card radio driver for Linux support
|
|
||||||
* (c) 2000 A. Tlalka, atlka@pg.gda.pl
|
|
||||||
* Notes on the hardware
|
|
||||||
*
|
|
||||||
* + Frequency control is done digitally
|
|
||||||
* + No volume control - only mute/unmute - you have to use Aux line volume
|
|
||||||
* control on Maestro card to set the volume
|
|
||||||
* + Radio status (tuned/not_tuned and stereo/mono) is valid some time after
|
|
||||||
* frequency setting (>100ms) and only when the radio is unmuted.
|
|
||||||
* version 0.02
|
|
||||||
* + io port is automatically detected - only the first radio is used
|
|
||||||
* version 0.03
|
|
||||||
* + thread access locking additions
|
|
||||||
* version 0.04
|
|
||||||
* + code improvements
|
|
||||||
* + VIDEO_TUNER_LOW is permanent
|
|
||||||
*
|
|
||||||
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
|
|
||||||
#include <linux/pci.h>
|
|
||||||
#include <linux/videodev2.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <media/v4l2-device.h>
|
|
||||||
#include <media/v4l2-ioctl.h>
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
|
|
||||||
MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
static int radio_nr = -1;
|
|
||||||
module_param(radio_nr, int, 0);
|
|
||||||
|
|
||||||
#define RADIO_VERSION KERNEL_VERSION(0, 0, 6)
|
|
||||||
#define DRIVER_VERSION "0.06"
|
|
||||||
|
|
||||||
#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
|
|
||||||
|
|
||||||
#define IO_MASK 4 /* mask register offset from GPIO_DATA
|
|
||||||
bits 1=unmask write to given bit */
|
|
||||||
#define IO_DIR 8 /* direction register offset from GPIO_DATA
|
|
||||||
bits 0/1=read/write direction */
|
|
||||||
|
|
||||||
#define GPIO6 0x0040 /* mask bits for GPIO lines */
|
|
||||||
#define GPIO7 0x0080
|
|
||||||
#define GPIO8 0x0100
|
|
||||||
#define GPIO9 0x0200
|
|
||||||
|
|
||||||
#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */
|
|
||||||
#define STR_CLK GPIO7
|
|
||||||
#define STR_WREN GPIO8
|
|
||||||
#define STR_MOST GPIO9
|
|
||||||
|
|
||||||
#define FREQ_LO 50*16000
|
|
||||||
#define FREQ_HI 150*16000
|
|
||||||
|
|
||||||
#define FREQ_IF 171200 /* 10.7*16000 */
|
|
||||||
#define FREQ_STEP 200 /* 12.5*16 */
|
|
||||||
|
|
||||||
#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
|
|
||||||
/(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
|
|
||||||
|
|
||||||
#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
|
|
||||||
|
|
||||||
struct maestro {
|
|
||||||
struct v4l2_device v4l2_dev;
|
|
||||||
struct video_device vdev;
|
|
||||||
struct pci_dev *pdev;
|
|
||||||
struct mutex lock;
|
|
||||||
|
|
||||||
u16 io; /* base of Maestro card radio io (GPIO_DATA)*/
|
|
||||||
u16 muted; /* VIDEO_AUDIO_MUTE */
|
|
||||||
u16 stereo; /* VIDEO_TUNER_STEREO_ON */
|
|
||||||
u16 tuned; /* signal strength (0 or 0xffff) */
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct maestro *to_maestro(struct v4l2_device *v4l2_dev)
|
|
||||||
{
|
|
||||||
return container_of(v4l2_dev, struct maestro, v4l2_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 radio_bits_get(struct maestro *dev)
|
|
||||||
{
|
|
||||||
u16 io = dev->io, l, rdata;
|
|
||||||
u32 data = 0;
|
|
||||||
u16 omask;
|
|
||||||
|
|
||||||
omask = inw(io + IO_MASK);
|
|
||||||
outw(~(STR_CLK | STR_WREN), io + IO_MASK);
|
|
||||||
outw(0, io);
|
|
||||||
udelay(16);
|
|
||||||
|
|
||||||
for (l = 24; l--;) {
|
|
||||||
outw(STR_CLK, io); /* HI state */
|
|
||||||
udelay(2);
|
|
||||||
if (!l)
|
|
||||||
dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
|
|
||||||
outw(0, io); /* LO state */
|
|
||||||
udelay(2);
|
|
||||||
data <<= 1; /* shift data */
|
|
||||||
rdata = inw(io);
|
|
||||||
if (!l)
|
|
||||||
dev->stereo = (rdata & STR_MOST) ? 0 : 1;
|
|
||||||
else if (rdata & STR_DATA)
|
|
||||||
data++;
|
|
||||||
udelay(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->muted)
|
|
||||||
outw(STR_WREN, io);
|
|
||||||
|
|
||||||
udelay(4);
|
|
||||||
outw(omask, io + IO_MASK);
|
|
||||||
|
|
||||||
return data & 0x3ffe;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void radio_bits_set(struct maestro *dev, u32 data)
|
|
||||||
{
|
|
||||||
u16 io = dev->io, l, bits;
|
|
||||||
u16 omask, odir;
|
|
||||||
|
|
||||||
omask = inw(io + IO_MASK);
|
|
||||||
odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
|
|
||||||
outw(odir | STR_DATA, io + IO_DIR);
|
|
||||||
outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
|
|
||||||
udelay(16);
|
|
||||||
for (l = 25; l; l--) {
|
|
||||||
bits = ((data >> 18) & STR_DATA) | STR_WREN;
|
|
||||||
data <<= 1; /* shift data */
|
|
||||||
outw(bits, io); /* start strobe */
|
|
||||||
udelay(2);
|
|
||||||
outw(bits | STR_CLK, io); /* HI level */
|
|
||||||
udelay(2);
|
|
||||||
outw(bits, io); /* LO level */
|
|
||||||
udelay(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dev->muted)
|
|
||||||
outw(0, io);
|
|
||||||
|
|
||||||
udelay(4);
|
|
||||||
outw(omask, io + IO_MASK);
|
|
||||||
outw(odir, io + IO_DIR);
|
|
||||||
msleep(125);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_querycap(struct file *file, void *priv,
|
|
||||||
struct v4l2_capability *v)
|
|
||||||
{
|
|
||||||
struct maestro *dev = video_drvdata(file);
|
|
||||||
|
|
||||||
strlcpy(v->driver, "radio-maestro", sizeof(v->driver));
|
|
||||||
strlcpy(v->card, "Maestro Radio", sizeof(v->card));
|
|
||||||
snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
|
|
||||||
v->version = RADIO_VERSION;
|
|
||||||
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_g_tuner(struct file *file, void *priv,
|
|
||||||
struct v4l2_tuner *v)
|
|
||||||
{
|
|
||||||
struct maestro *dev = video_drvdata(file);
|
|
||||||
|
|
||||||
if (v->index > 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
radio_bits_get(dev);
|
|
||||||
|
|
||||||
strlcpy(v->name, "FM", sizeof(v->name));
|
|
||||||
v->type = V4L2_TUNER_RADIO;
|
|
||||||
v->rangelow = FREQ_LO;
|
|
||||||
v->rangehigh = FREQ_HI;
|
|
||||||
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
|
||||||
v->capability = V4L2_TUNER_CAP_LOW;
|
|
||||||
if (dev->stereo)
|
|
||||||
v->audmode = V4L2_TUNER_MODE_STEREO;
|
|
||||||
else
|
|
||||||
v->audmode = V4L2_TUNER_MODE_MONO;
|
|
||||||
v->signal = dev->tuned;
|
|
||||||
mutex_unlock(&dev->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_s_tuner(struct file *file, void *priv,
|
|
||||||
struct v4l2_tuner *v)
|
|
||||||
{
|
|
||||||
return v->index ? -EINVAL : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_s_frequency(struct file *file, void *priv,
|
|
||||||
struct v4l2_frequency *f)
|
|
||||||
{
|
|
||||||
struct maestro *dev = video_drvdata(file);
|
|
||||||
|
|
||||||
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
|
||||||
return -EINVAL;
|
|
||||||
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
|
|
||||||
return -EINVAL;
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
radio_bits_set(dev, FREQ2BITS(f->frequency));
|
|
||||||
mutex_unlock(&dev->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_g_frequency(struct file *file, void *priv,
|
|
||||||
struct v4l2_frequency *f)
|
|
||||||
{
|
|
||||||
struct maestro *dev = video_drvdata(file);
|
|
||||||
|
|
||||||
if (f->tuner != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
f->type = V4L2_TUNER_RADIO;
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
f->frequency = BITS2FREQ(radio_bits_get(dev));
|
|
||||||
mutex_unlock(&dev->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_queryctrl(struct file *file, void *priv,
|
|
||||||
struct v4l2_queryctrl *qc)
|
|
||||||
{
|
|
||||||
switch (qc->id) {
|
|
||||||
case V4L2_CID_AUDIO_MUTE:
|
|
||||||
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
|
|
||||||
}
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_g_ctrl(struct file *file, void *priv,
|
|
||||||
struct v4l2_control *ctrl)
|
|
||||||
{
|
|
||||||
struct maestro *dev = video_drvdata(file);
|
|
||||||
|
|
||||||
switch (ctrl->id) {
|
|
||||||
case V4L2_CID_AUDIO_MUTE:
|
|
||||||
ctrl->value = dev->muted;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_s_ctrl(struct file *file, void *priv,
|
|
||||||
struct v4l2_control *ctrl)
|
|
||||||
{
|
|
||||||
struct maestro *dev = video_drvdata(file);
|
|
||||||
u16 io = dev->io;
|
|
||||||
u16 omask;
|
|
||||||
|
|
||||||
switch (ctrl->id) {
|
|
||||||
case V4L2_CID_AUDIO_MUTE:
|
|
||||||
mutex_lock(&dev->lock);
|
|
||||||
omask = inw(io + IO_MASK);
|
|
||||||
outw(~STR_WREN, io + IO_MASK);
|
|
||||||
dev->muted = ctrl->value;
|
|
||||||
outw(dev->muted ? STR_WREN : 0, io);
|
|
||||||
udelay(4);
|
|
||||||
outw(omask, io + IO_MASK);
|
|
||||||
msleep(125);
|
|
||||||
mutex_unlock(&dev->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
|
|
||||||
{
|
|
||||||
*i = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
|
|
||||||
{
|
|
||||||
return i ? -EINVAL : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_g_audio(struct file *file, void *priv,
|
|
||||||
struct v4l2_audio *a)
|
|
||||||
{
|
|
||||||
a->index = 0;
|
|
||||||
strlcpy(a->name, "Radio", sizeof(a->name));
|
|
||||||
a->capability = V4L2_AUDCAP_STEREO;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vidioc_s_audio(struct file *file, void *priv,
|
|
||||||
struct v4l2_audio *a)
|
|
||||||
{
|
|
||||||
return a->index ? -EINVAL : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct v4l2_file_operations maestro_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.unlocked_ioctl = video_ioctl2,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct v4l2_ioctl_ops maestro_ioctl_ops = {
|
|
||||||
.vidioc_querycap = vidioc_querycap,
|
|
||||||
.vidioc_g_tuner = vidioc_g_tuner,
|
|
||||||
.vidioc_s_tuner = vidioc_s_tuner,
|
|
||||||
.vidioc_g_audio = vidioc_g_audio,
|
|
||||||
.vidioc_s_audio = vidioc_s_audio,
|
|
||||||
.vidioc_g_input = vidioc_g_input,
|
|
||||||
.vidioc_s_input = vidioc_s_input,
|
|
||||||
.vidioc_g_frequency = vidioc_g_frequency,
|
|
||||||
.vidioc_s_frequency = vidioc_s_frequency,
|
|
||||||
.vidioc_queryctrl = vidioc_queryctrl,
|
|
||||||
.vidioc_g_ctrl = vidioc_g_ctrl,
|
|
||||||
.vidioc_s_ctrl = vidioc_s_ctrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
static u16 __devinit radio_power_on(struct maestro *dev)
|
|
||||||
{
|
|
||||||
register u16 io = dev->io;
|
|
||||||
register u32 ofreq;
|
|
||||||
u16 omask, odir;
|
|
||||||
|
|
||||||
omask = inw(io + IO_MASK);
|
|
||||||
odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
|
|
||||||
outw(odir & ~STR_WREN, io + IO_DIR);
|
|
||||||
dev->muted = inw(io) & STR_WREN ? 0 : 1;
|
|
||||||
outw(odir, io + IO_DIR);
|
|
||||||
outw(~(STR_WREN | STR_CLK), io + IO_MASK);
|
|
||||||
outw(dev->muted ? 0 : STR_WREN, io);
|
|
||||||
udelay(16);
|
|
||||||
outw(omask, io + IO_MASK);
|
|
||||||
ofreq = radio_bits_get(dev);
|
|
||||||
|
|
||||||
if ((ofreq < FREQ2BITS(FREQ_LO)) || (ofreq > FREQ2BITS(FREQ_HI)))
|
|
||||||
ofreq = FREQ2BITS(FREQ_LO);
|
|
||||||
radio_bits_set(dev, ofreq);
|
|
||||||
|
|
||||||
return (ofreq == radio_bits_get(dev));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __devinit maestro_probe(struct pci_dev *pdev,
|
|
||||||
const struct pci_device_id *ent)
|
|
||||||
{
|
|
||||||
struct maestro *dev;
|
|
||||||
struct v4l2_device *v4l2_dev;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = pci_enable_device(pdev);
|
|
||||||
if (retval) {
|
|
||||||
dev_err(&pdev->dev, "enabling pci device failed!\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = -ENOMEM;
|
|
||||||
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
||||||
if (dev == NULL) {
|
|
||||||
dev_err(&pdev->dev, "not enough memory\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
v4l2_dev = &dev->v4l2_dev;
|
|
||||||
mutex_init(&dev->lock);
|
|
||||||
dev->pdev = pdev;
|
|
||||||
|
|
||||||
strlcpy(v4l2_dev->name, "maestro", sizeof(v4l2_dev->name));
|
|
||||||
|
|
||||||
retval = v4l2_device_register(&pdev->dev, v4l2_dev);
|
|
||||||
if (retval < 0) {
|
|
||||||
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
|
|
||||||
goto errfr;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->io = pci_resource_start(pdev, 0) + GPIO_DATA;
|
|
||||||
|
|
||||||
strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
|
|
||||||
dev->vdev.v4l2_dev = v4l2_dev;
|
|
||||||
dev->vdev.fops = &maestro_fops;
|
|
||||||
dev->vdev.ioctl_ops = &maestro_ioctl_ops;
|
|
||||||
dev->vdev.release = video_device_release_empty;
|
|
||||||
video_set_drvdata(&dev->vdev, dev);
|
|
||||||
|
|
||||||
if (!radio_power_on(dev)) {
|
|
||||||
retval = -EIO;
|
|
||||||
goto errfr1;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr);
|
|
||||||
if (retval) {
|
|
||||||
v4l2_err(v4l2_dev, "can't register video device!\n");
|
|
||||||
goto errfr1;
|
|
||||||
}
|
|
||||||
|
|
||||||
v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
errfr1:
|
|
||||||
v4l2_device_unregister(v4l2_dev);
|
|
||||||
errfr:
|
|
||||||
kfree(dev);
|
|
||||||
err:
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __devexit maestro_remove(struct pci_dev *pdev)
|
|
||||||
{
|
|
||||||
struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
|
|
||||||
struct maestro *dev = to_maestro(v4l2_dev);
|
|
||||||
|
|
||||||
video_unregister_device(&dev->vdev);
|
|
||||||
v4l2_device_unregister(&dev->v4l2_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct pci_device_id maestro_r_pci_tbl[] = {
|
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968),
|
|
||||||
.class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
|
|
||||||
.class_mask = 0xffff00 },
|
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978),
|
|
||||||
.class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
|
|
||||||
.class_mask = 0xffff00 },
|
|
||||||
{ 0 }
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl);
|
|
||||||
|
|
||||||
static struct pci_driver maestro_r_driver = {
|
|
||||||
.name = "maestro_radio",
|
|
||||||
.id_table = maestro_r_pci_tbl,
|
|
||||||
.probe = maestro_probe,
|
|
||||||
.remove = __devexit_p(maestro_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init maestro_radio_init(void)
|
|
||||||
{
|
|
||||||
int retval = pci_register_driver(&maestro_r_driver);
|
|
||||||
|
|
||||||
if (retval)
|
|
||||||
printk(KERN_ERR "error during registration pci driver\n");
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit maestro_radio_exit(void)
|
|
||||||
{
|
|
||||||
pci_unregister_driver(&maestro_r_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(maestro_radio_init);
|
|
||||||
module_exit(maestro_radio_exit);
|
|
|
@ -113,6 +113,7 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, v
|
||||||
void snd_ctl_free_one(struct snd_kcontrol * kcontrol);
|
void snd_ctl_free_one(struct snd_kcontrol * kcontrol);
|
||||||
int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
|
int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
|
||||||
int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
|
int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
|
||||||
|
int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace);
|
||||||
int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
|
int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
|
||||||
int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
|
int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
|
||||||
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
|
int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
|
||||||
|
|
|
@ -26,29 +26,37 @@
|
||||||
#include <media/v4l2-dev.h>
|
#include <media/v4l2-dev.h>
|
||||||
#include <media/v4l2-ioctl.h>
|
#include <media/v4l2-ioctl.h>
|
||||||
|
|
||||||
|
#define TEA575X_FMIF 10700
|
||||||
|
|
||||||
|
#define TEA575X_DATA (1 << 0)
|
||||||
|
#define TEA575X_CLK (1 << 1)
|
||||||
|
#define TEA575X_WREN (1 << 2)
|
||||||
|
#define TEA575X_MOST (1 << 3)
|
||||||
|
|
||||||
struct snd_tea575x;
|
struct snd_tea575x;
|
||||||
|
|
||||||
struct snd_tea575x_ops {
|
struct snd_tea575x_ops {
|
||||||
void (*write)(struct snd_tea575x *tea, unsigned int val);
|
void (*set_pins)(struct snd_tea575x *tea, u8 pins);
|
||||||
unsigned int (*read)(struct snd_tea575x *tea);
|
u8 (*get_pins)(struct snd_tea575x *tea);
|
||||||
void (*mute)(struct snd_tea575x *tea, unsigned int mute);
|
void (*set_direction)(struct snd_tea575x *tea, bool output);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct snd_tea575x {
|
struct snd_tea575x {
|
||||||
struct snd_card *card;
|
|
||||||
struct video_device *vd; /* video device */
|
struct video_device *vd; /* video device */
|
||||||
int dev_nr; /* requested device number + 1 */
|
bool tea5759; /* 5759 chip is present */
|
||||||
int tea5759; /* 5759 chip is present */
|
bool mute; /* Device is muted? */
|
||||||
int mute; /* Device is muted? */
|
bool stereo; /* receiving stereo */
|
||||||
unsigned int freq_fixup; /* crystal onboard */
|
bool tuned; /* tuned to a station */
|
||||||
unsigned int val; /* hw value */
|
unsigned int val; /* hw value */
|
||||||
unsigned long freq; /* frequency */
|
unsigned long freq; /* frequency */
|
||||||
unsigned long in_use; /* set if the device is in use */
|
unsigned long in_use; /* set if the device is in use */
|
||||||
struct snd_tea575x_ops *ops;
|
struct snd_tea575x_ops *ops;
|
||||||
void *private_data;
|
void *private_data;
|
||||||
|
u8 card[32];
|
||||||
|
u8 bus_info[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
void snd_tea575x_init(struct snd_tea575x *tea);
|
int snd_tea575x_init(struct snd_tea575x *tea);
|
||||||
void snd_tea575x_exit(struct snd_tea575x *tea);
|
void snd_tea575x_exit(struct snd_tea575x *tea);
|
||||||
|
|
||||||
#endif /* __SOUND_TEA575X_TUNER_H */
|
#endif /* __SOUND_TEA575X_TUNER_H */
|
||||||
|
|
|
@ -365,6 +365,70 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
|
||||||
|
|
||||||
EXPORT_SYMBOL(snd_ctl_add);
|
EXPORT_SYMBOL(snd_ctl_add);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_ctl_replace - replace the control instance of the card
|
||||||
|
* @card: the card instance
|
||||||
|
* @kcontrol: the control instance to replace
|
||||||
|
* @add_on_replace: add the control if not already added
|
||||||
|
*
|
||||||
|
* Replaces the given control. If the given control does not exist
|
||||||
|
* and the add_on_replace flag is set, the control is added. If the
|
||||||
|
* control exists, it is destroyed first.
|
||||||
|
*
|
||||||
|
* Returns zero if successful, or a negative error code on failure.
|
||||||
|
*
|
||||||
|
* It frees automatically the control which cannot be added or replaced.
|
||||||
|
*/
|
||||||
|
int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
|
||||||
|
bool add_on_replace)
|
||||||
|
{
|
||||||
|
struct snd_ctl_elem_id id;
|
||||||
|
unsigned int idx;
|
||||||
|
struct snd_kcontrol *old;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!kcontrol)
|
||||||
|
return -EINVAL;
|
||||||
|
if (snd_BUG_ON(!card || !kcontrol->info)) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
id = kcontrol->id;
|
||||||
|
down_write(&card->controls_rwsem);
|
||||||
|
old = snd_ctl_find_id(card, &id);
|
||||||
|
if (!old) {
|
||||||
|
if (add_on_replace)
|
||||||
|
goto add;
|
||||||
|
up_write(&card->controls_rwsem);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
ret = snd_ctl_remove(card, old);
|
||||||
|
if (ret < 0) {
|
||||||
|
up_write(&card->controls_rwsem);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
add:
|
||||||
|
if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
|
||||||
|
up_write(&card->controls_rwsem);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
list_add_tail(&kcontrol->list, &card->controls);
|
||||||
|
card->controls_count += kcontrol->count;
|
||||||
|
kcontrol->id.numid = card->last_numid + 1;
|
||||||
|
card->last_numid += kcontrol->count;
|
||||||
|
up_write(&card->controls_rwsem);
|
||||||
|
for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
|
||||||
|
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
snd_ctl_free_one(kcontrol);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(snd_ctl_replace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* snd_ctl_remove - remove the control from the card and release it
|
* snd_ctl_remove - remove the control from the card and release it
|
||||||
* @card: the card instance
|
* @card: the card instance
|
||||||
|
|
|
@ -514,7 +514,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
|
||||||
id = card->id;
|
id = card->id;
|
||||||
|
|
||||||
if (*id == '\0')
|
if (*id == '\0')
|
||||||
strcpy(id, "default");
|
strcpy(id, "Default");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (loops-- == 0) {
|
if (loops-- == 0) {
|
||||||
|
|
|
@ -189,6 +189,7 @@ static void xrun(struct snd_pcm_substream *substream)
|
||||||
#define XRUN_LOG_CNT 10
|
#define XRUN_LOG_CNT 10
|
||||||
|
|
||||||
struct hwptr_log_entry {
|
struct hwptr_log_entry {
|
||||||
|
unsigned int in_interrupt;
|
||||||
unsigned long jiffies;
|
unsigned long jiffies;
|
||||||
snd_pcm_uframes_t pos;
|
snd_pcm_uframes_t pos;
|
||||||
snd_pcm_uframes_t period_size;
|
snd_pcm_uframes_t period_size;
|
||||||
|
@ -204,7 +205,7 @@ struct snd_pcm_hwptr_log {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void xrun_log(struct snd_pcm_substream *substream,
|
static void xrun_log(struct snd_pcm_substream *substream,
|
||||||
snd_pcm_uframes_t pos)
|
snd_pcm_uframes_t pos, int in_interrupt)
|
||||||
{
|
{
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
|
struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
|
||||||
|
@ -220,6 +221,7 @@ static void xrun_log(struct snd_pcm_substream *substream,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
entry = &log->entries[log->idx];
|
entry = &log->entries[log->idx];
|
||||||
|
entry->in_interrupt = in_interrupt;
|
||||||
entry->jiffies = jiffies;
|
entry->jiffies = jiffies;
|
||||||
entry->pos = pos;
|
entry->pos = pos;
|
||||||
entry->period_size = runtime->period_size;
|
entry->period_size = runtime->period_size;
|
||||||
|
@ -246,9 +248,11 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
|
||||||
entry = &log->entries[idx];
|
entry = &log->entries[idx];
|
||||||
if (entry->period_size == 0)
|
if (entry->period_size == 0)
|
||||||
break;
|
break;
|
||||||
snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
|
snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
|
||||||
"hwptr=%ld/%ld\n",
|
"hwptr=%ld/%ld\n",
|
||||||
name, entry->jiffies, (unsigned long)entry->pos,
|
name, entry->in_interrupt ? "[Q] " : "",
|
||||||
|
entry->jiffies,
|
||||||
|
(unsigned long)entry->pos,
|
||||||
(unsigned long)entry->period_size,
|
(unsigned long)entry->period_size,
|
||||||
(unsigned long)entry->buffer_size,
|
(unsigned long)entry->buffer_size,
|
||||||
(unsigned long)entry->old_hw_ptr,
|
(unsigned long)entry->old_hw_ptr,
|
||||||
|
@ -262,7 +266,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
|
||||||
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
|
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
|
||||||
|
|
||||||
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
|
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
|
||||||
#define xrun_log(substream, pos) do { } while (0)
|
#define xrun_log(substream, pos, in_interrupt) do { } while (0)
|
||||||
#define xrun_log_show(substream) do { } while (0)
|
#define xrun_log_show(substream) do { } while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -326,7 +330,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
pos -= pos % runtime->min_align;
|
pos -= pos % runtime->min_align;
|
||||||
if (xrun_debug(substream, XRUN_DEBUG_LOG))
|
if (xrun_debug(substream, XRUN_DEBUG_LOG))
|
||||||
xrun_log(substream, pos);
|
xrun_log(substream, pos, in_interrupt);
|
||||||
hw_base = runtime->hw_ptr_base;
|
hw_base = runtime->hw_ptr_base;
|
||||||
new_hw_ptr = hw_base + pos;
|
new_hw_ptr = hw_base + pos;
|
||||||
if (in_interrupt) {
|
if (in_interrupt) {
|
||||||
|
|
|
@ -22,4 +22,15 @@ config SND_FIREWIRE_SPEAKERS
|
||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called snd-firewire-speakers.
|
will be called snd-firewire-speakers.
|
||||||
|
|
||||||
|
config SND_ISIGHT
|
||||||
|
tristate "Apple iSight microphone"
|
||||||
|
select SND_PCM
|
||||||
|
select SND_FIREWIRE_LIB
|
||||||
|
help
|
||||||
|
Say Y here to include support for the front and rear microphones
|
||||||
|
of the Apple iSight web camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called snd-isight.
|
||||||
|
|
||||||
endif # SND_FIREWIRE
|
endif # SND_FIREWIRE
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
|
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
|
||||||
fcp.o cmp.o amdtp.o
|
fcp.o cmp.o amdtp.o
|
||||||
snd-firewire-speakers-objs := speakers.o
|
snd-firewire-speakers-objs := speakers.o
|
||||||
|
snd-isight-objs := isight.o
|
||||||
|
|
||||||
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
|
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
|
||||||
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
|
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
|
||||||
|
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
|
||||||
|
|
|
@ -0,0 +1,755 @@
|
||||||
|
/*
|
||||||
|
* Apple iSight audio driver
|
||||||
|
*
|
||||||
|
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||||
|
* Licensed under the terms of the GNU General Public License, version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/byteorder.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/firewire.h>
|
||||||
|
#include <linux/firewire-constants.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <sound/control.h>
|
||||||
|
#include <sound/core.h>
|
||||||
|
#include <sound/initval.h>
|
||||||
|
#include <sound/pcm.h>
|
||||||
|
#include <sound/tlv.h>
|
||||||
|
#include "lib.h"
|
||||||
|
#include "iso-resources.h"
|
||||||
|
#include "packets-buffer.h"
|
||||||
|
|
||||||
|
#define OUI_APPLE 0x000a27
|
||||||
|
#define MODEL_APPLE_ISIGHT 0x000008
|
||||||
|
#define SW_ISIGHT_AUDIO 0x000010
|
||||||
|
|
||||||
|
#define REG_AUDIO_ENABLE 0x000
|
||||||
|
#define AUDIO_ENABLE 0x80000000
|
||||||
|
#define REG_DEF_AUDIO_GAIN 0x204
|
||||||
|
#define REG_GAIN_RAW_START 0x210
|
||||||
|
#define REG_GAIN_RAW_END 0x214
|
||||||
|
#define REG_GAIN_DB_START 0x218
|
||||||
|
#define REG_GAIN_DB_END 0x21c
|
||||||
|
#define REG_SAMPLE_RATE_INQUIRY 0x280
|
||||||
|
#define REG_ISO_TX_CONFIG 0x300
|
||||||
|
#define SPEED_SHIFT 16
|
||||||
|
#define REG_SAMPLE_RATE 0x400
|
||||||
|
#define RATE_48000 0x80000000
|
||||||
|
#define REG_GAIN 0x500
|
||||||
|
#define REG_MUTE 0x504
|
||||||
|
|
||||||
|
#define MAX_FRAMES_PER_PACKET 475
|
||||||
|
|
||||||
|
#define QUEUE_LENGTH 20
|
||||||
|
|
||||||
|
struct isight {
|
||||||
|
struct snd_card *card;
|
||||||
|
struct fw_unit *unit;
|
||||||
|
struct fw_device *device;
|
||||||
|
u64 audio_base;
|
||||||
|
struct fw_address_handler iris_handler;
|
||||||
|
struct snd_pcm_substream *pcm;
|
||||||
|
struct mutex mutex;
|
||||||
|
struct iso_packets_buffer buffer;
|
||||||
|
struct fw_iso_resources resources;
|
||||||
|
struct fw_iso_context *context;
|
||||||
|
bool pcm_active;
|
||||||
|
bool pcm_running;
|
||||||
|
bool first_packet;
|
||||||
|
int packet_index;
|
||||||
|
u32 total_samples;
|
||||||
|
unsigned int buffer_pointer;
|
||||||
|
unsigned int period_counter;
|
||||||
|
s32 gain_min, gain_max;
|
||||||
|
unsigned int gain_tlv[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct audio_payload {
|
||||||
|
__be32 sample_count;
|
||||||
|
__be32 signature;
|
||||||
|
__be32 sample_total;
|
||||||
|
__be32 reserved;
|
||||||
|
__be16 samples[2 * MAX_FRAMES_PER_PACKET];
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("iSight audio driver");
|
||||||
|
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
||||||
|
static struct fw_iso_packet audio_packet = {
|
||||||
|
.payload_length = sizeof(struct audio_payload),
|
||||||
|
.interrupt = 1,
|
||||||
|
.header_length = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void isight_update_pointers(struct isight *isight, unsigned int count)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = isight->pcm->runtime;
|
||||||
|
unsigned int ptr;
|
||||||
|
|
||||||
|
smp_wmb(); /* update buffer data before buffer pointer */
|
||||||
|
|
||||||
|
ptr = isight->buffer_pointer;
|
||||||
|
ptr += count;
|
||||||
|
if (ptr >= runtime->buffer_size)
|
||||||
|
ptr -= runtime->buffer_size;
|
||||||
|
ACCESS_ONCE(isight->buffer_pointer) = ptr;
|
||||||
|
|
||||||
|
isight->period_counter += count;
|
||||||
|
if (isight->period_counter >= runtime->period_size) {
|
||||||
|
isight->period_counter -= runtime->period_size;
|
||||||
|
snd_pcm_period_elapsed(isight->pcm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_samples(struct isight *isight,
|
||||||
|
const __be16 *samples, unsigned int count)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime;
|
||||||
|
unsigned int count1;
|
||||||
|
|
||||||
|
if (!ACCESS_ONCE(isight->pcm_running))
|
||||||
|
return;
|
||||||
|
|
||||||
|
runtime = isight->pcm->runtime;
|
||||||
|
if (isight->buffer_pointer + count <= runtime->buffer_size) {
|
||||||
|
memcpy(runtime->dma_area + isight->buffer_pointer * 4,
|
||||||
|
samples, count * 4);
|
||||||
|
} else {
|
||||||
|
count1 = runtime->buffer_size - isight->buffer_pointer;
|
||||||
|
memcpy(runtime->dma_area + isight->buffer_pointer * 4,
|
||||||
|
samples, count1 * 4);
|
||||||
|
samples += count1 * 2;
|
||||||
|
memcpy(runtime->dma_area, samples, (count - count1) * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
isight_update_pointers(isight, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_pcm_abort(struct isight *isight)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (ACCESS_ONCE(isight->pcm_active)) {
|
||||||
|
snd_pcm_stream_lock_irqsave(isight->pcm, flags);
|
||||||
|
if (snd_pcm_running(isight->pcm))
|
||||||
|
snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN);
|
||||||
|
snd_pcm_stream_unlock_irqrestore(isight->pcm, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_dropped_samples(struct isight *isight, unsigned int total)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime;
|
||||||
|
u32 dropped;
|
||||||
|
unsigned int count1;
|
||||||
|
|
||||||
|
if (!ACCESS_ONCE(isight->pcm_running))
|
||||||
|
return;
|
||||||
|
|
||||||
|
runtime = isight->pcm->runtime;
|
||||||
|
dropped = total - isight->total_samples;
|
||||||
|
if (dropped < runtime->buffer_size) {
|
||||||
|
if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
|
||||||
|
memset(runtime->dma_area + isight->buffer_pointer * 4,
|
||||||
|
0, dropped * 4);
|
||||||
|
} else {
|
||||||
|
count1 = runtime->buffer_size - isight->buffer_pointer;
|
||||||
|
memset(runtime->dma_area + isight->buffer_pointer * 4,
|
||||||
|
0, count1 * 4);
|
||||||
|
memset(runtime->dma_area, 0, (dropped - count1) * 4);
|
||||||
|
}
|
||||||
|
isight_update_pointers(isight, dropped);
|
||||||
|
} else {
|
||||||
|
isight_pcm_abort(isight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_packet(struct fw_iso_context *context, u32 cycle,
|
||||||
|
size_t header_length, void *header, void *data)
|
||||||
|
{
|
||||||
|
struct isight *isight = data;
|
||||||
|
const struct audio_payload *payload;
|
||||||
|
unsigned int index, length, count, total;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (isight->packet_index < 0)
|
||||||
|
return;
|
||||||
|
index = isight->packet_index;
|
||||||
|
payload = isight->buffer.packets[index].buffer;
|
||||||
|
length = be32_to_cpup(header) >> 16;
|
||||||
|
|
||||||
|
if (likely(length >= 16 &&
|
||||||
|
payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
|
||||||
|
count = be32_to_cpu(payload->sample_count);
|
||||||
|
if (likely(count <= (length - 16) / 4)) {
|
||||||
|
total = be32_to_cpu(payload->sample_total);
|
||||||
|
if (unlikely(total != isight->total_samples)) {
|
||||||
|
if (!isight->first_packet)
|
||||||
|
isight_dropped_samples(isight, total);
|
||||||
|
isight->first_packet = false;
|
||||||
|
isight->total_samples = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
isight_samples(isight, payload->samples, count);
|
||||||
|
isight->total_samples += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fw_iso_context_queue(isight->context, &audio_packet,
|
||||||
|
&isight->buffer.iso_buffer,
|
||||||
|
isight->buffer.packets[index].offset);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(&isight->unit->device, "queueing error: %d\n", err);
|
||||||
|
isight_pcm_abort(isight);
|
||||||
|
isight->packet_index = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++index >= QUEUE_LENGTH)
|
||||||
|
index = 0;
|
||||||
|
isight->packet_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_connect(struct isight *isight)
|
||||||
|
{
|
||||||
|
int ch, err, rcode, errors = 0;
|
||||||
|
__be32 value;
|
||||||
|
|
||||||
|
retry_after_bus_reset:
|
||||||
|
ch = fw_iso_resources_allocate(&isight->resources,
|
||||||
|
sizeof(struct audio_payload),
|
||||||
|
isight->device->max_speed);
|
||||||
|
if (ch < 0) {
|
||||||
|
err = ch;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
|
||||||
|
for (;;) {
|
||||||
|
rcode = fw_run_transaction(
|
||||||
|
isight->device->card,
|
||||||
|
TCODE_WRITE_QUADLET_REQUEST,
|
||||||
|
isight->device->node_id,
|
||||||
|
isight->resources.generation,
|
||||||
|
isight->device->max_speed,
|
||||||
|
isight->audio_base + REG_ISO_TX_CONFIG,
|
||||||
|
&value, 4);
|
||||||
|
if (rcode == RCODE_COMPLETE) {
|
||||||
|
return 0;
|
||||||
|
} else if (rcode == RCODE_GENERATION) {
|
||||||
|
fw_iso_resources_free(&isight->resources);
|
||||||
|
goto retry_after_bus_reset;
|
||||||
|
} else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
|
||||||
|
err = -EIO;
|
||||||
|
goto err_resources;
|
||||||
|
}
|
||||||
|
msleep(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
err_resources:
|
||||||
|
fw_iso_resources_free(&isight->resources);
|
||||||
|
error:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_open(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
static const struct snd_pcm_hardware hardware = {
|
||||||
|
.info = SNDRV_PCM_INFO_MMAP |
|
||||||
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
|
SNDRV_PCM_INFO_BATCH |
|
||||||
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
|
SNDRV_PCM_INFO_BLOCK_TRANSFER,
|
||||||
|
.formats = SNDRV_PCM_FMTBIT_S16_BE,
|
||||||
|
.rates = SNDRV_PCM_RATE_48000,
|
||||||
|
.rate_min = 48000,
|
||||||
|
.rate_max = 48000,
|
||||||
|
.channels_min = 2,
|
||||||
|
.channels_max = 2,
|
||||||
|
.buffer_bytes_max = 4 * 1024 * 1024,
|
||||||
|
.period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
|
||||||
|
.period_bytes_max = 1024 * 1024,
|
||||||
|
.periods_min = 2,
|
||||||
|
.periods_max = UINT_MAX,
|
||||||
|
};
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
|
||||||
|
substream->runtime->hw = hardware;
|
||||||
|
|
||||||
|
return iso_packets_buffer_init(&isight->buffer, isight->unit,
|
||||||
|
QUEUE_LENGTH,
|
||||||
|
sizeof(struct audio_payload),
|
||||||
|
DMA_FROM_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_close(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
|
||||||
|
iso_packets_buffer_destroy(&isight->buffer, isight->unit);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *hw_params)
|
||||||
|
{
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||||
|
params_buffer_bytes(hw_params));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ACCESS_ONCE(isight->pcm_active) = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reg_read(struct isight *isight, int offset, __be32 *value)
|
||||||
|
{
|
||||||
|
return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
|
||||||
|
isight->audio_base + offset, value, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reg_write(struct isight *isight, int offset, __be32 value)
|
||||||
|
{
|
||||||
|
return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
|
||||||
|
isight->audio_base + offset, &value, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_stop_streaming(struct isight *isight)
|
||||||
|
{
|
||||||
|
if (!isight->context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fw_iso_context_stop(isight->context);
|
||||||
|
fw_iso_context_destroy(isight->context);
|
||||||
|
isight->context = NULL;
|
||||||
|
fw_iso_resources_free(&isight->resources);
|
||||||
|
reg_write(isight, REG_AUDIO_ENABLE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_hw_free(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
|
||||||
|
ACCESS_ONCE(isight->pcm_active) = false;
|
||||||
|
|
||||||
|
mutex_lock(&isight->mutex);
|
||||||
|
isight_stop_streaming(isight);
|
||||||
|
mutex_unlock(&isight->mutex);
|
||||||
|
|
||||||
|
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_start_streaming(struct isight *isight)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (isight->context) {
|
||||||
|
if (isight->packet_index < 0)
|
||||||
|
isight_stop_streaming(isight);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
|
||||||
|
if (err < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
err = isight_connect(isight);
|
||||||
|
if (err < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
|
||||||
|
if (err < 0)
|
||||||
|
goto err_resources;
|
||||||
|
|
||||||
|
isight->context = fw_iso_context_create(isight->device->card,
|
||||||
|
FW_ISO_CONTEXT_RECEIVE,
|
||||||
|
isight->resources.channel,
|
||||||
|
isight->device->max_speed,
|
||||||
|
4, isight_packet, isight);
|
||||||
|
if (IS_ERR(isight->context)) {
|
||||||
|
err = PTR_ERR(isight->context);
|
||||||
|
isight->context = NULL;
|
||||||
|
goto err_resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < QUEUE_LENGTH; ++i) {
|
||||||
|
err = fw_iso_context_queue(isight->context, &audio_packet,
|
||||||
|
&isight->buffer.iso_buffer,
|
||||||
|
isight->buffer.packets[i].offset);
|
||||||
|
if (err < 0)
|
||||||
|
goto err_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
isight->first_packet = true;
|
||||||
|
isight->packet_index = 0;
|
||||||
|
|
||||||
|
err = fw_iso_context_start(isight->context, -1, 0,
|
||||||
|
FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
|
||||||
|
if (err < 0)
|
||||||
|
goto err_context;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_context:
|
||||||
|
fw_iso_context_destroy(isight->context);
|
||||||
|
isight->context = NULL;
|
||||||
|
err_resources:
|
||||||
|
fw_iso_resources_free(&isight->resources);
|
||||||
|
reg_write(isight, REG_AUDIO_ENABLE, 0);
|
||||||
|
error:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_prepare(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
isight->buffer_pointer = 0;
|
||||||
|
isight->period_counter = 0;
|
||||||
|
|
||||||
|
mutex_lock(&isight->mutex);
|
||||||
|
err = isight_start_streaming(isight);
|
||||||
|
mutex_unlock(&isight->mutex);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||||
|
{
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
|
ACCESS_ONCE(isight->pcm_running) = true;
|
||||||
|
break;
|
||||||
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
|
ACCESS_ONCE(isight->pcm_running) = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct isight *isight = substream->private_data;
|
||||||
|
|
||||||
|
return ACCESS_ONCE(isight->buffer_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_create_pcm(struct isight *isight)
|
||||||
|
{
|
||||||
|
static struct snd_pcm_ops ops = {
|
||||||
|
.open = isight_open,
|
||||||
|
.close = isight_close,
|
||||||
|
.ioctl = snd_pcm_lib_ioctl,
|
||||||
|
.hw_params = isight_hw_params,
|
||||||
|
.hw_free = isight_hw_free,
|
||||||
|
.prepare = isight_prepare,
|
||||||
|
.trigger = isight_trigger,
|
||||||
|
.pointer = isight_pointer,
|
||||||
|
.page = snd_pcm_lib_get_vmalloc_page,
|
||||||
|
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||||
|
};
|
||||||
|
struct snd_pcm *pcm;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
pcm->private_data = isight;
|
||||||
|
strcpy(pcm->name, "iSight");
|
||||||
|
isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||||
|
isight->pcm->ops = &ops;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_gain_info(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_info *info)
|
||||||
|
{
|
||||||
|
struct isight *isight = ctl->private_data;
|
||||||
|
|
||||||
|
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||||
|
info->count = 1;
|
||||||
|
info->value.integer.min = isight->gain_min;
|
||||||
|
info->value.integer.max = isight->gain_max;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_gain_get(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_value *value)
|
||||||
|
{
|
||||||
|
struct isight *isight = ctl->private_data;
|
||||||
|
__be32 gain;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = reg_read(isight, REG_GAIN, &gain);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
value->value.integer.value[0] = (s32)be32_to_cpu(gain);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_gain_put(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_value *value)
|
||||||
|
{
|
||||||
|
struct isight *isight = ctl->private_data;
|
||||||
|
|
||||||
|
if (value->value.integer.value[0] < isight->gain_min ||
|
||||||
|
value->value.integer.value[0] > isight->gain_max)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return reg_write(isight, REG_GAIN,
|
||||||
|
cpu_to_be32(value->value.integer.value[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_mute_get(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_value *value)
|
||||||
|
{
|
||||||
|
struct isight *isight = ctl->private_data;
|
||||||
|
__be32 mute;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = reg_read(isight, REG_MUTE, &mute);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
value->value.integer.value[0] = !mute;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_mute_put(struct snd_kcontrol *ctl,
|
||||||
|
struct snd_ctl_elem_value *value)
|
||||||
|
{
|
||||||
|
struct isight *isight = ctl->private_data;
|
||||||
|
|
||||||
|
return reg_write(isight, REG_MUTE,
|
||||||
|
(__force __be32)!value->value.integer.value[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_create_mixer(struct isight *isight)
|
||||||
|
{
|
||||||
|
static const struct snd_kcontrol_new gain_control = {
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.name = "Mic Capture Volume",
|
||||||
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||||
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||||
|
.info = isight_gain_info,
|
||||||
|
.get = isight_gain_get,
|
||||||
|
.put = isight_gain_put,
|
||||||
|
};
|
||||||
|
static const struct snd_kcontrol_new mute_control = {
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.name = "Mic Capture Switch",
|
||||||
|
.info = snd_ctl_boolean_mono_info,
|
||||||
|
.get = isight_mute_get,
|
||||||
|
.put = isight_mute_put,
|
||||||
|
};
|
||||||
|
__be32 value;
|
||||||
|
struct snd_kcontrol *ctl;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = reg_read(isight, REG_GAIN_RAW_START, &value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
isight->gain_min = be32_to_cpu(value);
|
||||||
|
|
||||||
|
err = reg_read(isight, REG_GAIN_RAW_END, &value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
isight->gain_max = be32_to_cpu(value);
|
||||||
|
|
||||||
|
isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
|
||||||
|
isight->gain_tlv[1] = 2 * sizeof(unsigned int);
|
||||||
|
|
||||||
|
err = reg_read(isight, REG_GAIN_DB_START, &value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100;
|
||||||
|
|
||||||
|
err = reg_read(isight, REG_GAIN_DB_END, &value);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100;
|
||||||
|
|
||||||
|
ctl = snd_ctl_new1(&gain_control, isight);
|
||||||
|
if (ctl)
|
||||||
|
ctl->tlv.p = isight->gain_tlv;
|
||||||
|
err = snd_ctl_add(isight->card, ctl);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_card_free(struct snd_card *card)
|
||||||
|
{
|
||||||
|
struct isight *isight = card->private_data;
|
||||||
|
|
||||||
|
fw_iso_resources_destroy(&isight->resources);
|
||||||
|
fw_unit_put(isight->unit);
|
||||||
|
fw_device_put(isight->device);
|
||||||
|
mutex_destroy(&isight->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 get_unit_base(struct fw_unit *unit)
|
||||||
|
{
|
||||||
|
struct fw_csr_iterator i;
|
||||||
|
int key, value;
|
||||||
|
|
||||||
|
fw_csr_iterator_init(&i, unit->directory);
|
||||||
|
while (fw_csr_iterator_next(&i, &key, &value))
|
||||||
|
if (key == CSR_OFFSET)
|
||||||
|
return CSR_REGISTER_BASE + value * 4;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_probe(struct device *unit_dev)
|
||||||
|
{
|
||||||
|
struct fw_unit *unit = fw_unit(unit_dev);
|
||||||
|
struct fw_device *fw_dev = fw_parent_device(unit);
|
||||||
|
struct snd_card *card;
|
||||||
|
struct isight *isight;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
snd_card_set_dev(card, unit_dev);
|
||||||
|
|
||||||
|
isight = card->private_data;
|
||||||
|
isight->card = card;
|
||||||
|
mutex_init(&isight->mutex);
|
||||||
|
isight->unit = fw_unit_get(unit);
|
||||||
|
isight->device = fw_device_get(fw_dev);
|
||||||
|
isight->audio_base = get_unit_base(unit);
|
||||||
|
if (!isight->audio_base) {
|
||||||
|
dev_err(&unit->device, "audio unit base not found\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
goto err_unit;
|
||||||
|
}
|
||||||
|
fw_iso_resources_init(&isight->resources, unit);
|
||||||
|
|
||||||
|
card->private_free = isight_card_free;
|
||||||
|
|
||||||
|
strcpy(card->driver, "iSight");
|
||||||
|
strcpy(card->shortname, "Apple iSight");
|
||||||
|
snprintf(card->longname, sizeof(card->longname),
|
||||||
|
"Apple iSight (GUID %08x%08x) at %s, S%d",
|
||||||
|
fw_dev->config_rom[3], fw_dev->config_rom[4],
|
||||||
|
dev_name(&unit->device), 100 << fw_dev->max_speed);
|
||||||
|
strcpy(card->mixername, "iSight");
|
||||||
|
|
||||||
|
err = isight_create_pcm(isight);
|
||||||
|
if (err < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
err = isight_create_mixer(isight);
|
||||||
|
if (err < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
err = snd_card_register(card);
|
||||||
|
if (err < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
dev_set_drvdata(unit_dev, isight);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_unit:
|
||||||
|
fw_unit_put(isight->unit);
|
||||||
|
fw_device_put(isight->device);
|
||||||
|
mutex_destroy(&isight->mutex);
|
||||||
|
error:
|
||||||
|
snd_card_free(card);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isight_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct isight *isight = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
isight_pcm_abort(isight);
|
||||||
|
|
||||||
|
snd_card_disconnect(isight->card);
|
||||||
|
|
||||||
|
mutex_lock(&isight->mutex);
|
||||||
|
isight_stop_streaming(isight);
|
||||||
|
mutex_unlock(&isight->mutex);
|
||||||
|
|
||||||
|
snd_card_free_when_closed(isight->card);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isight_bus_reset(struct fw_unit *unit)
|
||||||
|
{
|
||||||
|
struct isight *isight = dev_get_drvdata(&unit->device);
|
||||||
|
|
||||||
|
if (fw_iso_resources_update(&isight->resources) < 0) {
|
||||||
|
isight_pcm_abort(isight);
|
||||||
|
|
||||||
|
mutex_lock(&isight->mutex);
|
||||||
|
isight_stop_streaming(isight);
|
||||||
|
mutex_unlock(&isight->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ieee1394_device_id isight_id_table[] = {
|
||||||
|
{
|
||||||
|
.match_flags = IEEE1394_MATCH_SPECIFIER_ID |
|
||||||
|
IEEE1394_MATCH_VERSION,
|
||||||
|
.specifier_id = OUI_APPLE,
|
||||||
|
.version = SW_ISIGHT_AUDIO,
|
||||||
|
},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
|
||||||
|
|
||||||
|
static struct fw_driver isight_driver = {
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = KBUILD_MODNAME,
|
||||||
|
.bus = &fw_bus_type,
|
||||||
|
.probe = isight_probe,
|
||||||
|
.remove = isight_remove,
|
||||||
|
},
|
||||||
|
.update = isight_bus_reset,
|
||||||
|
.id_table = isight_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init alsa_isight_init(void)
|
||||||
|
{
|
||||||
|
return driver_register(&isight_driver.driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit alsa_isight_exit(void)
|
||||||
|
{
|
||||||
|
driver_unregister(&isight_driver.driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(alsa_isight_init);
|
||||||
|
module_exit(alsa_isight_exit);
|
|
@ -36,6 +36,7 @@ int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(fw_iso_resources_init);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fw_iso_resources_destroy - destroy a resource manager
|
* fw_iso_resources_destroy - destroy a resource manager
|
||||||
|
@ -48,6 +49,7 @@ void fw_iso_resources_destroy(struct fw_iso_resources *r)
|
||||||
mutex_destroy(&r->mutex);
|
mutex_destroy(&r->mutex);
|
||||||
fw_unit_put(r->unit);
|
fw_unit_put(r->unit);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(fw_iso_resources_destroy);
|
||||||
|
|
||||||
static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
|
static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
|
||||||
{
|
{
|
||||||
|
@ -152,6 +154,7 @@ retry_after_bus_reset:
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(fw_iso_resources_allocate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fw_iso_resources_update - update resource allocations after a bus reset
|
* fw_iso_resources_update - update resource allocations after a bus reset
|
||||||
|
@ -203,6 +206,7 @@ int fw_iso_resources_update(struct fw_iso_resources *r)
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(fw_iso_resources_update);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fw_iso_resources_free - frees allocated resources
|
* fw_iso_resources_free - frees allocated resources
|
||||||
|
@ -230,3 +234,4 @@ void fw_iso_resources_free(struct fw_iso_resources *r)
|
||||||
|
|
||||||
mutex_unlock(&r->mutex);
|
mutex_unlock(&r->mutex);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(fw_iso_resources_free);
|
||||||
|
|
|
@ -60,6 +60,7 @@ err_packets:
|
||||||
error:
|
error:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(iso_packets_buffer_init);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iso_packets_buffer_destroy - frees packet buffer resources
|
* iso_packets_buffer_destroy - frees packet buffer resources
|
||||||
|
@ -72,3 +73,4 @@ void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
|
||||||
fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
|
fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
|
||||||
kfree(b->packets);
|
kfree(b->packets);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(iso_packets_buffer_destroy);
|
||||||
|
|
|
@ -14,4 +14,4 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
|
||||||
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
|
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
|
||||||
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
|
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
|
||||||
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
|
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
|
||||||
obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
|
obj-$(CONFIG_SND_TEA575X) += snd-tea575x-tuner.o
|
||||||
|
|
|
@ -37,8 +37,8 @@ static int radio_nr = -1;
|
||||||
module_param(radio_nr, int, 0);
|
module_param(radio_nr, int, 0);
|
||||||
|
|
||||||
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
|
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
|
||||||
#define FREQ_LO (87 * 16000)
|
#define FREQ_LO (50UL * 16000)
|
||||||
#define FREQ_HI (108 * 16000)
|
#define FREQ_HI (150UL * 16000)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* definitions
|
* definitions
|
||||||
|
@ -77,27 +77,95 @@ static struct v4l2_queryctrl radio_qctrl[] = {
|
||||||
* lowlevel part
|
* lowlevel part
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
|
||||||
|
{
|
||||||
|
u16 l;
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
tea->ops->set_direction(tea, 1);
|
||||||
|
udelay(16);
|
||||||
|
|
||||||
|
for (l = 25; l > 0; l--) {
|
||||||
|
data = (val >> 24) & TEA575X_DATA;
|
||||||
|
val <<= 1; /* shift data */
|
||||||
|
tea->ops->set_pins(tea, data | TEA575X_WREN);
|
||||||
|
udelay(2);
|
||||||
|
tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
|
||||||
|
udelay(2);
|
||||||
|
tea->ops->set_pins(tea, data | TEA575X_WREN);
|
||||||
|
udelay(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tea->mute)
|
||||||
|
tea->ops->set_pins(tea, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int snd_tea575x_read(struct snd_tea575x *tea)
|
||||||
|
{
|
||||||
|
u16 l, rdata;
|
||||||
|
u32 data = 0;
|
||||||
|
|
||||||
|
tea->ops->set_direction(tea, 0);
|
||||||
|
tea->ops->set_pins(tea, 0);
|
||||||
|
udelay(16);
|
||||||
|
|
||||||
|
for (l = 24; l--;) {
|
||||||
|
tea->ops->set_pins(tea, TEA575X_CLK);
|
||||||
|
udelay(2);
|
||||||
|
if (!l)
|
||||||
|
tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
|
||||||
|
tea->ops->set_pins(tea, 0);
|
||||||
|
udelay(2);
|
||||||
|
data <<= 1; /* shift data */
|
||||||
|
rdata = tea->ops->get_pins(tea);
|
||||||
|
if (!l)
|
||||||
|
tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1;
|
||||||
|
if (rdata & TEA575X_DATA)
|
||||||
|
data++;
|
||||||
|
udelay(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tea->mute)
|
||||||
|
tea->ops->set_pins(tea, TEA575X_WREN);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_tea575x_get_freq(struct snd_tea575x *tea)
|
||||||
|
{
|
||||||
|
unsigned long freq;
|
||||||
|
|
||||||
|
freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
|
||||||
|
/* freq *= 12.5 */
|
||||||
|
freq *= 125;
|
||||||
|
freq /= 10;
|
||||||
|
/* crystal fixup */
|
||||||
|
if (tea->tea5759)
|
||||||
|
freq += TEA575X_FMIF;
|
||||||
|
else
|
||||||
|
freq -= TEA575X_FMIF;
|
||||||
|
|
||||||
|
tea->freq = freq * 16; /* from kHz */
|
||||||
|
}
|
||||||
|
|
||||||
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
|
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
|
||||||
{
|
{
|
||||||
unsigned long freq;
|
unsigned long freq;
|
||||||
|
|
||||||
freq = tea->freq / 16; /* to kHz */
|
freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
|
||||||
if (freq > 108000)
|
freq /= 16; /* to kHz */
|
||||||
freq = 108000;
|
|
||||||
if (freq < 87000)
|
|
||||||
freq = 87000;
|
|
||||||
/* crystal fixup */
|
/* crystal fixup */
|
||||||
if (tea->tea5759)
|
if (tea->tea5759)
|
||||||
freq -= tea->freq_fixup;
|
freq -= TEA575X_FMIF;
|
||||||
else
|
else
|
||||||
freq += tea->freq_fixup;
|
freq += TEA575X_FMIF;
|
||||||
/* freq /= 12.5 */
|
/* freq /= 12.5 */
|
||||||
freq *= 10;
|
freq *= 10;
|
||||||
freq /= 125;
|
freq /= 125;
|
||||||
|
|
||||||
tea->val &= ~TEA575X_BIT_FREQ_MASK;
|
tea->val &= ~TEA575X_BIT_FREQ_MASK;
|
||||||
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
|
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
|
||||||
tea->ops->write(tea, tea->val);
|
snd_tea575x_write(tea, tea->val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -109,29 +177,34 @@ static int vidioc_querycap(struct file *file, void *priv,
|
||||||
{
|
{
|
||||||
struct snd_tea575x *tea = video_drvdata(file);
|
struct snd_tea575x *tea = video_drvdata(file);
|
||||||
|
|
||||||
strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
|
|
||||||
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
|
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
|
||||||
strlcpy(v->card, "Maestro Radio", sizeof(v->card));
|
strlcpy(v->card, tea->card, sizeof(v->card));
|
||||||
sprintf(v->bus_info, "PCI");
|
strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
|
||||||
|
strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
|
||||||
v->version = RADIO_VERSION;
|
v->version = RADIO_VERSION;
|
||||||
v->capabilities = V4L2_CAP_TUNER;
|
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vidioc_g_tuner(struct file *file, void *priv,
|
static int vidioc_g_tuner(struct file *file, void *priv,
|
||||||
struct v4l2_tuner *v)
|
struct v4l2_tuner *v)
|
||||||
{
|
{
|
||||||
|
struct snd_tea575x *tea = video_drvdata(file);
|
||||||
|
|
||||||
if (v->index > 0)
|
if (v->index > 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
snd_tea575x_read(tea);
|
||||||
|
|
||||||
strcpy(v->name, "FM");
|
strcpy(v->name, "FM");
|
||||||
v->type = V4L2_TUNER_RADIO;
|
v->type = V4L2_TUNER_RADIO;
|
||||||
|
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
|
||||||
v->rangelow = FREQ_LO;
|
v->rangelow = FREQ_LO;
|
||||||
v->rangehigh = FREQ_HI;
|
v->rangehigh = FREQ_HI;
|
||||||
v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
|
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
||||||
v->capability = V4L2_TUNER_CAP_LOW;
|
v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
|
||||||
v->audmode = V4L2_TUNER_MODE_MONO;
|
v->signal = tea->tuned ? 0xffff : 0;
|
||||||
v->signal = 0xffff;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +221,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
|
||||||
{
|
{
|
||||||
struct snd_tea575x *tea = video_drvdata(file);
|
struct snd_tea575x *tea = video_drvdata(file);
|
||||||
|
|
||||||
|
if (f->tuner != 0)
|
||||||
|
return -EINVAL;
|
||||||
f->type = V4L2_TUNER_RADIO;
|
f->type = V4L2_TUNER_RADIO;
|
||||||
|
snd_tea575x_get_freq(tea);
|
||||||
f->frequency = tea->freq;
|
f->frequency = tea->freq;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -158,6 +234,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
|
||||||
{
|
{
|
||||||
struct snd_tea575x *tea = video_drvdata(file);
|
struct snd_tea575x *tea = video_drvdata(file);
|
||||||
|
|
||||||
|
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
|
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -209,10 +288,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
|
||||||
|
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
case V4L2_CID_AUDIO_MUTE:
|
case V4L2_CID_AUDIO_MUTE:
|
||||||
if (tea->ops->mute) {
|
ctrl->value = tea->mute;
|
||||||
ctrl->value = tea->mute;
|
return 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -224,11 +301,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
|
||||||
|
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
case V4L2_CID_AUDIO_MUTE:
|
case V4L2_CID_AUDIO_MUTE:
|
||||||
if (tea->ops->mute) {
|
if (tea->mute != ctrl->value) {
|
||||||
tea->ops->mute(tea, ctrl->value);
|
|
||||||
tea->mute = ctrl->value;
|
tea->mute = ctrl->value;
|
||||||
return 0;
|
snd_tea575x_set_freq(tea);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -293,18 +370,16 @@ static struct video_device tea575x_radio = {
|
||||||
/*
|
/*
|
||||||
* initialize all the tea575x chips
|
* initialize all the tea575x chips
|
||||||
*/
|
*/
|
||||||
void snd_tea575x_init(struct snd_tea575x *tea)
|
int snd_tea575x_init(struct snd_tea575x *tea)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
unsigned int val;
|
|
||||||
struct video_device *tea575x_radio_inst;
|
struct video_device *tea575x_radio_inst;
|
||||||
|
|
||||||
val = tea->ops->read(tea);
|
tea->mute = 1;
|
||||||
if (val == 0x1ffffff || val == 0) {
|
|
||||||
snd_printk(KERN_ERR
|
snd_tea575x_write(tea, 0x55AA);
|
||||||
"tea575x-tuner: Cannot find TEA575x chip\n");
|
if (snd_tea575x_read(tea) != 0x55AA)
|
||||||
return;
|
return -ENODEV;
|
||||||
}
|
|
||||||
|
|
||||||
tea->in_use = 0;
|
tea->in_use = 0;
|
||||||
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
|
tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
|
||||||
|
@ -313,7 +388,7 @@ void snd_tea575x_init(struct snd_tea575x *tea)
|
||||||
tea575x_radio_inst = video_device_alloc();
|
tea575x_radio_inst = video_device_alloc();
|
||||||
if (tea575x_radio_inst == NULL) {
|
if (tea575x_radio_inst == NULL) {
|
||||||
printk(KERN_ERR "tea575x-tuner: not enough memory\n");
|
printk(KERN_ERR "tea575x-tuner: not enough memory\n");
|
||||||
return;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
|
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
|
||||||
|
@ -328,17 +403,13 @@ void snd_tea575x_init(struct snd_tea575x *tea)
|
||||||
if (retval) {
|
if (retval) {
|
||||||
printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
|
printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
|
||||||
kfree(tea575x_radio_inst);
|
kfree(tea575x_radio_inst);
|
||||||
return;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_tea575x_set_freq(tea);
|
snd_tea575x_set_freq(tea);
|
||||||
|
|
||||||
/* mute on init */
|
|
||||||
if (tea->ops->mute) {
|
|
||||||
tea->ops->mute(tea, 1);
|
|
||||||
tea->mute = 1;
|
|
||||||
}
|
|
||||||
tea->vd = tea575x_radio_inst;
|
tea->vd = tea575x_radio_inst;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void snd_tea575x_exit(struct snd_tea575x *tea)
|
void snd_tea575x_exit(struct snd_tea575x *tea)
|
||||||
|
|
|
@ -534,6 +534,14 @@ config SND_ES1968_INPUT
|
||||||
If you say N the buttons will directly control the master volume.
|
If you say N the buttons will directly control the master volume.
|
||||||
It is recommended to say Y.
|
It is recommended to say Y.
|
||||||
|
|
||||||
|
config SND_ES1968_RADIO
|
||||||
|
bool "Enable TEA5757 radio tuner support for es1968"
|
||||||
|
depends on SND_ES1968
|
||||||
|
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_ES1968
|
||||||
|
help
|
||||||
|
Say Y here to include support for TEA5757 radio tuner integrated on
|
||||||
|
some MediaForte cards (e.g. SF64-PCE2).
|
||||||
|
|
||||||
config SND_FM801
|
config SND_FM801
|
||||||
tristate "ForteMedia FM801"
|
tristate "ForteMedia FM801"
|
||||||
select SND_OPL3_LIB
|
select SND_OPL3_LIB
|
||||||
|
@ -552,13 +560,13 @@ config SND_FM801_TEA575X_BOOL
|
||||||
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
|
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
|
||||||
help
|
help
|
||||||
Say Y here to include support for soundcards based on the ForteMedia
|
Say Y here to include support for soundcards based on the ForteMedia
|
||||||
FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
|
FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
|
||||||
Forte SF256-PCS-02) into the snd-fm801 driver.
|
SF64-PCR) into the snd-fm801 driver.
|
||||||
|
|
||||||
config SND_FM801_TEA575X
|
config SND_TEA575X
|
||||||
tristate
|
tristate
|
||||||
depends on SND_FM801_TEA575X_BOOL
|
depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
|
||||||
default SND_FM801
|
default SND_FM801 || SND_ES1968
|
||||||
|
|
||||||
source "sound/pci/hda/Kconfig"
|
source "sound/pci/hda/Kconfig"
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,29 @@
|
||||||
#include <sound/tlv.h>
|
#include <sound/tlv.h>
|
||||||
#include <sound/hwdep.h>
|
#include <sound/hwdep.h>
|
||||||
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
|
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
|
||||||
MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
|
MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
|
||||||
|
|
||||||
|
#if defined CONFIG_SND_DEBUG
|
||||||
|
/* copied from pcm_lib.c, hope later patch will make that version public
|
||||||
|
and this copy can be removed */
|
||||||
|
static void pcm_debug_name(struct snd_pcm_substream *substream,
|
||||||
|
char *name, size_t len)
|
||||||
|
{
|
||||||
|
snprintf(name, len, "pcmC%dD%d%c:%d",
|
||||||
|
substream->pcm->card->number,
|
||||||
|
substream->pcm->device,
|
||||||
|
substream->stream ? 'c' : 'p',
|
||||||
|
substream->number);
|
||||||
|
}
|
||||||
|
#define DEBUG_NAME(substream, name) char name[16]; pcm_debug_name(substream, name, sizeof(name))
|
||||||
|
#else
|
||||||
|
#define pcm_debug_name(s, n, l) do { } while (0)
|
||||||
|
#define DEBUG_NAME(name, substream) do { } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined CONFIG_SND_DEBUG_VERBOSE
|
#if defined CONFIG_SND_DEBUG_VERBOSE
|
||||||
/**
|
/**
|
||||||
* snd_printddd - very verbose debug printk
|
* snd_printddd - very verbose debug printk
|
||||||
|
@ -58,7 +77,7 @@ MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
|
||||||
#define snd_printddd(format, args...) \
|
#define snd_printddd(format, args...) \
|
||||||
__snd_printk(3, __FILE__, __LINE__, format, ##args)
|
__snd_printk(3, __FILE__, __LINE__, format, ##args)
|
||||||
#else
|
#else
|
||||||
#define snd_printddd(format, args...) do { } while (0)
|
#define snd_printddd(format, args...) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
|
||||||
|
@ -101,13 +120,6 @@ static int adapter_fs = DEFAULT_SAMPLERATE;
|
||||||
#define PERIOD_BYTES_MIN 2048
|
#define PERIOD_BYTES_MIN 2048
|
||||||
#define BUFFER_BYTES_MAX (512 * 1024)
|
#define BUFFER_BYTES_MAX (512 * 1024)
|
||||||
|
|
||||||
/* convert stream to character */
|
|
||||||
#define SCHR(s) ((s == SNDRV_PCM_STREAM_PLAYBACK) ? 'P' : 'C')
|
|
||||||
|
|
||||||
/*#define TIMER_MILLISECONDS 20
|
|
||||||
#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
|
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
|
||||||
|
|
||||||
struct clk_source {
|
struct clk_source {
|
||||||
|
@ -136,7 +148,7 @@ struct snd_card_asihpi {
|
||||||
u32 h_mixer;
|
u32 h_mixer;
|
||||||
struct clk_cache cc;
|
struct clk_cache cc;
|
||||||
|
|
||||||
u16 support_mmap;
|
u16 can_dma;
|
||||||
u16 support_grouping;
|
u16 support_grouping;
|
||||||
u16 support_mrx;
|
u16 support_mrx;
|
||||||
u16 update_interval_frames;
|
u16 update_interval_frames;
|
||||||
|
@ -155,6 +167,7 @@ struct snd_card_asihpi_pcm {
|
||||||
unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
|
unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
|
||||||
unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
|
unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
|
||||||
unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
|
unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
|
||||||
|
unsigned int drained_count;
|
||||||
struct snd_pcm_substream *substream;
|
struct snd_pcm_substream *substream;
|
||||||
u32 h_stream;
|
u32 h_stream;
|
||||||
struct hpi_format format;
|
struct hpi_format format;
|
||||||
|
@ -288,19 +301,26 @@ static u16 handle_error(u16 err, int line, char *filename)
|
||||||
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
|
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
|
||||||
|
|
||||||
/***************************** GENERAL PCM ****************/
|
/***************************** GENERAL PCM ****************/
|
||||||
static void print_hwparams(struct snd_pcm_hw_params *p)
|
|
||||||
|
static void print_hwparams(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *p)
|
||||||
{
|
{
|
||||||
snd_printd("HWPARAMS \n");
|
DEBUG_NAME(substream, name);
|
||||||
snd_printd("samplerate %d \n", params_rate(p));
|
snd_printd("%s HWPARAMS\n", name);
|
||||||
snd_printd("Channels %d \n", params_channels(p));
|
snd_printd(" samplerate %d Hz\n", params_rate(p));
|
||||||
snd_printd("Format %d \n", params_format(p));
|
snd_printd(" channels %d\n", params_channels(p));
|
||||||
snd_printd("subformat %d \n", params_subformat(p));
|
snd_printd(" format %d\n", params_format(p));
|
||||||
snd_printd("Buffer bytes %d \n", params_buffer_bytes(p));
|
snd_printd(" subformat %d\n", params_subformat(p));
|
||||||
snd_printd("Period bytes %d \n", params_period_bytes(p));
|
snd_printd(" buffer %d B\n", params_buffer_bytes(p));
|
||||||
snd_printd("access %d \n", params_access(p));
|
snd_printd(" period %d B\n", params_period_bytes(p));
|
||||||
snd_printd("period_size %d \n", params_period_size(p));
|
snd_printd(" access %d\n", params_access(p));
|
||||||
snd_printd("periods %d \n", params_periods(p));
|
snd_printd(" period_size %d\n", params_period_size(p));
|
||||||
snd_printd("buffer_size %d \n", params_buffer_size(p));
|
snd_printd(" periods %d\n", params_periods(p));
|
||||||
|
snd_printd(" buffer_size %d\n", params_buffer_size(p));
|
||||||
|
snd_printd(" %d B/s\n", params_rate(p) *
|
||||||
|
params_channels(p) *
|
||||||
|
snd_pcm_format_width(params_format(p)) / 8);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static snd_pcm_format_t hpi_to_alsa_formats[] = {
|
static snd_pcm_format_t hpi_to_alsa_formats[] = {
|
||||||
|
@ -451,7 +471,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
int width;
|
int width;
|
||||||
unsigned int bytes_per_sec;
|
unsigned int bytes_per_sec;
|
||||||
|
|
||||||
print_hwparams(params);
|
print_hwparams(substream, params);
|
||||||
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
@ -459,10 +479,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
snd_printdd("format %d, %d chans, %d_hz\n",
|
|
||||||
format, params_channels(params),
|
|
||||||
params_rate(params));
|
|
||||||
|
|
||||||
hpi_handle_error(hpi_format_create(&dpcm->format,
|
hpi_handle_error(hpi_format_create(&dpcm->format,
|
||||||
params_channels(params),
|
params_channels(params),
|
||||||
format, params_rate(params), 0, 0));
|
format, params_rate(params), 0, 0));
|
||||||
|
@ -477,8 +493,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
|
|
||||||
dpcm->hpi_buffer_attached = 0;
|
dpcm->hpi_buffer_attached = 0;
|
||||||
if (card->support_mmap) {
|
if (card->can_dma) {
|
||||||
|
|
||||||
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
|
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
|
||||||
params_buffer_bytes(params), runtime->dma_addr);
|
params_buffer_bytes(params), runtime->dma_addr);
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
|
@ -509,8 +524,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
dpcm->bytes_per_sec = bytes_per_sec;
|
dpcm->bytes_per_sec = bytes_per_sec;
|
||||||
dpcm->buffer_bytes = params_buffer_bytes(params);
|
dpcm->buffer_bytes = params_buffer_bytes(params);
|
||||||
dpcm->period_bytes = params_period_bytes(params);
|
dpcm->period_bytes = params_period_bytes(params);
|
||||||
snd_printdd("buffer_bytes=%d, period_bytes=%d, bps=%d\n",
|
|
||||||
dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -564,9 +577,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
||||||
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
|
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
|
||||||
struct snd_pcm_substream *s;
|
struct snd_pcm_substream *s;
|
||||||
u16 e;
|
u16 e;
|
||||||
|
DEBUG_NAME(substream, name);
|
||||||
|
|
||||||
|
snd_printdd("%s trigger\n", name);
|
||||||
|
|
||||||
snd_printdd("%c%d trigger\n",
|
|
||||||
SCHR(substream->stream), substream->number);
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
snd_pcm_group_for_each_entry(s, substream) {
|
snd_pcm_group_for_each_entry(s, substream) {
|
||||||
|
@ -580,8 +594,8 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
||||||
if (substream->stream != s->stream)
|
if (substream->stream != s->stream)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
|
ds->drained_count = 0;
|
||||||
(card->support_mmap)) {
|
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
/* How do I know how much valid data is present
|
/* How do I know how much valid data is present
|
||||||
* in buffer? Must be at least one period!
|
* in buffer? Must be at least one period!
|
||||||
* Guessing 2 periods, but if
|
* Guessing 2 periods, but if
|
||||||
|
@ -599,9 +613,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card->support_grouping) {
|
if (card->support_grouping) {
|
||||||
snd_printdd("\t%c%d group\n",
|
snd_printdd("%d group\n", s->number);
|
||||||
SCHR(s->stream),
|
|
||||||
s->number);
|
|
||||||
e = hpi_stream_group_add(
|
e = hpi_stream_group_add(
|
||||||
dpcm->h_stream,
|
dpcm->h_stream,
|
||||||
ds->h_stream);
|
ds->h_stream);
|
||||||
|
@ -618,7 +630,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
||||||
/* start the master stream */
|
/* start the master stream */
|
||||||
snd_card_asihpi_pcm_timer_start(substream);
|
snd_card_asihpi_pcm_timer_start(substream);
|
||||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
|
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
|
||||||
!card->support_mmap)
|
!card->can_dma)
|
||||||
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
|
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -636,9 +648,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
||||||
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
|
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
|
||||||
|
|
||||||
if (card->support_grouping) {
|
if (card->support_grouping) {
|
||||||
snd_printdd("\t%c%d group\n",
|
snd_printdd("%d group\n", s->number);
|
||||||
SCHR(s->stream),
|
|
||||||
s->number);
|
|
||||||
snd_pcm_trigger_done(s, substream);
|
snd_pcm_trigger_done(s, substream);
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
|
@ -732,9 +742,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
int loops = 0;
|
int loops = 0;
|
||||||
u16 state;
|
u16 state;
|
||||||
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
|
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
|
||||||
|
DEBUG_NAME(substream, name);
|
||||||
|
|
||||||
snd_printdd("%c%d snd_card_asihpi_timer_function\n",
|
snd_printdd("%s snd_card_asihpi_timer_function\n", name);
|
||||||
SCHR(substream->stream), substream->number);
|
|
||||||
|
|
||||||
/* find minimum newdata and buffer pos in group */
|
/* find minimum newdata and buffer pos in group */
|
||||||
snd_pcm_group_for_each_entry(s, substream) {
|
snd_pcm_group_for_each_entry(s, substream) {
|
||||||
|
@ -756,6 +766,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
/* number of bytes in on-card buffer */
|
/* number of bytes in on-card buffer */
|
||||||
runtime->delay = on_card_bytes;
|
runtime->delay = on_card_bytes;
|
||||||
|
|
||||||
|
if (!card->can_dma)
|
||||||
|
on_card_bytes = bytes_avail;
|
||||||
|
|
||||||
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
|
pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
|
||||||
if (state == HPI_STATE_STOPPED) {
|
if (state == HPI_STATE_STOPPED) {
|
||||||
|
@ -763,12 +776,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
(on_card_bytes < ds->pcm_buf_host_rw_ofs)) {
|
(on_card_bytes < ds->pcm_buf_host_rw_ofs)) {
|
||||||
hpi_handle_error(hpi_stream_start(ds->h_stream));
|
hpi_handle_error(hpi_stream_start(ds->h_stream));
|
||||||
snd_printdd("P%d start\n", s->number);
|
snd_printdd("P%d start\n", s->number);
|
||||||
|
ds->drained_count = 0;
|
||||||
}
|
}
|
||||||
} else if (state == HPI_STATE_DRAINED) {
|
} else if (state == HPI_STATE_DRAINED) {
|
||||||
snd_printd(KERN_WARNING "P%d drained\n",
|
snd_printd(KERN_WARNING "P%d drained\n",
|
||||||
s->number);
|
s->number);
|
||||||
/*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
|
ds->drained_count++;
|
||||||
continue; */
|
if (ds->drained_count > 2) {
|
||||||
|
snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ds->drained_count = 0;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
|
pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
|
||||||
|
@ -786,16 +805,18 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
newdata);
|
newdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_printdd("hw_ptr x%04lX, appl_ptr x%04lX\n",
|
snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n",
|
||||||
(unsigned long)frames_to_bytes(runtime,
|
(unsigned long)frames_to_bytes(runtime,
|
||||||
runtime->status->hw_ptr),
|
runtime->status->hw_ptr),
|
||||||
(unsigned long)frames_to_bytes(runtime,
|
(unsigned long)frames_to_bytes(runtime,
|
||||||
runtime->control->appl_ptr));
|
runtime->control->appl_ptr));
|
||||||
|
|
||||||
snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X,"
|
snd_printdd("%d S=%d, "
|
||||||
" aux=x%04X space=x%04X\n",
|
"rw=0x%04X, dma=0x%04X, left=0x%04X, "
|
||||||
loops, SCHR(s->stream), s->number,
|
"aux=0x%04X space=0x%04X\n",
|
||||||
state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail,
|
s->number, state,
|
||||||
|
ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs,
|
||||||
|
(int)bytes_avail,
|
||||||
(int)on_card_bytes, buffer_size-bytes_avail);
|
(int)on_card_bytes, buffer_size-bytes_avail);
|
||||||
loops++;
|
loops++;
|
||||||
}
|
}
|
||||||
|
@ -814,7 +835,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
|
|
||||||
next_jiffies = max(next_jiffies, 1U);
|
next_jiffies = max(next_jiffies, 1U);
|
||||||
dpcm->timer.expires = jiffies + next_jiffies;
|
dpcm->timer.expires = jiffies + next_jiffies;
|
||||||
snd_printdd("jif %d buf pos x%04X newdata x%04X xfer x%04X\n",
|
snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n",
|
||||||
next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
|
next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
|
||||||
|
|
||||||
snd_pcm_group_for_each_entry(s, substream) {
|
snd_pcm_group_for_each_entry(s, substream) {
|
||||||
|
@ -826,30 +847,63 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
|
|
||||||
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
|
ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
|
||||||
|
|
||||||
if (xfercount && (on_card_bytes <= ds->period_bytes)) {
|
if (xfercount &&
|
||||||
if (card->support_mmap) {
|
/* Limit use of on card fifo for playback */
|
||||||
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
((on_card_bytes <= ds->period_bytes) ||
|
||||||
snd_printddd("P%d write x%04x\n",
|
(s->stream == SNDRV_PCM_STREAM_CAPTURE)))
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
|
||||||
|
unsigned int xfer1, xfer2;
|
||||||
|
char *pd = &s->runtime->dma_area[buf_ofs];
|
||||||
|
|
||||||
|
if (card->can_dma) { /* buffer wrap is handled at lower level */
|
||||||
|
xfer1 = xfercount;
|
||||||
|
xfer2 = 0;
|
||||||
|
} else {
|
||||||
|
xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
|
||||||
|
xfer2 = xfercount - xfer1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
|
snd_printddd("P%d write1 0x%04X 0x%04X\n",
|
||||||
|
s->number, xfer1, buf_ofs);
|
||||||
|
hpi_handle_error(
|
||||||
|
hpi_outstream_write_buf(
|
||||||
|
ds->h_stream, pd, xfer1,
|
||||||
|
&ds->format));
|
||||||
|
|
||||||
|
if (xfer2) {
|
||||||
|
pd = s->runtime->dma_area;
|
||||||
|
|
||||||
|
snd_printddd("P%d write2 0x%04X 0x%04X\n",
|
||||||
s->number,
|
s->number,
|
||||||
ds->period_bytes);
|
xfercount - xfer1, buf_ofs);
|
||||||
hpi_handle_error(
|
hpi_handle_error(
|
||||||
hpi_outstream_write_buf(
|
hpi_outstream_write_buf(
|
||||||
ds->h_stream,
|
ds->h_stream, pd,
|
||||||
&s->runtime->
|
xfercount - xfer1,
|
||||||
dma_area[0],
|
|
||||||
xfercount,
|
|
||||||
&ds->format));
|
&ds->format));
|
||||||
} else {
|
}
|
||||||
snd_printddd("C%d read x%04x\n",
|
} else {
|
||||||
s->number,
|
snd_printddd("C%d read1 0x%04x\n",
|
||||||
xfercount);
|
s->number, xfer1);
|
||||||
|
hpi_handle_error(
|
||||||
|
hpi_instream_read_buf(
|
||||||
|
ds->h_stream,
|
||||||
|
pd, xfer1));
|
||||||
|
if (xfer2) {
|
||||||
|
pd = s->runtime->dma_area;
|
||||||
|
snd_printddd("C%d read2 0x%04x\n",
|
||||||
|
s->number, xfer2);
|
||||||
hpi_handle_error(
|
hpi_handle_error(
|
||||||
hpi_instream_read_buf(
|
hpi_instream_read_buf(
|
||||||
ds->h_stream,
|
ds->h_stream,
|
||||||
NULL, xfercount));
|
pd, xfer2));
|
||||||
}
|
}
|
||||||
ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
|
}
|
||||||
} /* else R/W will be handled by read/write callbacks */
|
ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount;
|
||||||
ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs;
|
ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs;
|
||||||
snd_pcm_period_elapsed(s);
|
snd_pcm_period_elapsed(s);
|
||||||
}
|
}
|
||||||
|
@ -863,7 +917,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
||||||
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
|
static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
|
||||||
unsigned int cmd, void *arg)
|
unsigned int cmd, void *arg)
|
||||||
{
|
{
|
||||||
snd_printdd(KERN_INFO "Playback ioctl %d\n", cmd);
|
snd_printddd(KERN_INFO "P%d ioctl %d\n", substream->number, cmd);
|
||||||
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
return snd_pcm_lib_ioctl(substream, cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,7 +927,7 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
|
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
|
||||||
|
|
||||||
snd_printdd("playback prepare %d\n", substream->number);
|
snd_printdd("P%d prepare\n", substream->number);
|
||||||
|
|
||||||
hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
|
hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
|
||||||
dpcm->pcm_buf_host_rw_ofs = 0;
|
dpcm->pcm_buf_host_rw_ofs = 0;
|
||||||
|
@ -890,7 +944,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
|
||||||
snd_pcm_uframes_t ptr;
|
snd_pcm_uframes_t ptr;
|
||||||
|
|
||||||
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
|
ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
|
||||||
snd_printddd("playback_pointer=x%04lx\n", (unsigned long)ptr);
|
snd_printddd("P%d pointer = 0x%04lx\n", substream->number, (unsigned long)ptr);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -986,11 +1040,9 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
|
||||||
SNDRV_PCM_INFO_DOUBLE |
|
SNDRV_PCM_INFO_DOUBLE |
|
||||||
SNDRV_PCM_INFO_BATCH |
|
SNDRV_PCM_INFO_BATCH |
|
||||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||||
SNDRV_PCM_INFO_PAUSE;
|
SNDRV_PCM_INFO_PAUSE |
|
||||||
|
SNDRV_PCM_INFO_MMAP |
|
||||||
if (card->support_mmap)
|
SNDRV_PCM_INFO_MMAP_VALID;
|
||||||
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
|
|
||||||
SNDRV_PCM_INFO_MMAP_VALID;
|
|
||||||
|
|
||||||
if (card->support_grouping)
|
if (card->support_grouping)
|
||||||
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
|
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
|
||||||
|
@ -998,7 +1050,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
|
||||||
/* struct is copied, so can create initializer dynamically */
|
/* struct is copied, so can create initializer dynamically */
|
||||||
runtime->hw = snd_card_asihpi_playback;
|
runtime->hw = snd_card_asihpi_playback;
|
||||||
|
|
||||||
if (card->support_mmap)
|
if (card->can_dma)
|
||||||
err = snd_pcm_hw_constraint_pow2(runtime, 0,
|
err = snd_pcm_hw_constraint_pow2(runtime, 0,
|
||||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
|
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -1028,58 +1080,6 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
|
|
||||||
int channel,
|
|
||||||
snd_pcm_uframes_t pos,
|
|
||||||
void __user *src,
|
|
||||||
snd_pcm_uframes_t count)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
|
|
||||||
unsigned int len;
|
|
||||||
|
|
||||||
len = frames_to_bytes(runtime, count);
|
|
||||||
|
|
||||||
if (copy_from_user(runtime->dma_area, src, len))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
snd_printddd("playback copy%d %u bytes\n",
|
|
||||||
substream->number, len);
|
|
||||||
|
|
||||||
hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream,
|
|
||||||
runtime->dma_area, len, &dpcm->format));
|
|
||||||
|
|
||||||
dpcm->pcm_buf_host_rw_ofs += len;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
|
|
||||||
substream, int channel,
|
|
||||||
snd_pcm_uframes_t pos,
|
|
||||||
snd_pcm_uframes_t count)
|
|
||||||
{
|
|
||||||
/* Usually writes silence to DMA buffer, which should be overwritten
|
|
||||||
by real audio later. Our fifos cannot be overwritten, and are not
|
|
||||||
free-running DMAs. Silence is output on fifo underflow.
|
|
||||||
This callback is still required to allow the copy callback to be used.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
|
|
||||||
.open = snd_card_asihpi_playback_open,
|
|
||||||
.close = snd_card_asihpi_playback_close,
|
|
||||||
.ioctl = snd_card_asihpi_playback_ioctl,
|
|
||||||
.hw_params = snd_card_asihpi_pcm_hw_params,
|
|
||||||
.hw_free = snd_card_asihpi_hw_free,
|
|
||||||
.prepare = snd_card_asihpi_playback_prepare,
|
|
||||||
.trigger = snd_card_asihpi_trigger,
|
|
||||||
.pointer = snd_card_asihpi_playback_pointer,
|
|
||||||
.copy = snd_card_asihpi_playback_copy,
|
|
||||||
.silence = snd_card_asihpi_playback_silence,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
|
static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
|
||||||
.open = snd_card_asihpi_playback_open,
|
.open = snd_card_asihpi_playback_open,
|
||||||
.close = snd_card_asihpi_playback_close,
|
.close = snd_card_asihpi_playback_close,
|
||||||
|
@ -1211,18 +1211,16 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
|
||||||
snd_card_asihpi_capture_format(card, dpcm->h_stream,
|
snd_card_asihpi_capture_format(card, dpcm->h_stream,
|
||||||
&snd_card_asihpi_capture);
|
&snd_card_asihpi_capture);
|
||||||
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
|
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
|
||||||
snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
|
snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
|
SNDRV_PCM_INFO_MMAP |
|
||||||
if (card->support_mmap)
|
SNDRV_PCM_INFO_MMAP_VALID;
|
||||||
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
|
|
||||||
SNDRV_PCM_INFO_MMAP_VALID;
|
|
||||||
|
|
||||||
if (card->support_grouping)
|
if (card->support_grouping)
|
||||||
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
|
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
|
||||||
|
|
||||||
runtime->hw = snd_card_asihpi_capture;
|
runtime->hw = snd_card_asihpi_capture;
|
||||||
|
|
||||||
if (card->support_mmap)
|
if (card->can_dma)
|
||||||
err = snd_pcm_hw_constraint_pow2(runtime, 0,
|
err = snd_pcm_hw_constraint_pow2(runtime, 0,
|
||||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
|
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -1246,28 +1244,6 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
|
|
||||||
int channel, snd_pcm_uframes_t pos,
|
|
||||||
void __user *dst, snd_pcm_uframes_t count)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
|
|
||||||
u32 len;
|
|
||||||
|
|
||||||
len = frames_to_bytes(runtime, count);
|
|
||||||
|
|
||||||
snd_printddd("capture copy%d %d bytes\n", substream->number, len);
|
|
||||||
hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream,
|
|
||||||
runtime->dma_area, len));
|
|
||||||
|
|
||||||
dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len;
|
|
||||||
|
|
||||||
if (copy_to_user(dst, runtime->dma_area, len))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
|
static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
|
||||||
.open = snd_card_asihpi_capture_open,
|
.open = snd_card_asihpi_capture_open,
|
||||||
.close = snd_card_asihpi_capture_close,
|
.close = snd_card_asihpi_capture_close,
|
||||||
|
@ -1279,18 +1255,6 @@ static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
|
||||||
.pointer = snd_card_asihpi_capture_pointer,
|
.pointer = snd_card_asihpi_capture_pointer,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
|
|
||||||
.open = snd_card_asihpi_capture_open,
|
|
||||||
.close = snd_card_asihpi_capture_close,
|
|
||||||
.ioctl = snd_card_asihpi_capture_ioctl,
|
|
||||||
.hw_params = snd_card_asihpi_pcm_hw_params,
|
|
||||||
.hw_free = snd_card_asihpi_hw_free,
|
|
||||||
.prepare = snd_card_asihpi_capture_prepare,
|
|
||||||
.trigger = snd_card_asihpi_trigger,
|
|
||||||
.pointer = snd_card_asihpi_capture_pointer,
|
|
||||||
.copy = snd_card_asihpi_capture_copy
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
|
static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
|
||||||
int device, int substreams)
|
int device, int substreams)
|
||||||
{
|
{
|
||||||
|
@ -1303,17 +1267,10 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
/* pointer to ops struct is stored, dont change ops afterwards! */
|
/* pointer to ops struct is stored, dont change ops afterwards! */
|
||||||
if (asihpi->support_mmap) {
|
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||||
&snd_card_asihpi_playback_mmap_ops);
|
&snd_card_asihpi_playback_mmap_ops);
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||||
&snd_card_asihpi_capture_mmap_ops);
|
&snd_card_asihpi_capture_mmap_ops);
|
||||||
} else {
|
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
|
||||||
&snd_card_asihpi_playback_ops);
|
|
||||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
|
||||||
&snd_card_asihpi_capture_ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcm->private_data = asihpi;
|
pcm->private_data = asihpi;
|
||||||
pcm->info_flags = 0;
|
pcm->info_flags = 0;
|
||||||
|
@ -1413,14 +1370,16 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
|
||||||
struct hpi_control *hpi_ctl,
|
struct hpi_control *hpi_ctl,
|
||||||
char *name)
|
char *name)
|
||||||
{
|
{
|
||||||
char *dir = "";
|
char *dir;
|
||||||
memset(snd_control, 0, sizeof(*snd_control));
|
memset(snd_control, 0, sizeof(*snd_control));
|
||||||
snd_control->name = hpi_ctl->name;
|
snd_control->name = hpi_ctl->name;
|
||||||
snd_control->private_value = hpi_ctl->h_control;
|
snd_control->private_value = hpi_ctl->h_control;
|
||||||
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||||
snd_control->index = 0;
|
snd_control->index = 0;
|
||||||
|
|
||||||
if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
|
if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
|
||||||
|
dir = ""; /* clock is neither capture nor playback */
|
||||||
|
else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
|
||||||
dir = "Capture "; /* On or towards a PCM capture destination*/
|
dir = "Capture "; /* On or towards a PCM capture destination*/
|
||||||
else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
|
else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
|
||||||
(!hpi_ctl->dst_node_type))
|
(!hpi_ctl->dst_node_type))
|
||||||
|
@ -1433,7 +1392,7 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
|
||||||
dir = "Playback "; /* PCM Playback source, or output node */
|
dir = "Playback "; /* PCM Playback source, or output node */
|
||||||
|
|
||||||
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
|
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
|
||||||
sprintf(hpi_ctl->name, "%s%d %s%d %s%s",
|
sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
|
||||||
asihpi_src_names[hpi_ctl->src_node_type],
|
asihpi_src_names[hpi_ctl->src_node_type],
|
||||||
hpi_ctl->src_node_index,
|
hpi_ctl->src_node_index,
|
||||||
asihpi_dst_names[hpi_ctl->dst_node_type],
|
asihpi_dst_names[hpi_ctl->dst_node_type],
|
||||||
|
@ -2875,14 +2834,14 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
|
||||||
if (err)
|
if (err)
|
||||||
asihpi->update_interval_frames = 512;
|
asihpi->update_interval_frames = 512;
|
||||||
|
|
||||||
if (!asihpi->support_mmap)
|
if (!asihpi->can_dma)
|
||||||
asihpi->update_interval_frames *= 2;
|
asihpi->update_interval_frames *= 2;
|
||||||
|
|
||||||
hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
|
hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
|
||||||
0, &h_stream));
|
0, &h_stream));
|
||||||
|
|
||||||
err = hpi_instream_host_buffer_free(h_stream);
|
err = hpi_instream_host_buffer_free(h_stream);
|
||||||
asihpi->support_mmap = (!err);
|
asihpi->can_dma = (!err);
|
||||||
|
|
||||||
hpi_handle_error(hpi_instream_close(h_stream));
|
hpi_handle_error(hpi_instream_close(h_stream));
|
||||||
|
|
||||||
|
@ -2894,8 +2853,8 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
|
||||||
asihpi->out_max_chans = 2;
|
asihpi->out_max_chans = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
|
snd_printk(KERN_INFO "has dma:%d, grouping:%d, mrx:%d\n",
|
||||||
asihpi->support_mmap,
|
asihpi->can_dma,
|
||||||
asihpi->support_grouping,
|
asihpi->support_grouping,
|
||||||
asihpi->support_mrx
|
asihpi->support_mrx
|
||||||
);
|
);
|
||||||
|
@ -2925,10 +2884,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
|
||||||
by enable_hwdep module param*/
|
by enable_hwdep module param*/
|
||||||
snd_asihpi_hpi_new(asihpi, 0, NULL);
|
snd_asihpi_hpi_new(asihpi, 0, NULL);
|
||||||
|
|
||||||
if (asihpi->support_mmap)
|
strcpy(card->driver, "ASIHPI");
|
||||||
strcpy(card->driver, "ASIHPI-MMAP");
|
|
||||||
else
|
|
||||||
strcpy(card->driver, "ASIHPI");
|
|
||||||
|
|
||||||
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
|
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
|
||||||
sprintf(card->longname, "%s %i",
|
sprintf(card->longname, "%s %i",
|
||||||
|
|
|
@ -200,8 +200,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
|
||||||
static void subsys_create_adapter(struct hpi_message *phm,
|
static void subsys_create_adapter(struct hpi_message *phm,
|
||||||
struct hpi_response *phr);
|
struct hpi_response *phr);
|
||||||
|
|
||||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||||
struct hpi_response *phr);
|
struct hpi_message *phm, struct hpi_response *phr);
|
||||||
|
|
||||||
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
|
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
|
||||||
struct hpi_message *phm, struct hpi_response *phr);
|
struct hpi_message *phm, struct hpi_response *phr);
|
||||||
|
@ -222,9 +222,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
|
||||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||||
subsys_create_adapter(phm, phr);
|
subsys_create_adapter(phm, phr);
|
||||||
break;
|
break;
|
||||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
|
||||||
subsys_delete_adapter(phm, phr);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
phr->error = HPI_ERROR_INVALID_FUNC;
|
phr->error = HPI_ERROR_INVALID_FUNC;
|
||||||
break;
|
break;
|
||||||
|
@ -279,6 +276,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
|
||||||
adapter_get_asserts(pao, phm, phr);
|
adapter_get_asserts(pao, phm, phr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HPI_ADAPTER_DELETE:
|
||||||
|
adapter_delete(pao, phm, phr);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
hw_message(pao, phm, phr);
|
hw_message(pao, phm, phr);
|
||||||
break;
|
break;
|
||||||
|
@ -333,26 +334,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
|
||||||
{
|
{
|
||||||
struct hpi_adapter_obj *pao = NULL;
|
struct hpi_adapter_obj *pao = NULL;
|
||||||
|
|
||||||
/* subsytem messages get executed by every HPI. */
|
|
||||||
/* All other messages are ignored unless the adapter index matches */
|
|
||||||
/* an adapter in the HPI */
|
|
||||||
/*HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->wObject, phm->wFunction); */
|
|
||||||
|
|
||||||
/* if Dsp has crashed then do not communicate with it any more */
|
|
||||||
if (phm->object != HPI_OBJ_SUBSYSTEM) {
|
if (phm->object != HPI_OBJ_SUBSYSTEM) {
|
||||||
pao = hpi_find_adapter(phm->adapter_index);
|
pao = hpi_find_adapter(phm->adapter_index);
|
||||||
if (!pao) {
|
if (!pao) {
|
||||||
HPI_DEBUG_LOG(DEBUG,
|
hpi_init_response(phr, phm->object, phm->function,
|
||||||
" %d,%d refused, for another HPI?\n",
|
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||||
phm->object, phm->function);
|
HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
|
||||||
|
phm->adapter_index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't even try to communicate with crashed DSP */
|
||||||
if (pao->dsp_crashed >= 10) {
|
if (pao->dsp_crashed >= 10) {
|
||||||
hpi_init_response(phr, phm->object, phm->function,
|
hpi_init_response(phr, phm->object, phm->function,
|
||||||
HPI_ERROR_DSP_HARDWARE);
|
HPI_ERROR_DSP_HARDWARE);
|
||||||
HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
|
HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
|
||||||
phm->object, phm->function);
|
phm->adapter_index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,15 +460,9 @@ static void subsys_create_adapter(struct hpi_message *phm,
|
||||||
phr->error = 0;
|
phr->error = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||||
struct hpi_response *phr)
|
struct hpi_message *phm, struct hpi_response *phr)
|
||||||
{
|
{
|
||||||
struct hpi_adapter_obj *pao = NULL;
|
|
||||||
|
|
||||||
pao = hpi_find_adapter(phm->obj_index);
|
|
||||||
if (!pao)
|
|
||||||
return;
|
|
||||||
|
|
||||||
delete_adapter_obj(pao);
|
delete_adapter_obj(pao);
|
||||||
hpi_delete_adapter(pao);
|
hpi_delete_adapter(pao);
|
||||||
phr->error = 0;
|
phr->error = 0;
|
||||||
|
|
|
@ -152,8 +152,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
|
||||||
|
|
||||||
static void subsys_create_adapter(struct hpi_message *phm,
|
static void subsys_create_adapter(struct hpi_message *phm,
|
||||||
struct hpi_response *phr);
|
struct hpi_response *phr);
|
||||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||||
struct hpi_response *phr);
|
struct hpi_message *phm, struct hpi_response *phr);
|
||||||
|
|
||||||
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
||||||
u32 *pos_error_code);
|
u32 *pos_error_code);
|
||||||
|
@ -223,15 +223,13 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
|
static void subsys_message(struct hpi_adapter_obj *pao,
|
||||||
|
struct hpi_message *phm, struct hpi_response *phr)
|
||||||
{
|
{
|
||||||
switch (phm->function) {
|
switch (phm->function) {
|
||||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||||
subsys_create_adapter(phm, phr);
|
subsys_create_adapter(phm, phr);
|
||||||
break;
|
break;
|
||||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
|
||||||
subsys_delete_adapter(phm, phr);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
phr->error = HPI_ERROR_INVALID_FUNC;
|
phr->error = HPI_ERROR_INVALID_FUNC;
|
||||||
break;
|
break;
|
||||||
|
@ -279,6 +277,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
|
||||||
struct hpi_message *phm, struct hpi_response *phr)
|
struct hpi_message *phm, struct hpi_response *phr)
|
||||||
{
|
{
|
||||||
switch (phm->function) {
|
switch (phm->function) {
|
||||||
|
case HPI_ADAPTER_DELETE:
|
||||||
|
adapter_delete(pao, phm, phr);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
hw_message(pao, phm, phr);
|
hw_message(pao, phm, phr);
|
||||||
break;
|
break;
|
||||||
|
@ -371,36 +373,17 @@ static void instream_message(struct hpi_adapter_obj *pao,
|
||||||
/** Entry point to this HPI backend
|
/** Entry point to this HPI backend
|
||||||
* All calls to the HPI start here
|
* All calls to the HPI start here
|
||||||
*/
|
*/
|
||||||
void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
|
void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
|
||||||
|
struct hpi_response *phr)
|
||||||
{
|
{
|
||||||
struct hpi_adapter_obj *pao = NULL;
|
if (pao && (pao->dsp_crashed >= 10)
|
||||||
|
&& (phm->function != HPI_ADAPTER_DEBUG_READ)) {
|
||||||
/* subsytem messages are processed by every HPI.
|
/* allow last resort debug read even after crash */
|
||||||
* All other messages are ignored unless the adapter index matches
|
hpi_init_response(phr, phm->object, phm->function,
|
||||||
* an adapter in the HPI
|
HPI_ERROR_DSP_HARDWARE);
|
||||||
*/
|
HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object,
|
||||||
/* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject,
|
phm->function);
|
||||||
phm->wFunction); */
|
return;
|
||||||
|
|
||||||
/* if Dsp has crashed then do not communicate with it any more */
|
|
||||||
if (phm->object != HPI_OBJ_SUBSYSTEM) {
|
|
||||||
pao = hpi_find_adapter(phm->adapter_index);
|
|
||||||
if (!pao) {
|
|
||||||
HPI_DEBUG_LOG(DEBUG,
|
|
||||||
" %d,%d refused, for another HPI?\n",
|
|
||||||
phm->object, phm->function);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pao->dsp_crashed >= 10)
|
|
||||||
&& (phm->function != HPI_ADAPTER_DEBUG_READ)) {
|
|
||||||
/* allow last resort debug read even after crash */
|
|
||||||
hpi_init_response(phr, phm->object, phm->function,
|
|
||||||
HPI_ERROR_DSP_HARDWARE);
|
|
||||||
HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
|
|
||||||
phm->object, phm->function);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init default response */
|
/* Init default response */
|
||||||
|
@ -412,7 +395,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
|
||||||
case HPI_TYPE_MESSAGE:
|
case HPI_TYPE_MESSAGE:
|
||||||
switch (phm->object) {
|
switch (phm->object) {
|
||||||
case HPI_OBJ_SUBSYSTEM:
|
case HPI_OBJ_SUBSYSTEM:
|
||||||
subsys_message(phm, phr);
|
subsys_message(pao, phm, phr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HPI_OBJ_ADAPTER:
|
case HPI_OBJ_ADAPTER:
|
||||||
|
@ -444,6 +427,26 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
|
||||||
|
{
|
||||||
|
struct hpi_adapter_obj *pao = NULL;
|
||||||
|
|
||||||
|
if (phm->object != HPI_OBJ_SUBSYSTEM) {
|
||||||
|
/* normal messages must have valid adapter index */
|
||||||
|
pao = hpi_find_adapter(phm->adapter_index);
|
||||||
|
} else {
|
||||||
|
/* subsys messages don't address an adapter */
|
||||||
|
_HPI_6205(NULL, phm, phr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pao)
|
||||||
|
_HPI_6205(pao, phm, phr);
|
||||||
|
else
|
||||||
|
hpi_init_response(phr, phm->object, phm->function,
|
||||||
|
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* SUBSYSTEM */
|
/* SUBSYSTEM */
|
||||||
|
|
||||||
|
@ -491,13 +494,11 @@ static void subsys_create_adapter(struct hpi_message *phm,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** delete an adapter - required by WDM driver */
|
/** delete an adapter - required by WDM driver */
|
||||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||||
struct hpi_response *phr)
|
struct hpi_message *phm, struct hpi_response *phr)
|
||||||
{
|
{
|
||||||
struct hpi_adapter_obj *pao;
|
|
||||||
struct hpi_hw_obj *phw;
|
struct hpi_hw_obj *phw;
|
||||||
|
|
||||||
pao = hpi_find_adapter(phm->obj_index);
|
|
||||||
if (!pao) {
|
if (!pao) {
|
||||||
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
|
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
|
||||||
return;
|
return;
|
||||||
|
@ -563,11 +564,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = adapter_boot_load_dsp(pao, pos_error_code);
|
err = adapter_boot_load_dsp(pao, pos_error_code);
|
||||||
if (err)
|
if (err) {
|
||||||
|
HPI_DEBUG_LOG(ERROR, "DSP code load failed\n");
|
||||||
/* no need to clean up as SubSysCreateAdapter */
|
/* no need to clean up as SubSysCreateAdapter */
|
||||||
/* calls DeleteAdapter on error. */
|
/* calls DeleteAdapter on error. */
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
|
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
|
||||||
|
|
||||||
/* allow boot load even if mem alloc wont work */
|
/* allow boot load even if mem alloc wont work */
|
||||||
|
@ -604,6 +606,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
||||||
control_cache.number_of_controls,
|
control_cache.number_of_controls,
|
||||||
interface->control_cache.size_in_bytes,
|
interface->control_cache.size_in_bytes,
|
||||||
p_control_cache_virtual);
|
p_control_cache_virtual);
|
||||||
|
|
||||||
if (!phw->p_cache)
|
if (!phw->p_cache)
|
||||||
err = HPI_ERROR_MEMORY_ALLOC;
|
err = HPI_ERROR_MEMORY_ALLOC;
|
||||||
}
|
}
|
||||||
|
@ -675,16 +678,14 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Free memory areas allocated by adapter
|
/** Free memory areas allocated by adapter
|
||||||
* this routine is called from SubSysDeleteAdapter,
|
* this routine is called from AdapterDelete,
|
||||||
* and SubSysCreateAdapter if duplicate index
|
* and SubSysCreateAdapter if duplicate index
|
||||||
*/
|
*/
|
||||||
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
|
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
|
||||||
{
|
{
|
||||||
struct hpi_hw_obj *phw;
|
struct hpi_hw_obj *phw = pao->priv;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
phw = pao->priv;
|
|
||||||
|
|
||||||
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
|
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
|
||||||
hpios_locked_mem_free(&phw->h_control_cache);
|
hpios_locked_mem_free(&phw->h_control_cache);
|
||||||
hpi_free_control_cache(phw->p_cache);
|
hpi_free_control_cache(phw->p_cache);
|
||||||
|
@ -1275,6 +1276,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
|
||||||
case HPI_ADAPTER_FAMILY_ASI(0x6300):
|
case HPI_ADAPTER_FAMILY_ASI(0x6300):
|
||||||
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
|
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
|
||||||
break;
|
break;
|
||||||
|
case HPI_ADAPTER_FAMILY_ASI(0x5500):
|
||||||
case HPI_ADAPTER_FAMILY_ASI(0x5600):
|
case HPI_ADAPTER_FAMILY_ASI(0x5600):
|
||||||
case HPI_ADAPTER_FAMILY_ASI(0x6500):
|
case HPI_ADAPTER_FAMILY_ASI(0x6500):
|
||||||
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
|
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
|
||||||
|
@ -2059,7 +2061,6 @@ static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us)
|
||||||
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
|
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
|
||||||
{
|
{
|
||||||
struct bus_master_interface *interface = phw->p_interface_buffer;
|
struct bus_master_interface *interface = phw->p_interface_buffer;
|
||||||
|
|
||||||
u32 r;
|
u32 r;
|
||||||
|
|
||||||
interface->host_cmd = cmd;
|
interface->host_cmd = cmd;
|
||||||
|
|
|
@ -294,7 +294,7 @@ enum HPI_CONTROL_ATTRIBUTES {
|
||||||
|
|
||||||
/* These defines are used to fill in protocol information for an Ethernet packet
|
/* These defines are used to fill in protocol information for an Ethernet packet
|
||||||
sent using HMI on CS18102 */
|
sent using HMI on CS18102 */
|
||||||
/** ID supplied by Cirrius for ASI packets. */
|
/** ID supplied by Cirrus for ASI packets. */
|
||||||
#define HPI_ETHERNET_PACKET_ID 0x85
|
#define HPI_ETHERNET_PACKET_ID 0x85
|
||||||
/** Simple packet - no special routing required */
|
/** Simple packet - no special routing required */
|
||||||
#define HPI_ETHERNET_PACKET_V1 0x01
|
#define HPI_ETHERNET_PACKET_V1 0x01
|
||||||
|
@ -307,7 +307,7 @@ enum HPI_CONTROL_ATTRIBUTES {
|
||||||
/** This packet must make its way to the host across the HPI interface */
|
/** This packet must make its way to the host across the HPI interface */
|
||||||
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
|
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
|
||||||
|
|
||||||
#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
|
#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */
|
||||||
|
|
||||||
/** Default network timeout in milli-seconds. */
|
/** Default network timeout in milli-seconds. */
|
||||||
#define HPI_ETHERNET_TIMEOUT_MS 500
|
#define HPI_ETHERNET_TIMEOUT_MS 500
|
||||||
|
@ -397,14 +397,14 @@ enum HPI_FUNCTION_IDS {
|
||||||
HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
|
HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
|
||||||
HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
|
HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
|
||||||
HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
|
HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
|
||||||
HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4),
|
/* HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), */
|
||||||
HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
|
HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
|
||||||
HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
|
HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
|
||||||
HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7),
|
/* HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), */
|
||||||
HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
|
HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
|
||||||
HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
|
HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
|
||||||
HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10),
|
/* HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), */
|
||||||
HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11),
|
/* HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), */
|
||||||
HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
|
HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
|
||||||
HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
|
HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
|
||||||
HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
|
HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
|
||||||
|
@ -433,7 +433,8 @@ enum HPI_FUNCTION_IDS {
|
||||||
HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
|
HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
|
||||||
HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
|
HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
|
||||||
HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
|
HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
|
||||||
#define HPI_ADAPTER_FUNCTION_COUNT 20
|
HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21),
|
||||||
|
#define HPI_ADAPTER_FUNCTION_COUNT 21
|
||||||
|
|
||||||
HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
|
HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
|
||||||
HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
|
HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
|
||||||
|
@ -1561,8 +1562,6 @@ void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
|
||||||
u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
|
u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
|
||||||
u16 *pw_adapter_index);
|
u16 *pw_adapter_index);
|
||||||
|
|
||||||
u16 hpi_subsys_delete_adapter(u16 adapter_index);
|
|
||||||
|
|
||||||
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
|
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
|
||||||
struct hpi_hostbuffer_status **pp_status);
|
struct hpi_hostbuffer_status **pp_status);
|
||||||
|
|
||||||
|
@ -1584,9 +1583,7 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR);
|
||||||
|
|
||||||
/*////////////////////////////////////////////////////////////////////////// */
|
/*////////////////////////////////////////////////////////////////////////// */
|
||||||
/* declarations for individual HPI entry points */
|
/* declarations for individual HPI entry points */
|
||||||
hpi_handler_func HPI_1000;
|
|
||||||
hpi_handler_func HPI_6000;
|
hpi_handler_func HPI_6000;
|
||||||
hpi_handler_func HPI_6205;
|
hpi_handler_func HPI_6205;
|
||||||
hpi_handler_func HPI_COMMON;
|
|
||||||
|
|
||||||
#endif /* _HPI_INTERNAL_H_ */
|
#endif /* _HPI_INTERNAL_H_ */
|
||||||
|
|
|
@ -227,8 +227,9 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
|
||||||
if (info->control_type) {
|
if (info->control_type) {
|
||||||
pC->p_info[info->control_index] = info;
|
pC->p_info[info->control_index] = info;
|
||||||
cached++;
|
cached++;
|
||||||
} else /* dummy cache entry */
|
} else { /* dummy cache entry */
|
||||||
pC->p_info[info->control_index] = NULL;
|
pC->p_info[info->control_index] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
byte_count += info->size_in32bit_words * 4;
|
byte_count += info->size_in32bit_words * 4;
|
||||||
|
|
||||||
|
@ -298,7 +299,7 @@ struct pad_ofs_size {
|
||||||
unsigned int field_size;
|
unsigned int field_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pad_ofs_size pad_desc[] = {
|
static const struct pad_ofs_size pad_desc[] = {
|
||||||
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
|
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_artist), /* HPI_PAD_ARTIST */
|
||||||
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
|
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
|
||||||
|
@ -617,6 +618,10 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Allocate control cache.
|
||||||
|
|
||||||
|
\return Cache pointer, or NULL if allocation fails.
|
||||||
|
*/
|
||||||
struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
|
struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
|
||||||
const u32 size_in_bytes, u8 *p_dsp_control_buffer)
|
const u32 size_in_bytes, u8 *p_dsp_control_buffer)
|
||||||
{
|
{
|
||||||
|
@ -667,7 +672,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
|
||||||
phr->u.s.num_adapters = adapters.gw_num_adapters;
|
phr->u.s.num_adapters = adapters.gw_num_adapters;
|
||||||
break;
|
break;
|
||||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
phr->error = HPI_ERROR_INVALID_FUNC;
|
phr->error = HPI_ERROR_INVALID_FUNC;
|
||||||
|
|
|
@ -60,3 +60,5 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
|
||||||
struct hpi_message *phm, struct hpi_response *phr);
|
struct hpi_message *phm, struct hpi_response *phr);
|
||||||
|
|
||||||
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
|
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
|
||||||
|
|
||||||
|
hpi_handler_func HPI_COMMON;
|
||||||
|
|
|
@ -105,33 +105,6 @@ u16 hpi_subsys_get_version_ex(u32 *pversion_ex)
|
||||||
return hr.error;
|
return hr.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource,
|
|
||||||
u16 *pw_adapter_index)
|
|
||||||
{
|
|
||||||
struct hpi_message hm;
|
|
||||||
struct hpi_response hr;
|
|
||||||
|
|
||||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
|
||||||
HPI_SUBSYS_CREATE_ADAPTER);
|
|
||||||
hm.u.s.resource = *p_resource;
|
|
||||||
|
|
||||||
hpi_send_recv(&hm, &hr);
|
|
||||||
|
|
||||||
*pw_adapter_index = hr.u.s.adapter_index;
|
|
||||||
return hr.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 hpi_subsys_delete_adapter(u16 adapter_index)
|
|
||||||
{
|
|
||||||
struct hpi_message hm;
|
|
||||||
struct hpi_response hr;
|
|
||||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
|
||||||
HPI_SUBSYS_DELETE_ADAPTER);
|
|
||||||
hm.obj_index = adapter_index;
|
|
||||||
hpi_send_recv(&hm, &hr);
|
|
||||||
return hr.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
|
u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
|
||||||
{
|
{
|
||||||
struct hpi_message hm;
|
struct hpi_message hm;
|
||||||
|
|
|
@ -211,24 +211,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
|
||||||
HPIMSGX__init(phm, phr);
|
HPIMSGX__init(phm, phr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
|
||||||
HPIMSGX__cleanup(phm->obj_index, h_owner);
|
|
||||||
{
|
|
||||||
struct hpi_message hm;
|
|
||||||
struct hpi_response hr;
|
|
||||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
|
||||||
HPI_ADAPTER_CLOSE);
|
|
||||||
hm.adapter_index = phm->obj_index;
|
|
||||||
hw_entry_point(&hm, &hr);
|
|
||||||
}
|
|
||||||
if ((phm->obj_index < HPI_MAX_ADAPTERS)
|
|
||||||
&& hpi_entry_points[phm->obj_index]) {
|
|
||||||
hpi_entry_points[phm->obj_index] (phm, phr);
|
|
||||||
hpi_entry_points[phm->obj_index] = NULL;
|
|
||||||
} else
|
|
||||||
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
/* Must explicitly handle every subsys message in this switch */
|
/* Must explicitly handle every subsys message in this switch */
|
||||||
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
|
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
|
||||||
|
@ -247,6 +229,19 @@ static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
|
||||||
case HPI_ADAPTER_CLOSE:
|
case HPI_ADAPTER_CLOSE:
|
||||||
adapter_close(phm, phr);
|
adapter_close(phm, phr);
|
||||||
break;
|
break;
|
||||||
|
case HPI_ADAPTER_DELETE:
|
||||||
|
HPIMSGX__cleanup(phm->adapter_index, h_owner);
|
||||||
|
{
|
||||||
|
struct hpi_message hm;
|
||||||
|
struct hpi_response hr;
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
hw_entry_point(phm, phr);
|
hw_entry_point(phm, phr);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -25,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
|
||||||
#include "hpidebug.h"
|
#include "hpidebug.h"
|
||||||
#include "hpimsgx.h"
|
#include "hpimsgx.h"
|
||||||
#include "hpioctl.h"
|
#include "hpioctl.h"
|
||||||
|
#include "hpicmn.h"
|
||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -161,26 +162,24 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa = &adapters[hm->h.adapter_index];
|
switch (hm->h.function) {
|
||||||
|
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||||
|
case HPI_ADAPTER_DELETE:
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
hr->h.size = res_max_size;
|
hr->h.size = res_max_size;
|
||||||
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
|
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
|
||||||
switch (hm->h.function) {
|
hpi_send_recv_f(&hm->m0, &hr->r0, file);
|
||||||
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 {
|
} else {
|
||||||
u16 __user *ptr = NULL;
|
u16 __user *ptr = NULL;
|
||||||
u32 size = 0;
|
u32 size = 0;
|
||||||
|
@ -188,8 +187,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
/* -1=no data 0=read from user mem, 1=write to user mem */
|
/* -1=no data 0=read from user mem, 1=write to user mem */
|
||||||
int wrflag = -1;
|
int wrflag = -1;
|
||||||
u32 adapter = hm->h.adapter_index;
|
u32 adapter = hm->h.adapter_index;
|
||||||
|
pa = &adapters[adapter];
|
||||||
|
|
||||||
if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
|
if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) {
|
||||||
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
|
hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
|
||||||
HPI_ADAPTER_OPEN,
|
HPI_ADAPTER_OPEN,
|
||||||
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||||
|
@ -317,7 +317,7 @@ out:
|
||||||
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||||
const struct pci_device_id *pci_id)
|
const struct pci_device_id *pci_id)
|
||||||
{
|
{
|
||||||
int err, idx, nm;
|
int idx, nm;
|
||||||
unsigned int memlen;
|
unsigned int memlen;
|
||||||
struct hpi_message hm;
|
struct hpi_message hm;
|
||||||
struct hpi_response hr;
|
struct hpi_response hr;
|
||||||
|
@ -351,11 +351,8 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||||
nm = HPI_MAX_ADAPTER_MEM_SPACES;
|
nm = HPI_MAX_ADAPTER_MEM_SPACES;
|
||||||
|
|
||||||
for (idx = 0; idx < nm; idx++) {
|
for (idx = 0; idx < nm; idx++) {
|
||||||
HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
|
HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
|
||||||
idx, pci_dev->resource[idx].name,
|
&pci_dev->resource[idx]);
|
||||||
(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) {
|
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
|
||||||
memlen = pci_resource_len(pci_dev, idx);
|
memlen = pci_resource_len(pci_dev, idx);
|
||||||
|
@ -395,17 +392,20 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||||
|
|
||||||
adapter.index = hr.u.s.adapter_index;
|
adapter.index = hr.u.s.adapter_index;
|
||||||
adapter.type = hr.u.s.adapter_type;
|
adapter.type = hr.u.s.adapter_type;
|
||||||
hm.adapter_index = adapter.index;
|
|
||||||
|
|
||||||
err = hpi_adapter_open(adapter.index);
|
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||||
if (err)
|
HPI_ADAPTER_OPEN);
|
||||||
|
hm.adapter_index = adapter.index;
|
||||||
|
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||||
|
|
||||||
|
if (hr.error)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
adapter.snd_card_asihpi = NULL;
|
adapter.snd_card_asihpi = NULL;
|
||||||
/* WARNING can't init mutex in 'adapter'
|
/* WARNING can't init mutex in 'adapter'
|
||||||
* and then copy it to adapters[] ?!?!
|
* and then copy it to adapters[] ?!?!
|
||||||
*/
|
*/
|
||||||
adapters[hr.u.s.adapter_index] = adapter;
|
adapters[adapter.index] = adapter;
|
||||||
mutex_init(&adapters[adapter.index].mutex);
|
mutex_init(&adapters[adapter.index].mutex);
|
||||||
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
|
pci_set_drvdata(pci_dev, &adapters[adapter.index]);
|
||||||
|
|
||||||
|
@ -440,10 +440,9 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
|
||||||
struct hpi_adapter *pa;
|
struct hpi_adapter *pa;
|
||||||
pa = pci_get_drvdata(pci_dev);
|
pa = pci_get_drvdata(pci_dev);
|
||||||
|
|
||||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||||
HPI_SUBSYS_DELETE_ADAPTER);
|
HPI_ADAPTER_DELETE);
|
||||||
hm.obj_index = pa->index;
|
hm.adapter_index = pa->index;
|
||||||
hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
|
|
||||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||||
|
|
||||||
/* unmap PCI memory space, mapped during device init. */
|
/* unmap PCI memory space, mapped during device init. */
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#define CHIP_AU8810
|
#define CHIP_AU8810
|
||||||
|
|
||||||
#define CARD_NAME "Aureal Advantage 3D Sound Processor"
|
#define CARD_NAME "Aureal Advantage"
|
||||||
#define CARD_NAME_SHORT "au8810"
|
#define CARD_NAME_SHORT "au8810"
|
||||||
|
|
||||||
#define NR_ADB 0x10
|
#define NR_ADB 0x10
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#define CHIP_AU8820
|
#define CHIP_AU8820
|
||||||
|
|
||||||
#define CARD_NAME "Aureal Vortex 3D Sound Processor"
|
#define CARD_NAME "Aureal Vortex"
|
||||||
#define CARD_NAME_SHORT "au8820"
|
#define CARD_NAME_SHORT "au8820"
|
||||||
|
|
||||||
/* Number of ADB and WT channels */
|
/* Number of ADB and WT channels */
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#define CHIP_AU8830
|
#define CHIP_AU8830
|
||||||
|
|
||||||
#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
|
#define CARD_NAME "Aureal Vortex 2"
|
||||||
#define CARD_NAME_SHORT "au8830"
|
#define CARD_NAME_SHORT "au8830"
|
||||||
|
|
||||||
#define NR_ADB 0x20
|
#define NR_ADB 0x20
|
||||||
|
|
|
@ -426,11 +426,11 @@ static struct snd_pcm_ops snd_vortex_playback_ops = {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
|
static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
|
||||||
"AU88x0 ADB",
|
CARD_NAME " ADB",
|
||||||
"AU88x0 SPDIF",
|
CARD_NAME " SPDIF",
|
||||||
"AU88x0 A3D",
|
CARD_NAME " A3D",
|
||||||
"AU88x0 WT",
|
CARD_NAME " WT",
|
||||||
"AU88x0 I2S",
|
CARD_NAME " I2S",
|
||||||
};
|
};
|
||||||
static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
|
static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
|
||||||
"adb",
|
"adb",
|
||||||
|
@ -527,7 +527,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
|
||||||
nr_capt, &pcm);
|
nr_capt, &pcm);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
strcpy(pcm->name, vortex_pcm_name[idx]);
|
snprintf(pcm->name, sizeof(pcm->name),
|
||||||
|
"%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
|
||||||
chip->pcm[idx] = pcm;
|
chip->pcm[idx] = pcm;
|
||||||
// This is an evil hack, but it saves a lot of duplicated code.
|
// This is an evil hack, but it saves a lot of duplicated code.
|
||||||
VORTEX_PCM_TYPE(pcm) = idx;
|
VORTEX_PCM_TYPE(pcm) = idx;
|
||||||
|
|
|
@ -303,6 +303,9 @@ static const u32 db_table[101] = {
|
||||||
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
|
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
|
||||||
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
|
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
|
||||||
|
|
||||||
|
/* EMU10K1 bass/treble db gain */
|
||||||
|
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
|
||||||
|
|
||||||
static const u32 onoff_table[2] = {
|
static const u32 onoff_table[2] = {
|
||||||
0x00000000, 0x00000001
|
0x00000000, 0x00000001
|
||||||
};
|
};
|
||||||
|
@ -2163,6 +2166,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
|
||||||
ctl->min = 0;
|
ctl->min = 0;
|
||||||
ctl->max = 40;
|
ctl->max = 40;
|
||||||
ctl->value[0] = ctl->value[1] = 20;
|
ctl->value[0] = ctl->value[1] = 20;
|
||||||
|
ctl->tlv = snd_emu10k1_bass_treble_db_scale;
|
||||||
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
|
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
|
||||||
ctl = &controls[i + 1];
|
ctl = &controls[i + 1];
|
||||||
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||||
|
@ -2172,6 +2176,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
|
||||||
ctl->min = 0;
|
ctl->min = 0;
|
||||||
ctl->max = 40;
|
ctl->max = 40;
|
||||||
ctl->value[0] = ctl->value[1] = 20;
|
ctl->value[0] = ctl->value[1] = 20;
|
||||||
|
ctl->tlv = snd_emu10k1_bass_treble_db_scale;
|
||||||
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
|
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
|
||||||
|
|
||||||
#define BASS_GPR 0x8c
|
#define BASS_GPR 0x8c
|
||||||
|
|
|
@ -1729,8 +1729,6 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
||||||
"Master Mono Playback Volume",
|
"Master Mono Playback Volume",
|
||||||
"PCM Out Path & Mute",
|
"PCM Out Path & Mute",
|
||||||
"Mono Output Select",
|
"Mono Output Select",
|
||||||
"Front Playback Switch",
|
|
||||||
"Front Playback Volume",
|
|
||||||
"Surround Playback Switch",
|
"Surround Playback Switch",
|
||||||
"Surround Playback Volume",
|
"Surround Playback Volume",
|
||||||
"Center Playback Switch",
|
"Center Playback Switch",
|
||||||
|
@ -1879,6 +1877,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
||||||
emu->rear_ac97 = 1;
|
emu->rear_ac97 = 1;
|
||||||
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
|
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
|
||||||
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
|
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
|
||||||
|
remove_ctl(card,"Front Playback Volume");
|
||||||
|
remove_ctl(card,"Front Playback Switch");
|
||||||
}
|
}
|
||||||
/* remove unused AC97 controls */
|
/* remove unused AC97 controls */
|
||||||
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
|
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
|
||||||
|
@ -1913,6 +1913,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
||||||
for (; *c; c += 2)
|
for (; *c; c += 2)
|
||||||
rename_ctl(card, c[0], c[1]);
|
rename_ctl(card, c[0], c[1]);
|
||||||
|
|
||||||
|
if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
|
||||||
|
remove_ctl(card, "Center Playback Volume");
|
||||||
|
remove_ctl(card, "LFE Playback Volume");
|
||||||
|
remove_ctl(card, "Wave Center Playback Volume");
|
||||||
|
remove_ctl(card, "Wave LFE Playback Volume");
|
||||||
|
}
|
||||||
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
|
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
|
||||||
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
|
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
|
||||||
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
|
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
|
||||||
|
|
|
@ -112,6 +112,10 @@
|
||||||
#include <sound/ac97_codec.h>
|
#include <sound/ac97_codec.h>
|
||||||
#include <sound/initval.h>
|
#include <sound/initval.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_ES1968_RADIO
|
||||||
|
#include <sound/tea575x-tuner.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CARD_NAME "ESS Maestro1/2"
|
#define CARD_NAME "ESS Maestro1/2"
|
||||||
#define DRIVER_NAME "ES1968"
|
#define DRIVER_NAME "ES1968"
|
||||||
|
|
||||||
|
@ -553,6 +557,10 @@ struct es1968 {
|
||||||
spinlock_t ac97_lock;
|
spinlock_t ac97_lock;
|
||||||
struct tasklet_struct hwvol_tq;
|
struct tasklet_struct hwvol_tq;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_ES1968_RADIO
|
||||||
|
struct snd_tea575x tea;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
|
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
|
||||||
|
@ -2571,6 +2579,63 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SND_ES1968_INPUT */
|
#endif /* CONFIG_SND_ES1968_INPUT */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_ES1968_RADIO
|
||||||
|
#define GPIO_DATA 0x60
|
||||||
|
#define IO_MASK 4 /* mask register offset from GPIO_DATA
|
||||||
|
bits 1=unmask write to given bit */
|
||||||
|
#define IO_DIR 8 /* direction register offset from GPIO_DATA
|
||||||
|
bits 0/1=read/write direction */
|
||||||
|
/* mask bits for GPIO lines */
|
||||||
|
#define STR_DATA 0x0040 /* GPIO6 */
|
||||||
|
#define STR_CLK 0x0080 /* GPIO7 */
|
||||||
|
#define STR_WREN 0x0100 /* GPIO8 */
|
||||||
|
#define STR_MOST 0x0200 /* GPIO9 */
|
||||||
|
|
||||||
|
static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
|
||||||
|
{
|
||||||
|
struct es1968 *chip = tea->private_data;
|
||||||
|
unsigned long io = chip->io_port + GPIO_DATA;
|
||||||
|
u16 val = 0;
|
||||||
|
|
||||||
|
val |= (pins & TEA575X_DATA) ? STR_DATA : 0;
|
||||||
|
val |= (pins & TEA575X_CLK) ? STR_CLK : 0;
|
||||||
|
val |= (pins & TEA575X_WREN) ? STR_WREN : 0;
|
||||||
|
|
||||||
|
outw(val, io);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
|
||||||
|
{
|
||||||
|
struct es1968 *chip = tea->private_data;
|
||||||
|
unsigned long io = chip->io_port + GPIO_DATA;
|
||||||
|
u16 val = inw(io);
|
||||||
|
|
||||||
|
return (val & STR_DATA) ? TEA575X_DATA : 0 |
|
||||||
|
(val & STR_MOST) ? TEA575X_MOST : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
|
||||||
|
{
|
||||||
|
struct es1968 *chip = tea->private_data;
|
||||||
|
unsigned long io = chip->io_port + GPIO_DATA;
|
||||||
|
u16 odir = inw(io + IO_DIR);
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
|
||||||
|
outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR);
|
||||||
|
} else {
|
||||||
|
outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK);
|
||||||
|
outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct snd_tea575x_ops snd_es1968_tea_ops = {
|
||||||
|
.set_pins = snd_es1968_tea575x_set_pins,
|
||||||
|
.get_pins = snd_es1968_tea575x_get_pins,
|
||||||
|
.set_direction = snd_es1968_tea575x_set_direction,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static int snd_es1968_free(struct es1968 *chip)
|
static int snd_es1968_free(struct es1968 *chip)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SND_ES1968_INPUT
|
#ifdef CONFIG_SND_ES1968_INPUT
|
||||||
|
@ -2585,6 +2650,10 @@ static int snd_es1968_free(struct es1968 *chip)
|
||||||
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
|
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_ES1968_RADIO
|
||||||
|
snd_tea575x_exit(&chip->tea);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (chip->irq >= 0)
|
if (chip->irq >= 0)
|
||||||
free_irq(chip->irq, chip);
|
free_irq(chip->irq, chip);
|
||||||
snd_es1968_free_gameport(chip);
|
snd_es1968_free_gameport(chip);
|
||||||
|
@ -2723,6 +2792,15 @@ static int __devinit snd_es1968_create(struct snd_card *card,
|
||||||
|
|
||||||
snd_card_set_dev(card, &pci->dev);
|
snd_card_set_dev(card, &pci->dev);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SND_ES1968_RADIO
|
||||||
|
chip->tea.private_data = chip;
|
||||||
|
chip->tea.ops = &snd_es1968_tea_ops;
|
||||||
|
strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
|
||||||
|
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
||||||
|
if (!snd_tea575x_init(&chip->tea))
|
||||||
|
printk(KERN_INFO "es1968: detected TEA575x radio\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
*chip_ret = chip;
|
*chip_ret = chip;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
|
|
||||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||||
#include <sound/tea575x-tuner.h>
|
#include <sound/tea575x-tuner.h>
|
||||||
#define TEA575X_RADIO 1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||||
|
@ -53,7 +52,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card *
|
||||||
/*
|
/*
|
||||||
* Enable TEA575x tuner
|
* Enable TEA575x tuner
|
||||||
* 1 = MediaForte 256-PCS
|
* 1 = MediaForte 256-PCS
|
||||||
* 2 = MediaForte 256-PCPR
|
* 2 = MediaForte 256-PCP
|
||||||
* 3 = MediaForte 64-PCR
|
* 3 = MediaForte 64-PCR
|
||||||
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
|
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
|
||||||
* High 16-bits are video (radio) device number + 1
|
* High 16-bits are video (radio) device number + 1
|
||||||
|
@ -67,7 +66,7 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
|
||||||
module_param_array(enable, bool, NULL, 0444);
|
module_param_array(enable, bool, NULL, 0444);
|
||||||
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
|
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
|
||||||
module_param_array(tea575x_tuner, int, NULL, 0444);
|
module_param_array(tea575x_tuner, int, NULL, 0444);
|
||||||
MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
|
MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
|
||||||
|
|
||||||
#define TUNER_ONLY (1<<4)
|
#define TUNER_ONLY (1<<4)
|
||||||
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
|
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
|
||||||
|
@ -196,7 +195,7 @@ struct fm801 {
|
||||||
spinlock_t reg_lock;
|
spinlock_t reg_lock;
|
||||||
struct snd_info_entry *proc_entry;
|
struct snd_info_entry *proc_entry;
|
||||||
|
|
||||||
#ifdef TEA575X_RADIO
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||||
struct snd_tea575x tea;
|
struct snd_tea575x tea;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -715,310 +714,89 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
|
||||||
* TEA5757 radio
|
* TEA5757 radio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef TEA575X_RADIO
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||||
|
|
||||||
/* 256PCS GPIO numbers */
|
/* GPIO to TEA575x maps */
|
||||||
#define TEA_256PCS_DATA 1
|
struct snd_fm801_tea575x_gpio {
|
||||||
#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
|
u8 data, clk, wren, most;
|
||||||
#define TEA_256PCS_BUS_CLOCK 3
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
|
static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
|
||||||
|
{ .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
|
||||||
|
{ .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
|
||||||
|
{ .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
|
||||||
{
|
{
|
||||||
struct fm801 *chip = tea->private_data;
|
struct fm801 *chip = tea->private_data;
|
||||||
unsigned short reg;
|
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
|
||||||
int i = 25;
|
struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||||
|
|
||||||
|
reg &= ~(FM801_GPIO_GP(gpio.data) |
|
||||||
|
FM801_GPIO_GP(gpio.clk) |
|
||||||
|
FM801_GPIO_GP(gpio.wren));
|
||||||
|
|
||||||
|
reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
|
||||||
|
reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0;
|
||||||
|
/* WRITE_ENABLE is inverted */
|
||||||
|
reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
|
||||||
|
|
||||||
|
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
|
||||||
|
{
|
||||||
|
struct fm801 *chip = tea->private_data;
|
||||||
|
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
|
||||||
|
struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||||
|
|
||||||
|
return (reg & FM801_GPIO_GP(gpio.data)) ? TEA575X_DATA : 0 |
|
||||||
|
(reg & FM801_GPIO_GP(gpio.most)) ? TEA575X_MOST : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
|
||||||
|
{
|
||||||
|
struct fm801 *chip = tea->private_data;
|
||||||
|
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
|
||||||
|
struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
/* use GPIO lines and set write enable bit */
|
/* use GPIO lines and set write enable bit */
|
||||||
reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
|
reg |= FM801_GPIO_GS(gpio.data) |
|
||||||
FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
|
FM801_GPIO_GS(gpio.wren) |
|
||||||
FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
|
FM801_GPIO_GS(gpio.clk) |
|
||||||
/* all of lines are in the write direction */
|
FM801_GPIO_GS(gpio.most);
|
||||||
/* clear data and clock lines */
|
if (output) {
|
||||||
reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
|
/* all of lines are in the write direction */
|
||||||
FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
|
/* clear data and clock lines */
|
||||||
FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
|
reg &= ~(FM801_GPIO_GD(gpio.data) |
|
||||||
FM801_GPIO_GP(TEA_256PCS_DATA) |
|
FM801_GPIO_GD(gpio.wren) |
|
||||||
FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
|
FM801_GPIO_GD(gpio.clk) |
|
||||||
FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
|
FM801_GPIO_GP(gpio.data) |
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
FM801_GPIO_GP(gpio.clk) |
|
||||||
udelay(1);
|
FM801_GPIO_GP(gpio.wren));
|
||||||
|
} else {
|
||||||
while (i--) {
|
/* use GPIO lines, set data direction to input */
|
||||||
if (val & (1 << i))
|
reg |= FM801_GPIO_GD(gpio.data) |
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
|
FM801_GPIO_GD(gpio.most) |
|
||||||
else
|
FM801_GPIO_GP(gpio.data) |
|
||||||
reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
|
FM801_GPIO_GP(gpio.most) |
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
FM801_GPIO_GP(gpio.wren);
|
||||||
udelay(1);
|
/* all of lines are in the write direction, except data */
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
|
/* clear data, write enable and clock lines */
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
reg &= ~(FM801_GPIO_GD(gpio.wren) |
|
||||||
reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
|
FM801_GPIO_GD(gpio.clk) |
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
FM801_GPIO_GP(gpio.clk));
|
||||||
udelay(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* and reset the write enable bit */
|
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCS_DATA);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
|
static struct snd_tea575x_ops snd_fm801_tea_ops = {
|
||||||
{
|
.set_pins = snd_fm801_tea575x_set_pins,
|
||||||
struct fm801 *chip = tea->private_data;
|
.get_pins = snd_fm801_tea575x_get_pins,
|
||||||
unsigned short reg;
|
.set_direction = snd_fm801_tea575x_set_direction,
|
||||||
unsigned int val = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
/* use GPIO lines, set data direction to input */
|
|
||||||
reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
|
|
||||||
FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GD(TEA_256PCS_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCS_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
|
|
||||||
/* all of lines are in the write direction, except data */
|
|
||||||
/* clear data, write enable and clock lines */
|
|
||||||
reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
|
|
||||||
|
|
||||||
for (i = 0; i < 24; i++) {
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
val <<= 1;
|
|
||||||
if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
|
|
||||||
val |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 256PCPR GPIO numbers */
|
|
||||||
#define TEA_256PCPR_BUS_CLOCK 0
|
|
||||||
#define TEA_256PCPR_DATA 1
|
|
||||||
#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
|
|
||||||
|
|
||||||
static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
|
|
||||||
{
|
|
||||||
struct fm801 *chip = tea->private_data;
|
|
||||||
unsigned short reg;
|
|
||||||
int i = 25;
|
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
/* use GPIO lines and set write enable bit */
|
|
||||||
reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
|
|
||||||
FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
|
|
||||||
/* all of lines are in the write direction */
|
|
||||||
/* clear data and clock lines */
|
|
||||||
reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
|
|
||||||
FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
|
|
||||||
while (i--) {
|
|
||||||
if (val & (1 << i))
|
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
|
|
||||||
else
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* and reset the write enable bit */
|
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_DATA);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
|
|
||||||
{
|
|
||||||
struct fm801 *chip = tea->private_data;
|
|
||||||
unsigned short reg;
|
|
||||||
unsigned int val = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
/* use GPIO lines, set data direction to input */
|
|
||||||
reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
|
|
||||||
FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GD(TEA_256PCPR_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
|
|
||||||
/* all of lines are in the write direction, except data */
|
|
||||||
/* clear data, write enable and clock lines */
|
|
||||||
reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
|
|
||||||
|
|
||||||
for (i = 0; i < 24; i++) {
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
val <<= 1;
|
|
||||||
if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
|
|
||||||
val |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 64PCR GPIO numbers */
|
|
||||||
#define TEA_64PCR_BUS_CLOCK 0
|
|
||||||
#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
|
|
||||||
#define TEA_64PCR_DATA 2
|
|
||||||
|
|
||||||
static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
|
|
||||||
{
|
|
||||||
struct fm801 *chip = tea->private_data;
|
|
||||||
unsigned short reg;
|
|
||||||
int i = 25;
|
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
/* use GPIO lines and set write enable bit */
|
|
||||||
reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
|
|
||||||
FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
|
|
||||||
/* all of lines are in the write direction */
|
|
||||||
/* clear data and clock lines */
|
|
||||||
reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
|
|
||||||
FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
|
|
||||||
while (i--) {
|
|
||||||
if (val & (1 << i))
|
|
||||||
reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
|
|
||||||
else
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* and reset the write enable bit */
|
|
||||||
reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_DATA);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
|
|
||||||
{
|
|
||||||
struct fm801 *chip = tea->private_data;
|
|
||||||
unsigned short reg;
|
|
||||||
unsigned int val = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
/* use GPIO lines, set data direction to input */
|
|
||||||
reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
|
|
||||||
FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GD(TEA_64PCR_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_DATA) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
|
|
||||||
/* all of lines are in the write direction, except data */
|
|
||||||
/* clear data, write enable and clock lines */
|
|
||||||
reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
|
|
||||||
FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
|
|
||||||
FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
|
|
||||||
|
|
||||||
for (i = 0; i < 24; i++) {
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
val <<= 1;
|
|
||||||
if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
|
|
||||||
val |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
|
|
||||||
unsigned int mute)
|
|
||||||
{
|
|
||||||
struct fm801 *chip = tea->private_data;
|
|
||||||
unsigned short reg;
|
|
||||||
|
|
||||||
spin_lock_irq(&chip->reg_lock);
|
|
||||||
|
|
||||||
reg = inw(FM801_REG(chip, GPIO_CTRL));
|
|
||||||
if (mute)
|
|
||||||
/* 0xf800 (mute) */
|
|
||||||
reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
|
|
||||||
else
|
|
||||||
/* 0xf802 (unmute) */
|
|
||||||
reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
|
|
||||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
|
||||||
udelay(1);
|
|
||||||
|
|
||||||
spin_unlock_irq(&chip->reg_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
|
|
||||||
{
|
|
||||||
/* 1 = MediaForte 256-PCS */
|
|
||||||
.write = snd_fm801_tea575x_256pcs_write,
|
|
||||||
.read = snd_fm801_tea575x_256pcs_read,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/* 2 = MediaForte 256-PCPR */
|
|
||||||
.write = snd_fm801_tea575x_256pcpr_write,
|
|
||||||
.read = snd_fm801_tea575x_256pcpr_read,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/* 3 = MediaForte 64-PCR */
|
|
||||||
.write = snd_fm801_tea575x_64pcr_write,
|
|
||||||
.read = snd_fm801_tea575x_64pcr_read,
|
|
||||||
.mute = snd_fm801_tea575x_64pcr_mute,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1371,7 +1149,7 @@ static int snd_fm801_free(struct fm801 *chip)
|
||||||
outw(cmdw, FM801_REG(chip, IRQ_MASK));
|
outw(cmdw, FM801_REG(chip, IRQ_MASK));
|
||||||
|
|
||||||
__end_hw:
|
__end_hw:
|
||||||
#ifdef TEA575X_RADIO
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||||
snd_tea575x_exit(&chip->tea);
|
snd_tea575x_exit(&chip->tea);
|
||||||
#endif
|
#endif
|
||||||
if (chip->irq >= 0)
|
if (chip->irq >= 0)
|
||||||
|
@ -1450,16 +1228,25 @@ static int __devinit snd_fm801_create(struct snd_card *card,
|
||||||
|
|
||||||
snd_card_set_dev(card, &pci->dev);
|
snd_card_set_dev(card, &pci->dev);
|
||||||
|
|
||||||
#ifdef TEA575X_RADIO
|
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||||
|
chip->tea.private_data = chip;
|
||||||
|
chip->tea.ops = &snd_fm801_tea_ops;
|
||||||
|
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
||||||
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
|
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
|
||||||
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
||||||
chip->tea.dev_nr = tea575x_tuner >> 16;
|
if (snd_tea575x_init(&chip->tea))
|
||||||
chip->tea.card = card;
|
snd_printk(KERN_ERR "TEA575x radio not found\n");
|
||||||
chip->tea.freq_fixup = 10700;
|
} else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0)
|
||||||
chip->tea.private_data = chip;
|
/* autodetect tuner connection */
|
||||||
chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
|
||||||
snd_tea575x_init(&chip->tea);
|
chip->tea575x_tuner = tea575x_tuner;
|
||||||
}
|
if (!snd_tea575x_init(&chip->tea)) {
|
||||||
|
snd_printk(KERN_INFO "detected TEA575x radio type %s\n",
|
||||||
|
snd_fm801_tea575x_gpios[tea575x_tuner - 1].name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strlcpy(chip->tea.card, snd_fm801_tea575x_gpios[(tea575x_tuner & TUNER_TYPE_MASK) - 1].name, sizeof(chip->tea.card));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
*rchip = chip;
|
*rchip = chip;
|
||||||
|
|
|
@ -235,8 +235,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
|
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
|
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
|
||||||
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
|
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
|
||||||
|
{ PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
|
||||||
#if 0
|
#if 0
|
||||||
{ PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
|
|
||||||
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
|
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
|
||||||
#endif
|
#endif
|
||||||
{ 0, }
|
{ 0, }
|
||||||
|
@ -1261,9 +1261,9 @@ static struct shortname_table {
|
||||||
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
|
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
|
||||||
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
|
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
|
||||||
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
|
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
|
||||||
|
{ 0x746e, "AMD AMD8111" },
|
||||||
#if 0
|
#if 0
|
||||||
{ 0x5455, "ALi M5455" },
|
{ 0x5455, "ALi M5455" },
|
||||||
{ 0x746d, "AMD AMD8111" },
|
|
||||||
#endif
|
#endif
|
||||||
{ 0 },
|
{ 0 },
|
||||||
};
|
};
|
||||||
|
|
|
@ -1000,7 +1000,7 @@ static void device_change_handler(struct work_struct *work)
|
||||||
chip->lineout_sw_ctl);
|
chip->lineout_sw_ctl);
|
||||||
if (mix->anded_reset)
|
if (mix->anded_reset)
|
||||||
msleep(10);
|
msleep(10);
|
||||||
check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
|
check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify,
|
||||||
chip->speaker_sw_ctl);
|
chip->speaker_sw_ctl);
|
||||||
} else {
|
} else {
|
||||||
/* unmute speaker, mute others */
|
/* unmute speaker, mute others */
|
||||||
|
|
|
@ -65,6 +65,15 @@ init_data[] = {
|
||||||
{ 0 } /* TERMINATING ENTRY */
|
{ 0 } /* TERMINATING ENTRY */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
|
||||||
|
/* values to write to soundcard register for all samplerates */
|
||||||
|
static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
|
||||||
|
static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DIGITAL_THRU_ONLY_SAMPLERATE = 3
|
||||||
|
};
|
||||||
|
|
||||||
static void usb6fire_control_master_vol_update(struct control_runtime *rt)
|
static void usb6fire_control_master_vol_update(struct control_runtime *rt)
|
||||||
{
|
{
|
||||||
struct comm_runtime *comm_rt = rt->chip->comm;
|
struct comm_runtime *comm_rt = rt->chip->comm;
|
||||||
|
@ -95,6 +104,67 @@ static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct usb_device *device = rt->chip->dev;
|
||||||
|
struct comm_runtime *comm_rt = rt->chip->comm;
|
||||||
|
|
||||||
|
if (rate < 0 || rate >= CONTROL_N_RATES)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = usb_set_interface(device, 1, rates_altsetting[rate]);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* set soundcard clock */
|
||||||
|
ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
|
||||||
|
rates_6fire_vh[rate]);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb6fire_control_set_channels(
|
||||||
|
struct control_runtime *rt, int n_analog_out,
|
||||||
|
int n_analog_in, bool spdif_out, bool spdif_in)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct comm_runtime *comm_rt = rt->chip->comm;
|
||||||
|
|
||||||
|
/* enable analog inputs and outputs
|
||||||
|
* (one bit per stereo-channel) */
|
||||||
|
ret = comm_rt->write16(comm_rt, 0x02, 0x02,
|
||||||
|
(1 << (n_analog_out / 2)) - 1,
|
||||||
|
(1 << (n_analog_in / 2)) - 1);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* disable digital inputs and outputs */
|
||||||
|
/* TODO: use spdif_x to enable/disable digital channels */
|
||||||
|
ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb6fire_control_streaming_update(struct control_runtime *rt)
|
||||||
|
{
|
||||||
|
struct comm_runtime *comm_rt = rt->chip->comm;
|
||||||
|
|
||||||
|
if (comm_rt) {
|
||||||
|
if (!rt->usb_streaming && rt->digital_thru_switch)
|
||||||
|
usb6fire_control_set_rate(rt,
|
||||||
|
DIGITAL_THRU_ONLY_SAMPLERATE);
|
||||||
|
return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
|
||||||
|
(rt->usb_streaming ? 0x01 : 0x00) |
|
||||||
|
(rt->digital_thru_switch ? 0x08 : 0x00));
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
|
static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_info *uinfo)
|
struct snd_ctl_elem_info *uinfo)
|
||||||
{
|
{
|
||||||
|
@ -195,6 +265,28 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
|
||||||
|
int changed = 0;
|
||||||
|
|
||||||
|
if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
|
||||||
|
rt->digital_thru_switch = ucontrol->value.integer.value[0];
|
||||||
|
usb6fire_control_streaming_update(rt);
|
||||||
|
changed = 1;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
|
||||||
|
ucontrol->value.integer.value[0] = rt->digital_thru_switch;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct __devinitdata snd_kcontrol_new elements[] = {
|
static struct __devinitdata snd_kcontrol_new elements[] = {
|
||||||
{
|
{
|
||||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
@ -223,6 +315,15 @@ static struct __devinitdata snd_kcontrol_new elements[] = {
|
||||||
.get = usb6fire_control_opt_coax_get,
|
.get = usb6fire_control_opt_coax_get,
|
||||||
.put = usb6fire_control_opt_coax_put
|
.put = usb6fire_control_opt_coax_put
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.name = "Digital Thru Playback Route",
|
||||||
|
.index = 0,
|
||||||
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||||
|
.info = snd_ctl_boolean_mono_info,
|
||||||
|
.get = usb6fire_control_digital_thru_get,
|
||||||
|
.put = usb6fire_control_digital_thru_put
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -238,6 +339,9 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
rt->chip = chip;
|
rt->chip = chip;
|
||||||
|
rt->update_streaming = usb6fire_control_streaming_update;
|
||||||
|
rt->set_rate = usb6fire_control_set_rate;
|
||||||
|
rt->set_channels = usb6fire_control_set_channels;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while (init_data[i].type) {
|
while (init_data[i].type) {
|
||||||
|
@ -249,6 +353,7 @@ int __devinit usb6fire_control_init(struct sfire_chip *chip)
|
||||||
usb6fire_control_opt_coax_update(rt);
|
usb6fire_control_opt_coax_update(rt);
|
||||||
usb6fire_control_line_phono_update(rt);
|
usb6fire_control_line_phono_update(rt);
|
||||||
usb6fire_control_master_vol_update(rt);
|
usb6fire_control_master_vol_update(rt);
|
||||||
|
usb6fire_control_streaming_update(rt);
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while (elements[i].name) {
|
while (elements[i].name) {
|
||||||
|
|
|
@ -21,12 +21,29 @@ enum {
|
||||||
CONTROL_MAX_ELEMENTS = 32
|
CONTROL_MAX_ELEMENTS = 32
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CONTROL_RATE_44KHZ,
|
||||||
|
CONTROL_RATE_48KHZ,
|
||||||
|
CONTROL_RATE_88KHZ,
|
||||||
|
CONTROL_RATE_96KHZ,
|
||||||
|
CONTROL_RATE_176KHZ,
|
||||||
|
CONTROL_RATE_192KHZ,
|
||||||
|
CONTROL_N_RATES
|
||||||
|
};
|
||||||
|
|
||||||
struct control_runtime {
|
struct control_runtime {
|
||||||
|
int (*update_streaming)(struct control_runtime *rt);
|
||||||
|
int (*set_rate)(struct control_runtime *rt, int rate);
|
||||||
|
int (*set_channels)(struct control_runtime *rt, int n_analog_out,
|
||||||
|
int n_analog_in, bool spdif_out, bool spdif_in);
|
||||||
|
|
||||||
struct sfire_chip *chip;
|
struct sfire_chip *chip;
|
||||||
|
|
||||||
struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
|
struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
|
||||||
bool opt_coax_switch;
|
bool opt_coax_switch;
|
||||||
bool line_phono_switch;
|
bool line_phono_switch;
|
||||||
|
bool digital_thru_switch;
|
||||||
|
bool usb_streaming;
|
||||||
u8 master_vol;
|
u8 master_vol;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,6 @@
|
||||||
*
|
*
|
||||||
* Firmware loader
|
* Firmware loader
|
||||||
*
|
*
|
||||||
* Currently not working for all devices. To be able to use the device
|
|
||||||
* in linux, it is also possible to let the windows driver upload the firmware.
|
|
||||||
* For that, start the computer in windows and reboot.
|
|
||||||
* As long as the device is connected to the power supply, no firmware reload
|
|
||||||
* needs to be performed.
|
|
||||||
*
|
|
||||||
* Author: Torsten Schenk <torsten.schenk@zoho.com>
|
* Author: Torsten Schenk <torsten.schenk@zoho.com>
|
||||||
* Created: Jan 01, 2011
|
* Created: Jan 01, 2011
|
||||||
* Version: 0.3.0
|
* Version: 0.3.0
|
||||||
|
@ -21,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/bitrev.h>
|
||||||
|
|
||||||
#include "firmware.h"
|
#include "firmware.h"
|
||||||
#include "chip.h"
|
#include "chip.h"
|
||||||
|
@ -33,32 +28,6 @@ enum {
|
||||||
FPGA_BUFSIZE = 512, FPGA_EP = 2
|
FPGA_BUFSIZE = 512, FPGA_EP = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u8 BIT_REVERSE_TABLE[256] = {
|
|
||||||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50,
|
|
||||||
0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8,
|
|
||||||
0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04,
|
|
||||||
0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
|
|
||||||
0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c,
|
|
||||||
0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82,
|
|
||||||
0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32,
|
|
||||||
0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
|
|
||||||
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46,
|
|
||||||
0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6,
|
|
||||||
0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e,
|
|
||||||
0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
|
|
||||||
0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71,
|
|
||||||
0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99,
|
|
||||||
0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25,
|
|
||||||
0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
|
||||||
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d,
|
|
||||||
0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3,
|
|
||||||
0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b,
|
|
||||||
0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb,
|
|
||||||
0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67,
|
|
||||||
0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f,
|
|
||||||
0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f,
|
|
||||||
0xbf, 0x7f, 0xff };
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* wMaxPacketSize of pcm endpoints.
|
* wMaxPacketSize of pcm endpoints.
|
||||||
* keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
|
* keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
|
||||||
|
@ -72,6 +41,10 @@ static const u8 ep_w_max_packet_size[] = {
|
||||||
0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
|
0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const u8 known_fw_versions[][4] = {
|
||||||
|
{ 0x03, 0x01, 0x0b, 0x00 }
|
||||||
|
};
|
||||||
|
|
||||||
struct ihex_record {
|
struct ihex_record {
|
||||||
u16 address;
|
u16 address;
|
||||||
u8 len;
|
u8 len;
|
||||||
|
@ -340,7 +313,7 @@ static int usb6fire_fw_fpga_upload(
|
||||||
|
|
||||||
while (c != end) {
|
while (c != end) {
|
||||||
for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
|
for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
|
||||||
buffer[i] = BIT_REVERSE_TABLE[(u8) *c];
|
buffer[i] = byte_rev_table[(u8) *c];
|
||||||
|
|
||||||
ret = usb6fire_fw_fpga_write(device, buffer, i);
|
ret = usb6fire_fw_fpga_write(device, buffer, i);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -363,6 +336,25 @@ static int usb6fire_fw_fpga_upload(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check, if the firmware version the devices has currently loaded
|
||||||
|
* is known by this driver. 'version' needs to have 4 bytes version
|
||||||
|
* info data. */
|
||||||
|
static int usb6fire_fw_check(u8 *version)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)
|
||||||
|
if (!memcmp(version, known_fw_versions + i, 4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
|
||||||
|
"%02x %02x %02x %02x. "
|
||||||
|
"please reconnect to power. if this failure "
|
||||||
|
"still happens, check your firmware installation.",
|
||||||
|
version[0], version[1], version[2], version[3]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
int usb6fire_fw_init(struct usb_interface *intf)
|
int usb6fire_fw_init(struct usb_interface *intf)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -378,9 +370,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
||||||
"firmware state.\n");
|
"firmware state.\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55
|
if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
|
||||||
|| buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7]
|
|
||||||
!= 0x00) {
|
|
||||||
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
|
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
|
||||||
"received from device: ");
|
"received from device: ");
|
||||||
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
||||||
|
@ -389,7 +379,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
/* do we need fpga loader ezusb firmware? */
|
/* do we need fpga loader ezusb firmware? */
|
||||||
if (buffer[3] == 0x01 && buffer[6] == 0x19) {
|
if (buffer[3] == 0x01) {
|
||||||
ret = usb6fire_fw_ezusb_upload(intf,
|
ret = usb6fire_fw_ezusb_upload(intf,
|
||||||
"6fire/dmx6firel2.ihx", 0, NULL, 0);
|
"6fire/dmx6firel2.ihx", 0, NULL, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -397,7 +387,10 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
||||||
return FW_NOT_READY;
|
return FW_NOT_READY;
|
||||||
}
|
}
|
||||||
/* do we need fpga firmware and application ezusb firmware? */
|
/* do we need fpga firmware and application ezusb firmware? */
|
||||||
else if (buffer[3] == 0x02 && buffer[6] == 0x0b) {
|
else if (buffer[3] == 0x02) {
|
||||||
|
ret = usb6fire_fw_check(buffer + 4);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
|
ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -410,8 +403,8 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
||||||
return FW_NOT_READY;
|
return FW_NOT_READY;
|
||||||
}
|
}
|
||||||
/* all fw loaded? */
|
/* all fw loaded? */
|
||||||
else if (buffer[3] == 0x03 && buffer[6] == 0x0b)
|
else if (buffer[3] == 0x03)
|
||||||
return 0;
|
return usb6fire_fw_check(buffer + 4);
|
||||||
/* unknown data? */
|
/* unknown data? */
|
||||||
else {
|
else {
|
||||||
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
|
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
|
||||||
|
|
|
@ -17,26 +17,23 @@
|
||||||
#include "pcm.h"
|
#include "pcm.h"
|
||||||
#include "chip.h"
|
#include "chip.h"
|
||||||
#include "comm.h"
|
#include "comm.h"
|
||||||
|
#include "control.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
|
OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
/* keep next two synced with
|
/* keep next two synced with
|
||||||
* FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */
|
* FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE
|
||||||
|
* and CONTROL_RATE_XXX in control.h */
|
||||||
static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
|
static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
|
||||||
static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
|
static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
|
||||||
static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
|
static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
|
||||||
static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
|
|
||||||
static const int rates_alsaid[] = {
|
static const int rates_alsaid[] = {
|
||||||
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
|
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
|
||||||
SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
|
SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
|
||||||
SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
|
SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
|
||||||
|
|
||||||
/* values to write to soundcard register for all samplerates */
|
|
||||||
static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
|
|
||||||
static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
|
|
||||||
|
|
||||||
enum { /* settings for pcm */
|
enum { /* settings for pcm */
|
||||||
OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
|
OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
|
||||||
};
|
};
|
||||||
|
@ -48,15 +45,6 @@ enum { /* pcm streaming states */
|
||||||
STREAM_STOPPING
|
STREAM_STOPPING
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { /* pcm sample rates (also index into RATES_XXX[]) */
|
|
||||||
RATE_44KHZ,
|
|
||||||
RATE_48KHZ,
|
|
||||||
RATE_88KHZ,
|
|
||||||
RATE_96KHZ,
|
|
||||||
RATE_176KHZ,
|
|
||||||
RATE_192KHZ
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct snd_pcm_hardware pcm_hw = {
|
static const struct snd_pcm_hardware pcm_hw = {
|
||||||
.info = SNDRV_PCM_INFO_MMAP |
|
.info = SNDRV_PCM_INFO_MMAP |
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
|
@ -64,7 +52,7 @@ static const struct snd_pcm_hardware pcm_hw = {
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
SNDRV_PCM_INFO_BATCH,
|
SNDRV_PCM_INFO_BATCH,
|
||||||
|
|
||||||
.formats = SNDRV_PCM_FMTBIT_S24_LE,
|
.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
|
||||||
|
|
||||||
.rates = SNDRV_PCM_RATE_44100 |
|
.rates = SNDRV_PCM_RATE_44100 |
|
||||||
SNDRV_PCM_RATE_48000 |
|
SNDRV_PCM_RATE_48000 |
|
||||||
|
@ -87,57 +75,34 @@ static const struct snd_pcm_hardware pcm_hw = {
|
||||||
static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
|
static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct usb_device *device = rt->chip->dev;
|
struct control_runtime *ctrl_rt = rt->chip->control;
|
||||||
struct comm_runtime *comm_rt = rt->chip->comm;
|
|
||||||
|
|
||||||
if (rt->rate >= ARRAY_SIZE(rates))
|
ctrl_rt->usb_streaming = false;
|
||||||
return -EINVAL;
|
ret = ctrl_rt->update_streaming(ctrl_rt);
|
||||||
/* disable streaming */
|
|
||||||
ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
snd_printk(KERN_ERR PREFIX "error stopping streaming while "
|
snd_printk(KERN_ERR PREFIX "error stopping streaming while "
|
||||||
"setting samplerate %d.\n", rates[rt->rate]);
|
"setting samplerate %d.\n", rates[rt->rate]);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]);
|
ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
|
||||||
if (ret < 0) {
|
|
||||||
snd_printk(KERN_ERR PREFIX "error setting interface "
|
|
||||||
"altsetting %d for samplerate %d.\n",
|
|
||||||
rates_altsetting[rt->rate], rates[rt->rate]);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set soundcard clock */
|
|
||||||
ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate],
|
|
||||||
rates_6fire_vh[rt->rate]);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
|
snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
|
||||||
rates[rt->rate]);
|
rates[rt->rate]);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enable analog inputs and outputs
|
ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
|
||||||
* (one bit per stereo-channel) */
|
false, false);
|
||||||
ret = comm_rt->write16(comm_rt, 0x02, 0x02,
|
|
||||||
(1 << (OUT_N_CHANNELS / 2)) - 1,
|
|
||||||
(1 << (IN_N_CHANNELS / 2)) - 1);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
snd_printk(KERN_ERR PREFIX "error initializing analog channels "
|
snd_printk(KERN_ERR PREFIX "error initializing channels "
|
||||||
"while setting samplerate %d.\n",
|
"while setting samplerate %d.\n",
|
||||||
rates[rt->rate]);
|
rates[rt->rate]);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
/* disable digital inputs and outputs */
|
|
||||||
ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
|
|
||||||
if (ret < 0) {
|
|
||||||
snd_printk(KERN_ERR PREFIX "error initializing digital "
|
|
||||||
"channels while setting samplerate %d.\n",
|
|
||||||
rates[rt->rate]);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01);
|
ctrl_rt->usb_streaming = true;
|
||||||
|
ret = ctrl_rt->update_streaming(ctrl_rt);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
snd_printk(KERN_ERR PREFIX "error starting streaming while "
|
snd_printk(KERN_ERR PREFIX "error starting streaming while "
|
||||||
"setting samplerate %d.\n", rates[rt->rate]);
|
"setting samplerate %d.\n", rates[rt->rate]);
|
||||||
|
@ -168,12 +133,15 @@ static struct pcm_substream *usb6fire_pcm_get_substream(
|
||||||
static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
|
static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct control_runtime *ctrl_rt = rt->chip->control;
|
||||||
|
|
||||||
if (rt->stream_state != STREAM_DISABLED) {
|
if (rt->stream_state != STREAM_DISABLED) {
|
||||||
for (i = 0; i < PCM_N_URBS; i++) {
|
for (i = 0; i < PCM_N_URBS; i++) {
|
||||||
usb_kill_urb(&rt->in_urbs[i].instance);
|
usb_kill_urb(&rt->in_urbs[i].instance);
|
||||||
usb_kill_urb(&rt->out_urbs[i].instance);
|
usb_kill_urb(&rt->out_urbs[i].instance);
|
||||||
}
|
}
|
||||||
|
ctrl_rt->usb_streaming = false;
|
||||||
|
ctrl_rt->update_streaming(ctrl_rt);
|
||||||
rt->stream_state = STREAM_DISABLED;
|
rt->stream_state = STREAM_DISABLED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,7 +196,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
|
||||||
unsigned int total_length = 0;
|
unsigned int total_length = 0;
|
||||||
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
|
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
|
||||||
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
|
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
|
||||||
u32 *src = (u32 *) urb->buffer;
|
u32 *src = NULL;
|
||||||
u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
|
u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
|
||||||
* (alsa_rt->frame_bits >> 3));
|
* (alsa_rt->frame_bits >> 3));
|
||||||
u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
|
u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
|
||||||
|
@ -244,7 +212,12 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
|
||||||
else
|
else
|
||||||
frame_count = 0;
|
frame_count = 0;
|
||||||
|
|
||||||
src = (u32 *) (urb->buffer + total_length);
|
if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
|
||||||
|
src = (u32 *) (urb->buffer + total_length);
|
||||||
|
else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
|
||||||
|
src = (u32 *) (urb->buffer - 1 + total_length);
|
||||||
|
else
|
||||||
|
return;
|
||||||
src++; /* skip leading 4 bytes of every packet */
|
src++; /* skip leading 4 bytes of every packet */
|
||||||
total_length += urb->packets[i].length;
|
total_length += urb->packets[i].length;
|
||||||
for (frame = 0; frame < frame_count; frame++) {
|
for (frame = 0; frame < frame_count; frame++) {
|
||||||
|
@ -274,9 +247,18 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub,
|
||||||
* (alsa_rt->frame_bits >> 3));
|
* (alsa_rt->frame_bits >> 3));
|
||||||
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
|
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
|
||||||
* (alsa_rt->frame_bits >> 3));
|
* (alsa_rt->frame_bits >> 3));
|
||||||
u32 *dest = (u32 *) urb->buffer;
|
u32 *dest;
|
||||||
int bytes_per_frame = alsa_rt->channels << 2;
|
int bytes_per_frame = alsa_rt->channels << 2;
|
||||||
|
|
||||||
|
if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
|
||||||
|
dest = (u32 *) (urb->buffer - 1);
|
||||||
|
else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
|
||||||
|
dest = (u32 *) (urb->buffer);
|
||||||
|
else {
|
||||||
|
snd_printk(KERN_ERR PREFIX "Unknown sample format.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
||||||
/* at least 4 header bytes for valid packet.
|
/* at least 4 header bytes for valid packet.
|
||||||
* after that: 32 bits per sample for analog channels */
|
* after that: 32 bits per sample for analog channels */
|
||||||
|
@ -456,7 +438,7 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
|
||||||
/* all substreams closed? if so, stop streaming */
|
/* all substreams closed? if so, stop streaming */
|
||||||
if (!rt->playback.instance && !rt->capture.instance) {
|
if (!rt->playback.instance && !rt->capture.instance) {
|
||||||
usb6fire_pcm_stream_stop(rt);
|
usb6fire_pcm_stream_stop(rt);
|
||||||
rt->rate = -1;
|
rt->rate = ARRAY_SIZE(rates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&rt->stream_mutex);
|
mutex_unlock(&rt->stream_mutex);
|
||||||
|
@ -480,7 +462,6 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
|
||||||
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
|
struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
|
||||||
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
|
struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
|
||||||
struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
|
struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
|
||||||
int i;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (rt->panic)
|
if (rt->panic)
|
||||||
|
@ -493,12 +474,10 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
|
||||||
sub->period_off = 0;
|
sub->period_off = 0;
|
||||||
|
|
||||||
if (rt->stream_state == STREAM_DISABLED) {
|
if (rt->stream_state == STREAM_DISABLED) {
|
||||||
for (i = 0; i < ARRAY_SIZE(rates); i++)
|
for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
|
||||||
if (alsa_rt->rate == rates[i]) {
|
if (alsa_rt->rate == rates[rt->rate])
|
||||||
rt->rate = i;
|
|
||||||
break;
|
break;
|
||||||
}
|
if (rt->rate == ARRAY_SIZE(rates)) {
|
||||||
if (i == ARRAY_SIZE(rates)) {
|
|
||||||
mutex_unlock(&rt->stream_mutex);
|
mutex_unlock(&rt->stream_mutex);
|
||||||
snd_printk("invalid rate %d in prepare.\n",
|
snd_printk("invalid rate %d in prepare.\n",
|
||||||
alsa_rt->rate);
|
alsa_rt->rate);
|
||||||
|
@ -613,7 +592,7 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip)
|
||||||
|
|
||||||
rt->chip = chip;
|
rt->chip = chip;
|
||||||
rt->stream_state = STREAM_DISABLED;
|
rt->stream_state = STREAM_DISABLED;
|
||||||
rt->rate = -1;
|
rt->rate = ARRAY_SIZE(rates);
|
||||||
init_waitqueue_head(&rt->stream_wait_queue);
|
init_waitqueue_head(&rt->stream_wait_queue);
|
||||||
mutex_init(&rt->stream_mutex);
|
mutex_init(&rt->stream_mutex);
|
||||||
|
|
||||||
|
|
|
@ -100,19 +100,17 @@ config SND_USB_US122L
|
||||||
|
|
||||||
config SND_USB_6FIRE
|
config SND_USB_6FIRE
|
||||||
tristate "TerraTec DMX 6Fire USB"
|
tristate "TerraTec DMX 6Fire USB"
|
||||||
depends on EXPERIMENTAL
|
|
||||||
select FW_LOADER
|
select FW_LOADER
|
||||||
|
select BITREVERSE
|
||||||
select SND_RAWMIDI
|
select SND_RAWMIDI
|
||||||
select SND_PCM
|
select SND_PCM
|
||||||
help
|
help
|
||||||
Say Y here to include support for TerraTec 6fire DMX USB interface.
|
Say Y here to include support for TerraTec 6fire DMX USB interface.
|
||||||
|
|
||||||
You will need firmware files in order to be able to use the device
|
You will need firmware files in order to be able to use the device
|
||||||
after it has been coldstarted. This driver currently does not support
|
after it has been coldstarted. An install script for the firmware
|
||||||
firmware loading for all devices. If you own such a device,
|
and further help can be found at
|
||||||
you could start windows and let the windows driver upload
|
http://sixfireusb.sourceforge.net
|
||||||
the firmware. As long as you do not unplug your device from power,
|
|
||||||
it should be usable.
|
|
||||||
|
|
||||||
endif # SND_USB
|
endif # SND_USB
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,15 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
||||||
int err;
|
int err;
|
||||||
unsigned char data;
|
unsigned char data;
|
||||||
struct usb_device *dev = chip->dev;
|
struct usb_device *dev = chip->dev;
|
||||||
|
struct uac_clock_source_descriptor *cs_desc =
|
||||||
|
snd_usb_find_clock_source(chip->ctrl_intf, source_id);
|
||||||
|
|
||||||
|
if (!cs_desc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* If a clock source can't tell us whether it's valid, we assume it is */
|
||||||
|
if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID))
|
||||||
|
return 1;
|
||||||
|
|
||||||
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||||
|
@ -114,7 +123,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
|
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
|
||||||
__func__, source_id);
|
__func__, source_id);
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!data;
|
return !!data;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#ifdef HW_CONST_DEBUG
|
#ifdef HW_CONST_DEBUG
|
||||||
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
|
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
|
||||||
#else
|
#else
|
||||||
#define hwc_debug(fmt, args...) /**/
|
#define hwc_debug(fmt, args...) do { } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __USBAUDIO_DEBUG_H */
|
#endif /* __USBAUDIO_DEBUG_H */
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse the audio format type I descriptor
|
* parse the audio format type I descriptor
|
||||||
|
|
|
@ -1097,11 +1097,13 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
|
||||||
append_ctl_name(kctl, control == UAC_FU_MUTE ?
|
append_ctl_name(kctl, control == UAC_FU_MUTE ?
|
||||||
" Switch" : " Volume");
|
" Switch" : " Volume");
|
||||||
if (control == UAC_FU_VOLUME) {
|
if (control == UAC_FU_VOLUME) {
|
||||||
kctl->tlv.c = mixer_vol_tlv;
|
|
||||||
kctl->vd[0].access |=
|
|
||||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
|
||||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
|
|
||||||
check_mapped_dB(map, cval);
|
check_mapped_dB(map, cval);
|
||||||
|
if (cval->dBmin < cval->dBmax) {
|
||||||
|
kctl->tlv.c = mixer_vol_tlv;
|
||||||
|
kctl->vd[0].access |=
|
||||||
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||||
|
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ static const struct rc_config {
|
||||||
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
|
{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6, 18, 0x0013 }, /* Audigy 2 NX */
|
||||||
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
|
{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */
|
||||||
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
|
{ USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */
|
||||||
|
{ USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
|
||||||
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
|
{ USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -188,6 +189,12 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
||||||
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
||||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||||
!value, 0, NULL, 0, 100);
|
!value, 0, NULL, 0, 100);
|
||||||
|
/* USB X-Fi S51 Pro */
|
||||||
|
if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
|
||||||
|
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||||
|
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
||||||
|
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||||
|
!value, 0, NULL, 0, 100);
|
||||||
else
|
else
|
||||||
err = snd_usb_ctl_msg(mixer->chip->dev,
|
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||||
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
||||||
|
@ -234,9 +241,13 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
|
||||||
/* USB X-Fi S51 doesn't have a CMSS LED */
|
/* USB X-Fi S51 doesn't have a CMSS LED */
|
||||||
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
|
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
|
||||||
continue;
|
continue;
|
||||||
|
/* USB X-Fi S51 Pro doesn't have one either */
|
||||||
|
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0)
|
||||||
|
continue;
|
||||||
if (i > 1 && /* Live24ext has 2 LEDs only */
|
if (i > 1 && /* Live24ext has 2 LEDs only */
|
||||||
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
|
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
|
||||||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
|
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
|
||||||
|
mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
|
||||||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
|
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
|
||||||
break;
|
break;
|
||||||
err = snd_ctl_add(mixer->chip->card,
|
err = snd_ctl_add(mixer->chip->card,
|
||||||
|
@ -512,6 +523,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
|
||||||
case USB_ID(0x041e, 0x3020):
|
case USB_ID(0x041e, 0x3020):
|
||||||
case USB_ID(0x041e, 0x3040):
|
case USB_ID(0x041e, 0x3040):
|
||||||
case USB_ID(0x041e, 0x3042):
|
case USB_ID(0x041e, 0x3042):
|
||||||
|
case USB_ID(0x041e, 0x30df):
|
||||||
case USB_ID(0x041e, 0x3048):
|
case USB_ID(0x041e, 0x3048):
|
||||||
err = snd_audigy2nx_controls_create(mixer);
|
err = snd_audigy2nx_controls_create(mixer);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
@ -1651,6 +1651,32 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
USB_DEVICE(0x0582, 0x0127),
|
||||||
|
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||||
|
/* .vendor_name = "Roland", */
|
||||||
|
/* .product_name = "GR-55", */
|
||||||
|
.ifnum = QUIRK_ANY_INTERFACE,
|
||||||
|
.type = QUIRK_COMPOSITE,
|
||||||
|
.data = (const struct snd_usb_audio_quirk[]) {
|
||||||
|
{
|
||||||
|
.ifnum = 0,
|
||||||
|
.type = QUIRK_AUDIO_STANDARD_INTERFACE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ifnum = 1,
|
||||||
|
.type = QUIRK_AUDIO_STANDARD_INTERFACE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ifnum = 2,
|
||||||
|
.type = QUIRK_MIDI_STANDARD_INTERFACE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.ifnum = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/* Guillemot devices */
|
/* Guillemot devices */
|
||||||
{
|
{
|
||||||
|
@ -1953,7 +1979,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
USB_DEVICE(0x0763, 0x2080),
|
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
|
||||||
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||||
/* .vendor_name = "M-Audio", */
|
/* .vendor_name = "M-Audio", */
|
||||||
/* .product_name = "Fast Track Ultra", */
|
/* .product_name = "Fast Track Ultra", */
|
||||||
|
@ -2020,7 +2046,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
USB_DEVICE(0x0763, 0x2081),
|
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2081),
|
||||||
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||||
/* .vendor_name = "M-Audio", */
|
/* .vendor_name = "M-Audio", */
|
||||||
/* .product_name = "Fast Track Ultra 8R", */
|
/* .product_name = "Fast Track Ultra 8R", */
|
||||||
|
@ -2179,6 +2205,17 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* KORG devices */
|
||||||
|
{
|
||||||
|
USB_DEVICE_VENDOR_SPEC(0x0944, 0x0200),
|
||||||
|
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||||
|
.vendor_name = "KORG, Inc.",
|
||||||
|
/* .product_name = "PANDORA PX5D", */
|
||||||
|
.ifnum = 3,
|
||||||
|
.type = QUIRK_MIDI_STANDARD_INTERFACE,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/* AKAI devices */
|
/* AKAI devices */
|
||||||
{
|
{
|
||||||
USB_DEVICE(0x09e8, 0x0062),
|
USB_DEVICE(0x09e8, 0x0062),
|
||||||
|
@ -2331,6 +2368,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Native Instruments MK2 series */
|
/* Native Instruments MK2 series */
|
||||||
|
{
|
||||||
|
/* Komplete Audio 6 */
|
||||||
|
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||||
|
.idVendor = 0x17cc,
|
||||||
|
.idProduct = 0x1000,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/* Traktor Audio 6 */
|
/* Traktor Audio 6 */
|
||||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||||
|
|
|
@ -540,6 +540,7 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev,
|
||||||
/* Access Music VirusTI Desktop */
|
/* Access Music VirusTI Desktop */
|
||||||
return snd_usb_accessmusic_boot_quirk(dev);
|
return snd_usb_accessmusic_boot_quirk(dev);
|
||||||
|
|
||||||
|
case USB_ID(0x17cc, 0x1000): /* Komplete Audio 6 */
|
||||||
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
|
case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */
|
||||||
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
|
case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */
|
||||||
return snd_usb_nativeinstruments_boot_quirk(dev);
|
return snd_usb_nativeinstruments_boot_quirk(dev);
|
||||||
|
|
Loading…
Reference in New Issue