mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-04-21 12:27:27 +03:00
[ubicom32]: move new files out from platform support patch
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@19815 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
42
target/linux/ubicom32/files/sound/ubicom32/Kconfig
Normal file
42
target/linux/ubicom32/files/sound/ubicom32/Kconfig
Normal file
@@ -0,0 +1,42 @@
|
||||
# ALSA Ubicom32 drivers
|
||||
|
||||
menuconfig SND_UBI32
|
||||
tristate "Ubicom32 sound devices"
|
||||
select SND_PCM
|
||||
default n
|
||||
help
|
||||
Say Y here to include support for audio on the Ubicom32 platform.
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called snd_ubi32.
|
||||
|
||||
if SND_UBI32
|
||||
|
||||
config SND_UBI32_AUDIO_GENERIC_CAPTURE
|
||||
bool "Generic Capture Support"
|
||||
default n
|
||||
help
|
||||
Use this option to support ADCs which don't require special drivers.
|
||||
|
||||
config SND_UBI32_AUDIO_GENERIC
|
||||
bool "Generic Playback Support"
|
||||
default n
|
||||
help
|
||||
Use this option to support DACs which don't require special drivers.
|
||||
|
||||
comment "I2C Based Codecs"
|
||||
|
||||
config SND_UBI32_AUDIO_CS4350
|
||||
bool "Cirrus Logic CS4350 DAC"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Support for the Cirrus Logic CS4350 DAC.
|
||||
|
||||
config SND_UBI32_AUDIO_CS4384
|
||||
bool "Cirrus Logic CS4384 DAC"
|
||||
depends on I2C
|
||||
default n
|
||||
help
|
||||
Support for the Cirrus Logic CS4384 DAC.
|
||||
|
||||
endif #SND_UBI32
|
||||
41
target/linux/ubicom32/files/sound/ubicom32/Makefile
Normal file
41
target/linux/ubicom32/files/sound/ubicom32/Makefile
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# sound/ubicom32/Makefile
|
||||
# Makefile for ALSA
|
||||
#
|
||||
# (C) Copyright 2009, Ubicom, Inc.
|
||||
#
|
||||
# This file is part of the Ubicom32 Linux Kernel Port.
|
||||
#
|
||||
# The Ubicom32 Linux Kernel Port 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.
|
||||
#
|
||||
# The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
# see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Ubicom32 implementation derived from (with many thanks):
|
||||
# arch/m68knommu
|
||||
# arch/blackfin
|
||||
# arch/parisc
|
||||
#
|
||||
|
||||
CFLAGS_ubi32.o += -O2
|
||||
snd-ubi32-pcm-objs := ubi32-pcm.o
|
||||
snd-ubi32-generic-objs := ubi32-generic.o
|
||||
snd-ubi32-generic-capture-objs := ubi32-generic-capture.o
|
||||
snd-ubi32-cs4350-objs := ubi32-cs4350.o
|
||||
snd-ubi32-cs4384-objs := ubi32-cs4384.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_UBI32) += snd-ubi32-pcm.o
|
||||
obj-$(CONFIG_SND_UBI32_AUDIO_GENERIC) += snd-ubi32-generic.o
|
||||
obj-$(CONFIG_SND_UBI32_AUDIO_GENERIC_CAPTURE) += snd-ubi32-generic-capture.o
|
||||
obj-$(CONFIG_SND_UBI32_AUDIO_CS4350) += snd-ubi32-cs4350.o
|
||||
obj-$(CONFIG_SND_UBI32_AUDIO_CS4384) += snd-ubi32-cs4384.o
|
||||
583
target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4350.c
Normal file
583
target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4350.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* sound/ubicom32/ubi32-cs4350.c
|
||||
* Interface to ubicom32 virtual audio peripheral - using CS4350 DAC
|
||||
*
|
||||
* (C) Copyright 2009, Ubicom, Inc.
|
||||
*
|
||||
* This file is part of the Ubicom32 Linux Kernel Port.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include "ubi32.h"
|
||||
|
||||
#define DRIVER_NAME "snd-ubi32-cs4350"
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
static const struct i2c_device_id snd_ubi32_cs4350_id[] = {
|
||||
{"cs4350", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ubicom32audio_id);
|
||||
|
||||
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
|
||||
|
||||
/*
|
||||
* The dB scale for the Cirrus Logic cs4350. The output range is from
|
||||
* -127.5 dB to 0 dB.
|
||||
*/
|
||||
static const DECLARE_TLV_DB_SCALE(snd_ubi32_cs4350_db, -12750, 50, 0);
|
||||
|
||||
#define ubi32_cs4350_mute_info snd_ctl_boolean_stereo_info
|
||||
|
||||
/*
|
||||
* Private data for cs4350 chip
|
||||
*/
|
||||
struct ubi32_cs4350_priv {
|
||||
/*
|
||||
* The current volume settings
|
||||
*/
|
||||
uint8_t volume[2];
|
||||
|
||||
/*
|
||||
* Bitmask of mutes MSB (unused, ..., unused, right_ch, left_ch) LSB
|
||||
*/
|
||||
uint8_t mute;
|
||||
|
||||
/*
|
||||
* Lock to protect this struct because callbacks are not atomic.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* The info for the cs4350. The volume currently has one channel,
|
||||
* and 255 possible settings.
|
||||
*/
|
||||
static int ubi32_cs4350_volume_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 2;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 255; // 8 bits in cirrus logic cs4350 volume register
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ubi32_cs4350_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol);
|
||||
struct ubi32_cs4350_priv *cs4350_priv;
|
||||
unsigned long flags;
|
||||
|
||||
cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv);
|
||||
|
||||
spin_lock_irqsave(&cs4350_priv->lock, flags);
|
||||
|
||||
ucontrol->value.integer.value[0] = cs4350_priv->volume[0];
|
||||
ucontrol->value.integer.value[1] = cs4350_priv->volume[1];
|
||||
|
||||
spin_unlock_irqrestore(&cs4350_priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ubi32_cs4350_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol);
|
||||
struct i2c_client *client = (struct i2c_client *)ubi32_priv->client;
|
||||
struct ubi32_cs4350_priv *cs4350_priv;
|
||||
unsigned long flags;
|
||||
int ret, changed;
|
||||
char send[2];
|
||||
uint8_t volume_reg_value_left, volume_reg_value_right;
|
||||
|
||||
changed = 0;
|
||||
|
||||
cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv);
|
||||
volume_reg_value_left = 255 - (ucontrol->value.integer.value[0] & 0xFF);
|
||||
volume_reg_value_right = 255 - (ucontrol->value.integer.value[1] & 0xFF);
|
||||
|
||||
#if SND_UBI32_DEBUG
|
||||
snd_printk(KERN_INFO "Setting volume: writing %d,%d to CS4350 volume registers\n", volume_reg_value_left, volume_reg_value_right);
|
||||
#endif
|
||||
spin_lock_irqsave(&cs4350_priv->lock, flags);
|
||||
|
||||
if (cs4350_priv->volume[0] != ucontrol->value.integer.value[0]) {
|
||||
send[0] = 0x05; // left channel
|
||||
send[1] = volume_reg_value_left;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n");
|
||||
return changed;
|
||||
}
|
||||
cs4350_priv->volume[0] = ucontrol->value.integer.value[0];
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
if (cs4350_priv->volume[1] != ucontrol->value.integer.value[1]) {
|
||||
send[0] = 0x06; // right channel
|
||||
send[1] = volume_reg_value_right;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set channel B volume on CS4350\n");
|
||||
return changed;
|
||||
}
|
||||
cs4350_priv->volume[1] = ucontrol->value.integer.value[1];
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cs4350_priv->lock, flags);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new ubi32_cs4350_volume __devinitdata = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "PCM Playback Volume",
|
||||
.info = ubi32_cs4350_volume_info,
|
||||
.get = ubi32_cs4350_volume_get,
|
||||
.put = ubi32_cs4350_volume_put,
|
||||
.tlv.p = snd_ubi32_cs4350_db,
|
||||
};
|
||||
|
||||
static int ubi32_cs4350_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol);
|
||||
struct ubi32_cs4350_priv *cs4350_priv;
|
||||
unsigned long flags;
|
||||
|
||||
cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv);
|
||||
|
||||
spin_lock_irqsave(&cs4350_priv->lock, flags);
|
||||
|
||||
ucontrol->value.integer.value[0] = cs4350_priv->mute & 1;
|
||||
ucontrol->value.integer.value[1] = (cs4350_priv->mute & (1 << 1)) ? 1 : 0;
|
||||
|
||||
spin_unlock_irqrestore(&cs4350_priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ubi32_cs4350_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv = snd_kcontrol_chip(kcontrol);
|
||||
struct i2c_client *client = (struct i2c_client *)ubi32_priv->client;
|
||||
struct ubi32_cs4350_priv *cs4350_priv;
|
||||
unsigned long flags;
|
||||
int ret, changed;
|
||||
char send[2];
|
||||
char recv[1];
|
||||
uint8_t mute;
|
||||
|
||||
changed = 0;
|
||||
|
||||
cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv);
|
||||
|
||||
spin_lock_irqsave(&cs4350_priv->lock, flags);
|
||||
|
||||
if ((cs4350_priv->mute & 1) != ucontrol->value.integer.value[0]) {
|
||||
send[0] = 0x04;
|
||||
ret = i2c_master_send(client, send, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed to write to mute register: channel 0\n");
|
||||
return changed;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, recv, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed to read mute register: channel 0\n");
|
||||
return changed;
|
||||
}
|
||||
|
||||
mute = recv[0];
|
||||
|
||||
if (ucontrol->value.integer.value[0]) {
|
||||
cs4350_priv->mute |= 1;
|
||||
mute &= ~(1 << 4);
|
||||
#if SND_UBI32_DEBUG
|
||||
snd_printk(KERN_INFO "Unmuted channel A\n");
|
||||
#endif
|
||||
} else {
|
||||
cs4350_priv->mute &= ~1;
|
||||
mute |= (1 << 4);
|
||||
#if SND_UBI32_DEBUG
|
||||
snd_printk(KERN_INFO "Muted channel A\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
send[0] = 0x04;
|
||||
send[1] = mute;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set channel A mute on CS4350\n");
|
||||
return changed;
|
||||
}
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
if (((cs4350_priv->mute & 2) >> 1) != ucontrol->value.integer.value[1]) {
|
||||
send[0] = 0x04;
|
||||
ret = i2c_master_send(client, send, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed to write to mute register: channel 1\n");
|
||||
return changed;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, recv, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed to read mute register: channel 1\n");
|
||||
return changed;
|
||||
}
|
||||
|
||||
mute = recv[0];
|
||||
|
||||
if (ucontrol->value.integer.value[1]) {
|
||||
cs4350_priv->mute |= (1 << 1);
|
||||
mute &= ~(1 << 3);
|
||||
#if SND_UBI32_DEBUG
|
||||
snd_printk(KERN_INFO "Unmuted channel B\n");
|
||||
#endif
|
||||
} else {
|
||||
cs4350_priv->mute &= ~(1 << 1);
|
||||
mute |= (1 << 3);
|
||||
#if SND_UBI32_DEBUG
|
||||
snd_printk(KERN_INFO "Muted channel B\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
send[0] = 0x04;
|
||||
send[1] = mute;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set channel A mute on CS4350\n");
|
||||
return changed;
|
||||
}
|
||||
changed = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cs4350_priv->lock, flags);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new ubi32_cs4350_mute __devinitdata = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.name = "PCM Playback Switch",
|
||||
.info = ubi32_cs4350_mute_info,
|
||||
.get = ubi32_cs4350_mute_get,
|
||||
.put = ubi32_cs4350_mute_put,
|
||||
};
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4350_free
|
||||
* Card private data free function
|
||||
*/
|
||||
void snd_ubi32_cs4350_free(struct snd_card *card)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
struct ubi32_cs4350_priv *cs4350_priv;
|
||||
|
||||
ubi32_priv = card->private_data;
|
||||
cs4350_priv = snd_ubi32_priv_get_drv(ubi32_priv);
|
||||
if (cs4350_priv) {
|
||||
kfree(cs4350_priv);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4350_dac_init
|
||||
*/
|
||||
static int snd_ubi32_cs4350_dac_init(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
char send[2];
|
||||
char recv[8];
|
||||
|
||||
/*
|
||||
* Initialize the CS4350 DAC over the I2C interface
|
||||
*/
|
||||
snd_printk(KERN_INFO "Initializing CS4350 DAC\n");
|
||||
|
||||
/*
|
||||
* Register 0x01: device/revid
|
||||
*/
|
||||
send[0] = 0x01;
|
||||
ret = i2c_master_send(client, send, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed 1st attempt to write to CS4350 register 0x01\n");
|
||||
goto fail;
|
||||
}
|
||||
ret = i2c_master_recv(client, recv, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed initial read of CS4350 registers\n");
|
||||
goto fail;
|
||||
}
|
||||
snd_printk(KERN_INFO "CS4350 DAC Device/Rev: %08x\n", recv[0]);
|
||||
|
||||
/*
|
||||
* Register 0x02: Mode control
|
||||
* I2S DIF[2:0] = 001, no De-Emphasis, Auto speed mode
|
||||
*/
|
||||
send[0] = 0x02;
|
||||
send[1] = 0x10;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set CS4350 to I2S mode\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register 0x05/0x06: Volume control
|
||||
* Channel A volume set to 0 dB
|
||||
* Channel B volume set to 0 dB
|
||||
*/
|
||||
send[0] = 0x05;
|
||||
send[1] = 0x00;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
send[0] = 0x06;
|
||||
send[1] = 0x00;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set channel A volume on CS4350\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the changes took place, this helps verify we are talking to
|
||||
* the correct chip.
|
||||
*/
|
||||
send[0] = 0x81;
|
||||
ret = i2c_master_send(client, send, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed to initiate readback\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, recv, 8);
|
||||
if (ret != 8) {
|
||||
snd_printk(KERN_ERR "Failed second read of CS4350 registers\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((recv[1] != 0x10) || (recv[4] != 0x00) || (recv[5] != 0x00)) {
|
||||
snd_printk(KERN_ERR "Failed to initialize CS4350 DAC\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snd_printk(KERN_INFO "CS4350 DAC Initialized\n");
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4350_i2c_probe
|
||||
*/
|
||||
static int snd_ubi32_cs4350_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
struct ubi32_cs4350_priv *cs4350_priv;
|
||||
int err, ret;
|
||||
struct platform_device *pdev;
|
||||
|
||||
pdev = client->dev.platform_data;
|
||||
if (!pdev) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the CS4350 DAC
|
||||
*/
|
||||
ret = snd_ubi32_cs4350_dac_init(client, id);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Initialization failed. Propagate the error.
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a snd_card structure
|
||||
*/
|
||||
card = snd_card_new(index, "Ubi32-CS4350", THIS_MODULE, sizeof(struct ubi32_snd_priv));
|
||||
if (card == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->private_free = snd_ubi32_cs4350_free; /* Not sure if correct */
|
||||
ubi32_priv = card->private_data;
|
||||
|
||||
/*
|
||||
* CS4350 DAC has a minimum sample rate of 30khz and an
|
||||
* upper limit of 216khz for it's auto-detect.
|
||||
*/
|
||||
ubi32_priv->min_sample_rate = 30000;
|
||||
ubi32_priv->max_sample_rate = 216000;
|
||||
|
||||
/*
|
||||
* Initialize the snd_card's private data structure
|
||||
*/
|
||||
ubi32_priv->card = card;
|
||||
ubi32_priv->client = client;
|
||||
|
||||
/*
|
||||
* Create our private data structure
|
||||
*/
|
||||
cs4350_priv = kzalloc(sizeof(struct ubi32_cs4350_priv), GFP_KERNEL);
|
||||
if (!cs4350_priv) {
|
||||
snd_card_free(card);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_ubi32_priv_set_drv(ubi32_priv, cs4350_priv);
|
||||
spin_lock_init(&cs4350_priv->lock);
|
||||
|
||||
/*
|
||||
* Initial volume is set to max by probe function
|
||||
*/
|
||||
cs4350_priv->volume[0] = 0xFF;
|
||||
cs4350_priv->volume[1] = 0xFF;
|
||||
|
||||
/*
|
||||
* The CS4350 starts off unmuted (bit set = not muted)
|
||||
*/
|
||||
cs4350_priv->mute = 3;
|
||||
|
||||
/*
|
||||
* Create the new PCM instance
|
||||
*/
|
||||
err = snd_ubi32_pcm_probe(ubi32_priv, pdev);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err; /* What is err? Need to include correct file */
|
||||
}
|
||||
|
||||
strcpy(card->driver, "Ubi32-CS4350");
|
||||
strcpy(card->shortname, "Ubi32-CS4350");
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s at sendirq=%d.%d recvirq=%d.%d regs=%p",
|
||||
card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx,
|
||||
ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr);
|
||||
|
||||
snd_card_set_dev(card, &client->dev);
|
||||
|
||||
/*
|
||||
* Set up the mixer components
|
||||
*/
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&ubi32_cs4350_volume, ubi32_priv));
|
||||
if (err) {
|
||||
snd_printk(KERN_WARNING "Failed to add volume mixer control\n");
|
||||
}
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&ubi32_cs4350_mute, ubi32_priv));
|
||||
if (err) {
|
||||
snd_printk(KERN_WARNING "Failed to add mute mixer control\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Register the sound card
|
||||
*/
|
||||
if ((err = snd_card_register(card)) != 0) {
|
||||
snd_printk(KERN_WARNING "snd_card_register error\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Store card for access from other methods
|
||||
*/
|
||||
i2c_set_clientdata(client, card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4350_i2c_remove
|
||||
*/
|
||||
static int __devexit snd_ubi32_cs4350_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
|
||||
card = i2c_get_clientdata(client);
|
||||
|
||||
ubi32_priv = card->private_data;
|
||||
snd_ubi32_pcm_remove(ubi32_priv);
|
||||
|
||||
snd_card_free(i2c_get_clientdata(client));
|
||||
i2c_set_clientdata(client, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C driver description
|
||||
*/
|
||||
static struct i2c_driver snd_ubi32_cs4350_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = snd_ubi32_cs4350_id,
|
||||
.probe = snd_ubi32_cs4350_i2c_probe,
|
||||
.remove = __devexit_p(snd_ubi32_cs4350_i2c_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
* Driver init
|
||||
*/
|
||||
static int __init snd_ubi32_cs4350_init(void)
|
||||
{
|
||||
return i2c_add_driver(&snd_ubi32_cs4350_driver);
|
||||
}
|
||||
module_init(snd_ubi32_cs4350_init);
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4350_exit
|
||||
*/
|
||||
static void __exit snd_ubi32_cs4350_exit(void)
|
||||
{
|
||||
i2c_del_driver(&snd_ubi32_cs4350_driver);
|
||||
}
|
||||
module_exit(snd_ubi32_cs4350_exit);
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
MODULE_ALIAS("i2c:" DRIVER_NAME);
|
||||
MODULE_AUTHOR("Patrick Tjin");
|
||||
MODULE_DESCRIPTION("Driver for Ubicom32 audio devices CS4350");
|
||||
MODULE_LICENSE("GPL");
|
||||
996
target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4384.c
Normal file
996
target/linux/ubicom32/files/sound/ubicom32/ubi32-cs4384.c
Normal file
@@ -0,0 +1,996 @@
|
||||
/*
|
||||
* sound/ubicom32/ubi32-cs4384.c
|
||||
* Interface to ubicom32 virtual audio peripheral - using CS4384 DAC
|
||||
*
|
||||
* (C) Copyright 2009, Ubicom, Inc.
|
||||
*
|
||||
* This file is part of the Ubicom32 Linux Kernel Port.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/ip5000.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/audio.h>
|
||||
#include <asm/ubi32-cs4384.h>
|
||||
#include "ubi32.h"
|
||||
|
||||
#define DRIVER_NAME "snd-ubi32-cs4384"
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
static const struct i2c_device_id snd_ubi32_cs4384_id[] = {
|
||||
{"cs4384", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ubicom32audio_id);
|
||||
|
||||
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
|
||||
|
||||
/*
|
||||
* Mixer properties
|
||||
*/
|
||||
enum {
|
||||
/*
|
||||
* Be careful of changing the order of these IDs, they
|
||||
* are used to index the volume array.
|
||||
*/
|
||||
SND_UBI32_CS4384_FRONT_ID,
|
||||
SND_UBI32_CS4384_SURROUND_ID,
|
||||
SND_UBI32_CS4384_CENTER_ID,
|
||||
SND_UBI32_CS4384_LFE_ID,
|
||||
SND_UBI32_CS4384_REAR_ID,
|
||||
|
||||
/*
|
||||
* This should be the last ID
|
||||
*/
|
||||
SND_UBI32_CS4384_LAST_ID,
|
||||
};
|
||||
static const u8_t snd_ubi32_cs4384_ch_ofs[] = {0, 2, 4, 5, 6};
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(snd_ubi32_cs4384_db, -12750, 50, 0);
|
||||
|
||||
#define snd_ubi32_cs4384_info_mute snd_ctl_boolean_stereo_info
|
||||
#define snd_ubi32_cs4384_info_mute_mono snd_ctl_boolean_mono_info
|
||||
|
||||
/*
|
||||
* Mixer controls
|
||||
*/
|
||||
static int snd_ubi32_cs4384_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo);
|
||||
static int snd_ubi32_cs4384_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
|
||||
static int snd_ubi32_cs4384_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
|
||||
static int snd_ubi32_cs4384_get_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
|
||||
static int snd_ubi32_cs4384_put_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
|
||||
|
||||
/*
|
||||
* Make sure to update these if the structure below is changed
|
||||
*/
|
||||
#define SND_UBI32_MUTE_CTL_START 5
|
||||
#define SND_UBI32_MUTE_CTL_END 9
|
||||
static struct snd_kcontrol_new snd_ubi32_cs4384_controls[] __devinitdata = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Front Playback Volume",
|
||||
.info = snd_ubi32_cs4384_info_volume,
|
||||
.get = snd_ubi32_cs4384_get_volume,
|
||||
.put = snd_ubi32_cs4384_put_volume,
|
||||
.private_value = SND_UBI32_CS4384_FRONT_ID,
|
||||
.tlv = {
|
||||
.p = snd_ubi32_cs4384_db,
|
||||
},
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Surround Playback Volume",
|
||||
.info = snd_ubi32_cs4384_info_volume,
|
||||
.get = snd_ubi32_cs4384_get_volume,
|
||||
.put = snd_ubi32_cs4384_put_volume,
|
||||
.private_value = SND_UBI32_CS4384_SURROUND_ID,
|
||||
.tlv = {
|
||||
.p = snd_ubi32_cs4384_db,
|
||||
},
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Center Playback Volume",
|
||||
.info = snd_ubi32_cs4384_info_volume,
|
||||
.get = snd_ubi32_cs4384_get_volume,
|
||||
.put = snd_ubi32_cs4384_put_volume,
|
||||
.private_value = SND_UBI32_CS4384_CENTER_ID,
|
||||
.tlv = {
|
||||
.p = snd_ubi32_cs4384_db,
|
||||
},
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "LFE Playback Volume",
|
||||
.info = snd_ubi32_cs4384_info_volume,
|
||||
.get = snd_ubi32_cs4384_get_volume,
|
||||
.put = snd_ubi32_cs4384_put_volume,
|
||||
.private_value = SND_UBI32_CS4384_LFE_ID,
|
||||
.tlv = {
|
||||
.p = snd_ubi32_cs4384_db,
|
||||
},
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Rear Playback Volume",
|
||||
.info = snd_ubi32_cs4384_info_volume,
|
||||
.get = snd_ubi32_cs4384_get_volume,
|
||||
.put = snd_ubi32_cs4384_put_volume,
|
||||
.private_value = SND_UBI32_CS4384_REAR_ID,
|
||||
.tlv = {
|
||||
.p = snd_ubi32_cs4384_db,
|
||||
},
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Front Playback Switch",
|
||||
.info = snd_ubi32_cs4384_info_mute,
|
||||
.get = snd_ubi32_cs4384_get_mute,
|
||||
.put = snd_ubi32_cs4384_put_mute,
|
||||
.private_value = SND_UBI32_CS4384_FRONT_ID,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Surround Playback Switch",
|
||||
.info = snd_ubi32_cs4384_info_mute,
|
||||
.get = snd_ubi32_cs4384_get_mute,
|
||||
.put = snd_ubi32_cs4384_put_mute,
|
||||
.private_value = SND_UBI32_CS4384_SURROUND_ID,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Center Playback Switch",
|
||||
.info = snd_ubi32_cs4384_info_mute_mono,
|
||||
.get = snd_ubi32_cs4384_get_mute,
|
||||
.put = snd_ubi32_cs4384_put_mute,
|
||||
.private_value = SND_UBI32_CS4384_CENTER_ID,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "LFE Playback Switch",
|
||||
.info = snd_ubi32_cs4384_info_mute_mono,
|
||||
.get = snd_ubi32_cs4384_get_mute,
|
||||
.put = snd_ubi32_cs4384_put_mute,
|
||||
.private_value = SND_UBI32_CS4384_LFE_ID,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.name = "Rear Playback Switch",
|
||||
.info = snd_ubi32_cs4384_info_mute,
|
||||
.get = snd_ubi32_cs4384_get_mute,
|
||||
.put = snd_ubi32_cs4384_put_mute,
|
||||
.private_value = SND_UBI32_CS4384_REAR_ID,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Our private data
|
||||
*/
|
||||
struct snd_ubi32_cs4384_priv {
|
||||
/*
|
||||
* Array of current volumes
|
||||
* (L, R, SL, SR, C, LFE, RL, RR)
|
||||
*/
|
||||
uint8_t volume[8];
|
||||
|
||||
/*
|
||||
* Bitmask of mutes
|
||||
* MSB (RR, RL, LFE, C, SR, SL, R, L) LSB
|
||||
*/
|
||||
uint8_t mute;
|
||||
|
||||
/*
|
||||
* Array of controls
|
||||
*/
|
||||
struct snd_kcontrol *kctls[ARRAY_SIZE(snd_ubi32_cs4384_controls)];
|
||||
|
||||
/*
|
||||
* Lock to protect our card
|
||||
*/
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_info_volume
|
||||
*/
|
||||
static int snd_ubi32_cs4384_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
unsigned int id = (unsigned int)kcontrol->private_value;
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 1;
|
||||
if ((id != SND_UBI32_CS4384_LFE_ID) &&
|
||||
(id != SND_UBI32_CS4384_CENTER_ID)) {
|
||||
uinfo->count = 2;
|
||||
}
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_get_volume
|
||||
*/
|
||||
static int snd_ubi32_cs4384_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
unsigned int id = (unsigned int)kcontrol->private_value;
|
||||
int ch = snd_ubi32_cs4384_ch_ofs[id];
|
||||
unsigned long flags;
|
||||
|
||||
if (id >= SND_UBI32_CS4384_LAST_ID) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(priv);
|
||||
|
||||
spin_lock_irqsave(&cs4384_priv->lock, flags);
|
||||
|
||||
ucontrol->value.integer.value[0] = cs4384_priv->volume[ch];
|
||||
if ((id != SND_UBI32_CS4384_LFE_ID) &&
|
||||
(id != SND_UBI32_CS4384_CENTER_ID)) {
|
||||
ch++;
|
||||
ucontrol->value.integer.value[1] = cs4384_priv->volume[ch];
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cs4384_priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_put_volume
|
||||
*/
|
||||
static int snd_ubi32_cs4384_put_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol);
|
||||
struct i2c_client *client = (struct i2c_client *)priv->client;
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
unsigned int id = (unsigned int)kcontrol->private_value;
|
||||
int ch = snd_ubi32_cs4384_ch_ofs[id];
|
||||
unsigned long flags;
|
||||
unsigned char send[3];
|
||||
int nch;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (id >= SND_UBI32_CS4384_LAST_ID) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(priv);
|
||||
|
||||
spin_lock_irqsave(&cs4384_priv->lock, flags);
|
||||
|
||||
send[0] = 0;
|
||||
switch (id) {
|
||||
case SND_UBI32_CS4384_REAR_ID:
|
||||
send[0] = 0x06;
|
||||
|
||||
/*
|
||||
* Fall through
|
||||
*/
|
||||
|
||||
case SND_UBI32_CS4384_SURROUND_ID:
|
||||
send[0] += 0x03;
|
||||
|
||||
/*
|
||||
* Fall through
|
||||
*/
|
||||
|
||||
case SND_UBI32_CS4384_FRONT_ID:
|
||||
send[0] += 0x8B;
|
||||
nch = 2;
|
||||
send[1] = 255 - (ucontrol->value.integer.value[0] & 0xFF);
|
||||
send[2] = 255 - (ucontrol->value.integer.value[1] & 0xFF);
|
||||
cs4384_priv->volume[ch++] = send[1];
|
||||
cs4384_priv->volume[ch] = send[2];
|
||||
break;
|
||||
|
||||
case SND_UBI32_CS4384_LFE_ID:
|
||||
send[0] = 0x81;
|
||||
|
||||
/*
|
||||
* Fall through
|
||||
*/
|
||||
|
||||
case SND_UBI32_CS4384_CENTER_ID:
|
||||
send[0] += 0x11;
|
||||
nch = 1;
|
||||
send[1] = 255 - (ucontrol->value.integer.value[0] & 0xFF);
|
||||
cs4384_priv->volume[ch] = send[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
spin_unlock_irqrestore(&cs4384_priv->lock, flags);
|
||||
goto done;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the volume to the chip
|
||||
*/
|
||||
nch++;
|
||||
ret = i2c_master_send(client, send, nch);
|
||||
if (ret != nch) {
|
||||
snd_printk(KERN_ERR "Failed to set volume on CS4384\n");
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&cs4384_priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_get_mute
|
||||
*/
|
||||
static int snd_ubi32_cs4384_get_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
unsigned int id = (unsigned int)kcontrol->private_value;
|
||||
int ch = snd_ubi32_cs4384_ch_ofs[id];
|
||||
unsigned long flags;
|
||||
|
||||
if (id >= SND_UBI32_CS4384_LAST_ID) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(priv);
|
||||
|
||||
spin_lock_irqsave(&cs4384_priv->lock, flags);
|
||||
|
||||
ucontrol->value.integer.value[0] = !(cs4384_priv->mute & (1 << ch));
|
||||
|
||||
if ((id != SND_UBI32_CS4384_LFE_ID) &&
|
||||
(id != SND_UBI32_CS4384_CENTER_ID)) {
|
||||
ch++;
|
||||
ucontrol->value.integer.value[1] = !(cs4384_priv->mute & (1 << ch));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cs4384_priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_put_mute
|
||||
*/
|
||||
static int snd_ubi32_cs4384_put_mute(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct ubi32_snd_priv *priv = snd_kcontrol_chip(kcontrol);
|
||||
struct i2c_client *client = (struct i2c_client *)priv->client;
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
unsigned int id = (unsigned int)kcontrol->private_value;
|
||||
int ch = snd_ubi32_cs4384_ch_ofs[id];
|
||||
unsigned long flags;
|
||||
unsigned char send[2];
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (id >= SND_UBI32_CS4384_LAST_ID) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(priv);
|
||||
|
||||
spin_lock_irqsave(&cs4384_priv->lock, flags);
|
||||
|
||||
if (ucontrol->value.integer.value[0]) {
|
||||
cs4384_priv->mute &= ~(1 << ch);
|
||||
} else {
|
||||
cs4384_priv->mute |= (1 << ch);
|
||||
}
|
||||
|
||||
if ((id != SND_UBI32_CS4384_LFE_ID) && (id != SND_UBI32_CS4384_CENTER_ID)) {
|
||||
ch++;
|
||||
if (ucontrol->value.integer.value[1]) {
|
||||
cs4384_priv->mute &= ~(1 << ch);
|
||||
} else {
|
||||
cs4384_priv->mute |= (1 << ch);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the chip's mute reigster
|
||||
*/
|
||||
send[0] = 0x09;
|
||||
send[1] = cs4384_priv->mute;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set mute on CS4384\n");
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cs4384_priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_mixer
|
||||
* Setup the mixer controls
|
||||
*/
|
||||
static int __devinit snd_ubi32_cs4384_mixer(struct ubi32_snd_priv *priv)
|
||||
{
|
||||
struct snd_card *card = priv->card;
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
int i;
|
||||
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(priv);
|
||||
for (i = 0; i < ARRAY_SIZE(snd_ubi32_cs4384_controls); i++) {
|
||||
int err;
|
||||
|
||||
cs4384_priv->kctls[i] = snd_ctl_new1(&snd_ubi32_cs4384_controls[i], priv);
|
||||
err = snd_ctl_add(card, cs4384_priv->kctls[i]);
|
||||
if (err) {
|
||||
snd_printk(KERN_WARNING "Failed to add control %d\n", i);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_free
|
||||
* Card private data free function
|
||||
*/
|
||||
void snd_ubi32_cs4384_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
|
||||
ubi32_priv = card->private_data;
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(ubi32_priv);
|
||||
if (cs4384_priv) {
|
||||
kfree(cs4384_priv);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_setup_mclk
|
||||
*/
|
||||
static int snd_ubi32_cs4384_setup_mclk(struct ubi32_cs4384_platform_data *pdata)
|
||||
{
|
||||
struct ubicom32_io_port *ioa = (struct ubicom32_io_port *)RA;
|
||||
struct ubicom32_io_port *ioc = (struct ubicom32_io_port *)RC;
|
||||
struct ubicom32_io_port *iod = (struct ubicom32_io_port *)RD;
|
||||
struct ubicom32_io_port *ioe = (struct ubicom32_io_port *)RE;
|
||||
struct ubicom32_io_port *ioh = (struct ubicom32_io_port *)RH;
|
||||
unsigned int ctl0;
|
||||
unsigned int ctlx;
|
||||
unsigned int div;
|
||||
|
||||
div = pdata->mclk_entries[0].div;
|
||||
|
||||
ctl0 = (1 << 13);
|
||||
ctlx = ((div - 1) << 16) | (div / 2);
|
||||
|
||||
switch (pdata->mclk_src) {
|
||||
case UBI32_CS4384_MCLK_PWM_0:
|
||||
ioc->function |= 2;
|
||||
ioc->ctl0 |= ctl0;
|
||||
ioc->ctl1 = ctlx;
|
||||
if (!ioa->function) {
|
||||
ioa->function = 3;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_PWM_1:
|
||||
ioc->function |= 2;
|
||||
ioc->ctl0 |= ctl0 << 16;
|
||||
ioc->ctl2 = ctlx;
|
||||
if (!ioe->function) {
|
||||
ioe->function = 3;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_PWM_2:
|
||||
ioh->ctl0 |= ctl0;
|
||||
ioh->ctl1 = ctlx;
|
||||
if (!iod->function) {
|
||||
iod->function = 3;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_CLKDIV_1:
|
||||
ioa->gpio_mask &= (1 << 7);
|
||||
ioa->ctl1 &= ~(0x7F << 14);
|
||||
ioa->ctl1 |= ((div - 1) << 14);
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_OTHER:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_set_rate
|
||||
*/
|
||||
static int snd_ubi32_cs4384_set_rate(struct ubi32_snd_priv *priv, int rate)
|
||||
{
|
||||
struct ubi32_cs4384_platform_data *cpd = priv->pdata->priv_data;
|
||||
struct ubicom32_io_port *ioa = (struct ubicom32_io_port *)RA;
|
||||
struct ubicom32_io_port *ioc = (struct ubicom32_io_port *)RC;
|
||||
struct ubicom32_io_port *ioh = (struct ubicom32_io_port *)RH;
|
||||
unsigned int ctl;
|
||||
unsigned int div = 0;
|
||||
const u16_t mult[] = {64, 96, 128, 192, 256, 384, 512, 768, 1024};
|
||||
int i;
|
||||
int j;
|
||||
|
||||
|
||||
for (i = 0; i < sizeof(mult) / sizeof(u16_t); i++) {
|
||||
for (j = 0; j < cpd->n_mclk; j++) {
|
||||
if (((unsigned int)rate * (unsigned int)mult[i]) ==
|
||||
cpd->mclk_entries[j].rate) {
|
||||
div = cpd->mclk_entries[j].div;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctl = ((div - 1) << 16) | (div / 2);
|
||||
|
||||
switch (cpd->mclk_src) {
|
||||
case UBI32_CS4384_MCLK_PWM_0:
|
||||
ioc->ctl1 = ctl;
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_PWM_1:
|
||||
ioc->ctl2 = ctl;
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_PWM_2:
|
||||
ioh->ctl1 = ctl;
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_CLKDIV_1:
|
||||
ioa->ctl1 &= ~(0x7F << 14);
|
||||
ioa->ctl1 |= ((div - 1) << 14);
|
||||
return 0;
|
||||
|
||||
case UBI32_CS4384_MCLK_OTHER:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_set_channels
|
||||
* Mute unused channels
|
||||
*/
|
||||
static int snd_ubi32_cs4384_set_channels(struct ubi32_snd_priv *priv, int channels)
|
||||
{
|
||||
struct i2c_client *client = (struct i2c_client *)priv->client;
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
unsigned char send[2];
|
||||
int ret;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Only support 0, 2, 4, 6, 8 channels
|
||||
*/
|
||||
if ((channels > 8) || (channels & 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs4384_priv = snd_ubi32_priv_get_drv(priv);
|
||||
spin_lock_irqsave(&cs4384_priv->lock, flags);
|
||||
|
||||
/*
|
||||
* Address 09h, Mute control
|
||||
*/
|
||||
send[0] = 0x09;
|
||||
send[1] = (unsigned char)(0xFF << channels);
|
||||
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
|
||||
spin_unlock_irqrestore(&cs4384_priv->lock, flags);
|
||||
|
||||
/*
|
||||
* Notify the system that we changed the mutes
|
||||
*/
|
||||
cs4384_priv->mute = (unsigned char)(0xFF << channels);
|
||||
|
||||
for (i = SND_UBI32_MUTE_CTL_START; i < SND_UBI32_MUTE_CTL_END; i++) {
|
||||
snd_ctl_notify(priv->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&cs4384_priv->kctls[i]->id);
|
||||
}
|
||||
|
||||
if (ret != 2) {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_dac_init
|
||||
*/
|
||||
static int snd_ubi32_cs4384_dac_init(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
unsigned char send[2];
|
||||
unsigned char recv[2];
|
||||
|
||||
/*
|
||||
* Initialize the CS4384 DAC over the I2C interface
|
||||
*/
|
||||
snd_printk(KERN_INFO "Initializing CS4384 DAC\n");
|
||||
|
||||
/*
|
||||
* Register 0x01: device/revid
|
||||
*/
|
||||
send[0] = 0x01;
|
||||
ret = i2c_master_send(client, send, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed 1st attempt to write to CS4384 register 0x01\n");
|
||||
goto fail;
|
||||
}
|
||||
ret = i2c_master_recv(client, recv, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed initial read of CS4384 registers\n");
|
||||
goto fail;
|
||||
}
|
||||
snd_printk(KERN_INFO "CS4384 DAC Device/Rev: %08x\n", recv[0]);
|
||||
|
||||
/*
|
||||
* Register 0x02: Mode Control 1
|
||||
* Control Port Enable, PCM, All DACs enabled, Power Down
|
||||
*/
|
||||
send[0] = 0x02;
|
||||
send[1] = 0x81;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set CPEN CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register 0x08: Ramp and Mute
|
||||
* RMP_UP, RMP_DN, PAMUTE, DAMUTE
|
||||
*/
|
||||
send[0] = 0x08;
|
||||
send[1] = 0xBC;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set CPEN CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register 0x03: PCM Control
|
||||
* I2S DIF[3:0] = 0001, no De-Emphasis, Auto speed mode
|
||||
*/
|
||||
send[0] = 0x03;
|
||||
send[1] = 0x13;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to set CS4384 to I2S mode\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register 0x0B/0x0C: Volume control A1/B1
|
||||
* Register 0x0E/0x0F: Volume control A2/B2
|
||||
* Register 0x11/0x12: Volume control A3/B3
|
||||
* Register 0x14/0x15: Volume control A4/B4
|
||||
*/
|
||||
send[0] = 0x80 | 0x0B;
|
||||
send[1] = 0x00;
|
||||
send[2] = 0x00;
|
||||
ret = i2c_master_send(client, send, 3);
|
||||
if (ret != 3) {
|
||||
snd_printk(KERN_ERR "Failed to set ch1 volume on CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
send[0] = 0x80 | 0x0E;
|
||||
send[1] = 0x00;
|
||||
send[2] = 0x00;
|
||||
ret = i2c_master_send(client, send, 3);
|
||||
if (ret != 3) {
|
||||
snd_printk(KERN_ERR "Failed to set ch2 volume on CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
send[0] = 0x80 | 0x11;
|
||||
send[1] = 0x00;
|
||||
send[2] = 0x00;
|
||||
ret = i2c_master_send(client, send, 3);
|
||||
if (ret != 3) {
|
||||
snd_printk(KERN_ERR "Failed to set ch3 volume on CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
send[0] = 0x80 | 0x14;
|
||||
send[1] = 0x00;
|
||||
send[2] = 0x00;
|
||||
ret = i2c_master_send(client, send, 3);
|
||||
if (ret != 3) {
|
||||
snd_printk(KERN_ERR "Failed to set ch4 volume on CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register 09h: Mute control
|
||||
* Mute all (we will unmute channels as needed)
|
||||
*/
|
||||
send[0] = 0x09;
|
||||
send[1] = 0xFF;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to power up CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register 0x02: Mode Control 1
|
||||
* Control Port Enable, PCM, All DACs enabled, Power Up
|
||||
*/
|
||||
send[0] = 0x02;
|
||||
send[1] = 0x80;
|
||||
ret = i2c_master_send(client, send, 2);
|
||||
if (ret != 2) {
|
||||
snd_printk(KERN_ERR "Failed to power up CS4384\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the changes took place, this helps verify we are talking to
|
||||
* the correct chip.
|
||||
*/
|
||||
send[0] = 0x80 | 0x03;
|
||||
ret = i2c_master_send(client, send, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed to initiate readback\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, recv, 1);
|
||||
if (ret != 1) {
|
||||
snd_printk(KERN_ERR "Failed second read of CS4384 registers\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (recv[0] != 0x13) {
|
||||
snd_printk(KERN_ERR "Failed to initialize CS4384 DAC\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
snd_printk(KERN_INFO "CS4384 DAC Initialized\n");
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_i2c_probe
|
||||
*/
|
||||
static int snd_ubi32_cs4384_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
int err, ret;
|
||||
struct platform_device *pdev;
|
||||
struct ubi32_cs4384_platform_data *pdata;
|
||||
struct snd_ubi32_cs4384_priv *cs4384_priv;
|
||||
|
||||
/*
|
||||
* pdev is audio device
|
||||
*/
|
||||
pdev = client->dev.platform_data;
|
||||
if (!pdev) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* pdev->dev.platform_data is ubi32-pcm platform_data
|
||||
*/
|
||||
pdata = audio_device_priv(pdev);
|
||||
if (!pdata) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the CS4384 DAC
|
||||
*/
|
||||
ret = snd_ubi32_cs4384_dac_init(client, id);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Initialization failed. Propagate the error.
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (snd_ubi32_cs4384_setup_mclk(pdata)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a snd_card structure
|
||||
*/
|
||||
card = snd_card_new(index, "Ubi32-CS4384", THIS_MODULE, sizeof(struct ubi32_snd_priv));
|
||||
if (card == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->private_free = snd_ubi32_cs4384_free;
|
||||
ubi32_priv = card->private_data;
|
||||
|
||||
/*
|
||||
* Initialize the snd_card's private data structure
|
||||
*/
|
||||
ubi32_priv->card = card;
|
||||
ubi32_priv->client = client;
|
||||
ubi32_priv->set_channels = snd_ubi32_cs4384_set_channels;
|
||||
ubi32_priv->set_rate = snd_ubi32_cs4384_set_rate;
|
||||
|
||||
/*
|
||||
* CS4384 DAC has a minimum sample rate of 4khz and an
|
||||
* upper limit of 216khz for it's auto-detect.
|
||||
*/
|
||||
ubi32_priv->min_sample_rate = 4000;
|
||||
ubi32_priv->max_sample_rate = 216000;
|
||||
|
||||
/*
|
||||
* Create our private data (to manage volume, etc)
|
||||
*/
|
||||
cs4384_priv = kzalloc(sizeof(struct snd_ubi32_cs4384_priv), GFP_KERNEL);
|
||||
if (!cs4384_priv) {
|
||||
snd_card_free(card);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_ubi32_priv_set_drv(ubi32_priv, cs4384_priv);
|
||||
spin_lock_init(&cs4384_priv->lock);
|
||||
|
||||
/*
|
||||
* We start off all muted and max volume
|
||||
*/
|
||||
cs4384_priv->mute = 0xFF;
|
||||
memset(cs4384_priv->volume, 0xFF, 8);
|
||||
|
||||
/*
|
||||
* Create the new PCM instance
|
||||
*/
|
||||
err = snd_ubi32_pcm_probe(ubi32_priv, pdev);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err; /* What is err? Need to include correct file */
|
||||
}
|
||||
|
||||
strcpy(card->driver, "Ubi32-CS4384");
|
||||
strcpy(card->shortname, "Ubi32-CS4384");
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s at sendirq=%d.%d recvirq=%d.%d regs=%p",
|
||||
card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx,
|
||||
ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr);
|
||||
|
||||
snd_card_set_dev(card, &client->dev);
|
||||
|
||||
/*
|
||||
* Set up the mixer
|
||||
*/
|
||||
snd_ubi32_cs4384_mixer(ubi32_priv);
|
||||
|
||||
/*
|
||||
* Register the sound card
|
||||
*/
|
||||
if ((err = snd_card_register(card)) != 0) {
|
||||
snd_printk(KERN_INFO "snd_card_register error\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Store card for access from other methods
|
||||
*/
|
||||
i2c_set_clientdata(client, card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_i2c_remove
|
||||
*/
|
||||
static int __devexit snd_ubi32_cs4384_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
|
||||
card = i2c_get_clientdata(client);
|
||||
|
||||
ubi32_priv = card->private_data;
|
||||
snd_ubi32_pcm_remove(ubi32_priv);
|
||||
|
||||
snd_card_free(i2c_get_clientdata(client));
|
||||
i2c_set_clientdata(client, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C driver description
|
||||
*/
|
||||
static struct i2c_driver snd_ubi32_cs4384_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = snd_ubi32_cs4384_id,
|
||||
.probe = snd_ubi32_cs4384_i2c_probe,
|
||||
.remove = __devexit_p(snd_ubi32_cs4384_i2c_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
* Driver init
|
||||
*/
|
||||
static int __init snd_ubi32_cs4384_init(void)
|
||||
{
|
||||
return i2c_add_driver(&snd_ubi32_cs4384_driver);
|
||||
}
|
||||
module_init(snd_ubi32_cs4384_init);
|
||||
|
||||
/*
|
||||
* snd_ubi32_cs4384_exit
|
||||
*/
|
||||
static void __exit snd_ubi32_cs4384_exit(void)
|
||||
{
|
||||
i2c_del_driver(&snd_ubi32_cs4384_driver);
|
||||
}
|
||||
module_exit(snd_ubi32_cs4384_exit);
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
MODULE_ALIAS("i2c:" DRIVER_NAME);
|
||||
MODULE_AUTHOR("Patrick Tjin");
|
||||
MODULE_DESCRIPTION("Driver for Ubicom32 audio devices CS4384");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* sound/ubicom32/ubi32-generic-capture.c
|
||||
* Interface to ubicom32 virtual audio peripheral
|
||||
*
|
||||
* (C) Copyright 2009, Ubicom, Inc.
|
||||
*
|
||||
* This file is part of the Ubicom32 Linux Kernel Port.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Ubicom32 implementation derived from (with many thanks):
|
||||
* arch/m68knommu
|
||||
* arch/blackfin
|
||||
* arch/parisc
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include "ubi32.h"
|
||||
|
||||
#define DRIVER_NAME "snd-ubi32-generic-capture"
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
|
||||
|
||||
/*
|
||||
* Card private data free function
|
||||
*/
|
||||
void snd_ubi32_generic_capture_free(struct snd_card *card)
|
||||
{
|
||||
/*
|
||||
* Free all the fields in the snd_ubi32_priv struct
|
||||
*/
|
||||
// Nothing to free at this time because ubi32_priv just maintains pointers
|
||||
}
|
||||
|
||||
/*
|
||||
* Ubicom audio driver probe() method. Args change depending on whether we use
|
||||
* platform_device or i2c_device.
|
||||
*/
|
||||
static int snd_ubi32_generic_capture_probe(struct platform_device *dev)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Create a snd_card structure
|
||||
*/
|
||||
card = snd_card_new(index, "Ubi32-Generic-C", THIS_MODULE, sizeof(struct ubi32_snd_priv));
|
||||
|
||||
if (card == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->private_free = snd_ubi32_generic_capture_free; /* Not sure if correct */
|
||||
ubi32_priv = card->private_data;
|
||||
|
||||
/*
|
||||
* Initialize the snd_card's private data structure
|
||||
*/
|
||||
ubi32_priv->card = card;
|
||||
ubi32_priv->is_capture = 1;
|
||||
|
||||
/*
|
||||
* Create the new PCM instance
|
||||
*/
|
||||
err = snd_ubi32_pcm_probe(ubi32_priv, dev);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(card->driver, "Ubi32-Generic-C");
|
||||
strcpy(card->shortname, "Ubi32-Generic-C");
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s at sendirq=%d.%d recvirq=%d.%d regs=%p",
|
||||
card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx,
|
||||
ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr);
|
||||
|
||||
snd_card_set_dev(card, &dev->dev);
|
||||
|
||||
/* Register the sound card */
|
||||
if ((err = snd_card_register(card)) != 0) {
|
||||
snd_printk(KERN_INFO "snd_card_register error\n");
|
||||
}
|
||||
|
||||
/* Store card for access from other methods */
|
||||
platform_set_drvdata(dev, card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ubicom audio driver remove() method
|
||||
*/
|
||||
static int __devexit snd_ubi32_generic_capture_remove(struct platform_device *dev)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
|
||||
card = platform_get_drvdata(dev);
|
||||
ubi32_priv = card->private_data;
|
||||
snd_ubi32_pcm_remove(ubi32_priv);
|
||||
|
||||
snd_card_free(platform_get_drvdata(dev));
|
||||
platform_set_drvdata(dev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform driver definition
|
||||
*/
|
||||
static struct platform_driver snd_ubi32_generic_capture_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = snd_ubi32_generic_capture_probe,
|
||||
.remove = __devexit_p(snd_ubi32_generic_capture_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
* snd_ubi32_generic_capture_init
|
||||
*/
|
||||
static int __init snd_ubi32_generic_capture_init(void)
|
||||
{
|
||||
return platform_driver_register(&snd_ubi32_generic_capture_driver);
|
||||
}
|
||||
module_init(snd_ubi32_generic_capture_init);
|
||||
|
||||
/*
|
||||
* snd_ubi32_generic_capture_exit
|
||||
*/
|
||||
static void __exit snd_ubi32_generic_capture_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&snd_ubi32_generic_capture_driver);
|
||||
}
|
||||
module_exit(snd_ubi32_generic_capture_exit);
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
//#if defined(CONFIG_SND_UBI32_AUDIO_I2C)
|
||||
//MODULE_ALIAS("i2c:snd-ubi32");
|
||||
//#endif
|
||||
MODULE_AUTHOR("Patrick Tjin");
|
||||
MODULE_DESCRIPTION("Driver for Ubicom32 audio devices");
|
||||
MODULE_LICENSE("GPL");
|
||||
166
target/linux/ubicom32/files/sound/ubicom32/ubi32-generic.c
Normal file
166
target/linux/ubicom32/files/sound/ubicom32/ubi32-generic.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* sound/ubicom32/ubi32-generic.c
|
||||
* Interface to ubicom32 virtual audio peripheral
|
||||
*
|
||||
* (C) Copyright 2009, Ubicom, Inc.
|
||||
*
|
||||
* This file is part of the Ubicom32 Linux Kernel Port.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Ubicom32 implementation derived from (with many thanks):
|
||||
* arch/m68knommu
|
||||
* arch/blackfin
|
||||
* arch/parisc
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
#include "ubi32.h"
|
||||
|
||||
#define DRIVER_NAME "snd-ubi32-generic"
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
|
||||
|
||||
/*
|
||||
* Card private data free function
|
||||
*/
|
||||
void snd_ubi32_generic_free(struct snd_card *card)
|
||||
{
|
||||
/*
|
||||
* Free all the fields in the snd_ubi32_priv struct
|
||||
*/
|
||||
// Nothing to free at this time because ubi32_priv just maintains pointers
|
||||
}
|
||||
|
||||
/*
|
||||
* Ubicom audio driver probe() method. Args change depending on whether we use
|
||||
* platform_device or i2c_device.
|
||||
*/
|
||||
static int snd_ubi32_generic_probe(struct platform_device *dev)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Create a snd_card structure
|
||||
*/
|
||||
card = snd_card_new(index, "Ubi32-Generic", THIS_MODULE, sizeof(struct ubi32_snd_priv));
|
||||
|
||||
if (card == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->private_free = snd_ubi32_generic_free; /* Not sure if correct */
|
||||
ubi32_priv = card->private_data;
|
||||
|
||||
/*
|
||||
* Initialize the snd_card's private data structure
|
||||
*/
|
||||
ubi32_priv->card = card;
|
||||
|
||||
/*
|
||||
* Create the new PCM instance
|
||||
*/
|
||||
err = snd_ubi32_pcm_probe(ubi32_priv, dev);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(card->driver, "Ubi32-Generic");
|
||||
strcpy(card->shortname, "Ubi32-Generic");
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s at sendirq=%d.%d recvirq=%d.%d regs=%p",
|
||||
card->shortname, ubi32_priv->tx_irq, ubi32_priv->irq_idx,
|
||||
ubi32_priv->rx_irq, ubi32_priv->irq_idx, ubi32_priv->adr);
|
||||
|
||||
snd_card_set_dev(card, &dev->dev);
|
||||
|
||||
/* Register the sound card */
|
||||
if ((err = snd_card_register(card)) != 0) {
|
||||
snd_printk(KERN_INFO "snd_card_register error\n");
|
||||
}
|
||||
|
||||
/* Store card for access from other methods */
|
||||
platform_set_drvdata(dev, card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ubicom audio driver remove() method
|
||||
*/
|
||||
static int __devexit snd_ubi32_generic_remove(struct platform_device *dev)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct ubi32_snd_priv *ubi32_priv;
|
||||
|
||||
card = platform_get_drvdata(dev);
|
||||
ubi32_priv = card->private_data;
|
||||
snd_ubi32_pcm_remove(ubi32_priv);
|
||||
|
||||
snd_card_free(platform_get_drvdata(dev));
|
||||
platform_set_drvdata(dev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform driver definition
|
||||
*/
|
||||
static struct platform_driver snd_ubi32_generic_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = snd_ubi32_generic_probe,
|
||||
.remove = __devexit_p(snd_ubi32_generic_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
* snd_ubi32_generic_init
|
||||
*/
|
||||
static int __init snd_ubi32_generic_init(void)
|
||||
{
|
||||
return platform_driver_register(&snd_ubi32_generic_driver);
|
||||
}
|
||||
module_init(snd_ubi32_generic_init);
|
||||
|
||||
/*
|
||||
* snd_ubi32_generic_exit
|
||||
*/
|
||||
static void __exit snd_ubi32_generic_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&snd_ubi32_generic_driver);
|
||||
}
|
||||
module_exit(snd_ubi32_generic_exit);
|
||||
|
||||
/*
|
||||
* Module properties
|
||||
*/
|
||||
//#if defined(CONFIG_SND_UBI32_AUDIO_I2C)
|
||||
//MODULE_ALIAS("i2c:snd-ubi32");
|
||||
//#endif
|
||||
MODULE_AUTHOR("Aaron Jow, Patrick Tjin");
|
||||
MODULE_DESCRIPTION("Driver for Ubicom32 audio devices");
|
||||
MODULE_LICENSE("GPL");
|
||||
711
target/linux/ubicom32/files/sound/ubicom32/ubi32-pcm.c
Normal file
711
target/linux/ubicom32/files/sound/ubicom32/ubi32-pcm.c
Normal file
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
* sound/ubicom32/ubi32-pcm.c
|
||||
* Interface to ubicom32 virtual audio peripheral
|
||||
*
|
||||
* (C) Copyright 2009, Ubicom, Inc.
|
||||
*
|
||||
* This file is part of the Ubicom32 Linux Kernel Port.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Ubicom32 implementation derived from (with many thanks):
|
||||
* arch/m68knommu
|
||||
* arch/blackfin
|
||||
* arch/parisc
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <asm/ip5000.h>
|
||||
#include <asm/ubi32-pcm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include "ubi32.h"
|
||||
|
||||
struct ubi32_snd_runtime_data {
|
||||
dma_addr_t dma_buffer; /* Physical address of DMA buffer */
|
||||
dma_addr_t dma_buffer_end; /* First address beyond end of DMA buffer */
|
||||
size_t period_size;
|
||||
dma_addr_t period_ptr; /* Physical address of next period */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static void snd_ubi32_vp_int_set(struct snd_pcm *pcm)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv = pcm->private_data;
|
||||
ubi32_priv->ar->int_req |= (1 << ubi32_priv->irq_idx);
|
||||
ubicom32_set_interrupt(ubi32_priv->tx_irq);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t snd_ubi32_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
|
||||
struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream);
|
||||
struct audio_dev_regs *adr = ubi32_priv->adr;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data;
|
||||
|
||||
dma_addr_t read_pos;
|
||||
|
||||
snd_pcm_uframes_t frames;
|
||||
if (!adr->primary_os_buffer_ptr) {
|
||||
/*
|
||||
* If primary_os_buffer_ptr is NULL (e.g. right after the HW is started or
|
||||
* when the HW is stopped), then handle this case separately.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_pos = (dma_addr_t)adr->primary_os_buffer_ptr;
|
||||
frames = bytes_to_frames(runtime, read_pos - ubi32_rd->dma_buffer);
|
||||
if (frames == runtime->buffer_size) {
|
||||
frames = 0;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
/*
|
||||
* Audio trigger
|
||||
*/
|
||||
static int snd_ubi32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data;
|
||||
struct audio_dev_regs *adr = ubi32_priv->adr;
|
||||
struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data;
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_trigger cmd=%d=", cmd);
|
||||
#endif
|
||||
|
||||
if (adr->command != AUDIO_CMD_NONE) {
|
||||
snd_printk(KERN_WARNING "Can't send command to audio device at this time\n");
|
||||
// Set a timer to call this function back later. How to do this?
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set interrupt flag to indicate that we interrupted audio device
|
||||
* to send a command
|
||||
*/
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "START\n");
|
||||
#endif
|
||||
/*
|
||||
* Ready the DMA transfer
|
||||
*/
|
||||
ubi32_rd->period_ptr = ubi32_rd->dma_buffer;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "trigger period_ptr=%lx\n", (unsigned long)ubi32_rd->period_ptr);
|
||||
#endif
|
||||
adr->dma_xfer_requests[0].ptr = (void *)ubi32_rd->period_ptr;
|
||||
adr->dma_xfer_requests[0].ctr = ubi32_rd->period_size;
|
||||
adr->dma_xfer_requests[0].active = 1;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "xfer_request 0 ptr=0x%x ctr=%u\n", ubi32_rd->period_ptr, ubi32_rd->period_size);
|
||||
#endif
|
||||
|
||||
ubi32_rd->period_ptr += ubi32_rd->period_size;
|
||||
adr->dma_xfer_requests[1].ptr = (void *)ubi32_rd->period_ptr;
|
||||
adr->dma_xfer_requests[1].ctr = ubi32_rd->period_size;
|
||||
adr->dma_xfer_requests[1].active = 1;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "xfer_request 1 ptr=0x%x ctr=%u\n", ubi32_rd->period_ptr, ubi32_rd->period_size);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tell the VP that we want to begin playback by filling in the
|
||||
* command field and then interrupting the audio VP
|
||||
*/
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
adr->command = AUDIO_CMD_START;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "STOP\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tell the VP that we want to stop playback by filling in the
|
||||
* command field and then interrupting the audio VP
|
||||
*/
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
adr->command = AUDIO_CMD_STOP;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "PAUSE_PUSH\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tell the VP that we want to pause playback by filling in the
|
||||
* command field and then interrupting the audio VP
|
||||
*/
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
adr->command = AUDIO_CMD_PAUSE;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "PAUSE_RELEASE\n");
|
||||
#endif
|
||||
/*
|
||||
* Tell the VP that we want to resume paused playback by filling
|
||||
* in the command field and then interrupting the audio VP
|
||||
*/
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
adr->command = AUDIO_CMD_RESUME;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
break;
|
||||
|
||||
default:
|
||||
snd_printk(KERN_WARNING "Unhandled trigger\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare to transfer an audio stream to the codec
|
||||
*/
|
||||
static int snd_ubi32_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/*
|
||||
* Configure registers and setup the runtime instance for DMA transfers
|
||||
*/
|
||||
struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data;
|
||||
struct audio_dev_regs *adr = ubi32_priv->adr;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_prepare: sending STOP command to audio device\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure the audio device is stopped
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set interrupt flag to indicate that we interrupted audio device
|
||||
* to send a command
|
||||
*/
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
adr->command = AUDIO_CMD_STOP;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate DMA buffers from preallocated memory.
|
||||
* Preallocation was done in snd_ubi32_pcm_new()
|
||||
*/
|
||||
static int snd_ubi32_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ubi32_snd_priv *ubi32_priv = substream->pcm->private_data;
|
||||
struct audio_dev_regs *adr = ubi32_priv->adr;
|
||||
struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data;
|
||||
|
||||
/*
|
||||
* Use pre-allocated memory from ubi32_snd_pcm_new() to satisfy
|
||||
* this memory request.
|
||||
*/
|
||||
int ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_hw_params\n");
|
||||
#endif
|
||||
|
||||
if (!(adr->channel_mask & (1 << params_channels(hw_params)))) {
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_hw_params unsupported number of channels %d mask %08x\n", params_channels(hw_params), adr->channel_mask);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ubi32_priv->set_channels) {
|
||||
int ret = ubi32_priv->set_channels(ubi32_priv, params_channels(hw_params));
|
||||
if (ret) {
|
||||
snd_printk(KERN_WARNING "Unable to set channels to %d, ret=%d\n", params_channels(hw_params), ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (ubi32_priv->set_rate) {
|
||||
int ret = ubi32_priv->set_rate(ubi32_priv, params_rate(hw_params));
|
||||
if (ret) {
|
||||
snd_printk(KERN_WARNING "Unable to set rate to %d, ret=%d\n", params_rate(hw_params), ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (ubi32_priv->pdata->set_rate) {
|
||||
int ret = ubi32_priv->pdata->set_rate(ubi32_priv->pdata->appdata, params_rate(hw_params));
|
||||
if (ret) {
|
||||
snd_printk(KERN_WARNING "Unable to set rate to %d, ret=%d\n", params_rate(hw_params), ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (adr->command != AUDIO_CMD_NONE) {
|
||||
snd_printk(KERN_WARNING "snd_ubi32_pcm_hw_params: tio busy\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S16_LE) {
|
||||
adr->flags |= CMD_START_FLAG_LE;
|
||||
} else {
|
||||
adr->flags &= ~CMD_START_FLAG_LE;
|
||||
}
|
||||
adr->channels = params_channels(hw_params);
|
||||
adr->sample_rate = params_rate(hw_params);
|
||||
adr->command = AUDIO_CMD_SETUP;
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
|
||||
/*
|
||||
* Wait for the command to complete
|
||||
*/
|
||||
while (adr->command != AUDIO_CMD_NONE) {
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Put the DMA info into the DMA descriptor that we will
|
||||
* use to do transfers to our audio VP "hardware"
|
||||
*/
|
||||
|
||||
/*
|
||||
* Mark both DMA transfers as not ready/inactive
|
||||
*/
|
||||
adr->dma_xfer_requests[0].active = 0;
|
||||
adr->dma_xfer_requests[1].active = 0;
|
||||
|
||||
/*
|
||||
* Put the location of the buffer into the runtime data instance
|
||||
*/
|
||||
ubi32_rd->dma_buffer = (dma_addr_t)runtime->dma_area;
|
||||
ubi32_rd->dma_buffer_end = (dma_addr_t)(runtime->dma_area + runtime->dma_bytes);
|
||||
|
||||
/*
|
||||
* Get the period size
|
||||
*/
|
||||
ubi32_rd->period_size = params_period_bytes(hw_params);
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "DMA for ubi32 audio initialized dma_area=0x%x dma_bytes=%d, period_size=%d\n", (unsigned int)runtime->dma_area, (unsigned int)runtime->dma_bytes, ubi32_rd->period_size);
|
||||
snd_printk(KERN_INFO "Private buffer ubi32_rd: dma_buffer=0x%x dma_buffer_end=0x%x ret=%d\n", ubi32_rd->dma_buffer, ubi32_rd->dma_buffer_end, ret);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the reverse of snd_ubi32_pcm_hw_params
|
||||
*/
|
||||
static int snd_ubi32_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_hw_free\n");
|
||||
#endif
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
/*
|
||||
* Audio virtual peripheral capabilities (capture and playback are identical)
|
||||
*/
|
||||
static struct snd_pcm_hardware snd_ubi32_pcm_hw =
|
||||
{
|
||||
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.buffer_bytes_max = (64*1024),
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = 8184,//8184,//8176,
|
||||
.periods_min = 2,
|
||||
.periods_max = 255,
|
||||
.fifo_size = 0, // THIS IS IGNORED BY ALSA
|
||||
};
|
||||
|
||||
/*
|
||||
* We fill this in later
|
||||
*/
|
||||
static struct snd_pcm_hw_constraint_list ubi32_pcm_rates;
|
||||
|
||||
/*
|
||||
* snd_ubi32_pcm_close
|
||||
*/
|
||||
static int snd_ubi32_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/* Disable codec, stop DMA, free private data structures */
|
||||
//struct ubi32_snd_priv *ubi32_priv = snd_pcm_substream_chip(substream);
|
||||
struct ubi32_snd_runtime_data *ubi32_rd = substream->runtime->private_data;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_close\n");
|
||||
#endif
|
||||
|
||||
substream->runtime->private_data = NULL;
|
||||
|
||||
kfree(ubi32_rd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* snd_ubi32_pcm_open
|
||||
*/
|
||||
static int snd_ubi32_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ubi32_snd_runtime_data *ubi32_rd;
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "ubi32 pcm open\n");
|
||||
#endif
|
||||
|
||||
/* Associate capabilities with component */
|
||||
runtime->hw = snd_ubi32_pcm_hw;
|
||||
|
||||
/*
|
||||
* Inform ALSA about constraints of the audio device
|
||||
*/
|
||||
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &ubi32_pcm_rates);
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_INFO "invalid rate\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Force the buffer size to be an integer multiple of period size */
|
||||
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_INFO "invalid period\n");
|
||||
goto out;
|
||||
}
|
||||
/* Initialize structures/registers */
|
||||
ubi32_rd = kzalloc(sizeof(struct ubi32_snd_runtime_data), GFP_KERNEL);
|
||||
if (ubi32_rd == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
runtime->private_data = ubi32_rd;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_open returned 0\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
out:
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "snd_ubi32_pcm_open returned %d\n", ret);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops snd_ubi32_pcm_ops = {
|
||||
.open = snd_ubi32_pcm_open, /* Open */
|
||||
.close = snd_ubi32_pcm_close, /* Close */
|
||||
.ioctl = snd_pcm_lib_ioctl, /* Generic IOCTL handler */
|
||||
.hw_params = snd_ubi32_pcm_hw_params, /* Hardware parameters/capabilities */
|
||||
.hw_free = snd_ubi32_pcm_hw_free, /* Free function for hw_params */
|
||||
.prepare = snd_ubi32_pcm_prepare,
|
||||
.trigger = snd_ubi32_pcm_trigger,
|
||||
.pointer = snd_ubi32_pcm_pointer,
|
||||
};
|
||||
|
||||
/*
|
||||
* Interrupt handler that gets called when the audio device
|
||||
* interrupts Linux
|
||||
*/
|
||||
static irqreturn_t snd_ubi32_pcm_interrupt(int irq, void *appdata)
|
||||
{
|
||||
struct snd_pcm *pcm = (struct snd_pcm *)appdata;
|
||||
struct ubi32_snd_priv *ubi32_priv = pcm->private_data;
|
||||
struct audio_dev_regs *adr = ubi32_priv->adr;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct ubi32_snd_runtime_data *ubi32_rd;
|
||||
int dma_to_fill = 0;
|
||||
|
||||
/*
|
||||
* Check to see if the interrupt is for us
|
||||
*/
|
||||
if (!(ubi32_priv->ar->int_status & (1 << ubi32_priv->irq_idx))) {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the interrupt
|
||||
*/
|
||||
ubi32_priv->ar->int_status &= ~(1 << ubi32_priv->irq_idx);
|
||||
|
||||
/*
|
||||
* We only have one stream since we don't mix. Therefore
|
||||
* we don't need to search through substreams.
|
||||
*/
|
||||
if (ubi32_priv->is_capture) {
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||
} else {
|
||||
substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
}
|
||||
|
||||
if (!substream->runtime) {
|
||||
snd_printk(KERN_WARNING "No runtime data\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ubi32_rd = substream->runtime->private_data;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "Ubi32 ALSA interrupt\n");
|
||||
#endif
|
||||
|
||||
if (ubi32_rd == NULL) {
|
||||
snd_printk(KERN_WARNING "No private data\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
// Check interrupt cause
|
||||
if (0) {
|
||||
// Handle the underflow case
|
||||
} else if ((adr->status & AUDIO_STATUS_PLAY_DMA0_REQUEST) ||
|
||||
(adr->status & AUDIO_STATUS_PLAY_DMA1_REQUEST)) {
|
||||
if (adr->status & AUDIO_STATUS_PLAY_DMA0_REQUEST) {
|
||||
dma_to_fill = 0;
|
||||
adr->status &= ~AUDIO_STATUS_PLAY_DMA0_REQUEST;
|
||||
} else if (adr->status & AUDIO_STATUS_PLAY_DMA1_REQUEST) {
|
||||
dma_to_fill = 1;
|
||||
adr->status &= ~AUDIO_STATUS_PLAY_DMA1_REQUEST;
|
||||
}
|
||||
ubi32_rd->period_ptr += ubi32_rd->period_size;
|
||||
if (ubi32_rd->period_ptr >= ubi32_rd->dma_buffer_end) {
|
||||
ubi32_rd->period_ptr = ubi32_rd->dma_buffer;
|
||||
}
|
||||
adr->dma_xfer_requests[dma_to_fill].ptr = (void *)ubi32_rd->period_ptr;
|
||||
adr->dma_xfer_requests[dma_to_fill].ctr = ubi32_rd->period_size;
|
||||
adr->dma_xfer_requests[dma_to_fill].active = 1;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
snd_printk(KERN_INFO "xfer_request %d ptr=0x%x ctr=%u\n", dma_to_fill, ubi32_rd->period_ptr, ubi32_rd->period_size);
|
||||
#endif
|
||||
adr->int_flags |= AUDIO_INT_FLAG_MORE_SAMPLES;
|
||||
snd_ubi32_vp_int_set(substream->pcm);
|
||||
}
|
||||
// If we are interrupted by the VP, that means we completed
|
||||
// processing one period of audio. We need to inform the upper
|
||||
// layers of ALSA of this.
|
||||
snd_pcm_period_elapsed(substream);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void __devexit snd_ubi32_pcm_remove(struct ubi32_snd_priv *ubi32_priv)
|
||||
{
|
||||
struct snd_pcm *pcm = ubi32_priv->pcm;
|
||||
free_irq(ubi32_priv->rx_irq, pcm);
|
||||
}
|
||||
|
||||
#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
|
||||
#error "Change this table to match pcm.h"
|
||||
#endif
|
||||
static unsigned int rates[] __initdata = {5512, 8000, 11025, 16000, 22050,
|
||||
32000, 44100, 48000, 64000, 88200,
|
||||
96000, 176400, 192000};
|
||||
|
||||
/*
|
||||
* snd_ubi32_pcm_probe
|
||||
*/
|
||||
int __devinit snd_ubi32_pcm_probe(struct ubi32_snd_priv *ubi32_priv, struct platform_device *pdev)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int ret, err;
|
||||
int i;
|
||||
int j;
|
||||
int nrates;
|
||||
unsigned int rate_max = 0;
|
||||
unsigned int rate_min = 0xFFFFFFFF;
|
||||
unsigned int rate_mask = 0;
|
||||
struct audio_dev_regs *adr;
|
||||
struct resource *res_adr;
|
||||
struct resource *res_irq_tx;
|
||||
struct resource *res_irq_rx;
|
||||
struct ubi32pcm_platform_data *pdata;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get our resources, adr is the hardware driver base address
|
||||
* and the tx and rx irqs are used to communicate with the
|
||||
* hardware driver.
|
||||
*/
|
||||
res_adr = platform_get_resource(pdev, IORESOURCE_MEM, AUDIO_MEM_RESOURCE);
|
||||
res_irq_tx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIO_TX_IRQ_RESOURCE);
|
||||
res_irq_rx = platform_get_resource(pdev, IORESOURCE_IRQ, AUDIO_RX_IRQ_RESOURCE);
|
||||
if (!res_adr || !res_irq_tx || !res_irq_rx) {
|
||||
snd_printk(KERN_WARNING "Could not get resources");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ubi32_priv->ar = (struct audio_regs *)res_adr->start;
|
||||
ubi32_priv->tx_irq = res_irq_tx->start;
|
||||
ubi32_priv->rx_irq = res_irq_rx->start;
|
||||
ubi32_priv->irq_idx = pdata->inst_num;
|
||||
ubi32_priv->adr = &(ubi32_priv->ar->adr[pdata->inst_num]);
|
||||
|
||||
/*
|
||||
* Check the version
|
||||
*/
|
||||
adr = ubi32_priv->adr;
|
||||
if (adr->version != AUDIO_DEV_REGS_VERSION) {
|
||||
snd_printk(KERN_WARNING "This audio_dev_reg is not compatible with this driver\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out the standard rates, also find max and min rates
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(rates); i++) {
|
||||
int found = 0;
|
||||
for (j = 0; j < adr->n_sample_rates; j++) {
|
||||
if (rates[i] == adr->sample_rates[j]) {
|
||||
/*
|
||||
* Check to see if it is supported by the dac
|
||||
*/
|
||||
if ((rates[i] >= ubi32_priv->min_sample_rate) &&
|
||||
(!ubi32_priv->max_sample_rate ||
|
||||
(ubi32_priv->max_sample_rate && (rates[i] <= ubi32_priv->max_sample_rate)))) {
|
||||
found = 1;
|
||||
rate_mask |= (1 << i);
|
||||
nrates++;
|
||||
if (rates[i] < rate_min) {
|
||||
rate_min = rates[i];
|
||||
}
|
||||
if (rates[i] > rate_max) {
|
||||
rate_max = rates[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rate_mask |= SNDRV_PCM_RATE_KNOT;
|
||||
}
|
||||
}
|
||||
|
||||
snd_ubi32_pcm_hw.rates = rate_mask;
|
||||
snd_ubi32_pcm_hw.rate_min = rate_min;
|
||||
snd_ubi32_pcm_hw.rate_max = rate_max;
|
||||
ubi32_pcm_rates.count = adr->n_sample_rates;
|
||||
ubi32_pcm_rates.list = (unsigned int *)adr->sample_rates;
|
||||
ubi32_pcm_rates.mask = 0;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (adr->channel_mask & (1 << i)) {
|
||||
if (!snd_ubi32_pcm_hw.channels_min) {
|
||||
snd_ubi32_pcm_hw.channels_min = i;
|
||||
}
|
||||
snd_ubi32_pcm_hw.channels_max = i;
|
||||
}
|
||||
}
|
||||
snd_printk(KERN_INFO "Ubi32PCM: channels_min:%u channels_max:%u\n",
|
||||
snd_ubi32_pcm_hw.channels_min,
|
||||
snd_ubi32_pcm_hw.channels_max);
|
||||
|
||||
if (adr->caps & AUDIONODE_CAP_BE) {
|
||||
snd_ubi32_pcm_hw.formats |= SNDRV_PCM_FMTBIT_S16_BE;
|
||||
}
|
||||
if (adr->caps & AUDIONODE_CAP_LE) {
|
||||
snd_ubi32_pcm_hw.formats |= SNDRV_PCM_FMTBIT_S16_LE;
|
||||
}
|
||||
|
||||
snd_printk(KERN_INFO "Ubi32PCM: rates:%08x min:%u max:%u count:%d fmts:%016llx (%s)\n",
|
||||
snd_ubi32_pcm_hw.rates,
|
||||
snd_ubi32_pcm_hw.rate_min,
|
||||
snd_ubi32_pcm_hw.rate_max,
|
||||
ubi32_pcm_rates.count,
|
||||
snd_ubi32_pcm_hw.formats,
|
||||
ubi32_priv->is_capture ? "capture" : "playback");
|
||||
|
||||
if (ubi32_priv->is_capture) {
|
||||
ret = snd_pcm_new(ubi32_priv->card, "Ubi32 PCM", 0, 0, 1, &pcm);
|
||||
} else {
|
||||
ret = snd_pcm_new(ubi32_priv->card, "Ubi32 PCM", 0, 1, 0, &pcm);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcm->private_data = ubi32_priv;
|
||||
ubi32_priv->pcm = pcm;
|
||||
ubi32_priv->pdata = pdata;
|
||||
|
||||
pcm->info_flags = 0;
|
||||
|
||||
strcpy(pcm->name, "Ubi32-PCM");
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
45*1024, 64*1024);
|
||||
|
||||
if (ubi32_priv->is_capture) {
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ubi32_pcm_ops);
|
||||
} else {
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ubi32_pcm_ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start up the audio device
|
||||
*/
|
||||
adr->int_flags |= AUDIO_INT_FLAG_COMMAND;
|
||||
adr->command = AUDIO_CMD_ENABLE;
|
||||
snd_ubi32_vp_int_set(pcm);
|
||||
|
||||
/*
|
||||
* Request IRQ
|
||||
*/
|
||||
err = request_irq(ubi32_priv->rx_irq, snd_ubi32_pcm_interrupt, IRQF_SHARED | IRQF_DISABLED, pcm->name, pcm);
|
||||
if (err) {
|
||||
snd_printk(KERN_WARNING "request_irq failed: irq=%d err=%d\n", ubi32_priv->rx_irq, err);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
102
target/linux/ubicom32/files/sound/ubicom32/ubi32.h
Normal file
102
target/linux/ubicom32/files/sound/ubicom32/ubi32.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* sound/ubicom32/ubi32.h
|
||||
* Common header file for all ubi32- sound drivers
|
||||
*
|
||||
* (C) Copyright 2009, Ubicom, Inc.
|
||||
*
|
||||
* This file is part of the Ubicom32 Linux Kernel Port.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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.
|
||||
*
|
||||
* The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _UBI32_H
|
||||
#define _UBI32_H
|
||||
|
||||
#define SND_UBI32_DEBUG 0 // Debug flag
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/devtree.h>
|
||||
#include <asm/audio.h>
|
||||
#include <asm/ubi32-pcm.h>
|
||||
|
||||
struct ubi32_snd_priv;
|
||||
|
||||
typedef int (*set_channels_t)(struct ubi32_snd_priv *priv, int channels);
|
||||
typedef int (*set_rate_t)(struct ubi32_snd_priv *priv, int rate);
|
||||
|
||||
struct ubi32_snd_priv {
|
||||
/*
|
||||
* Any variables that are needed locally here but NOT in
|
||||
* the VP itself should go in here.
|
||||
*/
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
/*
|
||||
* capture (1) or playback (0)
|
||||
*/
|
||||
int is_capture;
|
||||
/*
|
||||
* DAC parameters. These are the parameters for the specific
|
||||
* DAC we are driving. The I2S component can run at a range
|
||||
* of frequencies, but the DAC may be limited. We may want
|
||||
* to make this an array of some sort in the future?
|
||||
*
|
||||
* min/max_sample_rate if set to 0 are ignored.
|
||||
*/
|
||||
int max_sample_rate;
|
||||
int min_sample_rate;
|
||||
|
||||
/*
|
||||
* The size a period (group) of audio samples. The VP does
|
||||
* not need to know this; each DMA transfer is made to be
|
||||
* one period.
|
||||
*/
|
||||
u32_t period_size;
|
||||
|
||||
spinlock_t ubi32_lock;
|
||||
|
||||
struct audio_regs *ar;
|
||||
struct audio_dev_regs *adr;
|
||||
u32 irq_idx;
|
||||
u8 tx_irq;
|
||||
u8 rx_irq;
|
||||
|
||||
void *client;
|
||||
|
||||
/*
|
||||
* Operations which the base DAC driver can implement
|
||||
*/
|
||||
set_channels_t set_channels;
|
||||
set_rate_t set_rate;
|
||||
|
||||
/*
|
||||
* platform data
|
||||
*/
|
||||
struct ubi32pcm_platform_data *pdata;
|
||||
|
||||
/*
|
||||
* Private driver data (used for DAC driver control, etc)
|
||||
*/
|
||||
void *drvdata;
|
||||
};
|
||||
|
||||
#define snd_ubi32_priv_get_drv(priv) ((priv)->drvdata)
|
||||
#define snd_ubi32_priv_set_drv(priv, data) (((priv)->drvdata) = (void *)(data))
|
||||
|
||||
extern int snd_ubi32_pcm_probe(struct ubi32_snd_priv *ubi32_priv, struct platform_device *pdev);
|
||||
extern void snd_ubi32_pcm_remove(struct ubi32_snd_priv *ubi32_priv);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user