1
0
Fork 0

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (346 commits)
  ASoC: core: Don't set "(null)" as a driver name
  ALSA: hda - Use LPIB for ATI/AMD chipsets as default
  Revert "ALSA: hda - Use position_fix=3 as default for AMD chipsets"
  ASoC: Tegra: Fix compile when debugfs not enabled
  ASoC: spdif-dit: Add missing MODULE_*
  SOUND: OSS: Remove Au1550 driver.
  ALSA: hda - add Intel Panther Point HDMI codec id
  ALSA: emu10k1 - Add dB range to Bass and Treble for SB Live!
  ALSA: hda - Remove PCM mixer elements from Virtual Master of realtek
  ALSA: hda - Fix input-src parse in patch_analog.c
  ASoC: davinci-mcasp: enable ping-pong SRAM buffers
  ASoC: add iPAQ hx4700 machine driver
  ASoC: Asahi Kasei AK4641 codec driver
  ALSA: hda - Enable Realtek ALC269 codec input layer beep
  ALSA: intel8x0m: enable AMD8111 modem
  ALSA: HDA: Add jack detection for HDMI
  ALSA: sound, core, pcm_lib: fix xrun_log
  ASoC: Max98095: Move existing NULL check before pointer dereference.
  ALSA: sound, core, pcm_lib: xrun_log: log also in_interrupt
  ALSA: usb-audio - Add support for USB X-Fi S51 Pro
  ...
hifive-unleashed-5.1
Linus Torvalds 2011-05-23 08:52:38 -07:00
commit 710421cc7d
185 changed files with 24512 additions and 10569 deletions

View File

@ -1230,6 +1230,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
This module supports multiple cards.
The driver requires the firmware loader support on kernel.
Module snd-lola
---------------
Module for Digigram Lola PCI-e boards
This module supports multiple cards.
Module snd-lx6464es
-------------------

View File

@ -94,7 +94,7 @@ ALC662/663/272
3stack-dig 3-stack (2-channel) with SPDIF
3stack-6ch 3-stack (6-channel)
3stack-6ch-dig 3-stack (6-channel) with SPDIF
6stack-dig 6-stack with SPDIF
5stack-dig 5-stack with SPDIF
lenovo-101e Lenovo laptop
eeepc-p701 ASUS Eeepc P701
eeepc-ep20 ASUS Eeepc EP20

View File

@ -4269,6 +4269,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
@ -5868,7 +5875,7 @@ F: include/sound/
F: sound/
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
M: Liam Girdwood <lrg@slimlogic.co.uk>
M: Liam Girdwood <lrg@ti.com>
M: Mark Brown <broonie@opensource.wolfsonmicro.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
@ -6119,7 +6126,7 @@ F: drivers/mmc/host/tifm_sd.c
F: include/linux/tifm.h
TI TWL4030 SERIES SOC CODEC DRIVER
M: Peter Ujfalusi <peter.ujfalusi@nokia.com>
M: Peter Ujfalusi <peter.ujfalusi@ti.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained
F: sound/soc/codecs/twl4030*
@ -6763,7 +6770,7 @@ F: drivers/scsi/vmw_pvscsi.c
F: drivers/scsi/vmw_pvscsi.h
VOLTAGE AND CURRENT REGULATOR FRAMEWORK
M: Liam Girdwood <lrg@slimlogic.co.uk>
M: Liam Girdwood <lrg@ti.com>
M: Mark Brown <broonie@opensource.wolfsonmicro.com>
W: http://opensource.wolfsonmicro.com/node/15
W: http://www.slimlogic.co.uk/?p=48

View File

@ -27,12 +27,14 @@ comment "Tegra board type"
config MACH_HARMONY
bool "Harmony board"
select MACH_HAS_SND_SOC_TEGRA_WM8903
help
Support for nVidia Harmony development platform
config MACH_KAEN
bool "Kaen board"
select MACH_SEABOARD
select MACH_HAS_SND_SOC_TEGRA_WM8903
help
Support for the Kaen version of Seaboard
@ -43,6 +45,7 @@ config MACH_PAZ00
config MACH_SEABOARD
bool "Seaboard board"
select MACH_HAS_SND_SOC_TEGRA_WM8903
help
Support for nVidia Seaboard development platform. It will
also be included for some of the derivative boards that

View File

@ -34,7 +34,7 @@
#include <asm/mach/time.h>
#include <asm/setup.h>
#include <mach/harmony_audio.h>
#include <mach/tegra_wm8903_pdata.h>
#include <mach/iomap.h>
#include <mach/irqs.h>
#include <mach/sdhci.h>
@ -67,15 +67,16 @@ static struct platform_device debug_uart = {
},
};
static struct harmony_audio_platform_data harmony_audio_pdata = {
static struct tegra_wm8903_platform_data harmony_audio_pdata = {
.gpio_spkr_en = TEGRA_GPIO_SPKR_EN,
.gpio_hp_det = TEGRA_GPIO_HP_DET,
.gpio_hp_mute = -1,
.gpio_int_mic_en = TEGRA_GPIO_INT_MIC_EN,
.gpio_ext_mic_en = TEGRA_GPIO_EXT_MIC_EN,
};
static struct platform_device harmony_audio_device = {
.name = "tegra-snd-harmony",
.name = "tegra-snd-wm8903",
.id = 0,
.dev = {
.platform_data = &harmony_audio_pdata,

View File

@ -1,5 +1,5 @@
/*
* arch/arm/mach-tegra/include/mach/harmony_audio.h
* arch/arm/mach-tegra/include/mach/tegra_wm8903_pdata.h
*
* Copyright 2011 NVIDIA, Inc.
*
@ -14,9 +14,10 @@
*
*/
struct harmony_audio_platform_data {
struct tegra_wm8903_platform_data {
int gpio_spkr_en;
int gpio_hp_det;
int gpio_hp_mute;
int gpio_int_mic_en;
int gpio_ext_mic_en;
};

View File

@ -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

View File

@ -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/

View File

@ -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);

View File

@ -508,7 +508,6 @@ int register_sst_card(struct intel_sst_card_ops *card)
sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE;
sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/
card->pcm_control = sst_pmic_ops.pcm_control;
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
return 0;
} else {
pr_err("strcmp fail %s\n", card->module_name);

View File

@ -32,6 +32,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/firmware.h>
#include <sound/control.h>
#include <asm/mrst.h>
#include <sound/pcm.h>
@ -40,6 +41,8 @@
#include <sound/initval.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
#include "intelmid_snd_control.h"
#include "intelmid.h"
@ -802,6 +805,7 @@ static int __devinit snd_intelmad_sst_register(
pr_err("sst card registration failed\n");
return ret_val;
}
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id;
intelmaddata->pmic_status = PMIC_UNINIT;

View File

@ -32,6 +32,10 @@ struct wm8994_ldo_pdata {
#define WM8994_EQ_REGS 20
#define WM8958_MBC_CUTOFF_REGS 20
#define WM8958_MBC_COEFF_REGS 48
#define WM8958_MBC_COMBINED_REGS 56
#define WM8958_VSS_HPF_REGS 2
#define WM8958_VSS_REGS 148
#define WM8958_ENH_EQ_REGS 32
/**
* DRC configurations are specified with a label and a set of register
@ -71,6 +75,42 @@ struct wm8958_mbc_cfg {
const char *name;
u16 cutoff_regs[WM8958_MBC_CUTOFF_REGS];
u16 coeff_regs[WM8958_MBC_COEFF_REGS];
/* Coefficient layout when using MBC+VSS firmware */
u16 combined_regs[WM8958_MBC_COMBINED_REGS];
};
/**
* VSS HPF configurations are specified with a label and two values to
* write. Configurations are expected to be generated using the
* multiband compressor configuration panel in WISCE - see
* http://www.wolfsonmicro.com/wisce/
*/
struct wm8958_vss_hpf_cfg {
const char *name;
u16 regs[WM8958_VSS_HPF_REGS];
};
/**
* VSS configurations are specified with a label and array of values
* to write. Configurations are expected to be generated using the
* multiband compressor configuration panel in WISCE - see
* http://www.wolfsonmicro.com/wisce/
*/
struct wm8958_vss_cfg {
const char *name;
u16 regs[WM8958_VSS_REGS];
};
/**
* Enhanced EQ configurations are specified with a label and array of
* values to write. Configurations are expected to be generated using
* the multiband compressor configuration panel in WISCE - see
* http://www.wolfsonmicro.com/wisce/
*/
struct wm8958_enh_eq_cfg {
const char *name;
u16 regs[WM8958_ENH_EQ_REGS];
};
struct wm8994_pdata {
@ -95,6 +135,15 @@ struct wm8994_pdata {
int num_mbc_cfgs;
struct wm8958_mbc_cfg *mbc_cfgs;
int num_vss_cfgs;
struct wm8958_vss_cfg *vss_cfgs;
int num_vss_hpf_cfgs;
struct wm8958_vss_hpf_cfg *vss_hpf_cfgs;
int num_enh_eq_cfgs;
struct wm8958_enh_eq_cfg *enh_eq_cfgs;
/* LINEOUT can be differential or single ended */
unsigned int lineout1_diff:1;
unsigned int lineout2_diff:1;

View File

@ -0,0 +1,26 @@
/*
* AK4641 ALSA SoC Codec driver
*
* Copyright 2009 Philipp Zabel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __AK4641_H
#define __AK4641_H
/**
* struct ak4641_platform_data - platform specific AK4641 configuration
* @gpio_power: GPIO to control external power to AK4641
* @gpio_npdn: GPIO connected to AK4641 nPDN pin
*
* Both GPIO parameters are optional.
*/
struct ak4641_platform_data {
int gpio_power;
int gpio_npdn;
};
#endif /* __AK4641_H */

View File

@ -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,

View File

@ -0,0 +1,54 @@
/*
* Platform data for MAX98095
*
* Copyright 2011 Maxim Integrated Products
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __SOUND_MAX98095_PDATA_H__
#define __SOUND_MAX98095_PDATA_H__
/* Equalizer filter response configuration */
struct max98095_eq_cfg {
const char *name;
unsigned int rate;
u16 band1[5];
u16 band2[5];
u16 band3[5];
u16 band4[5];
u16 band5[5];
};
/* Biquad filter response configuration */
struct max98095_biquad_cfg {
const char *name;
unsigned int rate;
u16 band1[5];
u16 band2[5];
};
/* codec platform data */
struct max98095_pdata {
/* Equalizers for DAI1 and DAI2 */
struct max98095_eq_cfg *eq_cfg;
unsigned int eq_cfgcnt;
/* Biquad filter for DAI1 and DAI2 */
struct max98095_biquad_cfg *bq_cfg;
unsigned int bq_cfgcnt;
/* Analog/digital microphone configuration:
* 0 = analog microphone input (normal setting)
* 1 = digital microphone input
*/
unsigned int digmic_left_mode:1;
unsigned int digmic_right_mode:1;
};
#endif

View File

@ -24,7 +24,7 @@
* SoC dynamic audio power management
*
* We can have up to 4 power domains
* 1. Codec domain - VREF, VMID
* 1. Codec domain - VREF, VMID
* Usually controlled at codec probe/remove, although can be set
* at stream time if power is not needed for sidetone, etc.
* 2. Platform/Machine domain - physically connected inputs and outputs
@ -39,30 +39,30 @@
/* codec domain */
#define SND_SOC_DAPM_VMID(wname) \
{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0}
/* platform domain */
#define SND_SOC_DAPM_INPUT(wname) \
{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_OUTPUT(wname) \
{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_MIC(wname, wevent) \
{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_HP(wname, wevent) \
{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_SPK(wname, wevent) \
{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_LINE(wname, wevent) \
{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
@ -70,91 +70,91 @@
#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
.invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
wcontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
wcontrols, wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \
.invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \
.invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */
@ -173,26 +173,26 @@
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags}
#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags}
#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
wcontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \
.invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */
#define SND_SOC_DAPM_PRE(wname, wevent) \
{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_POST(wname, wevent) \
{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \
{ .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
@ -232,7 +232,7 @@
/* generic widgets */
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \
{ .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
@ -356,7 +356,8 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card);
/* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev);
void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm);
void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
struct dentry *parent);
/* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm,
@ -472,7 +473,8 @@ struct snd_soc_dapm_widget {
/* kcontrols that relate to this widget */
int num_kcontrols;
const struct snd_kcontrol_new *kcontrols;
const struct snd_kcontrol_new *kcontrol_news;
struct snd_kcontrol **kcontrols;
/* widget input and outputs */
struct list_head sources;
@ -516,4 +518,10 @@ struct snd_soc_dapm_context {
#endif
};
/* A list of widgets associated with an object, typically a snd_kcontrol */
struct snd_soc_dapm_widget_list {
int num_widgets;
struct snd_soc_dapm_widget *widgets[0];
};
#endif

View File

@ -248,7 +248,7 @@ typedef int (*hw_write_t)(void *,const char* ,int);
extern struct snd_ac97_bus_ops soc_ac97_ops;
enum snd_soc_control_type {
SND_SOC_CUSTOM,
SND_SOC_CUSTOM = 1,
SND_SOC_I2C,
SND_SOC_SPI,
};
@ -278,6 +278,10 @@ int snd_soc_register_codec(struct device *dev,
void snd_soc_unregister_codec(struct device *dev);
int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
int addr_bits, int data_bits,
enum snd_soc_control_type control);
@ -292,6 +296,8 @@ int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_default_readable_register(struct snd_soc_codec *codec,
unsigned int reg);
int snd_soc_default_writable_register(struct snd_soc_codec *codec,
unsigned int reg);
/* Utility functions to get clock rates from various things */
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@ -523,6 +529,7 @@ struct snd_soc_codec {
size_t reg_size; /* reg_cache_size * reg_word_size */
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int);
/* runtime */
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
@ -539,10 +546,12 @@ struct snd_soc_codec {
/* codec IO */
void *control_data; /* codec control (i2c/3wire) data */
enum snd_soc_control_type control_type;
hw_write_t hw_write;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*bulk_write_raw)(struct snd_soc_codec *, unsigned int, const void *, size_t);
void *reg_cache;
const void *reg_def_copy;
const struct snd_soc_cache_ops *cache_ops;
@ -568,7 +577,9 @@ struct snd_soc_codec_driver {
pm_message_t state);
int (*resume)(struct snd_soc_codec *);
/* Default DAPM setup, added after probe() is run */
/* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls;
int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
@ -587,6 +598,7 @@ struct snd_soc_codec_driver {
size_t, unsigned int);
int (*volatile_register)(struct snd_soc_codec *, unsigned int);
int (*readable_register)(struct snd_soc_codec *, unsigned int);
int (*writable_register)(struct snd_soc_codec *, unsigned int);
short reg_cache_size;
short reg_cache_step;
short reg_word_size;
@ -690,6 +702,8 @@ struct snd_soc_aux_dev {
/* SoC card */
struct snd_soc_card {
const char *name;
const char *long_name;
const char *driver_name;
struct device *dev;
struct snd_card *snd_card;
struct module *owner;
@ -737,12 +751,15 @@ struct snd_soc_card {
struct snd_soc_pcm_runtime *rtd_aux;
int num_aux_rtd;
const struct snd_kcontrol_new *controls;
int num_controls;
/*
* Card-specific routes and widgets.
*/
struct snd_soc_dapm_widget *dapm_widgets;
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
struct snd_soc_dapm_route *dapm_routes;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
struct work_struct deferred_resume_work;
@ -805,7 +822,7 @@ struct soc_enum {
unsigned char shift_r;
unsigned int max;
unsigned int mask;
const char **texts;
const char * const *texts;
const unsigned int *values;
void *dapm;
};
@ -814,6 +831,8 @@ struct soc_enum {
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
unsigned int snd_soc_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int val);
unsigned int snd_soc_bulk_write_raw(struct snd_soc_codec *codec,
unsigned int reg, const void *data, size_t len);
/* device driver data */
@ -871,6 +890,9 @@ static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
INIT_LIST_HEAD(&card->dapm_list);
}
int snd_soc_util_init(void);
void snd_soc_util_exit(void);
#include <sound/soc-dai.h>
#ifdef CONFIG_DEBUG_FS

View File

@ -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 */

View File

@ -1,7 +1,7 @@
/*
* Platform header for Texas Instruments TLV320DAC33 codec driver
*
* Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*

View File

@ -3,7 +3,7 @@
*
* Copyright (C) Nokia Corporation
*
* Written by Peter Ujfalusi <peter.ujfalusi@nokia.com>
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License

View File

@ -0,0 +1,55 @@
/*
* linux/sound/wm8915.h -- Platform data for WM8915
*
* Copyright 2011 Wolfson Microelectronics. PLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __LINUX_SND_WM8903_H
#define __LINUX_SND_WM8903_H
enum wm8915_inmode {
WM8915_DIFFERRENTIAL_1 = 0, /* IN1xP - IN1xN */
WM8915_INVERTING = 1, /* IN1xN */
WM8915_NON_INVERTING = 2, /* IN1xP */
WM8915_DIFFERENTIAL_2 = 3, /* IN2xP - IN2xP */
};
/**
* ReTune Mobile configurations are specified with a label, sample
* rate and set of values to write (the enable bits will be ignored).
*
* Configurations are expected to be generated using the ReTune Mobile
* control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
*/
struct wm8915_retune_mobile_config {
const char *name;
int rate;
u16 regs[20];
};
#define WM8915_SET_DEFAULT 0x10000
struct wm8915_pdata {
int irq_flags; /** Set IRQ trigger flags; default active low */
int ldo_ena; /** GPIO for LDO1; -1 for none */
int micdet_def; /** Default MICDET_SRC/HP1FB_SRC/MICD_BIAS */
enum wm8915_inmode inl_mode;
enum wm8915_inmode inr_mode;
u32 spkmute_seq; /** Value for register 0x802 */
int gpio_base;
u32 gpio_default[5];
int num_retune_mobile_cfgs;
struct wm8915_retune_mobile_config *retune_mobile_cfgs;
};
#endif

View File

@ -14,6 +14,28 @@
/* Use to set GPIO default values to zero */
#define WM8962_GPIO_SET 0x10000
#define WM8962_GPIO_FN_CLKOUT 0
#define WM8962_GPIO_FN_LOGIC 1
#define WM8962_GPIO_FN_SDOUT 2
#define WM8962_GPIO_FN_IRQ 3
#define WM8962_GPIO_FN_THERMAL 4
#define WM8962_GPIO_FN_PLL2_LOCK 6
#define WM8962_GPIO_FN_PLL3_LOCK 7
#define WM8962_GPIO_FN_FLL_LOCK 9
#define WM8962_GPIO_FN_DRC_ACT 10
#define WM8962_GPIO_FN_WSEQ_DONE 11
#define WM8962_GPIO_FN_ALC_NG_ACT 12
#define WM8962_GPIO_FN_ALC_PEAK_LIMIT 13
#define WM8962_GPIO_FN_ALC_SATURATION 14
#define WM8962_GPIO_FN_ALC_LEVEL_THR 15
#define WM8962_GPIO_FN_ALC_LEVEL_LOCK 16
#define WM8962_GPIO_FN_FIFO_ERR 17
#define WM8962_GPIO_FN_OPCLK 18
#define WM8962_GPIO_FN_DMICCLK 19
#define WM8962_GPIO_FN_DMICDAT 20
#define WM8962_GPIO_FN_MICD 21
#define WM8962_GPIO_FN_MICSCD 22
struct wm8962_pdata {
int gpio_base;
u32 gpio_init[WM8962_MAX_GPIO];

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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

View 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);

View File

@ -31,6 +31,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
@ -42,6 +43,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)
{
@ -146,6 +148,7 @@ retry_after_bus_reset:
return channel;
}
EXPORT_SYMBOL(fw_iso_resources_allocate);
/**
* fw_iso_resources_update - update resource allocations after a bus reset
@ -197,6 +200,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
@ -224,3 +228,4 @@ void fw_iso_resources_free(struct fw_iso_resources *r)
mutex_unlock(&r->mutex);
}
EXPORT_SYMBOL(fw_iso_resources_free);

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -22,10 +22,6 @@ config SOUND_VWSND
<file:Documentation/sound/oss/vwsnd> for more info on this driver's
capabilities.
config SOUND_AU1550_AC97
tristate "Au1550/Au1200 AC97 Sound"
depends on SOC_AU1550 || SOC_AU1200
config SOUND_MSNDCLAS
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
depends on (m || !STANDALONE) && ISA

View File

@ -25,7 +25,6 @@ obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_DMASOUND) += dmasound/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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"
@ -658,6 +666,15 @@ config SND_KORG1212
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
config SND_LOLA
tristate "Digigram Lola"
select SND_PCM
help
Say Y to include support for Digigram Lola boards.
To compile this driver as a module, choose M here: the module
will be called snd-lola.
config SND_LX6464ES
tristate "Digigram LX6464ES"
select SND_PCM

View File

@ -64,6 +64,7 @@ obj-$(CONFIG_SND) += \
ca0106/ \
cs46xx/ \
cs5535audio/ \
lola/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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_ */

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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. */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -307,6 +307,12 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
static bool add_conn_list(struct snd_array *array, hda_nid_t nid);
static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
hda_nid_t *src, int len);
/**
* snd_hda_get_connections - get connection list
* @codec: the HDA codec
@ -320,7 +326,44 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
* Returns the number of connections, or a negative error code.
*/
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
hda_nid_t *conn_list, int max_conns)
{
struct snd_array *array = &codec->conn_lists;
int i, len, old_used;
hda_nid_t list[HDA_MAX_CONNECTIONS];
/* look up the cached results */
for (i = 0; i < array->used; ) {
hda_nid_t *p = snd_array_elem(array, i);
len = p[1];
if (nid == *p)
return copy_conn_list(nid, conn_list, max_conns,
p + 2, len);
i += len + 2;
}
len = _hda_get_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
if (len < 0)
return len;
/* add to the cache */
old_used = array->used;
if (!add_conn_list(array, nid) || !add_conn_list(array, len))
goto error_add;
for (i = 0; i < len; i++)
if (!add_conn_list(array, list[i]))
goto error_add;
return copy_conn_list(nid, conn_list, max_conns, list, len);
error_add:
array->used = old_used;
return -ENOMEM;
}
EXPORT_SYMBOL_HDA(snd_hda_get_connections);
static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
{
unsigned int parm;
int i, conn_len, conns;
@ -417,8 +460,28 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
}
return conns;
}
EXPORT_SYMBOL_HDA(snd_hda_get_connections);
static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
{
hda_nid_t *p = snd_array_new(array);
if (!p)
return false;
*p = nid;
return true;
}
static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
hda_nid_t *src, int len)
{
if (len > max_dst) {
snd_printk(KERN_ERR "hda_codec: "
"Too many connections %d for NID 0x%x\n",
len, nid);
return -EINVAL;
}
memcpy(dst, src, len * sizeof(hda_nid_t));
return len;
}
/**
* snd_hda_queue_unsol_event - add an unsolicited event to queue
@ -1019,6 +1082,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
list_del(&codec->list);
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
snd_array_free(&codec->conn_lists);
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
@ -1079,6 +1143,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
if (codec->bus->modelname) {
codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
if (!codec->modelname) {
@ -2556,7 +2621,7 @@ static unsigned int convert_to_spdif_status(unsigned short val)
static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
int verb, int val)
{
hda_nid_t *d;
const hda_nid_t *d;
snd_hda_codec_write_cache(codec, nid, 0, verb, val);
d = codec->slave_dig_outs;
@ -3807,7 +3872,8 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
int snd_hda_add_new_ctls(struct hda_codec *codec,
const struct snd_kcontrol_new *knew)
{
int err;
@ -3950,7 +4016,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid)
{
struct hda_amp_list *p;
const struct hda_amp_list *p;
int ch, v;
if (!check->amplist)
@ -4118,7 +4184,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
-1);
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
if (codec->slave_dig_outs) {
hda_nid_t *d;
const hda_nid_t *d;
for (d = codec->slave_dig_outs; *d; d++)
snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
format);
@ -4133,7 +4199,7 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
{
snd_hda_codec_cleanup_stream(codec, nid);
if (codec->slave_dig_outs) {
hda_nid_t *d;
const hda_nid_t *d;
for (d = codec->slave_dig_outs; *d; d++)
snd_hda_codec_cleanup_stream(codec, *d);
}
@ -4280,7 +4346,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
unsigned int format,
struct snd_pcm_substream *substream)
{
hda_nid_t *nids = mout->dac_nids;
const hda_nid_t *nids = mout->dac_nids;
int chs = substream->runtime->channels;
int i;
@ -4335,7 +4401,7 @@ EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout)
{
hda_nid_t *nids = mout->dac_nids;
const hda_nid_t *nids = mout->dac_nids;
int i;
for (i = 0; i < mout->num_dacs; i++)
@ -4360,7 +4426,7 @@ EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
* Helper for automatic pin configuration
*/
static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
{
for (; *list; list++)
if (*list == nid)
@ -4441,7 +4507,7 @@ static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
*/
int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
hda_nid_t *ignore_nids)
const hda_nid_t *ignore_nids)
{
hda_nid_t nid, end_nid;
short seq, assoc_line_out, assoc_speaker;
@ -4632,10 +4698,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
/*
* debug prints of the parsed results
*/
snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
cfg->line_out_pins[2], cfg->line_out_pins[3],
cfg->line_out_pins[4]);
cfg->line_out_pins[4],
cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
(cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
"speaker" : "line"));
snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
cfg->speaker_outs, cfg->speaker_pins[0],
cfg->speaker_pins[1], cfg->speaker_pins[2],
@ -4986,6 +5055,8 @@ static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid,
return "Line-out";
case SND_JACK_HEADSET:
return "Headset";
case SND_JACK_VIDEOOUT:
return "HDMI/DP";
default:
return "Misc";
}

View File

@ -825,12 +825,14 @@ struct hda_codec {
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
struct snd_array conn_lists; /* connection-list array */
struct mutex spdif_mutex;
struct mutex control_mutex;
unsigned int spdif_status; /* IEC958 status bits */
unsigned short spdif_ctls; /* SPDIF control bits */
unsigned int spdif_in_enable; /* SPDIF input enable? */
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_array init_pins; /* initial (BIOS) pin configurations */
struct snd_array driver_pins; /* pin configs set by codec parser */
struct snd_array cvt_setups; /* audio convert setups */

View File

@ -126,6 +126,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH10},"
"{Intel, PCH},"
"{Intel, CPT},"
"{Intel, PPT},"
"{Intel, PBG},"
"{Intel, SCH},"
"{ATI, SB450},"
@ -1091,7 +1092,13 @@ static void azx_init_pci(struct azx *chip)
? "Failed" : "OK");
}
break;
default:
/* AMD Hudson needs the similar snoop, as it seems... */
if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
update_pci_byte(chip->pci,
ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
break;
}
}
@ -1446,6 +1453,17 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
}
}
/* AMD chipsets often cause the communication stalls upon certain
* sequence like the pin-detection. It seems that forcing the synced
* access works around the stall. Grrr...
*/
if (chip->pci->vendor == PCI_VENDOR_ID_AMD ||
chip->pci->vendor == PCI_VENDOR_ID_ATI) {
snd_printk(KERN_INFO SFX "Enable sync_write for AMD chipset\n");
chip->bus->sync_write = 1;
chip->bus->allow_bus_reset = 1;
}
/* Then create codec instances */
for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
@ -2349,9 +2367,16 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
/* Check VIA/ATI HD Audio Controller exist */
switch (chip->driver_type) {
case AZX_DRIVER_VIA:
case AZX_DRIVER_ATI:
/* Use link position directly, avoid any transfer problem. */
return POS_FIX_VIACOMBO;
case AZX_DRIVER_ATI:
/* ATI chipsets don't work well with position-buffer */
return POS_FIX_LPIB;
case AZX_DRIVER_GENERIC:
/* AMD chipsets also don't work with position-buffer */
if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
return POS_FIX_LPIB;
break;
}
return POS_FIX_AUTO;
@ -2549,6 +2574,13 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
gcap &= ~ICH6_GCAP_64OK;
pci_dev_put(p_smbus);
}
} else {
/* FIXME: not sure whether this is really needed, but
* Hudson isn't stable enough for allowing everything...
* let's check later again.
*/
if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
gcap &= ~ICH6_GCAP_64OK;
}
/* disable 64bit DMA address for Teradici */
@ -2759,6 +2791,8 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
/* PBG */
{ PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
/* Panther Point */
{ PCI_DEVICE(0x8086, 0x1e20), .driver_data = AZX_DRIVER_PCH },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
/* Generic Intel */

View File

@ -267,11 +267,11 @@ enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
struct hda_multi_out {
int num_dacs; /* # of DACs, must be more than 1 */
hda_nid_t *dac_nids; /* DAC list */
const hda_nid_t *dac_nids; /* DAC list */
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
hda_nid_t dig_out_nid; /* digital out audio widget */
hda_nid_t *slave_dig_outs;
const hda_nid_t *slave_dig_outs;
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
@ -347,7 +347,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
int num_configs, const char * const *models,
const struct snd_pci_quirk *tbl);
int snd_hda_add_new_ctls(struct hda_codec *codec,
struct snd_kcontrol_new *knew);
const struct snd_kcontrol_new *knew);
/*
* unsolicited event handler
@ -443,7 +443,7 @@ struct auto_pin_cfg {
int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
hda_nid_t *ignore_nids);
const hda_nid_t *ignore_nids);
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
@ -493,6 +493,12 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
{
return (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT) &&
(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP);
}
/* flags for hda_nid_item */
#define HDA_NID_ITEM_AMP (1<<0)
@ -567,7 +573,7 @@ struct hda_amp_list {
};
struct hda_loopback_check {
struct hda_amp_list *amplist;
const struct hda_amp_list *amplist;
int power_on;
};

