Merge branch 'topic/misc' into for-linus
This commit is contained in:
commit
02e5fbf622
48 changed files with 1737 additions and 1312 deletions
|
@ -4264,6 +4264,13 @@ M: Tim Hockin <thockin@hockin.org>
|
|||
S: Maintained
|
||||
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
|
||||
M: Petr Vandrovec <petr@vandrovec.name>
|
||||
S: Odd Fixes
|
||||
|
|
|
@ -166,21 +166,6 @@ config RADIO_MAXIRADIO
|
|||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
tristate "miroSOUND PCM20 radio"
|
||||
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_I2C_SI4713) += si4713-i2c.o
|
||||
obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
|
||||
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
|
||||
obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
|
||||
obj-$(CONFIG_USB_DSBR) += dsbr100.o
|
||||
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);
|
||||
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_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_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,
|
||||
|
|
|
@ -26,29 +26,37 @@
|
|||
#include <media/v4l2-dev.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_ops {
|
||||
void (*write)(struct snd_tea575x *tea, unsigned int val);
|
||||
unsigned int (*read)(struct snd_tea575x *tea);
|
||||
void (*mute)(struct snd_tea575x *tea, unsigned int mute);
|
||||
void (*set_pins)(struct snd_tea575x *tea, u8 pins);
|
||||
u8 (*get_pins)(struct snd_tea575x *tea);
|
||||
void (*set_direction)(struct snd_tea575x *tea, bool output);
|
||||
};
|
||||
|
||||
struct snd_tea575x {
|
||||
struct snd_card *card;
|
||||
struct video_device *vd; /* video device */
|
||||
int dev_nr; /* requested device number + 1 */
|
||||
int tea5759; /* 5759 chip is present */
|
||||
int mute; /* Device is muted? */
|
||||
unsigned int freq_fixup; /* crystal onboard */
|
||||
bool tea5759; /* 5759 chip is present */
|
||||
bool mute; /* Device is muted? */
|
||||
bool stereo; /* receiving stereo */
|
||||
bool tuned; /* tuned to a station */
|
||||
unsigned int val; /* hw value */
|
||||
unsigned long freq; /* frequency */
|
||||
unsigned long in_use; /* set if the device is in use */
|
||||
struct snd_tea575x_ops *ops;
|
||||
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);
|
||||
|
||||
#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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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;
|
||||
|
||||
if (*id == '\0')
|
||||
strcpy(id, "default");
|
||||
strcpy(id, "Default");
|
||||
|
||||
while (1) {
|
||||
if (loops-- == 0) {
|
||||
|
|
|
@ -189,6 +189,7 @@ static void xrun(struct snd_pcm_substream *substream)
|
|||
#define XRUN_LOG_CNT 10
|
||||
|
||||
struct hwptr_log_entry {
|
||||
unsigned int in_interrupt;
|
||||
unsigned long jiffies;
|
||||
snd_pcm_uframes_t pos;
|
||||
snd_pcm_uframes_t period_size;
|
||||
|
@ -204,7 +205,7 @@ struct snd_pcm_hwptr_log {
|
|||
};
|
||||
|
||||
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_hwptr_log *log = runtime->hwptr_log;
|
||||
|
@ -220,6 +221,7 @@ static void xrun_log(struct snd_pcm_substream *substream,
|
|||
return;
|
||||
}
|
||||
entry = &log->entries[log->idx];
|
||||
entry->in_interrupt = in_interrupt;
|
||||
entry->jiffies = jiffies;
|
||||
entry->pos = pos;
|
||||
entry->period_size = runtime->period_size;
|
||||
|
@ -246,9 +248,11 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
|
|||
entry = &log->entries[idx];
|
||||
if (entry->period_size == 0)
|
||||
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",
|
||||
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->buffer_size,
|
||||
(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 */
|
||||
|
||||
#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)
|
||||
|
||||
#endif
|
||||
|
@ -326,7 +330,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|||
}
|
||||
pos -= pos % runtime->min_align;
|
||||
if (xrun_debug(substream, XRUN_DEBUG_LOG))
|
||||
xrun_log(substream, pos);
|
||||
xrun_log(substream, pos, in_interrupt);
|
||||
hw_base = runtime->hw_ptr_base;
|
||||
new_hw_ptr = hw_base + pos;
|
||||
if (in_interrupt) {
|
||||
|
|
|
@ -22,4 +22,15 @@ config SND_FIREWIRE_SPEAKERS
|
|||
To compile this driver as a module, choose M here: the module
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
|
||||
fcp.o cmp.o amdtp.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_SPEAKERS) += snd-firewire-speakers.o
|
||||
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
|
||||
|
|
755
sound/firewire/isight.c
Normal file
755
sound/firewire/isight.c
Normal file
|
@ -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;
|
||||
}
|
||||
EXPORT_SYMBOL(fw_iso_resources_init);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
fw_unit_put(r->unit);
|
||||
}
|
||||
EXPORT_SYMBOL(fw_iso_resources_destroy);
|
||||
|
||||
static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
|
||||
{
|
||||
|
@ -152,6 +154,7 @@ retry_after_bus_reset:
|
|||
|
||||
return channel;
|
||||
}
|
||||
EXPORT_SYMBOL(fw_iso_resources_allocate);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
EXPORT_SYMBOL(fw_iso_resources_update);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
EXPORT_SYMBOL(fw_iso_resources_free);
|
||||
|
|
|
@ -60,6 +60,7 @@ err_packets:
|
|||
error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(iso_packets_buffer_init);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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_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_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);
|
||||
|
||||
#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
|
||||
#define FREQ_LO (87 * 16000)
|
||||
#define FREQ_HI (108 * 16000)
|
||||
#define FREQ_LO (50UL * 16000)
|
||||
#define FREQ_HI (150UL * 16000)
|
||||
|
||||
/*
|
||||
* definitions
|
||||
|
@ -77,27 +77,95 @@ static struct v4l2_queryctrl radio_qctrl[] = {
|
|||
* 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)
|
||||
{
|
||||
unsigned long freq;
|
||||
|
||||
freq = tea->freq / 16; /* to kHz */
|
||||
if (freq > 108000)
|
||||
freq = 108000;
|
||||
if (freq < 87000)
|
||||
freq = 87000;
|
||||
freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
|
||||
freq /= 16; /* to kHz */
|
||||
/* crystal fixup */
|
||||
if (tea->tea5759)
|
||||
freq -= tea->freq_fixup;
|
||||
freq -= TEA575X_FMIF;
|
||||
else
|
||||
freq += tea->freq_fixup;
|
||||
freq += TEA575X_FMIF;
|
||||
/* freq /= 12.5 */
|
||||
freq *= 10;
|
||||
freq /= 125;
|
||||
|
||||
tea->val &= ~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);
|
||||
|
||||
strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
|
||||
strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
|
||||
strlcpy(v->card, "Maestro Radio", sizeof(v->card));
|
||||
sprintf(v->bus_info, "PCI");
|
||||
strlcpy(v->card, tea->card, sizeof(v->card));
|
||||
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->capabilities = V4L2_CAP_TUNER;
|
||||
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 snd_tea575x *tea = video_drvdata(file);
|
||||
|
||||
if (v->index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
snd_tea575x_read(tea);
|
||||
|
||||
strcpy(v->name, "FM");
|
||||
v->type = V4L2_TUNER_RADIO;
|
||||
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
|
||||
v->rangelow = FREQ_LO;
|
||||
v->rangehigh = FREQ_HI;
|
||||
v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
|
||||
v->capability = V4L2_TUNER_CAP_LOW;
|
||||
v->audmode = V4L2_TUNER_MODE_MONO;
|
||||
v->signal = 0xffff;
|
||||
v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
|
||||
v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
|
||||
v->signal = tea->tuned ? 0xffff : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -148,7 +221,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
|
|||
{
|
||||
struct snd_tea575x *tea = video_drvdata(file);
|
||||
|
||||
if (f->tuner != 0)
|
||||
return -EINVAL;
|
||||
f->type = V4L2_TUNER_RADIO;
|
||||
snd_tea575x_get_freq(tea);
|
||||
f->frequency = tea->freq;
|
||||
return 0;
|
||||
}
|
||||
|
@ -158,6 +234,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
|
|||
{
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -209,10 +288,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
|
|||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
if (tea->ops->mute) {
|
||||
ctrl->value = tea->mute;
|
||||
return 0;
|
||||
}
|
||||
ctrl->value = tea->mute;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -224,11 +301,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
|
|||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
if (tea->ops->mute) {
|
||||
tea->ops->mute(tea, ctrl->value);
|
||||
if (tea->mute != ctrl->value) {
|
||||
tea->mute = ctrl->value;
|
||||
return 0;
|
||||
snd_tea575x_set_freq(tea);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -293,18 +370,16 @@ static struct video_device tea575x_radio = {
|
|||
/*
|
||||
* initialize all the tea575x chips
|
||||
*/
|
||||
void snd_tea575x_init(struct snd_tea575x *tea)
|
||||
int snd_tea575x_init(struct snd_tea575x *tea)
|
||||
{
|
||||
int retval;
|
||||
unsigned int val;
|
||||
struct video_device *tea575x_radio_inst;
|
||||
|
||||
val = tea->ops->read(tea);
|
||||
if (val == 0x1ffffff || val == 0) {
|
||||
snd_printk(KERN_ERR
|
||||
"tea575x-tuner: Cannot find TEA575x chip\n");
|
||||
return;
|
||||
}
|
||||
tea->mute = 1;
|
||||
|
||||
snd_tea575x_write(tea, 0x55AA);
|
||||
if (snd_tea575x_read(tea) != 0x55AA)
|
||||
return -ENODEV;
|
||||
|
||||
tea->in_use = 0;
|
||||
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();
|
||||
if (tea575x_radio_inst == NULL) {
|
||||
printk(KERN_ERR "tea575x-tuner: not enough memory\n");
|
||||
return;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
|
||||
|
@ -328,17 +403,13 @@ void snd_tea575x_init(struct snd_tea575x *tea)
|
|||
if (retval) {
|
||||
printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
|
||||
kfree(tea575x_radio_inst);
|
||||
return;
|
||||
return retval;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
tristate "ForteMedia FM801"
|
||||
select SND_OPL3_LIB
|
||||
|
@ -552,13 +560,13 @@ config SND_FM801_TEA575X_BOOL
|
|||
depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
|
||||
help
|
||||
Say Y here to include support for soundcards based on the ForteMedia
|
||||
FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
|
||||
Forte SF256-PCS-02) into the snd-fm801 driver.
|
||||
FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
|
||||
SF64-PCR) into the snd-fm801 driver.
|
||||
|
||||
config SND_FM801_TEA575X
|
||||
config SND_TEA575X
|
||||
tristate
|
||||
depends on SND_FM801_TEA575X_BOOL
|
||||
default SND_FM801
|
||||
depends on SND_FM801_TEA575X_BOOL || SND_ES1968_RADIO
|
||||
default SND_FM801 || SND_ES1968
|
||||
|
||||
source "sound/pci/hda/Kconfig"
|
||||
|
||||
|
|
|
@ -42,10 +42,29 @@
|
|||
#include <sound/tlv.h>
|
||||
#include <sound/hwdep.h>
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
|
||||
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
|
||||
/**
|
||||
* snd_printddd - very verbose debug printk
|
||||
|
@ -58,7 +77,7 @@ MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
|
|||
#define snd_printddd(format, args...) \
|
||||
__snd_printk(3, __FILE__, __LINE__, format, ##args)
|
||||
#else
|
||||
#define snd_printddd(format, args...) do { } while (0)
|
||||
#define snd_printddd(format, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
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 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)
|
||||
|
||||
struct clk_source {
|
||||
|
@ -136,7 +148,7 @@ struct snd_card_asihpi {
|
|||
u32 h_mixer;
|
||||
struct clk_cache cc;
|
||||
|
||||
u16 support_mmap;
|
||||
u16 can_dma;
|
||||
u16 support_grouping;
|
||||
u16 support_mrx;
|
||||
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_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;
|
||||
u32 h_stream;
|
||||
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__)
|
||||
|
||||
/***************************** 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");
|
||||
snd_printd("samplerate %d \n", params_rate(p));
|
||||
snd_printd("Channels %d \n", params_channels(p));
|
||||
snd_printd("Format %d \n", params_format(p));
|
||||
snd_printd("subformat %d \n", params_subformat(p));
|
||||
snd_printd("Buffer bytes %d \n", params_buffer_bytes(p));
|
||||
snd_printd("Period bytes %d \n", params_period_bytes(p));
|
||||
snd_printd("access %d \n", params_access(p));
|
||||
snd_printd("period_size %d \n", params_period_size(p));
|
||||
snd_printd("periods %d \n", params_periods(p));
|
||||
snd_printd("buffer_size %d \n", params_buffer_size(p));
|
||||
DEBUG_NAME(substream, name);
|
||||
snd_printd("%s HWPARAMS\n", name);
|
||||
snd_printd(" samplerate %d Hz\n", params_rate(p));
|
||||
snd_printd(" channels %d\n", params_channels(p));
|
||||
snd_printd(" format %d\n", params_format(p));
|
||||
snd_printd(" subformat %d\n", params_subformat(p));
|
||||
snd_printd(" buffer %d B\n", params_buffer_bytes(p));
|
||||
snd_printd(" period %d B\n", params_period_bytes(p));
|
||||
snd_printd(" access %d\n", params_access(p));
|
||||
snd_printd(" period_size %d\n", params_period_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[] = {
|
||||
|
@ -451,7 +471,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
int width;
|
||||
unsigned int bytes_per_sec;
|
||||
|
||||
print_hwparams(params);
|
||||
print_hwparams(substream, params);
|
||||
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -459,10 +479,6 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
if (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,
|
||||
params_channels(params),
|
||||
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;
|
||||
if (card->support_mmap) {
|
||||
|
||||
if (card->can_dma) {
|
||||
err = hpi_stream_host_buffer_attach(dpcm->h_stream,
|
||||
params_buffer_bytes(params), runtime->dma_addr);
|
||||
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->buffer_bytes = params_buffer_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;
|
||||
}
|
||||
|
@ -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_pcm_substream *s;
|
||||
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) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
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)
|
||||
continue;
|
||||
|
||||
if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
|
||||
(card->support_mmap)) {
|
||||
ds->drained_count = 0;
|
||||
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
/* How do I know how much valid data is present
|
||||
* in buffer? Must be at least one period!
|
||||
* Guessing 2 periods, but if
|
||||
|
@ -599,9 +613,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
if (card->support_grouping) {
|
||||
snd_printdd("\t%c%d group\n",
|
||||
SCHR(s->stream),
|
||||
s->number);
|
||||
snd_printdd("%d group\n", s->number);
|
||||
e = hpi_stream_group_add(
|
||||
dpcm->h_stream,
|
||||
ds->h_stream);
|
||||
|
@ -618,7 +630,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
|||
/* start the master stream */
|
||||
snd_card_asihpi_pcm_timer_start(substream);
|
||||
if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
|
||||
!card->support_mmap)
|
||||
!card->can_dma)
|
||||
hpi_handle_error(hpi_stream_start(dpcm->h_stream));
|
||||
break;
|
||||
|
||||
|
@ -636,9 +648,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
|
|||
s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
|
||||
|
||||
if (card->support_grouping) {
|
||||
snd_printdd("\t%c%d group\n",
|
||||
SCHR(s->stream),
|
||||
s->number);
|
||||
snd_printdd("%d group\n", s->number);
|
||||
snd_pcm_trigger_done(s, substream);
|
||||
} else
|
||||
break;
|
||||
|
@ -732,9 +742,9 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
|||
int loops = 0;
|
||||
u16 state;
|
||||
u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
|
||||
DEBUG_NAME(substream, name);
|
||||
|
||||
snd_printdd("%c%d snd_card_asihpi_timer_function\n",
|
||||
SCHR(substream->stream), substream->number);
|
||||
snd_printdd("%s snd_card_asihpi_timer_function\n", name);
|
||||
|
||||
/* find minimum newdata and buffer pos in group */
|
||||
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 */
|
||||
runtime->delay = on_card_bytes;
|
||||
|
||||
if (!card->can_dma)
|
||||
on_card_bytes = bytes_avail;
|
||||
|
||||
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
|
||||
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)) {
|
||||
hpi_handle_error(hpi_stream_start(ds->h_stream));
|
||||
snd_printdd("P%d start\n", s->number);
|
||||
ds->drained_count = 0;
|
||||
}
|
||||
} else if (state == HPI_STATE_DRAINED) {
|
||||
snd_printd(KERN_WARNING "P%d drained\n",
|
||||
s->number);
|
||||
/*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
|
||||
continue; */
|
||||
ds->drained_count++;
|
||||
if (ds->drained_count > 2) {
|
||||
snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
ds->drained_count = 0;
|
||||
}
|
||||
} else
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
runtime->status->hw_ptr),
|
||||
(unsigned long)frames_to_bytes(runtime,
|
||||
runtime->control->appl_ptr));
|
||||
|
||||
snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X,"
|
||||
" aux=x%04X space=x%04X\n",
|
||||
loops, SCHR(s->stream), s->number,
|
||||
state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail,
|
||||
snd_printdd("%d S=%d, "
|
||||
"rw=0x%04X, dma=0x%04X, left=0x%04X, "
|
||||
"aux=0x%04X space=0x%04X\n",
|
||||
s->number, state,
|
||||
ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs,
|
||||
(int)bytes_avail,
|
||||
(int)on_card_bytes, buffer_size-bytes_avail);
|
||||
loops++;
|
||||
}
|
||||
|
@ -814,7 +835,7 @@ static void snd_card_asihpi_timer_function(unsigned long data)
|
|||
|
||||
next_jiffies = max(next_jiffies, 1U);
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
if (xfercount && (on_card_bytes <= ds->period_bytes)) {
|
||||
if (card->support_mmap) {
|
||||
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
snd_printddd("P%d write x%04x\n",
|
||||
if (xfercount &&
|
||||
/* Limit use of on card fifo for playback */
|
||||
((on_card_bytes <= ds->period_bytes) ||
|
||||
(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,
|
||||
ds->period_bytes);
|
||||
xfercount - xfer1, buf_ofs);
|
||||
hpi_handle_error(
|
||||
hpi_outstream_write_buf(
|
||||
ds->h_stream,
|
||||
&s->runtime->
|
||||
dma_area[0],
|
||||
xfercount,
|
||||
ds->h_stream, pd,
|
||||
xfercount - xfer1,
|
||||
&ds->format));
|
||||
} else {
|
||||
snd_printddd("C%d read x%04x\n",
|
||||
s->number,
|
||||
xfercount);
|
||||
}
|
||||
} else {
|
||||
snd_printddd("C%d read1 0x%04x\n",
|
||||
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_instream_read_buf(
|
||||
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;
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -873,7 +927,7 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
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));
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -986,11 +1040,9 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
|
|||
SNDRV_PCM_INFO_DOUBLE |
|
||||
SNDRV_PCM_INFO_BATCH |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_PAUSE;
|
||||
|
||||
if (card->support_mmap)
|
||||
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
|
||||
if (card->support_grouping)
|
||||
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 */
|
||||
runtime->hw = snd_card_asihpi_playback;
|
||||
|
||||
if (card->support_mmap)
|
||||
if (card->can_dma)
|
||||
err = snd_pcm_hw_constraint_pow2(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
|
||||
if (err < 0)
|
||||
|
@ -1028,58 +1080,6 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
|
|||
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 = {
|
||||
.open = snd_card_asihpi_playback_open,
|
||||
.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);
|
||||
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
|
||||
snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
|
||||
|
||||
if (card->support_mmap)
|
||||
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID;
|
||||
|
||||
if (card->support_grouping)
|
||||
snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
|
||||
|
||||
runtime->hw = snd_card_asihpi_capture;
|
||||
|
||||
if (card->support_mmap)
|
||||
if (card->can_dma)
|
||||
err = snd_pcm_hw_constraint_pow2(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
|
||||
if (err < 0)
|
||||
|
@ -1246,28 +1244,6 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
|
|||
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 = {
|
||||
.open = snd_card_asihpi_capture_open,
|
||||
.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,
|
||||
};
|
||||
|
||||
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,
|
||||
int device, int substreams)
|
||||
{
|
||||
|
@ -1303,17 +1267,10 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
|
|||
if (err < 0)
|
||||
return err;
|
||||
/* pointer to ops struct is stored, dont change ops afterwards! */
|
||||
if (asihpi->support_mmap) {
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&snd_card_asihpi_playback_mmap_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&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->info_flags = 0;
|
||||
|
@ -1413,14 +1370,16 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
|
|||
struct hpi_control *hpi_ctl,
|
||||
char *name)
|
||||
{
|
||||
char *dir = "";
|
||||
char *dir;
|
||||
memset(snd_control, 0, sizeof(*snd_control));
|
||||
snd_control->name = hpi_ctl->name;
|
||||
snd_control->private_value = hpi_ctl->h_control;
|
||||
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
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*/
|
||||
else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
|
||||
(!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 */
|
||||
|
||||
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],
|
||||
hpi_ctl->src_node_index,
|
||||
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)
|
||||
asihpi->update_interval_frames = 512;
|
||||
|
||||
if (!asihpi->support_mmap)
|
||||
if (!asihpi->can_dma)
|
||||
asihpi->update_interval_frames *= 2;
|
||||
|
||||
hpi_handle_error(hpi_instream_open(asihpi->adapter_index,
|
||||
0, &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));
|
||||
|
||||
|
@ -2894,8 +2853,8 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
|
|||
asihpi->out_max_chans = 2;
|
||||
}
|
||||
|
||||
snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
|
||||
asihpi->support_mmap,
|
||||
snd_printk(KERN_INFO "has dma:%d, grouping:%d, mrx:%d\n",
|
||||
asihpi->can_dma,
|
||||
asihpi->support_grouping,
|
||||
asihpi->support_mrx
|
||||
);
|
||||
|
@ -2925,10 +2884,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
|
|||
by enable_hwdep module param*/
|
||||
snd_asihpi_hpi_new(asihpi, 0, NULL);
|
||||
|
||||
if (asihpi->support_mmap)
|
||||
strcpy(card->driver, "ASIHPI-MMAP");
|
||||
else
|
||||
strcpy(card->driver, "ASIHPI");
|
||||
strcpy(card->driver, "ASIHPI");
|
||||
|
||||
sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
|
||||
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,
|
||||
struct hpi_response *phr);
|
||||
|
||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
||||
struct hpi_response *phr);
|
||||
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||
struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
|
||||
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:
|
||||
subsys_create_adapter(phm, phr);
|
||||
break;
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
subsys_delete_adapter(phm, phr);
|
||||
break;
|
||||
default:
|
||||
phr->error = HPI_ERROR_INVALID_FUNC;
|
||||
break;
|
||||
|
@ -279,6 +276,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
|
|||
adapter_get_asserts(pao, phm, phr);
|
||||
break;
|
||||
|
||||
case HPI_ADAPTER_DELETE:
|
||||
adapter_delete(pao, phm, phr);
|
||||
break;
|
||||
|
||||
default:
|
||||
hw_message(pao, phm, phr);
|
||||
break;
|
||||
|
@ -333,26 +334,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
|
|||
{
|
||||
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) {
|
||||
pao = hpi_find_adapter(phm->adapter_index);
|
||||
if (!pao) {
|
||||
HPI_DEBUG_LOG(DEBUG,
|
||||
" %d,%d refused, for another HPI?\n",
|
||||
phm->object, phm->function);
|
||||
hpi_init_response(phr, phm->object, phm->function,
|
||||
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||
HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
|
||||
phm->adapter_index);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't even try to communicate with crashed DSP */
|
||||
if (pao->dsp_crashed >= 10) {
|
||||
hpi_init_response(phr, phm->object, phm->function,
|
||||
HPI_ERROR_DSP_HARDWARE);
|
||||
HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
|
||||
phm->object, phm->function);
|
||||
HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
|
||||
phm->adapter_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -463,15 +460,9 @@ static void subsys_create_adapter(struct hpi_message *phm,
|
|||
phr->error = 0;
|
||||
}
|
||||
|
||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
||||
struct hpi_response *phr)
|
||||
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||
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);
|
||||
hpi_delete_adapter(pao);
|
||||
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,
|
||||
struct hpi_response *phr);
|
||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
||||
struct hpi_response *phr);
|
||||
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||
struct hpi_message *phm, struct hpi_response *phr);
|
||||
|
||||
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
||||
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) {
|
||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||
subsys_create_adapter(phm, phr);
|
||||
break;
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
subsys_delete_adapter(phm, phr);
|
||||
break;
|
||||
default:
|
||||
phr->error = HPI_ERROR_INVALID_FUNC;
|
||||
break;
|
||||
|
@ -279,6 +277,10 @@ static void adapter_message(struct hpi_adapter_obj *pao,
|
|||
struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
switch (phm->function) {
|
||||
case HPI_ADAPTER_DELETE:
|
||||
adapter_delete(pao, phm, phr);
|
||||
break;
|
||||
|
||||
default:
|
||||
hw_message(pao, phm, phr);
|
||||
break;
|
||||
|
@ -371,36 +373,17 @@ static void instream_message(struct hpi_adapter_obj *pao,
|
|||
/** Entry point to this HPI backend
|
||||
* 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;
|
||||
|
||||
/* subsytem messages are processed by every HPI.
|
||||
* All other messages are ignored unless the adapter index matches
|
||||
* an adapter in the HPI
|
||||
*/
|
||||
/* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject,
|
||||
phm->wFunction); */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
if (pao && (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 */
|
||||
|
@ -412,7 +395,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
|
|||
case HPI_TYPE_MESSAGE:
|
||||
switch (phm->object) {
|
||||
case HPI_OBJ_SUBSYSTEM:
|
||||
subsys_message(phm, phr);
|
||||
subsys_message(pao, phm, phr);
|
||||
break;
|
||||
|
||||
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 */
|
||||
|
||||
|
@ -491,13 +494,11 @@ static void subsys_create_adapter(struct hpi_message *phm,
|
|||
}
|
||||
|
||||
/** delete an adapter - required by WDM driver */
|
||||
static void subsys_delete_adapter(struct hpi_message *phm,
|
||||
struct hpi_response *phr)
|
||||
static void adapter_delete(struct hpi_adapter_obj *pao,
|
||||
struct hpi_message *phm, struct hpi_response *phr)
|
||||
{
|
||||
struct hpi_adapter_obj *pao;
|
||||
struct hpi_hw_obj *phw;
|
||||
|
||||
pao = hpi_find_adapter(phm->obj_index);
|
||||
if (!pao) {
|
||||
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
|
||||
return;
|
||||
|
@ -563,11 +564,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
|
|||
}
|
||||
|
||||
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 */
|
||||
/* calls DeleteAdapter on error. */
|
||||
return err;
|
||||
|
||||
}
|
||||
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
|
||||
|
||||
/* 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,
|
||||
interface->control_cache.size_in_bytes,
|
||||
p_control_cache_virtual);
|
||||
|
||||
if (!phw->p_cache)
|
||||
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
|
||||
* this routine is called from SubSysDeleteAdapter,
|
||||
* this routine is called from AdapterDelete,
|
||||
* and SubSysCreateAdapter if duplicate index
|
||||
*/
|
||||
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
|
||||
{
|
||||
struct hpi_hw_obj *phw;
|
||||
struct hpi_hw_obj *phw = pao->priv;
|
||||
int i;
|
||||
|
||||
phw = pao->priv;
|
||||
|
||||
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
|
||||
hpios_locked_mem_free(&phw->h_control_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):
|
||||
boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
|
||||
break;
|
||||
case HPI_ADAPTER_FAMILY_ASI(0x5500):
|
||||
case HPI_ADAPTER_FAMILY_ASI(0x5600):
|
||||
case HPI_ADAPTER_FAMILY_ASI(0x6500):
|
||||
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)
|
||||
{
|
||||
struct bus_master_interface *interface = phw->p_interface_buffer;
|
||||
|
||||
u32 r;
|
||||
|
||||
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
|
||||
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
|
||||
/** Simple packet - no special routing required */
|
||||
#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 */
|
||||
#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. */
|
||||
#define HPI_ETHERNET_TIMEOUT_MS 500
|
||||
|
@ -397,14 +397,14 @@ enum HPI_FUNCTION_IDS {
|
|||
HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
|
||||
HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
|
||||
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_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_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
|
||||
HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10),
|
||||
HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11),
|
||||
/* HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), */
|
||||
/* HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), */
|
||||
HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
|
||||
HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
|
||||
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_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
|
||||
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_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 *pw_adapter_index);
|
||||
|
||||
u16 hpi_subsys_delete_adapter(u16 adapter_index);
|
||||
|
||||
u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
|
||||
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 */
|
||||
hpi_handler_func HPI_1000;
|
||||
hpi_handler_func HPI_6000;
|
||||
hpi_handler_func HPI_6205;
|
||||
hpi_handler_func HPI_COMMON;
|
||||
|
||||
#endif /* _HPI_INTERNAL_H_ */
|
||||
|
|
|
@ -227,8 +227,9 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
|
|||
if (info->control_type) {
|
||||
pC->p_info[info->control_index] = info;
|
||||
cached++;
|
||||
} else /* dummy cache entry */
|
||||
} else { /* dummy cache entry */
|
||||
pC->p_info[info->control_index] = NULL;
|
||||
}
|
||||
|
||||
byte_count += info->size_in32bit_words * 4;
|
||||
|
||||
|
@ -298,7 +299,7 @@ struct pad_ofs_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_artist), /* HPI_PAD_ARTIST */
|
||||
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,
|
||||
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;
|
||||
break;
|
||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
break;
|
||||
default:
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct hpi_message hm;
|
||||
|
|
|
@ -211,24 +211,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
|
|||
HPIMSGX__init(phm, phr);
|
||||
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:
|
||||
/* Must explicitly handle every subsys message in this switch */
|
||||
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:
|
||||
adapter_close(phm, phr);
|
||||
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:
|
||||
hw_entry_point(phm, phr);
|
||||
break;
|
||||
|
|
|
@ -25,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions
|
|||
#include "hpidebug.h"
|
||||
#include "hpimsgx.h"
|
||||
#include "hpioctl.h"
|
||||
#include "hpicmn.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -161,26 +162,24 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
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;
|
||||
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
|
||||
switch (hm->h.function) {
|
||||
case HPI_SUBSYS_CREATE_ADAPTER:
|
||||
case HPI_SUBSYS_DELETE_ADAPTER:
|
||||
/* Application must not use these functions! */
|
||||
hr->h.size = sizeof(hr->h);
|
||||
hr->h.error = HPI_ERROR_INVALID_OPERATION;
|
||||
hr->h.function = hm->h.function;
|
||||
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
|
||||
if (uncopied_bytes)
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = 0;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
hpi_send_recv_f(&hm->m0, &hr->r0, file);
|
||||
}
|
||||
hpi_send_recv_f(&hm->m0, &hr->r0, file);
|
||||
} else {
|
||||
u16 __user *ptr = NULL;
|
||||
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 */
|
||||
int wrflag = -1;
|
||||
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_ADAPTER_OPEN,
|
||||
HPI_ERROR_BAD_ADAPTER_NUMBER);
|
||||
|
@ -317,7 +317,7 @@ out:
|
|||
int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
int err, idx, nm;
|
||||
int idx, nm;
|
||||
unsigned int memlen;
|
||||
struct hpi_message hm;
|
||||
struct hpi_response hr;
|
||||
|
@ -351,11 +351,8 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
|
|||
nm = HPI_MAX_ADAPTER_MEM_SPACES;
|
||||
|
||||
for (idx = 0; idx < nm; idx++) {
|
||||
HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
|
||||
idx, pci_dev->resource[idx].name,
|
||||
(unsigned long long)pci_resource_start(pci_dev, idx),
|
||||
(unsigned long long)pci_resource_end(pci_dev, idx),
|
||||
(unsigned long long)pci_resource_flags(pci_dev, idx));
|
||||
HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
|
||||
&pci_dev->resource[idx]);
|
||||
|
||||
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
|
||||
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.type = hr.u.s.adapter_type;
|
||||
hm.adapter_index = adapter.index;
|
||||
|
||||
err = hpi_adapter_open(adapter.index);
|
||||
if (err)
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_OPEN);
|
||||
hm.adapter_index = adapter.index;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
|
||||
if (hr.error)
|
||||
goto err;
|
||||
|
||||
adapter.snd_card_asihpi = NULL;
|
||||
/* WARNING can't init mutex in 'adapter'
|
||||
* and then copy it to adapters[] ?!?!
|
||||
*/
|
||||
adapters[hr.u.s.adapter_index] = adapter;
|
||||
adapters[adapter.index] = adapter;
|
||||
mutex_init(&adapters[adapter.index].mutex);
|
||||
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;
|
||||
pa = pci_get_drvdata(pci_dev);
|
||||
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
|
||||
HPI_SUBSYS_DELETE_ADAPTER);
|
||||
hm.obj_index = pa->index;
|
||||
hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
|
||||
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
|
||||
HPI_ADAPTER_DELETE);
|
||||
hm.adapter_index = pa->index;
|
||||
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
|
||||
|
||||
/* unmap PCI memory space, mapped during device init. */
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#define CHIP_AU8810
|
||||
|
||||
#define CARD_NAME "Aureal Advantage 3D Sound Processor"
|
||||
#define CARD_NAME "Aureal Advantage"
|
||||
#define CARD_NAME_SHORT "au8810"
|
||||
|
||||
#define NR_ADB 0x10
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#define CHIP_AU8820
|
||||
|
||||
#define CARD_NAME "Aureal Vortex 3D Sound Processor"
|
||||
#define CARD_NAME "Aureal Vortex"
|
||||
#define CARD_NAME_SHORT "au8820"
|
||||
|
||||
/* Number of ADB and WT channels */
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#define CHIP_AU8830
|
||||
|
||||
#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
|
||||
#define CARD_NAME "Aureal Vortex 2"
|
||||
#define CARD_NAME_SHORT "au8830"
|
||||
|
||||
#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] = {
|
||||
"AU88x0 ADB",
|
||||
"AU88x0 SPDIF",
|
||||
"AU88x0 A3D",
|
||||
"AU88x0 WT",
|
||||
"AU88x0 I2S",
|
||||
CARD_NAME " ADB",
|
||||
CARD_NAME " SPDIF",
|
||||
CARD_NAME " A3D",
|
||||
CARD_NAME " WT",
|
||||
CARD_NAME " I2S",
|
||||
};
|
||||
static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
|
||||
"adb",
|
||||
|
@ -527,7 +527,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
|
|||
nr_capt, &pcm);
|
||||
if (err < 0)
|
||||
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;
|
||||
// This is an evil hack, but it saves a lot of duplicated code.
|
||||
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_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] = {
|
||||
0x00000000, 0x00000001
|
||||
};
|
||||
|
@ -2163,6 +2166,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
|
|||
ctl->min = 0;
|
||||
ctl->max = 40;
|
||||
ctl->value[0] = ctl->value[1] = 20;
|
||||
ctl->tlv = snd_emu10k1_bass_treble_db_scale;
|
||||
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
|
||||
ctl = &controls[i + 1];
|
||||
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->max = 40;
|
||||
ctl->value[0] = ctl->value[1] = 20;
|
||||
ctl->tlv = snd_emu10k1_bass_treble_db_scale;
|
||||
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
|
||||
|
||||
#define BASS_GPR 0x8c
|
||||
|
|
|
@ -1729,8 +1729,6 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
|||
"Master Mono Playback Volume",
|
||||
"PCM Out Path & Mute",
|
||||
"Mono Output Select",
|
||||
"Front Playback Switch",
|
||||
"Front Playback Volume",
|
||||
"Surround Playback Switch",
|
||||
"Surround Playback Volume",
|
||||
"Center Playback Switch",
|
||||
|
@ -1879,6 +1877,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
|||
emu->rear_ac97 = 1;
|
||||
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);
|
||||
remove_ctl(card,"Front Playback Volume");
|
||||
remove_ctl(card,"Front Playback Switch");
|
||||
}
|
||||
/* remove unused AC97 controls */
|
||||
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)
|
||||
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 */
|
||||
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
|
||||
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
|
||||
|
|
|
@ -112,6 +112,10 @@
|
|||
#include <sound/ac97_codec.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#ifdef CONFIG_SND_ES1968_RADIO
|
||||
#include <sound/tea575x-tuner.h>
|
||||
#endif
|
||||
|
||||
#define CARD_NAME "ESS Maestro1/2"
|
||||
#define DRIVER_NAME "ES1968"
|
||||
|
||||
|
@ -553,6 +557,10 @@ struct es1968 {
|
|||
spinlock_t ac97_lock;
|
||||
struct tasklet_struct hwvol_tq;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_ES1968_RADIO
|
||||
struct snd_tea575x tea;
|
||||
#endif
|
||||
};
|
||||
|
||||
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 */
|
||||
|
||||
#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)
|
||||
{
|
||||
#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 */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_ES1968_RADIO
|
||||
snd_tea575x_exit(&chip->tea);
|
||||
#endif
|
||||
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, 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);
|
||||
|
||||
#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;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
#include <sound/tea575x-tuner.h>
|
||||
#define TEA575X_RADIO 1
|
||||
#endif
|
||||
|
||||
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
|
||||
* 1 = MediaForte 256-PCS
|
||||
* 2 = MediaForte 256-PCPR
|
||||
* 2 = MediaForte 256-PCP
|
||||
* 3 = MediaForte 64-PCR
|
||||
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
|
||||
* 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_PARM_DESC(enable, "Enable FM801 soundcard.");
|
||||
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_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
|
||||
|
@ -196,7 +195,7 @@ struct fm801 {
|
|||
spinlock_t reg_lock;
|
||||
struct snd_info_entry *proc_entry;
|
||||
|
||||
#ifdef TEA575X_RADIO
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
struct snd_tea575x tea;
|
||||
#endif
|
||||
|
||||
|
@ -715,310 +714,89 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
|
|||
* TEA5757 radio
|
||||
*/
|
||||
|
||||
#ifdef TEA575X_RADIO
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
|
||||
/* 256PCS GPIO numbers */
|
||||
#define TEA_256PCS_DATA 1
|
||||
#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
|
||||
#define TEA_256PCS_BUS_CLOCK 3
|
||||
/* GPIO to TEA575x maps */
|
||||
struct snd_fm801_tea575x_gpio {
|
||||
u8 data, clk, wren, most;
|
||||
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;
|
||||
unsigned short reg;
|
||||
int i = 25;
|
||||
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];
|
||||
|
||||
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 */
|
||||
reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
|
||||
FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
|
||||
FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
|
||||
/* all of lines are in the write direction */
|
||||
/* clear data and clock lines */
|
||||
reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
|
||||
FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
|
||||
FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
|
||||
FM801_GPIO_GP(TEA_256PCS_DATA) |
|
||||
FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
|
||||
FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
|
||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||
udelay(1);
|
||||
|
||||
while (i--) {
|
||||
if (val & (1 << i))
|
||||
reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
|
||||
else
|
||||
reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
|
||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||
udelay(1);
|
||||
reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
|
||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||
reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
|
||||
outw(reg, FM801_REG(chip, GPIO_CTRL));
|
||||
udelay(1);
|
||||
reg |= FM801_GPIO_GS(gpio.data) |
|
||||
FM801_GPIO_GS(gpio.wren) |
|
||||
FM801_GPIO_GS(gpio.clk) |
|
||||
FM801_GPIO_GS(gpio.most);
|
||||
if (output) {
|
||||
/* all of lines are in the write direction */
|
||||
/* clear data and clock lines */
|
||||
reg &= ~(FM801_GPIO_GD(gpio.data) |
|
||||
FM801_GPIO_GD(gpio.wren) |
|
||||
FM801_GPIO_GD(gpio.clk) |
|
||||
FM801_GPIO_GP(gpio.data) |
|
||||
FM801_GPIO_GP(gpio.clk) |
|
||||
FM801_GPIO_GP(gpio.wren));
|
||||
} else {
|
||||
/* use GPIO lines, set data direction to input */
|
||||
reg |= FM801_GPIO_GD(gpio.data) |
|
||||
FM801_GPIO_GD(gpio.most) |
|
||||
FM801_GPIO_GP(gpio.data) |
|
||||
FM801_GPIO_GP(gpio.most) |
|
||||
FM801_GPIO_GP(gpio.wren);
|
||||
/* all of lines are in the write direction, except data */
|
||||
/* clear data, write enable and clock lines */
|
||||
reg &= ~(FM801_GPIO_GD(gpio.wren) |
|
||||
FM801_GPIO_GD(gpio.clk) |
|
||||
FM801_GPIO_GP(gpio.clk));
|
||||
}
|
||||
|
||||
/* 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));
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static unsigned int snd_fm801_tea575x_256pcs_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_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,
|
||||
}
|
||||
static struct snd_tea575x_ops snd_fm801_tea_ops = {
|
||||
.set_pins = snd_fm801_tea575x_set_pins,
|
||||
.get_pins = snd_fm801_tea575x_get_pins,
|
||||
.set_direction = snd_fm801_tea575x_set_direction,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -1371,7 +1149,7 @@ static int snd_fm801_free(struct fm801 *chip)
|
|||
outw(cmdw, FM801_REG(chip, IRQ_MASK));
|
||||
|
||||
__end_hw:
|
||||
#ifdef TEA575X_RADIO
|
||||
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
|
||||
snd_tea575x_exit(&chip->tea);
|
||||
#endif
|
||||
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);
|
||||
|
||||
#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 &&
|
||||
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
||||
chip->tea.dev_nr = tea575x_tuner >> 16;
|
||||
chip->tea.card = card;
|
||||
chip->tea.freq_fixup = 10700;
|
||||
chip->tea.private_data = chip;
|
||||
chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||
snd_tea575x_init(&chip->tea);
|
||||
}
|
||||
if (snd_tea575x_init(&chip->tea))
|
||||
snd_printk(KERN_ERR "TEA575x radio not found\n");
|
||||
} else if ((tea575x_tuner & TUNER_TYPE_MASK) == 0)
|
||||
/* autodetect tuner connection */
|
||||
for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
|
||||
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
|
||||
|
||||
*rchip = chip;
|
||||
|
|
|
@ -235,8 +235,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
|
|||
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
|
||||
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
|
||||
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
|
||||
{ PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
|
||||
#if 0
|
||||
{ PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
|
||||
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
|
||||
#endif
|
||||
{ 0, }
|
||||
|
@ -1261,9 +1261,9 @@ static struct shortname_table {
|
|||
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
|
||||
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
|
||||
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
|
||||
{ 0x746e, "AMD AMD8111" },
|
||||
#if 0
|
||||
{ 0x5455, "ALi M5455" },
|
||||
{ 0x746d, "AMD AMD8111" },
|
||||
#endif
|
||||
{ 0 },
|
||||
};
|
||||
|
|
|
@ -1000,7 +1000,7 @@ static void device_change_handler(struct work_struct *work)
|
|||
chip->lineout_sw_ctl);
|
||||
if (mix->anded_reset)
|
||||
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);
|
||||
} else {
|
||||
/* unmute speaker, mute others */
|
||||
|
|
|
@ -65,6 +65,15 @@ init_data[] = {
|
|||
{ 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)
|
||||
{
|
||||
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,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
|
@ -195,6 +265,28 @@ static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
|
|||
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[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
|
@ -223,6 +315,15 @@ static struct __devinitdata snd_kcontrol_new elements[] = {
|
|||
.get = usb6fire_control_opt_coax_get,
|
||||
.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;
|
||||
|
||||
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;
|
||||
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_line_phono_update(rt);
|
||||
usb6fire_control_master_vol_update(rt);
|
||||
usb6fire_control_streaming_update(rt);
|
||||
|
||||
i = 0;
|
||||
while (elements[i].name) {
|
||||
|
|
|
@ -21,12 +21,29 @@ enum {
|
|||
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 {
|
||||
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 snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
|
||||
bool opt_coax_switch;
|
||||
bool line_phono_switch;
|
||||
bool digital_thru_switch;
|
||||
bool usb_streaming;
|
||||
u8 master_vol;
|
||||
};
|
||||
|
||||
|
|
|
@ -3,12 +3,6 @@
|
|||
*
|
||||
* 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>
|
||||
* Created: Jan 01, 2011
|
||||
* Version: 0.3.0
|
||||
|
@ -21,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/bitrev.h>
|
||||
|
||||
#include "firmware.h"
|
||||
#include "chip.h"
|
||||
|
@ -33,32 +28,6 @@ enum {
|
|||
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.
|
||||
* 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) */
|
||||
};
|
||||
|
||||
static const u8 known_fw_versions[][4] = {
|
||||
{ 0x03, 0x01, 0x0b, 0x00 }
|
||||
};
|
||||
|
||||
struct ihex_record {
|
||||
u16 address;
|
||||
u8 len;
|
||||
|
@ -340,7 +313,7 @@ static int usb6fire_fw_fpga_upload(
|
|||
|
||||
while (c != end) {
|
||||
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);
|
||||
if (ret < 0) {
|
||||
|
@ -363,6 +336,25 @@ static int usb6fire_fw_fpga_upload(
|
|||
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 i;
|
||||
|
@ -378,9 +370,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
|||
"firmware state.\n");
|
||||
return ret;
|
||||
}
|
||||
if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55
|
||||
|| buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7]
|
||||
!= 0x00) {
|
||||
if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
|
||||
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
|
||||
"received from device: ");
|
||||
for (i = 0; i < 8; i++)
|
||||
|
@ -389,7 +379,7 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
|||
return -EIO;
|
||||
}
|
||||
/* do we need fpga loader ezusb firmware? */
|
||||
if (buffer[3] == 0x01 && buffer[6] == 0x19) {
|
||||
if (buffer[3] == 0x01) {
|
||||
ret = usb6fire_fw_ezusb_upload(intf,
|
||||
"6fire/dmx6firel2.ihx", 0, NULL, 0);
|
||||
if (ret < 0)
|
||||
|
@ -397,7 +387,10 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
|||
return FW_NOT_READY;
|
||||
}
|
||||
/* 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");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -410,8 +403,8 @@ int usb6fire_fw_init(struct usb_interface *intf)
|
|||
return FW_NOT_READY;
|
||||
}
|
||||
/* all fw loaded? */
|
||||
else if (buffer[3] == 0x03 && buffer[6] == 0x0b)
|
||||
return 0;
|
||||
else if (buffer[3] == 0x03)
|
||||
return usb6fire_fw_check(buffer + 4);
|
||||
/* unknown data? */
|
||||
else {
|
||||
snd_printk(KERN_ERR PREFIX "unknown device firmware state "
|
||||
|
|
|
@ -17,26 +17,23 @@
|
|||
#include "pcm.h"
|
||||
#include "chip.h"
|
||||
#include "comm.h"
|
||||
#include "control.h"
|
||||
|
||||
enum {
|
||||
OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
|
||||
};
|
||||
|
||||
/* 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_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
|
||||
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[] = {
|
||||
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
|
||||
SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
|
||||
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 */
|
||||
OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
|
||||
};
|
||||
|
@ -48,15 +45,6 @@ enum { /* pcm streaming states */
|
|||
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 = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
|
@ -64,7 +52,7 @@ static const struct snd_pcm_hardware pcm_hw = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
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 |
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
struct usb_device *device = rt->chip->dev;
|
||||
struct comm_runtime *comm_rt = rt->chip->comm;
|
||||
struct control_runtime *ctrl_rt = rt->chip->control;
|
||||
|
||||
if (rt->rate >= ARRAY_SIZE(rates))
|
||||
return -EINVAL;
|
||||
/* disable streaming */
|
||||
ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00);
|
||||
ctrl_rt->usb_streaming = false;
|
||||
ret = ctrl_rt->update_streaming(ctrl_rt);
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR PREFIX "error stopping streaming while "
|
||||
"setting samplerate %d.\n", rates[rt->rate]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_set_interface(device, 1, rates_altsetting[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]);
|
||||
ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
|
||||
rates[rt->rate]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable analog inputs and outputs
|
||||
* (one bit per stereo-channel) */
|
||||
ret = comm_rt->write16(comm_rt, 0x02, 0x02,
|
||||
(1 << (OUT_N_CHANNELS / 2)) - 1,
|
||||
(1 << (IN_N_CHANNELS / 2)) - 1);
|
||||
ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
|
||||
false, false);
|
||||
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",
|
||||
rates[rt->rate]);
|
||||
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) {
|
||||
snd_printk(KERN_ERR PREFIX "error starting streaming while "
|
||||
"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)
|
||||
{
|
||||
int i;
|
||||
struct control_runtime *ctrl_rt = rt->chip->control;
|
||||
|
||||
if (rt->stream_state != STREAM_DISABLED) {
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
usb_kill_urb(&rt->in_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;
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +196,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
|
|||
unsigned int total_length = 0;
|
||||
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
|
||||
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
|
||||
* (alsa_rt->frame_bits >> 3));
|
||||
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
|
||||
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 */
|
||||
total_length += urb->packets[i].length;
|
||||
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));
|
||||
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
|
||||
* (alsa_rt->frame_bits >> 3));
|
||||
u32 *dest = (u32 *) urb->buffer;
|
||||
u32 *dest;
|
||||
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++) {
|
||||
/* at least 4 header bytes for valid packet.
|
||||
* 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 */
|
||||
if (!rt->playback.instance && !rt->capture.instance) {
|
||||
usb6fire_pcm_stream_stop(rt);
|
||||
rt->rate = -1;
|
||||
rt->rate = ARRAY_SIZE(rates);
|
||||
}
|
||||
}
|
||||
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_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
|
||||
struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (rt->panic)
|
||||
|
@ -493,12 +474,10 @@ static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
|
|||
sub->period_off = 0;
|
||||
|
||||
if (rt->stream_state == STREAM_DISABLED) {
|
||||
for (i = 0; i < ARRAY_SIZE(rates); i++)
|
||||
if (alsa_rt->rate == rates[i]) {
|
||||
rt->rate = i;
|
||||
for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
|
||||
if (alsa_rt->rate == rates[rt->rate])
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(rates)) {
|
||||
if (rt->rate == ARRAY_SIZE(rates)) {
|
||||
mutex_unlock(&rt->stream_mutex);
|
||||
snd_printk("invalid rate %d in prepare.\n",
|
||||
alsa_rt->rate);
|
||||
|
@ -613,7 +592,7 @@ int __devinit usb6fire_pcm_init(struct sfire_chip *chip)
|
|||
|
||||
rt->chip = chip;
|
||||
rt->stream_state = STREAM_DISABLED;
|
||||
rt->rate = -1;
|
||||
rt->rate = ARRAY_SIZE(rates);
|
||||
init_waitqueue_head(&rt->stream_wait_queue);
|
||||
mutex_init(&rt->stream_mutex);
|
||||
|
||||
|
|
|
@ -100,19 +100,17 @@ config SND_USB_US122L
|
|||
|
||||
config SND_USB_6FIRE
|
||||
tristate "TerraTec DMX 6Fire USB"
|
||||
depends on EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select BITREVERSE
|
||||
select SND_RAWMIDI
|
||||
select SND_PCM
|
||||
help
|
||||
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
|
||||
after it has been coldstarted. This driver currently does not support
|
||||
firmware loading for all devices. If you own such a device,
|
||||
you could start windows and let the windows driver upload
|
||||
the firmware. As long as you do not unplug your device from power,
|
||||
it should be usable.
|
||||
after it has been coldstarted. An install script for the firmware
|
||||
and further help can be found at
|
||||
http://sixfireusb.sourceforge.net
|
||||
|
||||
endif # SND_USB
|
||||
|
||||
|
|
|
@ -104,6 +104,15 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
|||
int err;
|
||||
unsigned char data;
|
||||
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,
|
||||
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) {
|
||||
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
|
||||
__func__, source_id);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return !!data;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#ifdef HW_CONST_DEBUG
|
||||
#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
|
||||
#else
|
||||
#define hwc_debug(fmt, args...) /**/
|
||||
#define hwc_debug(fmt, args...) do { } while(0)
|
||||
#endif
|
||||
|
||||
#endif /* __USBAUDIO_DEBUG_H */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
#include "clock.h"
|
||||
#include "format.h"
|
||||
|
||||
/*
|
||||
* 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 ?
|
||||
" Switch" : " 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);
|
||||
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;
|
||||
|
||||
|
|
|
@ -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, 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, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */
|
||||
{ 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_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
!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
|
||||
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||
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 */
|
||||
if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0)
|
||||
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 */
|
||||
(mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
|
||||
mixer->chip->usb_id == USB_ID(0x041e, 0x3042) ||
|
||||
mixer->chip->usb_id == USB_ID(0x041e, 0x30df) ||
|
||||
mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
|
||||
break;
|
||||
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, 0x3040):
|
||||
case USB_ID(0x041e, 0x3042):
|
||||
case USB_ID(0x041e, 0x30df):
|
||||
case USB_ID(0x041e, 0x3048):
|
||||
err = snd_audigy2nx_controls_create(mixer);
|
||||
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 */
|
||||
{
|
||||
|
@ -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) {
|
||||
/* .vendor_name = "M-Audio", */
|
||||
/* .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) {
|
||||
/* .vendor_name = "M-Audio", */
|
||||
/* .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 */
|
||||
{
|
||||
USB_DEVICE(0x09e8, 0x0062),
|
||||
|
@ -2331,6 +2368,12 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
|||
},
|
||||
|
||||
/* Native Instruments MK2 series */
|
||||
{
|
||||
/* Komplete Audio 6 */
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x17cc,
|
||||
.idProduct = 0x1000,
|
||||
},
|
||||
{
|
||||
/* Traktor Audio 6 */
|
||||
.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 */
|
||||
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, 0x1020): /* Traktor Audio 10 */
|
||||
return snd_usb_nativeinstruments_boot_quirk(dev);
|
||||
|
|
Loading…
Reference in a new issue