File diff suppressed because it is too large Load Diff

View File

@ -134,7 +134,7 @@ static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
/*
*/
static char *dirstr[2] = { "Playback", "Capture" };
static const char * const dirstr[2] = { "Playback", "Capture" };
static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
int chan, int dir)
@ -171,7 +171,7 @@ static int ca0110_build_controls(struct hda_codec *codec)
{
struct ca0110_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
static char *prefix[AUTO_CFG_MAX_OUTS] = {
static const char * const prefix[AUTO_CFG_MAX_OUTS] = {
"Front", "Surround", NULL, "Side", "Multi"
};
hda_nid_t mutenid;
@ -259,7 +259,7 @@ static int ca0110_build_controls(struct hda_codec *codec)
/*
*/
static struct hda_pcm_stream ca0110_pcm_analog_playback = {
static const struct hda_pcm_stream ca0110_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
@ -270,7 +270,7 @@ static struct hda_pcm_stream ca0110_pcm_analog_playback = {
},
};
static struct hda_pcm_stream ca0110_pcm_analog_capture = {
static const struct hda_pcm_stream ca0110_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -280,7 +280,7 @@ static struct hda_pcm_stream ca0110_pcm_analog_capture = {
},
};
static struct hda_pcm_stream ca0110_pcm_digital_playback = {
static const struct hda_pcm_stream ca0110_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -291,7 +291,7 @@ static struct hda_pcm_stream ca0110_pcm_digital_playback = {
},
};
static struct hda_pcm_stream ca0110_pcm_digital_capture = {
static const struct hda_pcm_stream ca0110_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -389,7 +389,7 @@ static void ca0110_free(struct hda_codec *codec)
kfree(codec->spec);
}
static struct hda_codec_ops ca0110_patch_ops = {
static const struct hda_codec_ops ca0110_patch_ops = {
.build_controls = ca0110_build_controls,
.build_pcms = ca0110_build_pcms,
.init = ca0110_init,
@ -539,7 +539,7 @@ static int patch_ca0110(struct hda_codec *codec)
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_ca0110[] = {
static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },

View File

@ -51,7 +51,7 @@ struct cs_spec {
unsigned int cur_adc_format;
hda_nid_t dig_in;
struct hda_bind_ctls *capture_bind[2];
const struct hda_bind_ctls *capture_bind[2];
unsigned int gpio_mask;
unsigned int gpio_dir;
@ -231,7 +231,7 @@ static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
/*
*/
static struct hda_pcm_stream cs_pcm_analog_playback = {
static const struct hda_pcm_stream cs_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -242,7 +242,7 @@ static struct hda_pcm_stream cs_pcm_analog_playback = {
},
};
static struct hda_pcm_stream cs_pcm_analog_capture = {
static const struct hda_pcm_stream cs_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -252,7 +252,7 @@ static struct hda_pcm_stream cs_pcm_analog_capture = {
},
};
static struct hda_pcm_stream cs_pcm_digital_playback = {
static const struct hda_pcm_stream cs_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -264,7 +264,7 @@ static struct hda_pcm_stream cs_pcm_digital_playback = {
},
};
static struct hda_pcm_stream cs_pcm_digital_capture = {
static const struct hda_pcm_stream cs_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -331,8 +331,8 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t pin = cfg->inputs[idx].pin;
unsigned int val = snd_hda_query_pin_caps(codec, pin);
if (!(val & AC_PINCAP_PRES_DETECT))
unsigned int val;
if (!is_jack_detectable(codec, pin))
return 0;
val = snd_hda_codec_get_pincfg(codec, pin);
return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
@ -349,8 +349,7 @@ static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t pins[2];
unsigned int type;
int j, nums;
type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
>> AC_WCAP_TYPE_SHIFT;
type = get_wcaps_type(get_wcaps(codec, nid));
if (type != AC_WID_AUD_IN)
continue;
nums = snd_hda_get_connections(codec, nid, pins,
@ -559,10 +558,10 @@ static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
const char *name;
int err, index;
struct snd_kcontrol *kctl;
static char *speakers[] = {
static const char * const speakers[] = {
"Front Speaker", "Surround Speaker", "Bass Speaker"
};
static char *line_outs[] = {
static const char * const line_outs[] = {
"Front Line-Out", "Surround Line-Out", "Bass Line-Out"
};
@ -642,7 +641,7 @@ static int build_output(struct hda_codec *codec)
/*
*/
static struct snd_kcontrol_new cs_capture_ctls[] = {
static const struct snd_kcontrol_new cs_capture_ctls[] = {
HDA_BIND_SW("Capture Switch", 0),
HDA_BIND_VOL("Capture Volume", 0),
};
@ -710,7 +709,7 @@ static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
return change_cur_input(codec, idx, 0);
}
static struct snd_kcontrol_new cs_capture_source = {
static const struct snd_kcontrol_new cs_capture_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@ -719,7 +718,7 @@ static struct snd_kcontrol_new cs_capture_source = {
.put = cs_capture_source_put,
};
static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
static const struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
struct hda_ctl_ops *ops)
{
struct cs_spec *spec = codec->spec;
@ -847,15 +846,14 @@ static void cs_automute(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int caps, hp_present;
unsigned int hp_present;
hda_nid_t nid;
int i;
hp_present = 0;
for (i = 0; i < cfg->hp_outs; i++) {
nid = cfg->hp_pins[i];
caps = snd_hda_query_pin_caps(codec, nid);
if (!(caps & AC_PINCAP_PRES_DETECT))
if (!is_jack_detectable(codec, nid))
continue;
hp_present = snd_hda_jack_detect(codec, nid);
if (hp_present)
@ -924,7 +922,7 @@ static void init_output(struct hda_codec *codec)
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
if (!cfg->speaker_outs)
continue;
if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
if (is_jack_detectable(codec, nid)) {
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | HP_EVENT);
@ -983,7 +981,7 @@ static void init_input(struct hda_codec *codec)
cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
}
static struct hda_verb cs_coef_init_verbs[] = {
static const struct hda_verb cs_coef_init_verbs[] = {
{0x11, AC_VERB_SET_PROC_STATE, 1},
{0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
{0x11, AC_VERB_SET_PROC_COEF,
@ -1017,7 +1015,7 @@ static struct hda_verb cs_coef_init_verbs[] = {
* blocks, which will alleviate the issue.
*/
static struct hda_verb cs_errata_init_verbs[] = {
static const struct hda_verb cs_errata_init_verbs[] = {
{0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
{0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
@ -1126,7 +1124,7 @@ static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
}
}
static struct hda_codec_ops cs_patch_ops = {
static const struct hda_codec_ops cs_patch_ops = {
.build_controls = cs_build_controls,
.build_pcms = cs_build_pcms,
.init = cs_init,
@ -1166,7 +1164,7 @@ static const char * const cs420x_models[CS420X_MODELS] = {
};
static struct snd_pci_quirk cs420x_cfg_tbl[] = {
static const struct snd_pci_quirk cs420x_cfg_tbl[] = {
SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
@ -1180,7 +1178,7 @@ struct cs_pincfg {
u32 val;
};
static struct cs_pincfg mbp53_pincfgs[] = {
static const struct cs_pincfg mbp53_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100141 },
{ 0x0b, 0x90100140 },
@ -1194,7 +1192,7 @@ static struct cs_pincfg mbp53_pincfgs[] = {
{} /* terminator */
};
static struct cs_pincfg mbp55_pincfgs[] = {
static const struct cs_pincfg mbp55_pincfgs[] = {
{ 0x09, 0x012b4030 },
{ 0x0a, 0x90100121 },
{ 0x0b, 0x90100120 },
@ -1208,7 +1206,7 @@ static struct cs_pincfg mbp55_pincfgs[] = {
{} /* terminator */
};
static struct cs_pincfg imac27_pincfgs[] = {
static const struct cs_pincfg imac27_pincfgs[] = {
{ 0x09, 0x012b4050 },
{ 0x0a, 0x90100140 },
{ 0x0b, 0x90100142 },
@ -1222,7 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
{} /* terminator */
};
static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
[CS420X_MBP53] = mbp53_pincfgs,
[CS420X_MBP55] = mbp55_pincfgs,
[CS420X_IMAC27] = imac27_pincfgs,
@ -1283,7 +1281,7 @@ static int patch_cs420x(struct hda_codec *codec)
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_cirrus[] = {
static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
{ .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
{ .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
{} /* terminator */

View File

@ -53,7 +53,7 @@ struct cmi_spec {
int num_dacs;
/* capture */
hda_nid_t *adc_nids;
const hda_nid_t *adc_nids;
hda_nid_t dig_in_nid;
/* capture source */
@ -110,7 +110,7 @@ static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
*/
/* 3-stack / 2 channel */
static struct hda_verb cmi9880_ch2_init[] = {
static const struct hda_verb cmi9880_ch2_init[] = {
/* set line-in PIN for input */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
/* set mic PIN for input, also enable vref */
@ -121,7 +121,7 @@ static struct hda_verb cmi9880_ch2_init[] = {
};
/* 3-stack / 6 channel */
static struct hda_verb cmi9880_ch6_init[] = {
static const struct hda_verb cmi9880_ch6_init[] = {
/* set line-in PIN for output */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
/* set mic PIN for output */
@ -132,7 +132,7 @@ static struct hda_verb cmi9880_ch6_init[] = {
};
/* 3-stack+front / 8 channel */
static struct hda_verb cmi9880_ch8_init[] = {
static const struct hda_verb cmi9880_ch8_init[] = {
/* set line-in PIN for output */
{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
/* set mic PIN for output */
@ -142,7 +142,7 @@ static struct hda_verb cmi9880_ch8_init[] = {
{}
};
static struct hda_channel_mode cmi9880_channel_modes[3] = {
static const struct hda_channel_mode cmi9880_channel_modes[3] = {
{ 2, cmi9880_ch2_init },
{ 6, cmi9880_ch6_init },
{ 8, cmi9880_ch8_init },
@ -174,7 +174,7 @@ static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
/*
*/
static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
static const struct snd_kcontrol_new cmi9880_basic_mixer[] = {
/* CMI9880 has no playback volumes! */
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
@ -205,7 +205,7 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
/*
* shared I/O pins
*/
static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
static const struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@ -219,7 +219,7 @@ static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
/* AUD-in selections:
* 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
*/
static struct hda_input_mux cmi9880_basic_mux = {
static const struct hda_input_mux cmi9880_basic_mux = {
.num_items = 4,
.items = {
{ "Front Mic", 0x5 },
@ -229,7 +229,7 @@ static struct hda_input_mux cmi9880_basic_mux = {
}
};
static struct hda_input_mux cmi9880_no_line_mux = {
static const struct hda_input_mux cmi9880_no_line_mux = {
.num_items = 3,
.items = {
{ "Front Mic", 0x5 },
@ -239,11 +239,11 @@ static struct hda_input_mux cmi9880_no_line_mux = {
};
/* front, rear, clfe, rear_surr */
static hda_nid_t cmi9880_dac_nids[4] = {
static const hda_nid_t cmi9880_dac_nids[4] = {
0x03, 0x04, 0x05, 0x06
};
/* ADC0, ADC1 */
static hda_nid_t cmi9880_adc_nids[2] = {
static const hda_nid_t cmi9880_adc_nids[2] = {
0x08, 0x09
};
@ -252,7 +252,7 @@ static hda_nid_t cmi9880_adc_nids[2] = {
/*
*/
static struct hda_verb cmi9880_basic_init[] = {
static const struct hda_verb cmi9880_basic_init[] = {
/* port-D for line out (rear panel) */
{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
/* port-E for HP out (front panel) */
@ -281,7 +281,7 @@ static struct hda_verb cmi9880_basic_init[] = {
{} /* terminator */
};
static struct hda_verb cmi9880_allout_init[] = {
static const struct hda_verb cmi9880_allout_init[] = {
/* port-D for line out (rear panel) */
{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
/* port-E for HP out (front panel) */
@ -528,7 +528,7 @@ static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
/*
*/
static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
static const struct hda_pcm_stream cmi9880_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
@ -540,7 +540,7 @@ static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
},
};
static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
static const struct hda_pcm_stream cmi9880_pcm_analog_capture = {
.substreams = 2,
.channels_min = 2,
.channels_max = 2,
@ -551,7 +551,7 @@ static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
},
};
static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
static const struct hda_pcm_stream cmi9880_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -563,7 +563,7 @@ static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
},
};
static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
static const struct hda_pcm_stream cmi9880_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -617,14 +617,14 @@ static const char * const cmi9880_models[CMI_MODELS] = {
[CMI_AUTO] = "auto",
};
static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
static const struct snd_pci_quirk cmi9880_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
SND_PCI_QUIRK(0x1854, 0x002b, "LG LS75", CMI_MINIMAL),
SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG),
{} /* terminator */
};
static struct hda_codec_ops cmi9880_patch_ops = {
static const struct hda_codec_ops cmi9880_patch_ops = {
.build_controls = cmi9880_build_controls,
.build_pcms = cmi9880_build_pcms,
.init = cmi9880_init,
@ -745,7 +745,7 @@ static int patch_cmi9880(struct hda_codec *codec)
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_cmedia[] = {
static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
{} /* terminator */

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
@ -76,7 +77,7 @@ struct hdmi_spec {
* ati/nvhdmi specific
*/
struct hda_multi_out multiout;
struct hda_pcm_stream *pcm_playback;
const struct hda_pcm_stream *pcm_playback;
/* misc flags */
/* PD bit indicates only the update, not the current state */
@ -720,6 +721,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
&spec->sink_eld[index]);
/* TODO: do real things about ELD */
}
snd_hda_input_jack_report(codec, tag);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@ -912,6 +915,7 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct hdmi_spec *spec = codec->spec;
int err;
if (spec->num_pins >= MAX_HDMI_PINS) {
snd_printk(KERN_WARNING
@ -919,6 +923,12 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
return -E2BIG;
}
err = snd_hda_input_jack_add(codec, pin_nid,
SND_JACK_VIDEOOUT, NULL);
if (err < 0)
return err;
snd_hda_input_jack_report(codec, pin_nid);
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
spec->pin[spec->num_pins] = pin_nid;
@ -1044,7 +1054,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
}
static struct hda_pcm_stream generic_hdmi_pcm_playback = {
static const struct hda_pcm_stream generic_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
.ops = {
@ -1120,11 +1130,12 @@ static void generic_hdmi_free(struct hda_codec *codec)
for (i = 0; i < spec->num_pins; i++)
snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
snd_hda_input_jack_free(codec);
kfree(spec);
}
static struct hda_codec_ops generic_hdmi_patch_ops = {
static const struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
.free = generic_hdmi_free,
.build_pcms = generic_hdmi_build_pcms,
@ -1169,12 +1180,12 @@ static int patch_generic_hdmi(struct hda_codec *codec)
#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05
static hda_nid_t nvhdmi_con_nids_7x[4] = {
static const hda_nid_t nvhdmi_con_nids_7x[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
static struct hda_verb nvhdmi_basic_init_7x[] = {
static const struct hda_verb nvhdmi_basic_init_7x[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
@ -1435,7 +1446,7 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
return 0;
}
static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
@ -1450,7 +1461,7 @@ static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
},
};
static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -1465,14 +1476,14 @@ static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
},
};
static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
.build_controls = generic_hdmi_build_controls,
.build_pcms = generic_hdmi_build_pcms,
.init = nvhdmi_7x_init,
.free = generic_hdmi_free,
};
static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
static const struct hda_codec_ops nvhdmi_patch_ops_2ch = {
.build_controls = generic_hdmi_build_controls,
.build_pcms = generic_hdmi_build_pcms,
.init = nvhdmi_7x_init,
@ -1568,7 +1579,7 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
return 0;
}
static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
static const struct hda_pcm_stream atihdmi_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@ -1580,7 +1591,7 @@ static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
},
};
static struct hda_verb atihdmi_basic_init[] = {
static const struct hda_verb atihdmi_basic_init[] = {
/* enable digital output on pin widget */
{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{} /* terminator */
@ -1599,7 +1610,7 @@ static int atihdmi_init(struct hda_codec *codec)
return 0;
}
static struct hda_codec_ops atihdmi_patch_ops = {
static const struct hda_codec_ops atihdmi_patch_ops = {
.build_controls = generic_hdmi_build_controls,
.build_pcms = generic_hdmi_build_pcms,
.init = atihdmi_init,
@ -1634,7 +1645,7 @@ static int patch_atihdmi(struct hda_codec *codec)
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_hdmi[] = {
static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
@ -1677,6 +1688,7 @@ static struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
{} /* terminator */
};
@ -1722,6 +1734,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862802");
MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
MODULE_ALIAS("snd-hda-codec-id:80862806");
MODULE_ALIAS("snd-hda-codec-id:808629fb");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -130,7 +130,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol,
}
static struct snd_kcontrol_new si3054_modem_mixer[] = {
static const struct snd_kcontrol_new si3054_modem_mixer[] = {
SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
{}
@ -181,7 +181,7 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
}
static struct hda_pcm_stream si3054_pcm = {
static const struct hda_pcm_stream si3054_pcm = {
.substreams = 1,
.channels_min = 1,
.channels_max = 1,
@ -200,12 +200,13 @@ static int si3054_build_pcms(struct hda_codec *codec)
{
struct si3054_spec *spec = codec->spec;
struct hda_pcm *info = &spec->pcm;
si3054_pcm.nid = codec->mfg;
codec->num_pcms = 1;
codec->pcm_info = info;
info->name = "Si3054 Modem";
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg;
info->pcm_type = HDA_PCM_TYPE_MODEM;
return 0;
}
@ -263,7 +264,7 @@ static void si3054_free(struct hda_codec *codec)
/*
*/
static struct hda_codec_ops si3054_patch_ops = {
static const struct hda_codec_ops si3054_patch_ops = {
.build_controls = si3054_build_controls,
.build_pcms = si3054_build_pcms,
.init = si3054_init,
@ -283,7 +284,7 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
static struct hda_codec_preset snd_hda_preset_si3054[] = {
static const struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 },
};

View File

@ -0,0 +1,4 @@
snd-lola-y := lola.o lola_pcm.o lola_clock.o lola_mixer.o
snd-lola-$(CONFIG_SND_DEBUG) += lola_proc.o
obj-$(CONFIG_SND_LOLA) += snd-lola.o

View File

@ -0,0 +1,791 @@
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include "lola.h"
/* Standard options */
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram Lola driver.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Digigram Lola driver.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Digigram Lola driver.");
/* Lola-specific options */
/* for instance use always max granularity which is compatible
* with all sample rates
*/
static int granularity[SNDRV_CARDS] = {
[0 ... (SNDRV_CARDS - 1)] = LOLA_GRANULARITY_MAX
};
/* below a sample_rate of 16kHz the analogue audio quality is NOT excellent */
static int sample_rate_min[SNDRV_CARDS] = {
[0 ... (SNDRV_CARDS - 1) ] = 16000
};
module_param_array(granularity, int, NULL, 0444);
MODULE_PARM_DESC(granularity, "Granularity value");
module_param_array(sample_rate_min, int, NULL, 0444);
MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
/*
*/
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Digigram, Lola}}");
MODULE_DESCRIPTION("Digigram Lola driver");
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
#ifdef CONFIG_SND_DEBUG_VERBOSE
static int debug;
module_param(debug, int, 0644);
#define verbose_debug(fmt, args...) \
do { if (debug > 1) printk(KERN_DEBUG SFX fmt, ##args); } while (0)
#else
#define verbose_debug(fmt, args...)
#endif
/*
* pseudo-codec read/write via CORB/RIRB
*/
static int corb_send_verb(struct lola *chip, unsigned int nid,
unsigned int verb, unsigned int data,
unsigned int extdata)
{
unsigned long flags;
int ret = -EIO;
chip->last_cmd_nid = nid;
chip->last_verb = verb;
chip->last_data = data;
chip->last_extdata = extdata;
data |= (nid << 20) | (verb << 8);
spin_lock_irqsave(&chip->reg_lock, flags);
if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
unsigned int wp = chip->corb.wp + 1;
wp %= LOLA_CORB_ENTRIES;
chip->corb.wp = wp;
chip->corb.buf[wp * 2] = cpu_to_le32(data);
chip->corb.buf[wp * 2 + 1] = cpu_to_le32(extdata);
lola_writew(chip, BAR0, CORBWP, wp);
chip->rirb.cmds++;
smp_wmb();
ret = 0;
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
return ret;
}
static void lola_queue_unsol_event(struct lola *chip, unsigned int res,
unsigned int res_ex)
{
lola_update_ext_clock_freq(chip, res);
}
/* retrieve RIRB entry - called from interrupt handler */
static void lola_update_rirb(struct lola *chip)
{
unsigned int rp, wp;
u32 res, res_ex;
wp = lola_readw(chip, BAR0, RIRBWP);
if (wp == chip->rirb.wp)
return;
chip->rirb.wp = wp;
while (chip->rirb.rp != wp) {
chip->rirb.rp++;
chip->rirb.rp %= LOLA_CORB_ENTRIES;
rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
res = le32_to_cpu(chip->rirb.buf[rp]);
if (res_ex & LOLA_RIRB_EX_UNSOL_EV)
lola_queue_unsol_event(chip, res, res_ex);
else if (chip->rirb.cmds) {
chip->res = res;
chip->res_ex = res_ex;
smp_wmb();
chip->rirb.cmds--;
}
}
}
static int rirb_get_response(struct lola *chip, unsigned int *val,
unsigned int *extval)
{
unsigned long timeout;
again:
timeout = jiffies + msecs_to_jiffies(1000);
for (;;) {
if (chip->polling_mode) {
spin_lock_irq(&chip->reg_lock);
lola_update_rirb(chip);
spin_unlock_irq(&chip->reg_lock);
}
if (!chip->rirb.cmds) {
*val = chip->res;
if (extval)
*extval = chip->res_ex;
verbose_debug("get_response: %x, %x\n",
chip->res, chip->res_ex);
if (chip->res_ex & LOLA_RIRB_EX_ERROR) {
printk(KERN_WARNING SFX "RIRB ERROR: "
"NID=%x, verb=%x, data=%x, ext=%x\n",
chip->last_cmd_nid,
chip->last_verb, chip->last_data,
chip->last_extdata);
return -EIO;
}
return 0;
}
if (time_after(jiffies, timeout))
break;
udelay(20);
cond_resched();
}
printk(KERN_WARNING SFX "RIRB response error\n");
if (!chip->polling_mode) {
printk(KERN_WARNING SFX "switching to polling mode\n");
chip->polling_mode = 1;
goto again;
}
return -EIO;
}
/* aynchronous write of a codec verb with data */
int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
unsigned int data, unsigned int extdata)
{
verbose_debug("codec_write NID=%x, verb=%x, data=%x, ext=%x\n",
nid, verb, data, extdata);
return corb_send_verb(chip, nid, verb, data, extdata);
}
/* write a codec verb with data and read the returned status */
int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
unsigned int data, unsigned int extdata,
unsigned int *val, unsigned int *extval)
{
int err;
verbose_debug("codec_read NID=%x, verb=%x, data=%x, ext=%x\n",
nid, verb, data, extdata);
err = corb_send_verb(chip, nid, verb, data, extdata);
if (err < 0)
return err;
err = rirb_get_response(chip, val, extval);
return err;
}
/* flush all pending codec writes */
int lola_codec_flush(struct lola *chip)
{
unsigned int tmp;
return rirb_get_response(chip, &tmp, NULL);
}
/*
* interrupt handler
*/
static irqreturn_t lola_interrupt(int irq, void *dev_id)
{
struct lola *chip = dev_id;
unsigned int notify_ins, notify_outs, error_ins, error_outs;
int handled = 0;
int i;
notify_ins = notify_outs = error_ins = error_outs = 0;
spin_lock(&chip->reg_lock);
for (;;) {
unsigned int status, in_sts, out_sts;
unsigned int reg;
status = lola_readl(chip, BAR1, DINTSTS);
if (!status || status == -1)
break;
in_sts = lola_readl(chip, BAR1, DIINTSTS);
out_sts = lola_readl(chip, BAR1, DOINTSTS);
/* clear Input Interrupts */
for (i = 0; in_sts && i < chip->pcm[CAPT].num_streams; i++) {
if (!(in_sts & (1 << i)))
continue;
in_sts &= ~(1 << i);
reg = lola_dsd_read(chip, i, STS);
if (reg & LOLA_DSD_STS_DESE) /* error */
error_ins |= (1 << i);
if (reg & LOLA_DSD_STS_BCIS) /* notify */
notify_ins |= (1 << i);
/* clear */
lola_dsd_write(chip, i, STS, reg);
}
/* clear Output Interrupts */
for (i = 0; out_sts && i < chip->pcm[PLAY].num_streams; i++) {
if (!(out_sts & (1 << i)))
continue;
out_sts &= ~(1 << i);
reg = lola_dsd_read(chip, i + MAX_STREAM_IN_COUNT, STS);
if (reg & LOLA_DSD_STS_DESE) /* error */
error_outs |= (1 << i);
if (reg & LOLA_DSD_STS_BCIS) /* notify */
notify_outs |= (1 << i);
lola_dsd_write(chip, i + MAX_STREAM_IN_COUNT, STS, reg);
}
if (status & LOLA_DINT_CTRL) {
unsigned char rbsts; /* ring status is byte access */
rbsts = lola_readb(chip, BAR0, RIRBSTS);
rbsts &= LOLA_RIRB_INT_MASK;
if (rbsts)
lola_writeb(chip, BAR0, RIRBSTS, rbsts);
rbsts = lola_readb(chip, BAR0, CORBSTS);
rbsts &= LOLA_CORB_INT_MASK;
if (rbsts)
lola_writeb(chip, BAR0, CORBSTS, rbsts);
lola_update_rirb(chip);
}
if (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)) {
/* clear global fifo error interrupt */
lola_writel(chip, BAR1, DINTSTS,
(status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)));
}
handled = 1;
}
spin_unlock(&chip->reg_lock);
lola_pcm_update(chip, &chip->pcm[CAPT], notify_ins);
lola_pcm_update(chip, &chip->pcm[PLAY], notify_outs);
return IRQ_RETVAL(handled);
}
/*
* controller
*/
static int reset_controller(struct lola *chip)
{
unsigned int gctl = lola_readl(chip, BAR0, GCTL);
unsigned long end_time;
if (gctl) {
/* to be sure */
lola_writel(chip, BAR1, BOARD_MODE, 0);
return 0;
}
chip->cold_reset = 1;
lola_writel(chip, BAR0, GCTL, LOLA_GCTL_RESET);
end_time = jiffies + msecs_to_jiffies(200);
do {
msleep(1);
gctl = lola_readl(chip, BAR0, GCTL);
if (gctl)
break;
} while (time_before(jiffies, end_time));
if (!gctl) {
printk(KERN_ERR SFX "cannot reset controller\n");
return -EIO;
}
return 0;
}
static void lola_irq_enable(struct lola *chip)
{
unsigned int val;
/* enalbe all I/O streams */
val = (1 << chip->pcm[PLAY].num_streams) - 1;
lola_writel(chip, BAR1, DOINTCTL, val);
val = (1 << chip->pcm[CAPT].num_streams) - 1;
lola_writel(chip, BAR1, DIINTCTL, val);
/* enable global irqs */
val = LOLA_DINT_GLOBAL | LOLA_DINT_CTRL | LOLA_DINT_FIFOERR |
LOLA_DINT_MUERR;
lola_writel(chip, BAR1, DINTCTL, val);
}
static void lola_irq_disable(struct lola *chip)
{
lola_writel(chip, BAR1, DINTCTL, 0);
lola_writel(chip, BAR1, DIINTCTL, 0);
lola_writel(chip, BAR1, DOINTCTL, 0);
}
static int setup_corb_rirb(struct lola *chip)
{
int err;
unsigned char tmp;
unsigned long end_time;
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
PAGE_SIZE, &chip->rb);
if (err < 0)
return err;
chip->corb.addr = chip->rb.addr;
chip->corb.buf = (u32 *)chip->rb.area;
chip->rirb.addr = chip->rb.addr + 2048;
chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
/* disable ringbuffer DMAs */
lola_writeb(chip, BAR0, RIRBCTL, 0);
lola_writeb(chip, BAR0, CORBCTL, 0);
end_time = jiffies + msecs_to_jiffies(200);
do {
if (!lola_readb(chip, BAR0, RIRBCTL) &&
!lola_readb(chip, BAR0, CORBCTL))
break;
msleep(1);
} while (time_before(jiffies, end_time));
/* CORB set up */
lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
/* set the corb size to 256 entries */
lola_writeb(chip, BAR0, CORBSIZE, 0x02);
/* set the corb write pointer to 0 */
lola_writew(chip, BAR0, CORBWP, 0);
/* reset the corb hw read pointer */
lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
/* enable corb dma */
lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
/* clear flags if set */
tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
if (tmp)
lola_writeb(chip, BAR0, CORBSTS, tmp);
chip->corb.wp = 0;
/* RIRB set up */
lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
/* set the rirb size to 256 entries */
lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
/* reset the rirb hw write pointer */
lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
/* set N=1, get RIRB response interrupt for new entry */
lola_writew(chip, BAR0, RINTCNT, 1);
/* enable rirb dma and response irq */
lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
/* clear flags if set */
tmp = lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
if (tmp)
lola_writeb(chip, BAR0, RIRBSTS, tmp);
chip->rirb.rp = chip->rirb.cmds = 0;
return 0;
}
static void stop_corb_rirb(struct lola *chip)
{
/* disable ringbuffer DMAs */
lola_writeb(chip, BAR0, RIRBCTL, 0);
lola_writeb(chip, BAR0, CORBCTL, 0);
}
static void lola_reset_setups(struct lola *chip)
{
/* update the granularity */
lola_set_granularity(chip, chip->granularity, true);
/* update the sample clock */
lola_set_clock_index(chip, chip->clock.cur_index);
/* enable unsolicited events of the clock widget */
lola_enable_clock_events(chip);
/* update the analog gains */
lola_setup_all_analog_gains(chip, CAPT, false); /* input, update */
/* update SRC configuration if applicable */
lola_set_src_config(chip, chip->input_src_mask, false);
/* update the analog outputs */
lola_setup_all_analog_gains(chip, PLAY, false); /* output, update */
}
static int lola_parse_tree(struct lola *chip)
{
unsigned int val;
int nid, err;
err = lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read VENDOR_ID\n");
return err;
}
val >>= 16;
if (val != 0x1369) {
printk(KERN_ERR SFX "Unknown codec vendor 0x%x\n", val);
return -EINVAL;
}
err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read FUNCTION_TYPE for 0x%x\n", nid);
return err;
}
if (val != 1) {
printk(KERN_ERR SFX "Unknown function type %d\n", val);
return -EINVAL;
}
err = lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read SPECCAPS\n");
return err;
}
chip->lola_caps = val;
chip->pin[CAPT].num_pins = LOLA_AFG_INPUT_PIN_COUNT(chip->lola_caps);
chip->pin[PLAY].num_pins = LOLA_AFG_OUTPUT_PIN_COUNT(chip->lola_caps);
snd_printdd(SFX "speccaps=0x%x, pins in=%d, out=%d\n",
chip->lola_caps,
chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
if (chip->pin[CAPT].num_pins > MAX_AUDIO_INOUT_COUNT ||
chip->pin[PLAY].num_pins > MAX_AUDIO_INOUT_COUNT) {
printk(KERN_ERR SFX "Invalid Lola-spec caps 0x%x\n", val);
return -EINVAL;
}
nid = 0x02;
err = lola_init_pcm(chip, CAPT, &nid);
if (err < 0)
return err;
err = lola_init_pcm(chip, PLAY, &nid);
if (err < 0)
return err;
err = lola_init_pins(chip, CAPT, &nid);
if (err < 0)
return err;
err = lola_init_pins(chip, PLAY, &nid);
if (err < 0)
return err;
if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
err = lola_init_clock_widget(chip, nid);
if (err < 0)
return err;
nid++;
}
if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
err = lola_init_mixer_widget(chip, nid);
if (err < 0)
return err;
nid++;
}
/* enable unsolicited events of the clock widget */
err = lola_enable_clock_events(chip);
if (err < 0)
return err;
/* if last ResetController was not a ColdReset, we don't know
* the state of the card; initialize here again
*/
if (!chip->cold_reset) {
lola_reset_setups(chip);
chip->cold_reset = 1;
} else {
/* set the granularity if it is not the default */
if (chip->granularity != LOLA_GRANULARITY_MIN)
lola_set_granularity(chip, chip->granularity, true);
}
return 0;
}
static void lola_stop_hw(struct lola *chip)
{
stop_corb_rirb(chip);
lola_irq_disable(chip);
}
static void lola_free(struct lola *chip)
{
if (chip->initialized)
lola_stop_hw(chip);
lola_free_pcm(chip);
lola_free_mixer(chip);
if (chip->irq >= 0)
free_irq(chip->irq, (void *)chip);
if (chip->bar[0].remap_addr)
iounmap(chip->bar[0].remap_addr);
if (chip->bar[1].remap_addr)
iounmap(chip->bar[1].remap_addr);
if (chip->rb.area)
snd_dma_free_pages(&chip->rb);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
}
static int lola_dev_free(struct snd_device *device)
{
lola_free(device->device_data);
return 0;
}
static int __devinit lola_create(struct snd_card *card, struct pci_dev *pci,
int dev, struct lola **rchip)
{
struct lola *chip;
int err;
unsigned int dever;
static struct snd_device_ops ops = {
.dev_free = lola_dev_free,
};
*rchip = NULL;
err = pci_enable_device(pci);
if (err < 0)
return err;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip) {
snd_printk(KERN_ERR SFX "cannot allocate chip\n");
pci_disable_device(pci);
return -ENOMEM;
}
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
chip->granularity = granularity[dev];
switch (chip->granularity) {
case 8:
chip->sample_rate_max = 48000;
break;
case 16:
chip->sample_rate_max = 96000;
break;
case 32:
chip->sample_rate_max = 192000;
break;
default:
snd_printk(KERN_WARNING SFX
"Invalid granularity %d, reset to %d\n",
chip->granularity, LOLA_GRANULARITY_MAX);
chip->granularity = LOLA_GRANULARITY_MAX;
chip->sample_rate_max = 192000;
break;
}
chip->sample_rate_min = sample_rate_min[dev];
if (chip->sample_rate_min > chip->sample_rate_max) {
snd_printk(KERN_WARNING SFX
"Invalid sample_rate_min %d, reset to 16000\n",
chip->sample_rate_min);
chip->sample_rate_min = 16000;
}
err = pci_request_regions(pci, DRVNAME);
if (err < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->bar[0].addr = pci_resource_start(pci, 0);
chip->bar[0].remap_addr = pci_ioremap_bar(pci, 0);
chip->bar[1].addr = pci_resource_start(pci, 2);
chip->bar[1].remap_addr = pci_ioremap_bar(pci, 2);
if (!chip->bar[0].remap_addr || !chip->bar[1].remap_addr) {
snd_printk(KERN_ERR SFX "ioremap error\n");
err = -ENXIO;
goto errout;
}
pci_set_master(pci);
err = reset_controller(chip);
if (err < 0)
goto errout;
if (request_irq(pci->irq, lola_interrupt, IRQF_SHARED,
DRVNAME, chip)) {
printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
err = -EBUSY;
goto errout;
}
chip->irq = pci->irq;
synchronize_irq(chip->irq);
dever = lola_readl(chip, BAR1, DEVER);
chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
chip->pcm[PLAY].num_streams = (dever >> 10) & 0x3ff;
chip->version = (dever >> 24) & 0xff;
snd_printdd(SFX "streams in=%d, out=%d, version=0x%x\n",
chip->pcm[CAPT].num_streams, chip->pcm[PLAY].num_streams,
chip->version);
/* Test LOLA_BAR1_DEVER */
if (chip->pcm[CAPT].num_streams > MAX_STREAM_IN_COUNT ||
chip->pcm[PLAY].num_streams > MAX_STREAM_OUT_COUNT ||
(!chip->pcm[CAPT].num_streams &&
!chip->pcm[PLAY].num_streams)) {
printk(KERN_ERR SFX "invalid DEVER = %x\n", dever);
err = -EINVAL;
goto errout;
}
err = setup_corb_rirb(chip);
if (err < 0)
goto errout;
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
goto errout;
}
strcpy(card->driver, "Lola");
strlcpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
card->shortname, chip->bar[0].addr, chip->irq);
strcpy(card->mixername, card->shortname);
lola_irq_enable(chip);
chip->initialized = 1;
*rchip = chip;
return 0;
errout:
lola_free(chip);
return err;
}
static int __devinit lola_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct lola *chip;
int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
if (err < 0) {
snd_printk(KERN_ERR SFX "Error creating card!\n");
return err;
}
snd_card_set_dev(card, &pci->dev);
err = lola_create(card, pci, dev, &chip);
if (err < 0)
goto out_free;
card->private_data = chip;
err = lola_parse_tree(chip);
if (err < 0)
goto out_free;
err = lola_create_pcm(chip);
if (err < 0)
goto out_free;
err = lola_create_mixer(chip);
if (err < 0)
goto out_free;
lola_proc_debug_new(chip);
err = snd_card_register(card);
if (err < 0)
goto out_free;
pci_set_drvdata(pci, card);
dev++;
return err;
out_free:
snd_card_free(card);
return err;
}
static void __devexit lola_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
/* PCI IDs */
static DEFINE_PCI_DEVICE_TABLE(lola_ids) = {
{ PCI_VDEVICE(DIGIGRAM, 0x0001) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, lola_ids);
/* pci_driver definition */
static struct pci_driver driver = {
.name = DRVNAME,
.id_table = lola_ids,
.probe = lola_probe,
.remove = __devexit_p(lola_remove),
};
static int __init alsa_card_lola_init(void)
{
return pci_register_driver(&driver);
}
static void __exit alsa_card_lola_exit(void)
{
pci_unregister_driver(&driver);
}
module_init(alsa_card_lola_init)
module_exit(alsa_card_lola_exit)

View File

@ -0,0 +1,527 @@
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _LOLA_H
#define _LOLA_H
#define DRVNAME "snd-lola"
#define SFX DRVNAME ": "
/*
* Lola HD Audio Registers BAR0
*/
#define LOLA_BAR0_GCAP 0x00
#define LOLA_BAR0_VMIN 0x02
#define LOLA_BAR0_VMAJ 0x03
#define LOLA_BAR0_OUTPAY 0x04
#define LOLA_BAR0_INPAY 0x06
#define LOLA_BAR0_GCTL 0x08
#define LOLA_BAR0_WAKEEN 0x0c
#define LOLA_BAR0_STATESTS 0x0e
#define LOLA_BAR0_GSTS 0x10
#define LOLA_BAR0_OUTSTRMPAY 0x18
#define LOLA_BAR0_INSTRMPAY 0x1a
#define LOLA_BAR0_INTCTL 0x20
#define LOLA_BAR0_INTSTS 0x24
#define LOLA_BAR0_WALCLK 0x30
#define LOLA_BAR0_SSYNC 0x38
#define LOLA_BAR0_CORBLBASE 0x40
#define LOLA_BAR0_CORBUBASE 0x44
#define LOLA_BAR0_CORBWP 0x48 /* no ULONG access */
#define LOLA_BAR0_CORBRP 0x4a /* no ULONG access */
#define LOLA_BAR0_CORBCTL 0x4c /* no ULONG access */
#define LOLA_BAR0_CORBSTS 0x4d /* UCHAR access only */
#define LOLA_BAR0_CORBSIZE 0x4e /* no ULONG access */
#define LOLA_BAR0_RIRBLBASE 0x50
#define LOLA_BAR0_RIRBUBASE 0x54
#define LOLA_BAR0_RIRBWP 0x58
#define LOLA_BAR0_RINTCNT 0x5a /* no ULONG access */
#define LOLA_BAR0_RIRBCTL 0x5c
#define LOLA_BAR0_RIRBSTS 0x5d /* UCHAR access only */
#define LOLA_BAR0_RIRBSIZE 0x5e /* no ULONG access */
#define LOLA_BAR0_ICW 0x60
#define LOLA_BAR0_IRR 0x64
#define LOLA_BAR0_ICS 0x68
#define LOLA_BAR0_DPLBASE 0x70
#define LOLA_BAR0_DPUBASE 0x74
/* stream register offsets from stream base 0x80 */
#define LOLA_BAR0_SD0_OFFSET 0x80
#define LOLA_REG0_SD_CTL 0x00
#define LOLA_REG0_SD_STS 0x03
#define LOLA_REG0_SD_LPIB 0x04
#define LOLA_REG0_SD_CBL 0x08
#define LOLA_REG0_SD_LVI 0x0c
#define LOLA_REG0_SD_FIFOW 0x0e
#define LOLA_REG0_SD_FIFOSIZE 0x10
#define LOLA_REG0_SD_FORMAT 0x12
#define LOLA_REG0_SD_BDLPL 0x18
#define LOLA_REG0_SD_BDLPU 0x1c
/*
* Lola Digigram Registers BAR1
*/
#define LOLA_BAR1_FPGAVER 0x00
#define LOLA_BAR1_DEVER 0x04
#define LOLA_BAR1_UCBMV 0x08
#define LOLA_BAR1_JTAG 0x0c
#define LOLA_BAR1_UARTRX 0x10
#define LOLA_BAR1_UARTTX 0x14
#define LOLA_BAR1_UARTCR 0x18
#define LOLA_BAR1_NVRAMVER 0x1c
#define LOLA_BAR1_CTRLSPI 0x20
#define LOLA_BAR1_DSPI 0x24
#define LOLA_BAR1_AISPI 0x28
#define LOLA_BAR1_GRAN 0x2c
#define LOLA_BAR1_DINTCTL 0x80
#define LOLA_BAR1_DIINTCTL 0x84
#define LOLA_BAR1_DOINTCTL 0x88
#define LOLA_BAR1_LRC 0x90
#define LOLA_BAR1_DINTSTS 0x94
#define LOLA_BAR1_DIINTSTS 0x98
#define LOLA_BAR1_DOINTSTS 0x9c
#define LOLA_BAR1_DSD0_OFFSET 0xa0
#define LOLA_BAR1_DSD_SIZE 0x18
#define LOLA_BAR1_DSDnSTS 0x00
#define LOLA_BAR1_DSDnLPIB 0x04
#define LOLA_BAR1_DSDnCTL 0x08
#define LOLA_BAR1_DSDnLVI 0x0c
#define LOLA_BAR1_DSDnBDPL 0x10
#define LOLA_BAR1_DSDnBDPU 0x14
#define LOLA_BAR1_SSYNC 0x03e8
#define LOLA_BAR1_BOARD_CTRL 0x0f00
#define LOLA_BAR1_BOARD_MODE 0x0f02
#define LOLA_BAR1_SOURCE_GAIN_ENABLE 0x1000
#define LOLA_BAR1_DEST00_MIX_GAIN_ENABLE 0x1004
#define LOLA_BAR1_DEST31_MIX_GAIN_ENABLE 0x1080
#define LOLA_BAR1_SOURCE00_01_GAIN 0x1084
#define LOLA_BAR1_SOURCE30_31_GAIN 0x10c0
#define LOLA_BAR1_SOURCE_GAIN(src) \
(LOLA_BAR1_SOURCE00_01_GAIN + (src) * 2)
#define LOLA_BAR1_DEST00_MIX00_01_GAIN 0x10c4
#define LOLA_BAR1_DEST00_MIX30_31_GAIN 0x1100
#define LOLA_BAR1_DEST01_MIX00_01_GAIN 0x1104
#define LOLA_BAR1_DEST01_MIX30_31_GAIN 0x1140
#define LOLA_BAR1_DEST31_MIX00_01_GAIN 0x1884
#define LOLA_BAR1_DEST31_MIX30_31_GAIN 0x18c0
#define LOLA_BAR1_MIX_GAIN(dest, mix) \
(LOLA_BAR1_DEST00_MIX00_01_GAIN + (dest) * 0x40 + (mix) * 2)
#define LOLA_BAR1_ANALOG_CLIP_IN 0x18c4
#define LOLA_BAR1_PEAKMETERS_SOURCE00_01 0x18c8
#define LOLA_BAR1_PEAKMETERS_SOURCE30_31 0x1904
#define LOLA_BAR1_PEAKMETERS_SOURCE(src) \
(LOLA_BAR1_PEAKMETERS_SOURCE00_01 + (src) * 2)
#define LOLA_BAR1_PEAKMETERS_DEST00_01 0x1908
#define LOLA_BAR1_PEAKMETERS_DEST30_31 0x1944
#define LOLA_BAR1_PEAKMETERS_DEST(dest) \
(LOLA_BAR1_PEAKMETERS_DEST00_01 + (dest) * 2)
#define LOLA_BAR1_PEAKMETERS_AGC00_01 0x1948
#define LOLA_BAR1_PEAKMETERS_AGC14_15 0x1964
#define LOLA_BAR1_PEAKMETERS_AGC(x) \
(LOLA_BAR1_PEAKMETERS_AGC00_01 + (x) * 2)
/* GCTL reset bit */
#define LOLA_GCTL_RESET (1 << 0)
/* GCTL unsolicited response enable bit */
#define LOLA_GCTL_UREN (1 << 8)
/* CORB/RIRB control, read/write pointer */
#define LOLA_RBCTL_DMA_EN 0x02 /* enable DMA */
#define LOLA_RBCTL_IRQ_EN 0x01 /* enable IRQ */
#define LOLA_RBRWP_CLR 0x8000 /* read/write pointer clear */
#define LOLA_RIRB_EX_UNSOL_EV 0x40000000
#define LOLA_RIRB_EX_ERROR 0x80000000
/* CORB int mask: CMEI[0] */
#define LOLA_CORB_INT_CMEI 0x01
#define LOLA_CORB_INT_MASK LOLA_CORB_INT_CMEI
/* RIRB int mask: overrun[2], response[0] */
#define LOLA_RIRB_INT_RESPONSE 0x01
#define LOLA_RIRB_INT_OVERRUN 0x04
#define LOLA_RIRB_INT_MASK (LOLA_RIRB_INT_RESPONSE | LOLA_RIRB_INT_OVERRUN)
/* DINTCTL and DINTSTS */
#define LOLA_DINT_GLOBAL 0x80000000 /* global interrupt enable bit */
#define LOLA_DINT_CTRL 0x40000000 /* controller interrupt enable bit */
#define LOLA_DINT_FIFOERR 0x20000000 /* global fifo error enable bit */
#define LOLA_DINT_MUERR 0x10000000 /* global microcontroller underrun error */
/* DSDnCTL bits */
#define LOLA_DSD_CTL_SRST 0x01 /* stream reset bit */
#define LOLA_DSD_CTL_SRUN 0x02 /* stream DMA start bit */
#define LOLA_DSD_CTL_IOCE 0x04 /* interrupt on completion enable */
#define LOLA_DSD_CTL_DEIE 0x10 /* descriptor error interrupt enable */
#define LOLA_DSD_CTL_VLRCV 0x20 /* valid LRCountValue information in bits 8..31 */
#define LOLA_LRC_MASK 0xffffff00
/* DSDnSTS */
#define LOLA_DSD_STS_BCIS 0x04 /* buffer completion interrupt status */
#define LOLA_DSD_STS_DESE 0x10 /* descriptor error interrupt */
#define LOLA_DSD_STS_FIFORDY 0x20 /* fifo ready */
#define LOLA_CORB_ENTRIES 256
#define MAX_STREAM_IN_COUNT 16
#define MAX_STREAM_OUT_COUNT 16
#define MAX_STREAM_COUNT 16
#define MAX_PINS MAX_STREAM_COUNT
#define MAX_STREAM_BUFFER_COUNT 16
#define MAX_AUDIO_INOUT_COUNT 16
#define LOLA_CLOCK_TYPE_INTERNAL 0
#define LOLA_CLOCK_TYPE_AES 1
#define LOLA_CLOCK_TYPE_AES_SYNC 2
#define LOLA_CLOCK_TYPE_WORDCLOCK 3
#define LOLA_CLOCK_TYPE_ETHERSOUND 4
#define LOLA_CLOCK_TYPE_VIDEO 5
#define LOLA_CLOCK_FORMAT_NONE 0
#define LOLA_CLOCK_FORMAT_NTSC 1
#define LOLA_CLOCK_FORMAT_PAL 2
#define MAX_SAMPLE_CLOCK_COUNT 48
/* parameters used with mixer widget's mixer capabilities */
#define LOLA_PEAK_METER_CAN_AGC_MASK 1
#define LOLA_PEAK_METER_CAN_ANALOG_CLIP_MASK 2
struct lola_bar {
unsigned long addr;
void __iomem *remap_addr;
};
/* CORB/RIRB */
struct lola_rb {
u32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
dma_addr_t addr; /* physical address of CORB/RIRB buffer */
unsigned short rp, wp; /* read/write pointers */
int cmds; /* number of pending requests */
};
/* Pin widget setup */
struct lola_pin {
unsigned int nid;
bool is_analog;
unsigned int amp_mute;
unsigned int amp_step_size;
unsigned int amp_num_steps;
unsigned int amp_offset;
unsigned int max_level;
unsigned int config_default_reg;
unsigned int fixed_gain_list_len;
unsigned int cur_gain_step;
};
struct lola_pin_array {
unsigned int num_pins;
unsigned int num_analog_pins;
struct lola_pin pins[MAX_PINS];
};
/* Clock widget setup */
struct lola_sample_clock {
unsigned int type;
unsigned int format;
unsigned int freq;
};
struct lola_clock_widget {
unsigned int nid;
unsigned int items;
unsigned int cur_index;
unsigned int cur_freq;
bool cur_valid;
struct lola_sample_clock sample_clock[MAX_SAMPLE_CLOCK_COUNT];
unsigned int idx_lookup[MAX_SAMPLE_CLOCK_COUNT];
};
#define LOLA_MIXER_DIM 32
struct lola_mixer_array {
u32 src_gain_enable;
u32 dest_mix_gain_enable[LOLA_MIXER_DIM];
u16 src_gain[LOLA_MIXER_DIM];
u16 dest_mix_gain[LOLA_MIXER_DIM][LOLA_MIXER_DIM];
};
/* Mixer widget setup */
struct lola_mixer_widget {
unsigned int nid;
unsigned int caps;
struct lola_mixer_array __user *array;
struct lola_mixer_array *array_saved;
unsigned int src_stream_outs;
unsigned int src_phys_ins;
unsigned int dest_stream_ins;
unsigned int dest_phys_outs;
unsigned int src_stream_out_ofs;
unsigned int dest_phys_out_ofs;
unsigned int src_mask;
unsigned int dest_mask;
};
/* Audio stream */
struct lola_stream {
unsigned int nid; /* audio widget NID */
unsigned int index; /* array index */
unsigned int dsd; /* DSD index */
bool can_float;
struct snd_pcm_substream *substream; /* assigned PCM substream */
struct lola_stream *master; /* master stream (for multi-channel) */
/* buffer setup */
unsigned int bufsize;
unsigned int period_bytes;
unsigned int frags;
/* format + channel setup */
unsigned int format_verb;
/* flags */
unsigned int opened:1;
unsigned int prepared:1;
unsigned int paused:1;
unsigned int running:1;
};
#define PLAY SNDRV_PCM_STREAM_PLAYBACK
#define CAPT SNDRV_PCM_STREAM_CAPTURE
struct lola_pcm {
unsigned int num_streams;
struct snd_dma_buffer bdl; /* BDL buffer */
struct lola_stream streams[MAX_STREAM_COUNT];
};
/* card instance */
struct lola {
struct snd_card *card;
struct pci_dev *pci;
/* pci resources */
struct lola_bar bar[2];
int irq;
/* locks */
spinlock_t reg_lock;
struct mutex open_mutex;
/* CORB/RIRB */
struct lola_rb corb;
struct lola_rb rirb;
unsigned int res, res_ex; /* last read values */
/* last command (for debugging) */
unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
/* CORB/RIRB buffers */
struct snd_dma_buffer rb;
/* unsolicited events */
unsigned int last_unsol_res;
/* streams */
struct lola_pcm pcm[2];
/* input src */
unsigned int input_src_caps_mask;
unsigned int input_src_mask;
/* pins */
struct lola_pin_array pin[2];
/* clock */
struct lola_clock_widget clock;
int ref_count_rate;
unsigned int sample_rate;
/* mixer */
struct lola_mixer_widget mixer;
/* hw info */
unsigned int version;
unsigned int lola_caps;
/* parameters */
unsigned int granularity;
unsigned int sample_rate_min;
unsigned int sample_rate_max;
/* flags */
unsigned int initialized:1;
unsigned int cold_reset:1;
unsigned int polling_mode:1;
/* for debugging */
unsigned int debug_res;
unsigned int debug_res_ex;
};
#define BAR0 0
#define BAR1 1
/* Helper macros */
#define lola_readl(chip, idx, name) \
readl((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
#define lola_readw(chip, idx, name) \
readw((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
#define lola_readb(chip, idx, name) \
readb((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
#define lola_writel(chip, idx, name, val) \
writel((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
#define lola_writew(chip, idx, name, val) \
writew((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
#define lola_writeb(chip, idx, name, val) \
writeb((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
#define lola_dsd_read(chip, dsd, name) \
readl((chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
(LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
#define lola_dsd_write(chip, dsd, name, val) \
writel((val), (chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
(LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
/* GET verbs HDAudio */
#define LOLA_VERB_GET_STREAM_FORMAT 0xa00
#define LOLA_VERB_GET_AMP_GAIN_MUTE 0xb00
#define LOLA_VERB_PARAMETERS 0xf00
#define LOLA_VERB_GET_POWER_STATE 0xf05
#define LOLA_VERB_GET_CONV 0xf06
#define LOLA_VERB_GET_UNSOLICITED_RESPONSE 0xf08
#define LOLA_VERB_GET_DIGI_CONVERT_1 0xf0d
#define LOLA_VERB_GET_CONFIG_DEFAULT 0xf1c
#define LOLA_VERB_GET_SUBSYSTEM_ID 0xf20
/* GET verbs Digigram */
#define LOLA_VERB_GET_FIXED_GAIN 0xfc0
#define LOLA_VERB_GET_GAIN_SELECT 0xfc1
#define LOLA_VERB_GET_MAX_LEVEL 0xfc2
#define LOLA_VERB_GET_CLOCK_LIST 0xfc3
#define LOLA_VERB_GET_CLOCK_SELECT 0xfc4
#define LOLA_VERB_GET_CLOCK_STATUS 0xfc5
/* SET verbs HDAudio */
#define LOLA_VERB_SET_STREAM_FORMAT 0x200
#define LOLA_VERB_SET_AMP_GAIN_MUTE 0x300
#define LOLA_VERB_SET_POWER_STATE 0x705
#define LOLA_VERB_SET_CHANNEL_STREAMID 0x706
#define LOLA_VERB_SET_UNSOLICITED_ENABLE 0x708
#define LOLA_VERB_SET_DIGI_CONVERT_1 0x70d
/* SET verbs Digigram */
#define LOLA_VERB_SET_GAIN_SELECT 0xf81
#define LOLA_VERB_SET_CLOCK_SELECT 0xf84
#define LOLA_VERB_SET_GRANULARITY_STEPS 0xf86
#define LOLA_VERB_SET_SOURCE_GAIN 0xf87
#define LOLA_VERB_SET_MIX_GAIN 0xf88
#define LOLA_VERB_SET_DESTINATION_GAIN 0xf89
#define LOLA_VERB_SET_SRC 0xf8a
/* Parameter IDs used with LOLA_VERB_PARAMETERS */
#define LOLA_PAR_VENDOR_ID 0x00
#define LOLA_PAR_FUNCTION_TYPE 0x05
#define LOLA_PAR_AUDIO_WIDGET_CAP 0x09
#define LOLA_PAR_PCM 0x0a
#define LOLA_PAR_STREAM_FORMATS 0x0b
#define LOLA_PAR_PIN_CAP 0x0c
#define LOLA_PAR_AMP_IN_CAP 0x0d
#define LOLA_PAR_CONNLIST_LEN 0x0e
#define LOLA_PAR_POWER_STATE 0x0f
#define LOLA_PAR_GPIO_CAP 0x11
#define LOLA_PAR_AMP_OUT_CAP 0x12
#define LOLA_PAR_SPECIFIC_CAPS 0x80
#define LOLA_PAR_FIXED_GAIN_LIST 0x81
/* extract results of LOLA_PAR_SPECIFIC_CAPS */
#define LOLA_AFG_MIXER_WIDGET_PRESENT(res) ((res & (1 << 21)) != 0)
#define LOLA_AFG_CLOCK_WIDGET_PRESENT(res) ((res & (1 << 20)) != 0)
#define LOLA_AFG_INPUT_PIN_COUNT(res) ((res >> 10) & 0x2ff)
#define LOLA_AFG_OUTPUT_PIN_COUNT(res) ((res) & 0x2ff)
/* extract results of LOLA_PAR_AMP_IN_CAP / LOLA_PAR_AMP_OUT_CAP */
#define LOLA_AMP_MUTE_CAPABLE(res) ((res & (1 << 31)) != 0)
#define LOLA_AMP_STEP_SIZE(res) ((res >> 24) & 0x7f)
#define LOLA_AMP_NUM_STEPS(res) ((res >> 12) & 0x3ff)
#define LOLA_AMP_OFFSET(res) ((res) & 0x3ff)
#define LOLA_GRANULARITY_MIN 8
#define LOLA_GRANULARITY_MAX 32
#define LOLA_GRANULARITY_STEP 8
/* parameters used with unsolicited command/response */
#define LOLA_UNSOLICITED_TAG_MASK 0x3f
#define LOLA_UNSOLICITED_TAG 0x1a
#define LOLA_UNSOLICITED_ENABLE 0x80
#define LOLA_UNSOL_RESP_TAG_OFFSET 26
/* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */
#define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f)
#define LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(res) ((res >> 7) & 0x1f)
int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
unsigned int data, unsigned int extdata);
int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
unsigned int data, unsigned int extdata,
unsigned int *val, unsigned int *extval);
int lola_codec_flush(struct lola *chip);
#define lola_read_param(chip, nid, param, val) \
lola_codec_read(chip, nid, LOLA_VERB_PARAMETERS, param, 0, val, NULL)
/* PCM */
int lola_create_pcm(struct lola *chip);
void lola_free_pcm(struct lola *chip);
int lola_init_pcm(struct lola *chip, int dir, int *nidp);
void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits);
/* clock */
int lola_init_clock_widget(struct lola *chip, int nid);
int lola_set_granularity(struct lola *chip, unsigned int val, bool force);
int lola_enable_clock_events(struct lola *chip);
int lola_set_clock_index(struct lola *chip, unsigned int idx);
int lola_set_clock(struct lola *chip, int idx);
int lola_set_sample_rate(struct lola *chip, int rate);
bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val);
unsigned int lola_sample_rate_convert(unsigned int coded);
/* mixer */
int lola_init_pins(struct lola *chip, int dir, int *nidp);
int lola_init_mixer_widget(struct lola *chip, int nid);
void lola_free_mixer(struct lola *chip);
int lola_create_mixer(struct lola *chip);
int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute);
void lola_save_mixer(struct lola *chip);
void lola_restore_mixer(struct lola *chip);
int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update);
/* proc */
#ifdef CONFIG_SND_DEBUG
void lola_proc_debug_new(struct lola *chip);
#else
#define lola_proc_debug_new(chip)
#endif
#endif /* _LOLA_H */

View File

@ -0,0 +1,323 @@
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "lola.h"
unsigned int lola_sample_rate_convert(unsigned int coded)
{
unsigned int freq;
/* base frequency */
switch (coded & 0x3) {
case 0: freq = 48000; break;
case 1: freq = 44100; break;
case 2: freq = 32000; break;
default: return 0; /* error */
}
/* multiplier / devisor */
switch (coded & 0x1c) {
case (0 << 2): break;
case (4 << 2): break;
case (1 << 2): freq *= 2; break;
case (2 << 2): freq *= 4; break;
case (5 << 2): freq /= 2; break;
case (6 << 2): freq /= 4; break;
default: return 0; /* error */
}
/* ajustement */
switch (coded & 0x60) {
case (0 << 5): break;
case (1 << 5): freq = (freq * 999) / 1000; break;
case (2 << 5): freq = (freq * 1001) / 1000; break;
default: return 0; /* error */
}
return freq;
}
/*
* Granualrity
*/
#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000
#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000
static bool check_gran_clock_compatibility(struct lola *chip,
unsigned int val,
unsigned int freq)
{
if (!chip->granularity)
return true;
if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
(val % LOLA_GRANULARITY_STEP) != 0)
return false;
if (val == LOLA_GRANULARITY_MIN) {
if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
return false;
} else if (val < LOLA_GRANULARITY_MAX) {
if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
return false;
}
return true;
}
int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
{
int err;
if (!force) {
if (val == chip->granularity)
return 0;
#if 0
/* change Gran only if there are no streams allocated ! */
if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
return -EBUSY;
#endif
if (!check_gran_clock_compatibility(chip, val,
chip->clock.cur_freq))
return -EINVAL;
}
chip->granularity = val;
val /= LOLA_GRANULARITY_STEP;
/* audio function group */
err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
val, 0);
if (err < 0)
return err;
/* this can be a very slow function !!! */
usleep_range(400 * val, 20000);
return lola_codec_flush(chip);
}
/*
* Clock widget handling
*/
int __devinit lola_init_clock_widget(struct lola *chip, int nid)
{
unsigned int val;
int i, j, nitems, nb_verbs, idx, idx_list;
int err;
err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
return err;
}
if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
snd_printdd("No valid clock widget\n");
return 0;
}
chip->clock.nid = nid;
chip->clock.items = val & 0xff;
snd_printdd("clock_list nid=%x, entries=%d\n", nid,
chip->clock.items);
if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
printk(KERN_ERR SFX "CLOCK_LIST too big: %d\n",
chip->clock.items);
return -EINVAL;
}
nitems = chip->clock.items;
nb_verbs = (nitems + 3) / 4;
idx = 0;
idx_list = 0;
for (i = 0; i < nb_verbs; i++) {
unsigned int res_ex;
unsigned short items[4];
err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
idx, 0, &val, &res_ex);
if (err < 0) {
printk(KERN_ERR SFX "Can't read CLOCK_LIST\n");
return -EINVAL;
}
items[0] = val & 0xfff;
items[1] = (val >> 16) & 0xfff;
items[2] = res_ex & 0xfff;
items[3] = (res_ex >> 16) & 0xfff;
for (j = 0; j < 4; j++) {
unsigned char type = items[j] >> 8;
unsigned int freq = items[j] & 0xff;
int format = LOLA_CLOCK_FORMAT_NONE;
bool add_clock = true;
if (type == LOLA_CLOCK_TYPE_INTERNAL) {
freq = lola_sample_rate_convert(freq);
if (freq < chip->sample_rate_min)
add_clock = false;
else if (freq == 48000) {
chip->clock.cur_index = idx_list;
chip->clock.cur_freq = 48000;
chip->clock.cur_valid = true;
}
} else if (type == LOLA_CLOCK_TYPE_VIDEO) {
freq = lola_sample_rate_convert(freq);
if (freq < chip->sample_rate_min)
add_clock = false;
/* video clock has a format (0:NTSC, 1:PAL)*/
if (items[j] & 0x80)
format = LOLA_CLOCK_FORMAT_NTSC;
else
format = LOLA_CLOCK_FORMAT_PAL;
}
if (add_clock) {
struct lola_sample_clock *sc;
sc = &chip->clock.sample_clock[idx_list];
sc->type = type;
sc->format = format;
sc->freq = freq;
/* keep the index used with the board */
chip->clock.idx_lookup[idx_list] = idx;
idx_list++;
} else {
chip->clock.items--;
}
if (++idx >= nitems)
break;
}
}
return 0;
}
/* enable unsolicited events of the clock widget */
int lola_enable_clock_events(struct lola *chip)
{
unsigned int res;
int err;
err = lola_codec_read(chip, chip->clock.nid,
LOLA_VERB_SET_UNSOLICITED_ENABLE,
LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
0, &res, NULL);
if (err < 0)
return err;
if (res) {
printk(KERN_WARNING SFX "error in enable_clock_events %d\n",
res);
return -EINVAL;
}
return 0;
}
int lola_set_clock_index(struct lola *chip, unsigned int idx)
{
unsigned int res;
int err;
err = lola_codec_read(chip, chip->clock.nid,
LOLA_VERB_SET_CLOCK_SELECT,
chip->clock.idx_lookup[idx],
0, &res, NULL);
if (err < 0)
return err;
if (res) {
printk(KERN_WARNING SFX "error in set_clock %d\n", res);
return -EINVAL;
}
return 0;
}
bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
{
unsigned int tag;
/* the current EXTERNAL clock information gets updated by interrupt
* with an unsolicited response
*/
if (!val)
return false;
tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
if (tag != LOLA_UNSOLICITED_TAG)
return false;
/* only for current = external clocks */
if (chip->clock.sample_clock[chip->clock.cur_index].type !=
LOLA_CLOCK_TYPE_INTERNAL) {
chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
chip->clock.cur_valid = (val & 0x100) != 0;
}
return true;
}
int lola_set_clock(struct lola *chip, int idx)
{
int freq = 0;
bool valid = false;
if (idx == chip->clock.cur_index) {
/* current clock is allowed */
freq = chip->clock.cur_freq;
valid = chip->clock.cur_valid;
} else if (chip->clock.sample_clock[idx].type ==
LOLA_CLOCK_TYPE_INTERNAL) {
/* internal clocks allowed */
freq = chip->clock.sample_clock[idx].freq;
valid = true;
}
if (!freq || !valid)
return -EINVAL;
if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
return -EINVAL;
if (idx != chip->clock.cur_index) {
int err = lola_set_clock_index(chip, idx);
if (err < 0)
return err;
/* update new settings */
chip->clock.cur_index = idx;
chip->clock.cur_freq = freq;
chip->clock.cur_valid = true;
}
return 0;
}
int lola_set_sample_rate(struct lola *chip, int rate)
{
int i;
if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
return 0;
/* search for new dwClockIndex */
for (i = 0; i < chip->clock.items; i++) {
if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
chip->clock.sample_clock[i].freq == rate)
break;
}
if (i >= chip->clock.items)
return -EINVAL;
return lola_set_clock(chip, i);
}

View File

@ -0,0 +1,839 @@
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "lola.h"
static int __devinit lola_init_pin(struct lola *chip, struct lola_pin *pin,
int dir, int nid)
{
unsigned int val;
int err;
pin->nid = nid;
err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
return err;
}
val &= 0x00f00fff; /* test TYPE and bits 0..11 */
if (val == 0x00400200) /* Type = 4, Digital = 1 */
pin->is_analog = false;
else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
pin->is_analog = true;
else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
pin->is_analog = true;
else {
printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", val, nid);
return -EINVAL;
}
/* analog parameters only following, so continue in case of Digital pin
*/
if (!pin->is_analog)
return 0;
if (dir == PLAY)
err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
else
err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read AMP-caps for 0x%x\n", nid);
return err;
}
pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
if (pin->amp_num_steps) {
/* zero as mute state */
pin->amp_num_steps++;
pin->amp_step_size++;
}
pin->amp_offset = LOLA_AMP_OFFSET(val);
err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
NULL);
if (err < 0) {
printk(KERN_ERR SFX "Can't get MAX_LEVEL 0x%x\n", nid);
return err;
}
pin->max_level = val & 0x3ff; /* 10 bits */
pin->config_default_reg = 0;
pin->fixed_gain_list_len = 0;
pin->cur_gain_step = 0;
return 0;
}
int __devinit lola_init_pins(struct lola *chip, int dir, int *nidp)
{
int i, err, nid;
nid = *nidp;
for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
if (err < 0)
return err;
if (chip->pin[dir].pins[i].is_analog)
chip->pin[dir].num_analog_pins++;
}
*nidp = nid;
return 0;
}
void lola_free_mixer(struct lola *chip)
{
if (chip->mixer.array_saved)
vfree(chip->mixer.array_saved);
}
int __devinit lola_init_mixer_widget(struct lola *chip, int nid)
{
unsigned int val;
int err;
err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
return err;
}
if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
snd_printdd("No valid mixer widget\n");
return 0;
}
chip->mixer.nid = nid;
chip->mixer.caps = val;
chip->mixer.array = (struct lola_mixer_array __iomem *)
(chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
/* reserve memory to copy mixer data for sleep mode transitions */
chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
/* mixer matrix sources are physical input data and play streams */
chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
/* mixer matrix destinations are record streams and physical output */
chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
/* mixer matrix can have unused areas between PhysIn and
* Play or Record and PhysOut zones
*/
chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
LOLA_MIXER_DEST_REC_OUTPUT_SEPATATION(val);
/* example : MixerMatrix of LoLa881
* 0-------8------16-------8------16
* | | | | |
* | INPUT | | INPUT | |
* | -> |unused | -> |unused |
* | RECORD| | OUTPUT| |
* | | | | |
* 8--------------------------------
* | | | | |
* | | | | |
* |unused |unused |unused |unused |
* | | | | |
* | | | | |
* 16-------------------------------
* | | | | |
* | PLAY | | PLAY | |
* | -> |unused | -> |unused |
* | RECORD| | OUTPUT| |
* | | | | |
* 8--------------------------------
* | | | | |
* | | | | |
* |unused |unused |unused |unused |
* | | | | |
* | | | | |
* 16-------------------------------
*/
if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
printk(KERN_ERR SFX "Invalid mixer widget size\n");
return -EINVAL;
}
chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
(((1U << chip->mixer.src_stream_outs) - 1)
<< chip->mixer.src_stream_out_ofs);
chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
(((1U << chip->mixer.dest_phys_outs) - 1)
<< chip->mixer.dest_phys_out_ofs);
return 0;
}
static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
unsigned short gain, bool on)
{
unsigned int oldval, val;
if (!(chip->mixer.src_mask & (1 << id)))
return -EINVAL;
writew(gain, &chip->mixer.array->src_gain[id]);
oldval = val = readl(&chip->mixer.array->src_gain_enable);
if (on)
val |= (1 << id);
else
val &= ~(1 << id);
writel(val, &chip->mixer.array->src_gain_enable);
lola_codec_flush(chip);
/* inform micro-controller about the new source gain */
return lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_SOURCE_GAIN, id, 0);
}
#if 0 /* not used */
static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
unsigned short *gains)
{
int i;
if ((chip->mixer.src_mask & mask) != mask)
return -EINVAL;
for (i = 0; i < LOLA_MIXER_DIM; i++) {
if (mask & (1 << i)) {
writew(*gains, &chip->mixer.array->src_gain[i]);
gains++;
}
}
writel(mask, &chip->mixer.array->src_gain_enable);
lola_codec_flush(chip);
if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
/* update for all srcs at once */
return lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
}
/* update manually */
for (i = 0; i < LOLA_MIXER_DIM; i++) {
if (mask & (1 << i)) {
lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_SOURCE_GAIN, i, 0);
}
}
return 0;
}
#endif /* not used */
static int lola_mixer_set_mapping_gain(struct lola *chip,
unsigned int src, unsigned int dest,
unsigned short gain, bool on)
{
unsigned int val;
if (!(chip->mixer.src_mask & (1 << src)) ||
!(chip->mixer.dest_mask & (1 << dest)))
return -EINVAL;
if (on)
writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
if (on)
val |= (1 << src);
else
val &= ~(1 << src);
writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
lola_codec_flush(chip);
return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
src, dest);
}
static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
unsigned int mask, unsigned short *gains)
{
int i;
if (!(chip->mixer.dest_mask & (1 << id)) ||
(chip->mixer.src_mask & mask) != mask)
return -EINVAL;
for (i = 0; i < LOLA_MIXER_DIM; i++) {
if (mask & (1 << i)) {
writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
gains++;
}
}
writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
lola_codec_flush(chip);
/* update for all dests at once */
return lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
}
/*
*/
static int set_analog_volume(struct lola *chip, int dir,
unsigned int idx, unsigned int val,
bool external_call);
int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
{
struct lola_pin *pin;
int idx, max_idx;
pin = chip->pin[dir].pins;
max_idx = chip->pin[dir].num_pins;
for (idx = 0; idx < max_idx; idx++) {
if (pin[idx].is_analog) {
unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
/* set volume and do not save the value */
set_analog_volume(chip, dir, idx, val, false);
}
}
return lola_codec_flush(chip);
}
void lola_save_mixer(struct lola *chip)
{
/* mute analog output */
if (chip->mixer.array_saved) {
/* store contents of mixer array */
memcpy_fromio(chip->mixer.array_saved, chip->mixer.array,
sizeof(*chip->mixer.array));
}
lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */
}
void lola_restore_mixer(struct lola *chip)
{
int i;
/*lola_reset_setups(chip);*/
if (chip->mixer.array_saved) {
/* restore contents of mixer array */
memcpy_toio(chip->mixer.array, chip->mixer.array_saved,
sizeof(*chip->mixer.array));
/* inform micro-controller about all restored values
* and ignore return values
*/
for (i = 0; i < chip->mixer.src_phys_ins; i++)
lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_SOURCE_GAIN,
i, 0);
for (i = 0; i < chip->mixer.src_stream_outs; i++)
lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_SOURCE_GAIN,
chip->mixer.src_stream_out_ofs + i, 0);
for (i = 0; i < chip->mixer.dest_stream_ins; i++)
lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_DESTINATION_GAIN,
i, 0);
for (i = 0; i < chip->mixer.dest_phys_outs; i++)
lola_codec_write(chip, chip->mixer.nid,
LOLA_VERB_SET_DESTINATION_GAIN,
chip->mixer.dest_phys_out_ofs + i, 0);
lola_codec_flush(chip);
}
}
/*
*/
static int set_analog_volume(struct lola *chip, int dir,
unsigned int idx, unsigned int val,
bool external_call)
{
struct lola_pin *pin;
int err;
if (idx >= chip->pin[dir].num_pins)
return -EINVAL;
pin = &chip->pin[dir].pins[idx];
if (!pin->is_analog || pin->amp_num_steps <= val)
return -EINVAL;
if (external_call && pin->cur_gain_step == val)
return 0;
if (external_call)
lola_codec_flush(chip);
err = lola_codec_write(chip, pin->nid,
LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
if (err < 0)
return err;
if (external_call)
pin->cur_gain_step = val;
return 0;
}
int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
{
int ret = 0;
int success = 0;
int n, err;
/* SRC can be activated and the dwInputSRCMask is valid? */
if ((chip->input_src_caps_mask & src_mask) != src_mask)
return -EINVAL;
/* handle all even Inputs - SRC is a stereo setting !!! */
for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
unsigned int mask = 3U << n; /* handle the stereo case */
unsigned int new_src, src_state;
if (!(chip->input_src_caps_mask & mask))
continue;
/* if one IO needs SRC, both stereo IO will get SRC */
new_src = (src_mask & mask) != 0;
if (update) {
src_state = (chip->input_src_mask & mask) != 0;
if (src_state == new_src)
continue; /* nothing to change for this IO */
}
err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
LOLA_VERB_SET_SRC, new_src, 0);
if (!err)
success++;
else
ret = err;
}
if (success)
ret = lola_codec_flush(chip);
if (!ret)
chip->input_src_mask = src_mask;
return ret;
}
/*
*/
static int init_mixer_values(struct lola *chip)
{
int i;
/* all src on */
lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
/* clear all matrix */
memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
/* set src gain to 0dB */
for (i = 0; i < chip->mixer.src_phys_ins; i++)
lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
for (i = 0; i < chip->mixer.src_stream_outs; i++)
lola_mixer_set_src_gain(chip,
i + chip->mixer.src_stream_out_ofs,
336, true); /* 0dB */
/* set 1:1 dest gain */
for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
int src = i % chip->mixer.src_phys_ins;
lola_mixer_set_mapping_gain(chip, src, i, 336, true);
}
for (i = 0; i < chip->mixer.src_stream_outs; i++) {
int src = chip->mixer.src_stream_out_ofs + i;
int dst = chip->mixer.dest_phys_out_ofs +
i % chip->mixer.dest_phys_outs;
lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
}
return 0;
}
/*
* analog mixer control element
*/
static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
int dir = kcontrol->private_value;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = chip->pin[dir].num_pins;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
return 0;
}
static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
int dir = kcontrol->private_value;
int i;
for (i = 0; i < chip->pin[dir].num_pins; i++)
ucontrol->value.integer.value[i] =
chip->pin[dir].pins[i].cur_gain_step;
return 0;
}
static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
int dir = kcontrol->private_value;
int i, err;
for (i = 0; i < chip->pin[dir].num_pins; i++) {
err = set_analog_volume(chip, dir, i,
ucontrol->value.integer.value[i],
true);
if (err < 0)
return err;
}
return 0;
}
static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
int dir = kcontrol->private_value;
unsigned int val1, val2;
struct lola_pin *pin;
if (size < 4 * sizeof(unsigned int))
return -ENOMEM;
pin = &chip->pin[dir].pins[0];
val2 = pin->amp_step_size * 25;
val1 = -1 * (int)pin->amp_offset * (int)val2;
#ifdef TLV_DB_SCALE_MUTE
val2 |= TLV_DB_SCALE_MUTE;
#endif
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
return -EFAULT;
if (put_user(2 * sizeof(unsigned int), tlv + 1))
return -EFAULT;
if (put_user(val1, tlv + 2))
return -EFAULT;
if (put_user(val2, tlv + 3))
return -EFAULT;
return 0;
}
static struct snd_kcontrol_new lola_analog_mixer __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
.info = lola_analog_vol_info,
.get = lola_analog_vol_get,
.put = lola_analog_vol_put,
.tlv.c = lola_analog_vol_tlv,
};
static int __devinit create_analog_mixer(struct lola *chip, int dir, char *name)
{
if (!chip->pin[dir].num_pins)
return 0;
/* no analog volumes on digital only adapters */
if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
return 0;
lola_analog_mixer.name = name;
lola_analog_mixer.private_value = dir;
return snd_ctl_add(chip->card,
snd_ctl_new1(&lola_analog_mixer, chip));
}
/*
* Hardware sample rate converter on digital input
*/
static int lola_input_src_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = chip->pin[CAPT].num_pins;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int lola_input_src_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
int i;
for (i = 0; i < chip->pin[CAPT].num_pins; i++)
ucontrol->value.integer.value[i] =
!!(chip->input_src_mask & (1 << i));
return 0;
}
static int lola_input_src_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
int i;
unsigned int mask;
mask = 0;
for (i = 0; i < chip->pin[CAPT].num_pins; i++)
if (ucontrol->value.integer.value[i])
mask |= 1 << i;
return lola_set_src_config(chip, mask, true);
}
static struct snd_kcontrol_new lola_input_src_mixer __devinitdata = {
.name = "Digital SRC Capture Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = lola_input_src_info,
.get = lola_input_src_get,
.put = lola_input_src_put,
};
/*
* Lola16161 or Lola881 can have Hardware sample rate converters
* on its digital input pins
*/
static int __devinit create_input_src_mixer(struct lola *chip)
{
if (!chip->input_src_caps_mask)
return 0;
return snd_ctl_add(chip->card,
snd_ctl_new1(&lola_input_src_mixer, chip));
}
/*
* src gain mixer
*/
static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
unsigned int count = (kcontrol->private_value >> 8) & 0xff;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = count;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 409;
return 0;
}
static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
unsigned int ofs = kcontrol->private_value & 0xff;
unsigned int count = (kcontrol->private_value >> 8) & 0xff;
unsigned int mask, i;
mask = readl(&chip->mixer.array->src_gain_enable);
for (i = 0; i < count; i++) {
unsigned int idx = ofs + i;
unsigned short val;
if (!(chip->mixer.src_mask & (1 << idx)))
return -EINVAL;
if (mask & (1 << idx))
val = readw(&chip->mixer.array->src_gain[idx]) + 1;
else
val = 0;
ucontrol->value.integer.value[i] = val;
}
return 0;
}
static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
unsigned int ofs = kcontrol->private_value & 0xff;
unsigned int count = (kcontrol->private_value >> 8) & 0xff;
int i, err;
for (i = 0; i < count; i++) {
unsigned int idx = ofs + i;
unsigned short val = ucontrol->value.integer.value[i];
if (val)
val--;
err = lola_mixer_set_src_gain(chip, idx, val, !!val);
if (err < 0)
return err;
}
return 0;
}
/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
static struct snd_kcontrol_new lola_src_gain_mixer __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = lola_src_gain_info,
.get = lola_src_gain_get,
.put = lola_src_gain_put,
.tlv.p = lola_src_gain_tlv,
};
static int __devinit create_src_gain_mixer(struct lola *chip,
int num, int ofs, char *name)
{
lola_src_gain_mixer.name = name;
lola_src_gain_mixer.private_value = ofs + (num << 8);
return snd_ctl_add(chip->card,
snd_ctl_new1(&lola_src_gain_mixer, chip));
}
/*
* destination gain (matrix-like) mixer
*/
static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = src_num;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 433;
return 0;
}
static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
unsigned int src_ofs = kcontrol->private_value & 0xff;
unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
unsigned int dst, mask, i;
dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
for (i = 0; i < src_num; i++) {
unsigned int src = src_ofs + i;
unsigned short val;
if (!(chip->mixer.src_mask & (1 << src)))
return -EINVAL;
if (mask & (1 << dst))
val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
else
val = 0;
ucontrol->value.integer.value[i] = val;
}
return 0;
}
static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct lola *chip = snd_kcontrol_chip(kcontrol);
unsigned int src_ofs = kcontrol->private_value & 0xff;
unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
unsigned int dst, mask;
unsigned short gains[MAX_STREAM_COUNT];
int i, num;
mask = 0;
num = 0;
for (i = 0; i < src_num; i++) {
unsigned short val = ucontrol->value.integer.value[i];
if (val) {
gains[num++] = val - 1;
mask |= 1 << i;
}
}
mask <<= src_ofs;
dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
return lola_mixer_set_dest_gains(chip, dst, mask, gains);
}
static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
static struct snd_kcontrol_new lola_dest_gain_mixer __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = lola_dest_gain_info,
.get = lola_dest_gain_get,
.put = lola_dest_gain_put,
.tlv.p = lola_dest_gain_tlv,
};
static int __devinit create_dest_gain_mixer(struct lola *chip,
int src_num, int src_ofs,
int num, int ofs, char *name)
{
lola_dest_gain_mixer.count = num;
lola_dest_gain_mixer.name = name;
lola_dest_gain_mixer.private_value =
src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
return snd_ctl_add(chip->card,
snd_ctl_new1(&lola_dest_gain_mixer, chip));
}
/*
*/
int __devinit lola_create_mixer(struct lola *chip)
{
int err;
err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
if (err < 0)
return err;
err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
if (err < 0)
return err;
err = create_input_src_mixer(chip);
if (err < 0)
return err;
err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
"Line Source Gain Volume");
if (err < 0)
return err;
err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
chip->mixer.src_stream_out_ofs,
"Stream Source Gain Volume");
if (err < 0)
return err;
err = create_dest_gain_mixer(chip,
chip->mixer.src_phys_ins, 0,
chip->mixer.dest_stream_ins, 0,
"Line Capture Volume");
if (err < 0)
return err;
err = create_dest_gain_mixer(chip,
chip->mixer.src_stream_outs,
chip->mixer.src_stream_out_ofs,
chip->mixer.dest_stream_ins, 0,
"Stream-Loopback Capture Volume");
if (err < 0)
return err;
err = create_dest_gain_mixer(chip,
chip->mixer.src_phys_ins, 0,
chip->mixer.dest_phys_outs,
chip->mixer.dest_phys_out_ofs,
"Line-Loopback Playback Volume");
if (err < 0)
return err;
err = create_dest_gain_mixer(chip,
chip->mixer.src_stream_outs,
chip->mixer.src_stream_out_ofs,
chip->mixer.dest_phys_outs,
chip->mixer.dest_phys_out_ofs,
"Stream Playback Volume");
if (err < 0)
return err;
return init_mixer_values(chip);
}

View File

@ -0,0 +1,706 @@
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "lola.h"
#define LOLA_MAX_BDL_ENTRIES 8
#define LOLA_MAX_BUF_SIZE (1024*1024*1024)
#define LOLA_BDL_ENTRY_SIZE (16 * 16)
static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
{
struct lola *chip = snd_pcm_substream_chip(substream);
return &chip->pcm[substream->stream];
}
static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream)
{
struct lola_pcm *pcm = lola_get_pcm(substream);
unsigned int idx = substream->number;
return &pcm->streams[idx];
}
static unsigned int lola_get_lrc(struct lola *chip)
{
return lola_readl(chip, BAR1, LRC);
}
static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync)
{
unsigned int tstamp = lola_get_lrc(chip) >> 8;
if (chip->granularity) {
unsigned int wait_banks = quick_no_sync ? 0 : 8;
tstamp += (wait_banks + 1) * chip->granularity - 1;
tstamp -= tstamp % chip->granularity;
}
return tstamp << 8;
}
/* clear any pending interrupt status */
static void lola_stream_clear_pending_irq(struct lola *chip,
struct lola_stream *str)
{
unsigned int val = lola_dsd_read(chip, str->dsd, STS);
val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS;
if (val)
lola_dsd_write(chip, str->dsd, STS, val);
}
static void lola_stream_start(struct lola *chip, struct lola_stream *str,
unsigned int tstamp)
{
lola_stream_clear_pending_irq(chip, str);
lola_dsd_write(chip, str->dsd, CTL,
LOLA_DSD_CTL_SRUN |
LOLA_DSD_CTL_IOCE |
LOLA_DSD_CTL_DEIE |
LOLA_DSD_CTL_VLRCV |
tstamp);
}
static void lola_stream_stop(struct lola *chip, struct lola_stream *str,
unsigned int tstamp)
{
lola_dsd_write(chip, str->dsd, CTL,
LOLA_DSD_CTL_IOCE |
LOLA_DSD_CTL_DEIE |
LOLA_DSD_CTL_VLRCV |
tstamp);
lola_stream_clear_pending_irq(chip, str);
}
static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
{
unsigned long end_time = jiffies + msecs_to_jiffies(200);
while (time_before(jiffies, end_time)) {
unsigned int val;
val = lola_dsd_read(chip, str->dsd, CTL);
if (!(val & LOLA_DSD_CTL_SRST))
return;
msleep(1);
}
printk(KERN_WARNING SFX "SRST not clear (stream %d)\n", str->dsd);
}
static int lola_stream_wait_for_fifo(struct lola *chip,
struct lola_stream *str,
bool ready)
{
unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
unsigned long end_time = jiffies + msecs_to_jiffies(200);
while (time_before(jiffies, end_time)) {
unsigned int reg = lola_dsd_read(chip, str->dsd, STS);
if ((reg & LOLA_DSD_STS_FIFORDY) == val)
return 0;
msleep(1);
}
printk(KERN_WARNING SFX "FIFO not ready (stream %d)\n", str->dsd);
return -EIO;
}
/* sync for FIFO ready/empty for all linked streams;
* clear paused flag when FIFO gets ready again
*/
static int lola_sync_wait_for_fifo(struct lola *chip,
struct snd_pcm_substream *substream,
bool ready)
{
unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
unsigned long end_time = jiffies + msecs_to_jiffies(200);
struct snd_pcm_substream *s;
int pending = 0;
while (time_before(jiffies, end_time)) {
pending = 0;
snd_pcm_group_for_each_entry(s, substream) {
struct lola_stream *str;
if (s->pcm->card != substream->pcm->card)
continue;
str = lola_get_stream(s);
if (str->prepared && str->paused) {
unsigned int reg;
reg = lola_dsd_read(chip, str->dsd, STS);
if ((reg & LOLA_DSD_STS_FIFORDY) != val) {
pending = str->dsd + 1;
break;
}
if (ready)
str->paused = 0;
}
}
if (!pending)
return 0;
msleep(1);
}
printk(KERN_WARNING SFX "FIFO not ready (pending %d)\n", pending - 1);
return -EIO;
}
/* finish pause - prepare for a new resume */
static void lola_sync_pause(struct lola *chip,
struct snd_pcm_substream *substream)
{
struct snd_pcm_substream *s;
lola_sync_wait_for_fifo(chip, substream, false);
snd_pcm_group_for_each_entry(s, substream) {
struct lola_stream *str;
if (s->pcm->card != substream->pcm->card)
continue;
str = lola_get_stream(s);
if (str->paused && str->prepared)
lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN |
LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
}
lola_sync_wait_for_fifo(chip, substream, true);
}
static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
{
if (str->prepared) {
if (str->paused)
lola_sync_pause(chip, str->substream);
str->prepared = 0;
lola_dsd_write(chip, str->dsd, CTL,
LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
lola_stream_wait_for_fifo(chip, str, false);
lola_stream_clear_pending_irq(chip, str);
lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST);
lola_dsd_write(chip, str->dsd, LVI, 0);
lola_dsd_write(chip, str->dsd, BDPU, 0);
lola_dsd_write(chip, str->dsd, BDPL, 0);
wait_for_srst_clear(chip, str);
}
}
static struct snd_pcm_hardware lola_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_FLOAT_LE),
.rates = SNDRV_PCM_RATE_8000_192000,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = LOLA_MAX_BUF_SIZE,
.period_bytes_min = 128,
.period_bytes_max = LOLA_MAX_BUF_SIZE / 2,
.periods_min = 2,
.periods_max = LOLA_MAX_BDL_ENTRIES,
.fifo_size = 0,
};
static int lola_pcm_open(struct snd_pcm_substream *substream)
{
struct lola *chip = snd_pcm_substream_chip(substream);
struct lola_pcm *pcm = lola_get_pcm(substream);
struct lola_stream *str = lola_get_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
mutex_lock(&chip->open_mutex);
if (str->opened) {
mutex_unlock(&chip->open_mutex);
return -EBUSY;
}
str->substream = substream;
str->master = NULL;
str->opened = 1;
runtime->hw = lola_pcm_hw;
runtime->hw.channels_max = pcm->num_streams - str->index;
if (chip->sample_rate) {
/* sample rate is locked */
runtime->hw.rate_min = chip->sample_rate;
runtime->hw.rate_max = chip->sample_rate;
} else {
runtime->hw.rate_min = chip->sample_rate_min;
runtime->hw.rate_max = chip->sample_rate_max;
}
chip->ref_count_rate++;
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
/* period size = multiple of chip->granularity (8, 16 or 32 frames)*/
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
chip->granularity);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
chip->granularity);
mutex_unlock(&chip->open_mutex);
return 0;
}
static void lola_cleanup_slave_streams(struct lola_pcm *pcm,
struct lola_stream *str)
{
int i;
for (i = str->index + 1; i < pcm->num_streams; i++) {
struct lola_stream *s = &pcm->streams[i];
if (s->master != str)
break;
s->master = NULL;
s->opened = 0;
}
}
static int lola_pcm_close(struct snd_pcm_substream *substream)
{
struct lola *chip = snd_pcm_substream_chip(substream);
struct lola_stream *str = lola_get_stream(substream);
mutex_lock(&chip->open_mutex);
if (str->substream == substream) {
str->substream = NULL;
str->opened = 0;
}
if (--chip->ref_count_rate == 0) {
/* release sample rate */
chip->sample_rate = 0;
}
mutex_unlock(&chip->open_mutex);
return 0;
}
static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct lola_stream *str = lola_get_stream(substream);
str->bufsize = 0;
str->period_bytes = 0;
str->format_verb = 0;
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct lola *chip = snd_pcm_substream_chip(substream);
struct lola_pcm *pcm = lola_get_pcm(substream);
struct lola_stream *str = lola_get_stream(substream);
mutex_lock(&chip->open_mutex);
lola_stream_reset(chip, str);
lola_cleanup_slave_streams(pcm, str);
mutex_unlock(&chip->open_mutex);
return snd_pcm_lib_free_pages(substream);
}
/*
* set up a BDL entry
*/
static int setup_bdle(struct snd_pcm_substream *substream,
struct lola_stream *str, u32 **bdlp,
int ofs, int size)
{
u32 *bdl = *bdlp;
while (size > 0) {
dma_addr_t addr;
int chunk;
if (str->frags >= LOLA_MAX_BDL_ENTRIES)
return -EINVAL;
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
/* program the address field of the BDL entry */
bdl[0] = cpu_to_le32((u32)addr);
bdl[1] = cpu_to_le32(upper_32_bits(addr));
/* program the size field of the BDL entry */
chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
bdl[2] = cpu_to_le32(chunk);
/* program the IOC to enable interrupt
* only when the whole fragment is processed
*/
size -= chunk;
bdl[3] = size ? 0 : cpu_to_le32(0x01);
bdl += 4;
str->frags++;
ofs += chunk;
}
*bdlp = bdl;
return ofs;
}
/*
* set up BDL entries
*/
static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
struct snd_pcm_substream *substream,
struct lola_stream *str)
{
u32 *bdl;
int i, ofs, periods, period_bytes;
period_bytes = str->period_bytes;
periods = str->bufsize / period_bytes;
/* program the initial BDL entries */
bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index);
ofs = 0;
str->frags = 0;
for (i = 0; i < periods; i++) {
ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes);
if (ofs < 0)
goto error;
}
return 0;
error:
snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
str->bufsize, period_bytes);
return -EINVAL;
}
static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream)
{
unsigned int verb;
switch (substream->runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
verb = 0x00000000;
break;
case SNDRV_PCM_FORMAT_S24_LE:
verb = 0x00000200;
break;
case SNDRV_PCM_FORMAT_S32_LE:
verb = 0x00000300;
break;
case SNDRV_PCM_FORMAT_FLOAT_LE:
verb = 0x00001300;
break;
default:
return 0;
}
verb |= substream->runtime->channels;
return verb;
}
static int lola_set_stream_config(struct lola *chip,
struct lola_stream *str,
int channels)
{
int i, err;
unsigned int verb, val;
/* set format info for all channels
* (with only one command for the first channel)
*/
err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT,
str->format_verb, 0, &val, NULL);
if (err < 0) {
printk(KERN_ERR SFX "Cannot set stream format 0x%x\n",
str->format_verb);
return err;
}
/* update stream - channel config */
for (i = 0; i < channels; i++) {
verb = (str->index << 6) | i;
err = lola_codec_read(chip, str[i].nid,
LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb,
&val, NULL);
if (err < 0) {
printk(KERN_ERR SFX "Cannot set stream channel %d\n", i);
return err;
}
}
return 0;
}
/*
* set up the SD for streaming
*/
static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
struct lola_stream *str)
{
dma_addr_t bdl;
if (str->prepared)
return -EINVAL;
/* set up BDL */
bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index;
lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl);
lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl));
/* program the stream LVI (last valid index) of the BDL */
lola_dsd_write(chip, str->dsd, LVI, str->frags - 1);
lola_stream_clear_pending_irq(chip, str);
lola_dsd_write(chip, str->dsd, CTL,
LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN);
str->prepared = 1;
return lola_stream_wait_for_fifo(chip, str, true);
}
static int lola_pcm_prepare(struct snd_pcm_substream *substream)
{
struct lola *chip = snd_pcm_substream_chip(substream);
struct lola_pcm *pcm = lola_get_pcm(substream);
struct lola_stream *str = lola_get_stream(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int bufsize, period_bytes, format_verb;
int i, err;
mutex_lock(&chip->open_mutex);
lola_stream_reset(chip, str);
lola_cleanup_slave_streams(pcm, str);
if (str->index + runtime->channels > pcm->num_streams) {
mutex_unlock(&chip->open_mutex);
return -EINVAL;
}
for (i = 1; i < runtime->channels; i++) {
str[i].master = str;
str[i].opened = 1;
}
mutex_unlock(&chip->open_mutex);
bufsize = snd_pcm_lib_buffer_bytes(substream);
period_bytes = snd_pcm_lib_period_bytes(substream);
format_verb = lola_get_format_verb(substream);
str->bufsize = bufsize;
str->period_bytes = period_bytes;
str->format_verb = format_verb;
err = lola_setup_periods(chip, pcm, substream, str);
if (err < 0)
return err;
err = lola_set_sample_rate(chip, runtime->rate);
if (err < 0)
return err;
chip->sample_rate = runtime->rate; /* sample rate gets locked */
err = lola_set_stream_config(chip, str, runtime->channels);
if (err < 0)
return err;
err = lola_setup_controller(chip, pcm, str);
if (err < 0) {
lola_stream_reset(chip, str);
return err;
}
return 0;
}
static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct lola *chip = snd_pcm_substream_chip(substream);
struct lola_stream *str;
struct snd_pcm_substream *s;
unsigned int start;
unsigned int tstamp;
bool sync_streams;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
start = 1;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
start = 0;
break;
default:
return -EINVAL;
}
/*
* sample correct synchronization is only needed starting several
* streams. On stop or if only one stream do as quick as possible
*/
sync_streams = (start && snd_pcm_stream_linked(substream));
tstamp = lola_get_tstamp(chip, !sync_streams);
spin_lock(&chip->reg_lock);
snd_pcm_group_for_each_entry(s, substream) {
if (s->pcm->card != substream->pcm->card)
continue;
str = lola_get_stream(s);
if (start)
lola_stream_start(chip, str, tstamp);
else
lola_stream_stop(chip, str, tstamp);
str->running = start;
str->paused = !start;
snd_pcm_trigger_done(s, substream);
}
spin_unlock(&chip->reg_lock);
return 0;
}
static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
{
struct lola *chip = snd_pcm_substream_chip(substream);
struct lola_stream *str = lola_get_stream(substream);
unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB);
if (pos >= str->bufsize)
pos = 0;
return bytes_to_frames(substream->runtime, pos);
}
void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
{
int i;
for (i = 0; bits && i < pcm->num_streams; i++) {
if (bits & (1 << i)) {
struct lola_stream *str = &pcm->streams[i];
if (str->substream && str->running)
snd_pcm_period_elapsed(str->substream);
bits &= ~(1 << i);
}
}
}
static struct snd_pcm_ops lola_pcm_ops = {
.open = lola_pcm_open,
.close = lola_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = lola_pcm_hw_params,
.hw_free = lola_pcm_hw_free,
.prepare = lola_pcm_prepare,
.trigger = lola_pcm_trigger,
.pointer = lola_pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
int __devinit lola_create_pcm(struct lola *chip)
{
struct snd_pcm *pcm;
int i, err;
for (i = 0; i < 2; i++) {
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
PAGE_SIZE, &chip->pcm[i].bdl);
if (err < 0)
return err;
}
err = snd_pcm_new(chip->card, "Digigram Lola", 0,
chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
&pcm);
if (err < 0)
return err;
strlcpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
pcm->private_data = chip;
for (i = 0; i < 2; i++) {
if (chip->pcm[i].num_streams)
snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
}
/* buffer pre-allocation */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
1024 * 64, 32 * 1024 * 1024);
return 0;
}
void lola_free_pcm(struct lola *chip)
{
snd_dma_free_pages(&chip->pcm[0].bdl);
snd_dma_free_pages(&chip->pcm[1].bdl);
}
/*
*/
static int lola_init_stream(struct lola *chip, struct lola_stream *str,
int idx, int nid, int dir)
{
unsigned int val;
int err;
str->nid = nid;
str->index = idx;
str->dsd = idx;
if (dir == PLAY)
str->dsd += MAX_STREAM_IN_COUNT;
err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid);
return err;
}
if (dir == PLAY) {
/* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */
if ((val & 0x00f00dff) != 0x00000010) {
printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
val, nid);
return -EINVAL;
}
} else {
/* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
* (bug : ignore bit8: Conn list = 0/1)
*/
if ((val & 0x00f00cff) != 0x00100010) {
printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n",
val, nid);
return -EINVAL;
}
/* test bit9:DIGITAL and bit12:SRC_PRESENT*/
if ((val & 0x00001200) == 0x00001200)
chip->input_src_caps_mask |= (1 << idx);
}
err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
if (err < 0) {
printk(KERN_ERR SFX "Can't read FORMATS 0x%x\n", nid);
return err;
}
val &= 3;
if (val == 3)
str->can_float = true;
if (!(val & 1)) {
printk(KERN_ERR SFX "Invalid formats 0x%x for 0x%x", val, nid);
return -EINVAL;
}
return 0;
}
int __devinit lola_init_pcm(struct lola *chip, int dir, int *nidp)
{
struct lola_pcm *pcm = &chip->pcm[dir];
int i, nid, err;
nid = *nidp;
for (i = 0; i < pcm->num_streams; i++, nid++) {
err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir);
if (err < 0)
return err;
}
*nidp = nid;
return 0;
}

View File

@ -0,0 +1,222 @@
/*
* Support for Digigram Lola PCI-e boards
*
* Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include "lola.h"
static void print_audio_widget(struct snd_info_buffer *buffer,
struct lola *chip, int nid, const char *name)
{
unsigned int val;
lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
snd_iprintf(buffer, " Formats: 0x%x\n", val);
}
static void print_pin_widget(struct snd_info_buffer *buffer,
struct lola *chip, int nid, unsigned int ampcap,
const char *name)
{
unsigned int val;
lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
if (val == 0x00400200)
return;
lola_read_param(chip, nid, ampcap, &val);
snd_iprintf(buffer, " Amp-Caps: 0x%x\n", val);
snd_iprintf(buffer, " mute=%d, step-size=%d, steps=%d, ofs=%d\n",
LOLA_AMP_MUTE_CAPABLE(val),
LOLA_AMP_STEP_SIZE(val),
LOLA_AMP_NUM_STEPS(val),
LOLA_AMP_OFFSET(val));
lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val, NULL);
snd_iprintf(buffer, " Max-level: 0x%x\n", val);
}
static void print_clock_widget(struct snd_info_buffer *buffer,
struct lola *chip, int nid)
{
int i, j, num_clocks;
unsigned int val;
lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
snd_iprintf(buffer, "Node 0x%02x [Clock] wcaps 0x%x\n", nid, val);
num_clocks = val & 0xff;
for (i = 0; i < num_clocks; i += 4) {
unsigned int res_ex;
unsigned short items[4];
const char *name;
lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
i, 0, &val, &res_ex);
items[0] = val & 0xfff;
items[1] = (val >> 16) & 0xfff;
items[2] = res_ex & 0xfff;
items[3] = (res_ex >> 16) & 0xfff;
for (j = 0; j < 4; j++) {
unsigned char type = items[j] >> 8;
unsigned int freq = items[j] & 0xff;
if (i + j >= num_clocks)
break;
if (type == LOLA_CLOCK_TYPE_INTERNAL) {
name = "Internal";
freq = lola_sample_rate_convert(freq);
} else if (type == LOLA_CLOCK_TYPE_VIDEO) {
name = "Video";
freq = lola_sample_rate_convert(freq);
} else {
name = "Other";
}
snd_iprintf(buffer, " Clock %d: Type %d:%s, freq=%d\n",
i + j, type, name, freq);
}
}
}
static void print_mixer_widget(struct snd_info_buffer *buffer,
struct lola *chip, int nid)
{
unsigned int val;
lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
snd_iprintf(buffer, "Node 0x%02x [Mixer] wcaps 0x%x\n", nid, val);
}
static void lola_proc_codec_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct lola *chip = entry->private_data;
unsigned int val;
int i, nid;
lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
snd_iprintf(buffer, "Vendor: 0x%08x\n", val);
lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
snd_iprintf(buffer, "Function Type: %d\n", val);
lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
snd_iprintf(buffer, "Specific-Caps: 0x%08x\n", val);
snd_iprintf(buffer, " Pins-In %d, Pins-Out %d\n",
chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
nid = 2;
for (i = 0; i < chip->pcm[CAPT].num_streams; i++, nid++)
print_audio_widget(buffer, chip, nid, "[Audio-In]");
for (i = 0; i < chip->pcm[PLAY].num_streams; i++, nid++)
print_audio_widget(buffer, chip, nid, "[Audio-Out]");
for (i = 0; i < chip->pin[CAPT].num_pins; i++, nid++)
print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_IN_CAP,
"[Pin-In]");
for (i = 0; i < chip->pin[PLAY].num_pins; i++, nid++)
print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_OUT_CAP,
"[Pin-Out]");
if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
print_clock_widget(buffer, chip, nid);
nid++;
}
if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
print_mixer_widget(buffer, chip, nid);
nid++;
}
}
/* direct codec access for debugging */
static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct lola *chip = entry->private_data;
char line[64];
unsigned int id, verb, data, extdata;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%i %i %i %i", &id, &verb, &data, &extdata) != 4)
continue;
lola_codec_read(chip, id, verb, data, extdata,
&chip->debug_res,
&chip->debug_res_ex);
}
}
static void lola_proc_codec_rw_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct lola *chip = entry->private_data;
snd_iprintf(buffer, "0x%x 0x%x\n", chip->debug_res, chip->debug_res_ex);
}
/*
* dump some registers
*/
static void lola_proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct lola *chip = entry->private_data;
int i;
for (i = 0; i < 0x40; i += 4) {
snd_iprintf(buffer, "BAR0 %02x: %08x\n", i,
readl(chip->bar[BAR0].remap_addr + i));
}
snd_iprintf(buffer, "\n");
for (i = 0; i < 0x30; i += 4) {
snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
readl(chip->bar[BAR1].remap_addr + i));
}
snd_iprintf(buffer, "\n");
for (i = 0x80; i < 0xa0; i += 4) {
snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
readl(chip->bar[BAR1].remap_addr + i));
}
snd_iprintf(buffer, "\n");
for (i = 0; i < 32; i++) {
snd_iprintf(buffer, "DSD %02x STS %08x\n", i,
lola_dsd_read(chip, i, STS));
snd_iprintf(buffer, "DSD %02x LPIB %08x\n", i,
lola_dsd_read(chip, i, LPIB));
snd_iprintf(buffer, "DSD %02x CTL %08x\n", i,
lola_dsd_read(chip, i, CTL));
snd_iprintf(buffer, "DSD %02x LVIL %08x\n", i,
lola_dsd_read(chip, i, LVI));
snd_iprintf(buffer, "DSD %02x BDPL %08x\n", i,
lola_dsd_read(chip, i, BDPL));
snd_iprintf(buffer, "DSD %02x BDPU %08x\n", i,
lola_dsd_read(chip, i, BDPU));
}
}
void __devinit lola_proc_debug_new(struct lola *chip)
{
struct snd_info_entry *entry;
if (!snd_card_proc_new(chip->card, "codec", &entry))
snd_info_set_text_ops(entry, chip, lola_proc_codec_read);
if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) {
snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read);
entry->mode |= S_IWUSR;
entry->c.text.write = lola_proc_codec_rw_write;
}
if (!snd_card_proc_new(chip->card, "regs", &entry))
snd_info_set_text_ops(entry, chip, lola_proc_regs_read);
}

View File

@ -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 */

View File

@ -184,7 +184,7 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
.codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio",
.codec_name = "wm8731-codec.0-001b",
.codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops,
};

View File

@ -77,7 +77,7 @@ static struct snd_soc_dai_link db1200_i2s_dai = {
.codec_dai_name = "wm8731-hifi",
.cpu_dai_name = "au1xpsc_i2s.1",
.platform_name = "au1xpsc-pcm.1",
.codec_name = "wm8731-codec.0-001b",
.codec_name = "wm8731.0-001b",
.ops = &db1200_i2s_wm8731_ops,
};

View File

@ -243,6 +243,9 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
@ -314,6 +317,9 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = bf5xx_pcm_hardware.buffer_bytes_max
@ -377,6 +383,9 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
struct snd_dma_buffer *buf;
int stream;
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
sizeof(struct ac97_frame) / 4;
#endif
@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
#endif
}
if (sport_handle)
sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@ -458,7 +465,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bf5xx_pcm_driver = {
.driver = {
.name = "bf5xx-pcm-audio",
.name = "bfin-ac97-pcm-audio",
.owner = THIS_MODULE,
},

View File

@ -41,48 +41,7 @@
* anomaly does not affect blackfin sound drivers.
*/
static int *cmd_count;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
#define SPORT_REQ(x) \
[x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
static u16 sport_req[][7] = {
#ifdef SPORT0_TCR1
SPORT_REQ(0),
#endif
#ifdef SPORT1_TCR1
SPORT_REQ(1),
#endif
#ifdef SPORT2_TCR1
SPORT_REQ(2),
#endif
#ifdef SPORT3_TCR1
SPORT_REQ(3),
#endif
};
#define SPORT_PARAMS(x) \
[x] = { \
.dma_rx_chan = CH_SPORT##x##_RX, \
.dma_tx_chan = CH_SPORT##x##_TX, \
.err_irq = IRQ_SPORT##x##_ERROR, \
.regs = (struct sport_register *)SPORT##x##_TCR1, \
}
static struct sport_param sport_params[4] = {
#ifdef SPORT0_TCR1
SPORT_PARAMS(0),
#endif
#ifdef SPORT1_TCR1
SPORT_PARAMS(1),
#endif
#ifdef SPORT2_TCR1
SPORT_PARAMS(2),
#endif
#ifdef SPORT3_TCR1
SPORT_PARAMS(3),
#endif
};
static struct sport_device *ac97_sport_handle;
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
size_t count, unsigned int chan_mask)
@ -140,7 +99,8 @@ static unsigned int sport_tx_curr_frag(struct sport_device *sport)
static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
{
struct sport_device *sport = sport_handle;
struct sport_device *sport = ac97_sport_handle;
int *cmd_count = sport->private_data;
int nextfrag = sport_tx_curr_frag(sport);
struct ac97_frame *nextwrite;
@ -161,6 +121,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct sport_device *sport_handle = ac97_sport_handle;
struct ac97_frame out_frame[2], in_frame[2];
pr_debug("%s enter 0x%x\n", __func__, reg);
@ -185,6 +146,8 @@ static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
struct sport_device *sport_handle = ac97_sport_handle;
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
if (sport_handle->tx_run) {
@ -203,28 +166,19 @@ void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
{
#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
(defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
#define CONCAT(a, b, c) a ## b ## c
#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
struct sport_device *sport_handle = ac97_sport_handle;
u16 gpio = P_IDENT(sport_handle->pin_req[3]);
pr_debug("%s enter\n", __func__);
peripheral_free(per);
peripheral_free_list(sport_handle->pin_req);
gpio_request(gpio, "bf5xx-ac97");
gpio_direction_output(gpio, 1);
udelay(2);
gpio_set_value(gpio, 0);
udelay(1);
gpio_free(gpio);
peripheral_request(per, "soc-audio");
#else
pr_info("%s: Not implemented\n", __func__);
#endif
peripheral_request_list(sport_handle->pin_req, "soc-audio");
}
static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
@ -306,18 +260,32 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#define bf5xx_ac97_resume NULL
#endif
static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
{
int ret = 0;
cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
if (cmd_count == NULL)
return -ENOMEM;
static struct snd_soc_dai_driver bfin_ac97_dai = {
.ac97_control = 1,
.suspend = bf5xx_ac97_suspend,
.resume = bf5xx_ac97_resume,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
.channels_max = 6,
#else
.channels_max = 2,
#endif
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
ret = -EFAULT;
goto peripheral_err;
}
static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
{
struct sport_device *sport_handle;
int ret;
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
@ -329,12 +297,14 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
}
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif
sport_handle = sport_init(&sport_params[sport_num], 2, \
sizeof(struct ac97_frame), NULL);
sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
PAGE_SIZE);
if (!sport_handle) {
ret = -ENODEV;
goto sport_err;
}
/*SPORT works in TDM mode to simulate AC97 transfers*/
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
@ -361,67 +331,37 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
goto sport_config_err;
}
ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
}
ac97_sport_handle = sport_handle;
return 0;
sport_config_err:
kfree(sport_handle);
sport_done(sport_handle);
sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
gpio_err:
#endif
peripheral_free_list(sport_req[sport_num]);
peripheral_err:
free_page((unsigned long)cmd_count);
cmd_count = NULL;
return ret;
}
static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
free_page((unsigned long)cmd_count);
cmd_count = NULL;
peripheral_free_list(sport_req[sport_num]);
struct sport_device *sport_handle = platform_get_drvdata(pdev);
snd_soc_unregister_dai(&pdev->dev);
sport_done(sport_handle);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
return 0;
}
struct snd_soc_dai_driver bfin_ac97_dai = {
.ac97_control = 1,
.probe = bf5xx_ac97_probe,
.remove = bf5xx_ac97_remove,
.suspend = bf5xx_ac97_suspend,
.resume = bf5xx_ac97_resume,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
.channels_max = 6,
#else
.channels_max = 2,
#endif
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
EXPORT_SYMBOL_GPL(bfin_ac97_dai);
static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
{
return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
}
static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0;
}

View File

@ -29,22 +29,12 @@
#include <asm/portmux.h>
#include "../codecs/ad1836.h"
#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad1836;
static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -75,23 +65,33 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ad1836_ops = {
.startup = bf5xx_ad1836_startup,
.hw_params = bf5xx_ad1836_hw_params,
};
static struct snd_soc_dai_link bf5xx_ad1836_dai = {
.name = "ad1836",
.stream_name = "AD1836",
.cpu_dai_name = "bf5xx-tdm",
.codec_dai_name = "ad1836-hifi",
.platform_name = "bf5xx-tdm-pcm-audio",
.codec_name = "ad1836-codec.0",
.ops = &bf5xx_ad1836_ops,
static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
{
.name = "ad1836",
.stream_name = "AD1836",
.cpu_dai_name = "bfin-tdm.0",
.codec_dai_name = "ad1836-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.codec_name = "ad1836.0",
.ops = &bf5xx_ad1836_ops,
},
{
.name = "ad1836",
.stream_name = "AD1836",
.cpu_dai_name = "bfin-tdm.1",
.codec_dai_name = "ad1836-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.codec_name = "ad1836.0",
.ops = &bf5xx_ad1836_ops,
},
};
static struct snd_soc_card bf5xx_ad1836 = {
.name = "bf5xx_ad1836",
.dai_link = &bf5xx_ad1836_dai,
.name = "bfin-ad1836",
.dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};

View File

@ -38,30 +38,28 @@
#include <asm/portmux.h>
#include "../codecs/ad193x.h"
#include "bf5xx-sport.h"
#include "bf5xx-tdm-pcm.h"
#include "bf5xx-tdm.h"
static struct snd_soc_card bf5xx_ad193x;
static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int clk = 0;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
switch (params_rate(params)) {
case 48000:
clk = 12288000;
break;
}
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
@ -74,6 +72,12 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set codec DAI slots, 8 channels, all channels are enabled */
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
if (ret < 0)
@ -89,23 +93,33 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ad193x_ops = {
.startup = bf5xx_ad193x_startup,
.hw_params = bf5xx_ad193x_hw_params,
};
static struct snd_soc_dai_link bf5xx_ad193x_dai = {
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai_name = "bf5xx-tdm",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bf5xx-tdm-pcm-audio",
.codec_name = "ad193x-codec.5",
.ops = &bf5xx_ad193x_ops,
static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
{
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai_name = "bfin-tdm.0",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.codec_name = "ad193x.5",
.ops = &bf5xx_ad193x_ops,
},
{
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai_name = "bfin-tdm.1",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bfin-tdm-pcm-audio",
.codec_name = "ad193x.5",
.ops = &bf5xx_ad193x_ops,
},
};
static struct snd_soc_card bf5xx_ad193x = {
.name = "bf5xx_ad193x",
.dai_link = &bf5xx_ad193x_dai,
.name = "bfin-ad193x",
.dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};

View File

@ -47,39 +47,34 @@
#include <asm/portmux.h>
#include "../codecs/ad1980.h"
#include "bf5xx-sport.h"
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
static int bf5xx_board_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static struct snd_soc_ops bf5xx_board_ops = {
.startup = bf5xx_board_startup,
};
static struct snd_soc_dai_link bf5xx_board_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai_name = "bfin-ac97",
.codec_dai_name = "ad1980-hifi",
.platform_name = "bfin-pcm-audio",
.codec_name = "ad1980-codec",
.ops = &bf5xx_board_ops,
static struct snd_soc_dai_link bf5xx_board_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai_name = "bfin-ac97.0",
.codec_dai_name = "ad1980-hifi",
.platform_name = "bfin-ac97-pcm-audio",
.codec_name = "ad1980",
},
{
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai_name = "bfin-ac97.1",
.codec_dai_name = "ad1980-hifi",
.platform_name = "bfin-ac97-pcm-audio",
.codec_name = "ad1980",
},
};
static struct snd_soc_card bf5xx_board = {
.name = "bf5xx-board",
.dai_link = &bf5xx_board_dai,
.name = "bfin-ad1980",
.dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};

View File

@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev)
return 0;
}
static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -176,24 +166,34 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
static struct snd_soc_ops bf5xx_ad73311_ops = {
.startup = bf5xx_ad73311_startup,
.hw_params = bf5xx_ad73311_hw_params,
};
static struct snd_soc_dai_link bf5xx_ad73311_dai = {
.name = "ad73311",
.stream_name = "AD73311",
.cpu_dai_name = "bf5xx-i2s",
.codec_dai_name = "ad73311-hifi",
.platform_name = "bfin-pcm-audio",
.codec_name = "ad73311-codec",
.ops = &bf5xx_ad73311_ops,
static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
{
.name = "ad73311",
.stream_name = "AD73311",
.cpu_dai_name = "bfin-i2s.0",
.codec_dai_name = "ad73311-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ad73311",
.ops = &bf5xx_ad73311_ops,
},
{
.name = "ad73311",
.stream_name = "AD73311",
.cpu_dai_name = "bfin-i2s.1",
.codec_dai_name = "ad73311-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ad73311",
.ops = &bf5xx_ad73311_ops,
},
};
static struct snd_soc_card bf5xx_ad73311 = {
.name = "bf5xx_ad73311",
.name = "bfin-ad73311",
.probe = bf5xx_probe,
.dai_link = &bf5xx_ad73311_dai,
.dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};

View File

@ -148,10 +148,15 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret;
pr_debug("%s enter\n", __func__);
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
ret = snd_pcm_hw_constraint_integer(runtime, \
@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
if (sport_handle != NULL)
if (sport_handle != NULL) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
runtime->private_data = sport_handle;
else {
} else {
pr_err("sport_handle is NULL\n");
return -1;
}
@ -214,11 +224,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
buf->area, buf->bytes);
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
return 0;
}
@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
if (sport_handle)
sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@ -292,7 +295,7 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
.name = "bfin-pcm-audio",
.name = "bfin-i2s-pcm-audio",
.owner = THIS_MODULE,
},

View File

@ -51,59 +51,24 @@ struct bf5xx_i2s_port {
int configured;
};
static struct bf5xx_i2s_port bf5xx_i2s;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
static struct sport_param sport_params[2] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
.err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
.err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
}
};
/*
* Setting the TFS pin selector for SPORT 0 based on whether the selected
* port id F or G. If the port is F then no conflict should exist for the
* TFS. When Port G is selected and EMAC then there is a conflict between
* the PHY interrupt line and TFS. Current settings prevent the conflict
* by ignoring the TFS pin when Port G is selected. This allows both
* codecs and EMAC using Port G concurrently.
*/
#ifdef CONFIG_BF527_SPORT0_PORTG
#define LOCAL_SPORT0_TFS (0)
#else
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
#endif
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
/* interface format:support I2S,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
bf5xx_i2s.tcr1 |= TFSR | TCKFE;
bf5xx_i2s.rcr1 |= RFSR | RCKFE;
bf5xx_i2s.tcr2 |= TSFSE;
bf5xx_i2s.rcr2 |= RSFSE;
bf5xx_i2s->tcr1 |= TFSR | TCKFE;
bf5xx_i2s->rcr1 |= RFSR | RCKFE;
bf5xx_i2s->tcr2 |= TSFSE;
bf5xx_i2s->rcr2 |= RSFSE;
break;
case SND_SOC_DAIFMT_DSP_A:
bf5xx_i2s.tcr1 |= TFSR;
bf5xx_i2s.rcr1 |= RFSR;
bf5xx_i2s->tcr1 |= TFSR;
bf5xx_i2s->rcr1 |= RFSR;
break;
case SND_SOC_DAIFMT_LEFT_J:
ret = -EINVAL;
@ -135,29 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret = 0;
bf5xx_i2s.tcr2 &= ~0x1f;
bf5xx_i2s.rcr2 &= ~0x1f;
bf5xx_i2s->tcr2 &= ~0x1f;
bf5xx_i2s->rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
bf5xx_i2s->tcr2 |= 7;
bf5xx_i2s->rcr2 |= 7;
sport_handle->wdsize = 1;
case SNDRV_PCM_FORMAT_S16_LE:
bf5xx_i2s.tcr2 |= 15;
bf5xx_i2s.rcr2 |= 15;
bf5xx_i2s->tcr2 |= 15;
bf5xx_i2s->rcr2 |= 15;
sport_handle->wdsize = 2;
break;
case SNDRV_PCM_FORMAT_S24_LE:
bf5xx_i2s.tcr2 |= 23;
bf5xx_i2s.rcr2 |= 23;
bf5xx_i2s->tcr2 |= 23;
bf5xx_i2s->rcr2 |= 23;
sport_handle->wdsize = 3;
break;
case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_i2s.tcr2 |= 31;
bf5xx_i2s.rcr2 |= 31;
bf5xx_i2s->tcr2 |= 31;
bf5xx_i2s->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
}
if (!bf5xx_i2s.configured) {
if (!bf5xx_i2s->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
@ -166,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
*
* CPU DAI:slave mode.
*/
bf5xx_i2s.configured = 1;
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
bf5xx_i2s.rcr2, 0, 0);
bf5xx_i2s->configured = 1;
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
bf5xx_i2s.tcr2, 0, 0);
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@ -188,41 +159,19 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
pr_debug("%s enter\n", __func__);
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_i2s.configured = 0;
}
static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
return -EFAULT;
}
/* request DMA for SPORT */
sport_handle = sport_init(&sport_params[sport_num], 4, \
2 * sizeof(u32), NULL);
if (!sport_handle) {
peripheral_free_list(&sport_req[sport_num][0]);
return -ENODEV;
}
return 0;
}
static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
peripheral_free_list(&sport_req[sport_num][0]);
return 0;
bf5xx_i2s->configured = 0;
}
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
@ -235,19 +184,21 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
int ret;
pr_debug("%s : sport %d\n", __func__, dai->id);
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
bf5xx_i2s.rcr2, 0, 0);
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
bf5xx_i2s->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
bf5xx_i2s.tcr2, 0, 0);
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
bf5xx_i2s->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@ -266,8 +217,11 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
#define BF5XX_I2S_FORMATS \
(SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.shutdown = bf5xx_i2s_shutdown,
@ -276,8 +230,6 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
};
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.probe = bf5xx_i2s_probe,
.remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
@ -293,23 +245,45 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.ops = &bf5xx_i2s_dai_ops,
};
static int bfin_i2s_drv_probe(struct platform_device *pdev)
static int __devinit bf5xx_i2s_probe(struct platform_device *pdev)
{
return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
struct sport_device *sport_handle;
int ret;
/* configure SPORT for I2S */
sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
sizeof(struct bf5xx_i2s_port));
if (!sport_handle)
return -ENODEV;
/* register with the ASoC layers */
ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
sport_done(sport_handle);
return ret;
}
return 0;
}
static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
static int __devexit bf5xx_i2s_remove(struct platform_device *pdev)
{
struct sport_device *sport_handle = platform_get_drvdata(pdev);
pr_debug("%s enter\n", __func__);
snd_soc_unregister_dai(&pdev->dev);
sport_done(sport_handle);
return 0;
}
static struct platform_driver bfin_i2s_driver = {
.probe = bfin_i2s_drv_probe,
.remove = __devexit_p(bfin_i2s_drv_remove),
.probe = bf5xx_i2s_probe,
.remove = __devexit_p(bf5xx_i2s_remove),
.driver = {
.name = "bf5xx-i2s",
.name = "bfin-i2s",
.owner = THIS_MODULE,
},
};

View File

@ -42,8 +42,6 @@
/* delay between frame sync pulse and first data bit in multichannel mode */
#define FRAME_DELAY (1<<12)
struct sport_device *sport_handle;
EXPORT_SYMBOL(sport_handle);
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
@ -798,86 +796,164 @@ int sport_set_err_callback(struct sport_device *sport,
}
EXPORT_SYMBOL(sport_set_err_callback);
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
unsigned dummy_count, void *private_data)
static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
{
int ret;
/* Extract settings from platform data */
struct device *dev = &pdev->dev;
struct bfin_snd_platform_data *pdata = dev->platform_data;
struct resource *res;
param->num = pdev->id;
if (!pdata) {
dev_err(dev, "no platform_data\n");
return -ENODEV;
}
param->pin_req = pdata->pin_req;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no MEM resource\n");
return -ENODEV;
}
param->regs = (struct sport_register *)res->start;
/* first RX, then TX */
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(dev, "no rx DMA resource\n");
return -ENODEV;
}
param->dma_rx_chan = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(dev, "no tx DMA resource\n");
return -ENODEV;
}
param->dma_tx_chan = res->start;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "no irq resource\n");
return -ENODEV;
}
param->err_irq = res->start;
return 0;
}
struct sport_device *sport_init(struct platform_device *pdev,
unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
{
struct device *dev = &pdev->dev;
struct sport_param param;
struct sport_device *sport;
pr_debug("%s enter\n", __func__);
BUG_ON(param == NULL);
BUG_ON(wdsize == 0 || dummy_count == 0);
sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
if (!sport) {
pr_err("Failed to allocate for sport device\n");
int ret;
dev_dbg(dev, "%s enter\n", __func__);
param.wdsize = wdsize;
param.dummy_count = dummy_count;
BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
ret = sport_config_pdev(pdev, &param);
if (ret)
return NULL;
if (peripheral_request_list(param.pin_req, "soc-audio")) {
dev_err(dev, "requesting Peripherals failed\n");
return NULL;
}
memset(sport, 0, sizeof(struct sport_device));
sport->dma_rx_chan = param->dma_rx_chan;
sport->dma_tx_chan = param->dma_tx_chan;
sport->err_irq = param->err_irq;
sport->regs = param->regs;
sport->private_data = private_data;
sport = kzalloc(sizeof(*sport), GFP_KERNEL);
if (!sport) {
dev_err(dev, "failed to allocate for sport device\n");
goto __init_err0;
}
sport->num = param.num;
sport->dma_rx_chan = param.dma_rx_chan;
sport->dma_tx_chan = param.dma_tx_chan;
sport->err_irq = param.err_irq;
sport->regs = param.regs;
sport->pin_req = param.pin_req;
if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
pr_err("Failed to request RX dma %d\n", \
sport->dma_rx_chan);
dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
goto __init_err1;
}
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
pr_err("Failed to request RX irq %d\n", \
sport->dma_rx_chan);
dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
goto __init_err2;
}
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
pr_err("Failed to request TX dma %d\n", \
sport->dma_tx_chan);
dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
goto __init_err2;
}
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
pr_err("Failed to request TX irq %d\n", \
sport->dma_tx_chan);
dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
goto __init_err3;
}
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
sport) < 0) {
pr_err("Failed to request err irq:%d\n", \
sport->err_irq);
dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
goto __init_err3;
}
pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
sport->dma_rx_chan, sport->dma_tx_chan,
sport->err_irq, sport->regs);
sport->wdsize = wdsize;
sport->dummy_count = dummy_count;
sport->wdsize = param.wdsize;
sport->dummy_count = param.dummy_count;
sport->private_data = kzalloc(priv_size, GFP_KERNEL);
if (!sport->private_data) {
dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
goto __init_err4;
}
if (L1_DATA_A_LENGTH)
sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
else
sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) {
pr_err("Failed to allocate dummy buffer\n");
goto __error;
dev_err(dev, "failed to allocate dummy buffer\n");
goto __error1;
}
ret = sport_config_rx_dummy(sport);
if (ret) {
pr_err("Failed to config rx dummy ring\n");
goto __error;
dev_err(dev, "failed to config rx dummy ring\n");
goto __error2;
}
ret = sport_config_tx_dummy(sport);
if (ret) {
pr_err("Failed to config tx dummy ring\n");
goto __error;
dev_err(dev, "failed to config tx dummy ring\n");
goto __error3;
}
platform_set_drvdata(pdev, sport);
return sport;
__error:
__error3:
if (L1_DATA_A_LENGTH)
l1_data_sram_free(sport->dummy_rx_desc);
else
dma_free_coherent(NULL, 2*sizeof(struct dmasg),
sport->dummy_rx_desc, 0);
__error2:
if (L1_DATA_A_LENGTH)
l1_data_sram_free(sport->dummy_buf);
else
kfree(sport->dummy_buf);
__error1:
kfree(sport->private_data);
__init_err4:
free_irq(sport->err_irq, sport);
__init_err3:
free_dma(sport->dma_tx_chan);
@ -885,6 +961,8 @@ __init_err2:
free_dma(sport->dma_rx_chan);
__init_err1:
kfree(sport);
__init_err0:
peripheral_free_list(param.pin_req);
return NULL;
}
EXPORT_SYMBOL(sport_init);
@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport)
free_dma(sport->dma_tx_chan);
free_irq(sport->err_irq, sport);
kfree(sport->private_data);
peripheral_free_list(sport->pin_req);
kfree(sport);
sport = NULL;
}
EXPORT_SYMBOL(sport_done);

View File

@ -1,5 +1,5 @@
/*
* File: bf5xx_ac97_sport.h
* File: bf5xx_sport.h
* Based on:
* Author: Roy Huang <roy.huang@analog.com>
*
@ -33,15 +33,18 @@
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <asm/dma.h>
#include <asm/bfin_sport.h>
#define DESC_ELEMENT_COUNT 9
struct sport_device {
int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
const unsigned short *pin_req;
struct sport_register *regs;
unsigned char *rx_buf;
@ -103,17 +106,20 @@ struct sport_device {
void *private_data;
};
extern struct sport_device *sport_handle;
struct sport_param {
int num;
int dma_rx_chan;
int dma_tx_chan;
int err_irq;
const unsigned short *pin_req;
struct sport_register *regs;
unsigned int wdsize;
unsigned int dummy_count;
void *private_data;
};
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
unsigned dummy_count, void *private_data);
struct sport_device *sport_init(struct platform_device *pdev,
unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
void sport_done(struct sport_device *sport);

View File

@ -44,16 +44,6 @@
static struct snd_soc_card bf5xx_ssm2602;
static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -109,23 +99,33 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
}
static struct snd_soc_ops bf5xx_ssm2602_ops = {
.startup = bf5xx_ssm2602_startup,
.hw_params = bf5xx_ssm2602_hw_params,
};
static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
.name = "ssm2602",
.stream_name = "SSM2602",
.cpu_dai_name = "bf5xx-i2s",
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bf5xx-pcm-audio",
.codec_name = "ssm2602-codec.0-001b",
.ops = &bf5xx_ssm2602_ops,
static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
{
.name = "ssm2602",
.stream_name = "SSM2602",
.cpu_dai_name = "bfin-i2s.0",
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ssm2602.0-001b",
.ops = &bf5xx_ssm2602_ops,
},
{
.name = "ssm2602",
.stream_name = "SSM2602",
.cpu_dai_name = "bfin-i2s.1",
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bfin-i2s-pcm-audio",
.codec_name = "ssm2602.0-001b",
.ops = &bf5xx_ssm2602_ops,
},
};
static struct snd_soc_card bf5xx_ssm2602 = {
.name = "bf5xx_ssm2602",
.dai_link = &bf5xx_ssm2602_dai,
.name = "bfin-ssm2602",
.dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
.num_links = 1,
};

View File

@ -154,7 +154,12 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dma_buffer *buf = &substream->dma_buffer;
int ret = 0;
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
@ -164,9 +169,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
if (sport_handle != NULL)
if (sport_handle != NULL) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
runtime->private_data = sport_handle;
else {
} else {
pr_err("sport_handle is NULL\n");
ret = -ENODEV;
}
@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
}
buf->bytes = size;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
sport_handle->tx_buf = buf->area;
else
sport_handle->rx_buf = buf->area;
return 0;
}
@ -274,8 +279,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
}
if (sport_handle)
sport_done(sport_handle);
}
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
@ -326,7 +329,7 @@ static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
static struct platform_driver bfin_tdm_driver = {
.driver = {
.name = "bf5xx-tdm-pcm-audio",
.name = "bfin-tdm-pcm-audio",
.owner = THIS_MODULE,
},

View File

@ -46,43 +46,6 @@
#include "bf5xx-sport.h"
#include "bf5xx-tdm.h"
static struct bf5xx_tdm_port bf5xx_tdm;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
static struct sport_param sport_params[2] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
.err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
.err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
}
};
/*
* Setting the TFS pin selector for SPORT 0 based on whether the selected
* port id F or G. If the port is F then no conflict should exist for the
* TFS. When Port G is selected and EMAC then there is a conflict between
* the PHY interrupt line and TFS. Current settings prevent the conflict
* by ignoring the TFS pin when Port G is selected. This allows both
* codecs and EMAC using Port G concurrently.
*/
#ifdef CONFIG_BF527_SPORT0_PORTG
#define LOCAL_SPORT0_TFS (0)
#else
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
#endif
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int ret = 0;
bf5xx_tdm.tcr2 &= ~0x1f;
bf5xx_tdm.rcr2 &= ~0x1f;
bf5xx_tdm->tcr2 &= ~0x1f;
bf5xx_tdm->rcr2 &= ~0x1f;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_tdm.tcr2 |= 31;
bf5xx_tdm.rcr2 |= 31;
bf5xx_tdm->tcr2 |= 31;
bf5xx_tdm->rcr2 |= 31;
sport_handle->wdsize = 4;
break;
/* at present, we only support 32bit transfer */
@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
break;
}
if (!bf5xx_tdm.configured) {
if (!bf5xx_tdm->configured) {
/*
* TX and RX are not independent,they are enabled at the
* same time, even if only one side is running. So, we
@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
*
* CPU DAI:slave mode.
*/
ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
bf5xx_tdm.rcr2, 0, 0);
ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
bf5xx_tdm->rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
bf5xx_tdm.tcr2, 0, 0);
ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
bf5xx_tdm->tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
bf5xx_tdm.configured = 1;
bf5xx_tdm->configured = 1;
}
return 0;
@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
/* No active stream, SPORT is allowed to be configured again. */
if (!dai->active)
bf5xx_tdm.configured = 0;
bf5xx_tdm->configured = 0;
}
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot)
{
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
int i;
unsigned int slot;
unsigned int tx_mapped = 0, rx_mapped = 0;
@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = tx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(tx_mapped & (1 << slot)))) {
bf5xx_tdm.tx_map[i] = slot;
bf5xx_tdm->tx_map[i] = slot;
tx_mapped |= 1 << slot;
} else
return -EINVAL;
@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
slot = rx_slot[i];
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
(!(rx_mapped & (1 << slot)))) {
bf5xx_tdm.rx_map[i] = slot;
bf5xx_tdm->rx_map[i] = slot;
rx_mapped |= 1 << slot;
} else
return -EINVAL;
@ -212,12 +182,14 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return 0;
if (dai->capture_active)
sport_rx_stop(sport);
if (dai->playback_active)
sport_tx_stop(sport);
if (dai->capture_active)
sport_rx_stop(sport);
/* isolate sync/clock pins from codec while sports resume */
peripheral_free_list(sport->pin_req);
return 0;
}
@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
int ret;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return 0;
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
if (ret) {
pr_err("SPORT is busy!\n");
@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
ret = -EBUSY;
}
peripheral_request_list(sport->pin_req, "soc-audio");
return 0;
}
@ -280,20 +251,14 @@ static struct snd_soc_dai_driver bf5xx_tdm_dai = {
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{
int ret = 0;
struct sport_device *sport_handle;
int ret;
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
return -EFAULT;
}
/* request DMA for SPORT */
sport_handle = sport_init(&sport_params[sport_num], 4, \
8 * sizeof(u32), NULL);
if (!sport_handle) {
peripheral_free_list(&sport_req[sport_num][0]);
/* configure SPORT for TDM */
sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
sizeof(struct bf5xx_tdm_port));
if (!sport_handle)
return -ENODEV;
}
/* SPORT works in TDM mode */
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
@ -323,18 +288,19 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
goto sport_config_err;
}
sport_handle->private_data = &bf5xx_tdm;
return 0;
sport_config_err:
peripheral_free_list(&sport_req[sport_num][0]);
sport_done(sport_handle);
return ret;
}
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{
peripheral_free_list(&sport_req[sport_num][0]);
struct sport_device *sport_handle = platform_get_drvdata(pdev);
snd_soc_unregister_dai(&pdev->dev);
sport_done(sport_handle);
return 0;
}

View File

@ -120,7 +120,7 @@
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
.shift = 0, .invert = 0, .kcontrols = NULL, \
.shift = 0, .invert = 0, .kcontrol_news = NULL, \
.num_kcontrols = 0, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }

View File

@ -16,10 +16,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AD1836 if SPI_MASTER
select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311
select SND_SOC_ADS117X
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
select SND_SOC_ALC5623 if I2C
@ -33,13 +34,14 @@ config SND_SOC_ALL_CODECS
select SND_SOC_JZ4740_CODEC if SOC_JZ4740
select SND_SOC_LM4857 if I2C
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SGTL5000 if I2C
select SND_SOC_SN95031 if INTEL_SCU_IPC
select SND_SOC_SPDIF
select SND_SOC_SSM2602 if I2C
select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
select SND_SOC_TLV320AIC23 if I2C
select SND_SOC_TLV320AIC26 if SPI_MASTER
@ -52,6 +54,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WL1273 if MFD_WL1273_CORE
select SND_SOC_WM1250_EV1 if I2C
select SND_SOC_WM2000 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
@ -72,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
select SND_SOC_WM8915 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
select SND_SOC_WM8960 if I2C
@ -136,6 +140,9 @@ config SND_SOC_AK4104
config SND_SOC_AK4535
tristate
config SND_SOC_AK4641
tristate
config SND_SOC_AK4642
tristate
@ -187,6 +194,9 @@ config SND_SOC_DMIC
config SND_SOC_MAX98088
tristate
config SND_SOC_MAX98095
tristate
config SND_SOC_MAX9850
tristate
@ -241,6 +251,9 @@ config SND_SOC_UDA1380
config SND_SOC_WL1273
tristate
config SND_SOC_WM1250_EV1
tristate
config SND_SOC_WM8350
tristate
@ -298,6 +311,9 @@ config SND_SOC_WM8903
config SND_SOC_WM8904
tristate
config SND_SOC_WM8915
tristate
config SND_SOC_WM8940
tristate

View File

@ -7,6 +7,7 @@ snd-soc-ad73311-objs := ad73311.o
snd-soc-ads117x-objs := ads117x.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
snd-soc-cq93vc-objs := cq93vc.o
@ -19,6 +20,7 @@ snd-soc-dfbmcs320-objs := dfbmcs320.o
snd-soc-dmic-objs := dmic.o
snd-soc-l3-objs := l3.o
snd-soc-max98088-objs := max98088.o
snd-soc-max98095-objs := max98095.o
snd-soc-max9850-objs := max9850.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-sgtl5000-objs := sgtl5000.o
@ -37,6 +39,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
@ -56,6 +59,7 @@ snd-soc-wm8804-objs := wm8804.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
snd-soc-wm8915-objs := wm8915.o
snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
@ -69,7 +73,7 @@ snd-soc-wm8988-objs := wm8988.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm8991-objs := wm8991.o
snd-soc-wm8993-objs := wm8993.o
snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
snd-soc-wm8994-objs := wm8994.o wm8994-tables.o wm8958-dsp2.o
snd-soc-wm8995-objs := wm8995.o
snd-soc-wm9081-objs := wm9081.o
snd-soc-wm9705-objs := wm9705.o
@ -94,6 +98,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
@ -108,6 +113,7 @@ obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
@ -125,6 +131,7 @@ obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
@ -144,6 +151,7 @@ obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
obj-$(CONFIG_SND_SOC_WM8915) += snd-soc-wm8915.o
obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o
obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o
obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o

View File

@ -23,8 +23,7 @@
/* codec private data */
struct ad193x_priv {
enum snd_soc_control_type bus_type;
void *control_data;
enum snd_soc_control_type control_type;
int sysclk;
};
@ -354,14 +353,12 @@ static int ad193x_probe(struct snd_soc_codec *codec)
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
codec->control_data = ad193x->control_data;
if (ad193x->bus_type == SND_SOC_I2C)
ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->bus_type);
if (ad193x->control_type == SND_SOC_I2C)
ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
else
ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->bus_type);
ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
if (ret < 0) {
dev_err(codec->dev, "failed to set cache I/O: %d\n",
ret);
dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
return ret;
}
@ -408,8 +405,7 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, ad193x);
ad193x->control_data = spi;
ad193x->bus_type = SND_SOC_SPI;
ad193x->control_type = SND_SOC_SPI;
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
@ -427,7 +423,7 @@ static int __devexit ad193x_spi_remove(struct spi_device *spi)
static struct spi_driver ad193x_spi_driver = {
.driver = {
.name = "ad193x-codec",
.name = "ad193x",
.owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
@ -454,8 +450,7 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, ad193x);
ad193x->control_data = client;
ad193x->bus_type = SND_SOC_I2C;
ad193x->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&client->dev,
&soc_codec_dev_ad193x, &ad193x_dai, 1);
@ -473,7 +468,7 @@ static int __devexit ad193x_i2c_remove(struct i2c_client *client)
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x-codec",
.name = "ad193x",
},
.probe = ad193x_i2c_probe,
.remove = __devexit_p(ad193x_i2c_remove),

View File

@ -266,7 +266,7 @@ static int __devexit ad1980_remove(struct platform_device *pdev)
static struct platform_driver ad1980_codec_driver = {
.driver = {
.name = "ad1980-codec",
.name = "ad1980",
.owner = THIS_MODULE,
},

View File

@ -55,7 +55,7 @@ static int __devexit ad73311_remove(struct platform_device *pdev)
static struct platform_driver ad73311_codec_driver = {
.driver = {
.name = "ad73311-codec",
.name = "ad73311",
.owner = THIS_MODULE,
},

View File

@ -230,7 +230,7 @@ static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("AIN"),
};
static const struct snd_soc_dapm_route audio_map[] = {
static const struct snd_soc_dapm_route ak4535_audio_map[] = {
/*stereo mixer */
{"Stereo Mixer", "Playback Switch", "DAC"},
{"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
@ -287,17 +287,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Input Mixer", "Aux Capture Switch", "Aux In"},
};
static int ak4535_add_widgets(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, ak4535_dapm_widgets,
ARRAY_SIZE(ak4535_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
return 0;
}
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
@ -457,8 +446,6 @@ static int ak4535_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
ak4535_add_widgets(codec);
return 0;
}
@ -480,6 +467,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
.reg_cache_size = ARRAY_SIZE(ak4535_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ak4535_reg,
.dapm_widgets = ak4535_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
.dapm_routes = ak4535_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)

Some files were not shown because too many files have changed in this diff Show More