add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

224
kernel/sound/stm/Kconfig Normal file
View File

@@ -0,0 +1,224 @@
menuconfig SND_STM
tristate "STMicroelectronics SoCs support"
depends on SND
depends on CPU_SUBTYPE_ST40
select STM_DMA
select SND_PCM
help
Say Y here for ALSA support on the STMicroelectronics SoCs.
This driver provides core functionality - you have to build
(at least one) SoC-specific driver as well. Additionally, it is
using dynamic device minor number allocation, so udev-like
utility is required.
It is recommended to have BPA2 enabled with dedicated
partition defined, however it is not absolutely necessary.
To compile this driver as a module, choose M here: the module
will be called snd-stm.ko
config SND_STM_PCM_PLAYER
tristate
depends on SND_STM
default n
config SND_STM_PCM_READER
tristate
depends on SND_STM
default n
config SND_STM_SPDIF_PLAYER
tristate
depends on SND_STM
default n
config SND_STM_CONV_DAC_MEMORY_MAPPED
tristate
depends on SND_STM
default n
config SND_STM_CONV_DAC_SYSCONF
tristate
depends on SND_STM
default n
config SND_STM_CONV_I2S_TO_SPDIF
tristate
depends on SND_STM
default n
config SND_STM_BPA2_PARTITION_NAME
string "Name of BPA2 partition to allocate buffers from"
depends on SND_STM
depends on BPA2
default "audio"
help
Define a name of BPA2 partition the driver should try to
allocate from.
If name is not given or given partition does not exist, driver
will try to use "bigphysmem" partition or eventually
preallocate some system memory using
ALSA snd_pcm_lib_preallocate_pages_for_all() method.
This value can be overridden by module parameter
"bpa2_part=<name>" (or kernel command line parameter
"snd-stm.bpa2_part=<name>", if driver is compiled into
kernel).
comment "SOC support"
depends on SND_STM
config SND_STM_FLI7510
tristate "Freeman 510/520/530/540 support"
depends on SND_STM
default y if CPU_SUBTYPE_FLI7510
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
help
Select this to get ALSA driver for Freeman 510/520/530/540
(FLI7510/FLI7520/FLI7530/FLI7540) chips.
If you compile this driver as a module, it will be
called snd-stm-fli7510.ko
config SND_STM_STX7100
tristate "STx7100 and STx7109 support"
depends on SND_STM
default y if CPU_SUBTYPE_STX7100
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
select SND_STM_CONV_DAC_MEMORY_MAPPED
select SND_STM_CONV_I2S_TO_SPDIF
help
Select this to get ALSA driver for STx7100 and STx7109 chips.
If you compile this driver as a module, it will be
called snd-stm-stx7100.ko
config SND_STM_STX7105
tristate "STx7105 and STx7106 support"
depends on SND_STM
default y if CPU_SUBTYPE_STX7105
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
select SND_STM_CONV_DAC_MEMORY_MAPPED
select SND_STM_CONV_I2S_TO_SPDIF
help
Select this to get ALSA driver for STx7105 and STx7106 chips.
If you compile this driver as a module, it will be
called snd-stm-stx7105.ko
config SND_STM_STX7108
tristate "STx7108 support"
depends on SND_STM
default y if CPU_SUBTYPE_STX7108
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
select SND_STM_CONV_DAC_SYSCONF
select SND_STM_CONV_I2S_TO_SPDIF
help
Select this to get ALSA driver for STx7108 chip.
If you compile this driver as a module, it will be
called snd-stm-stx7108.ko
config SND_STM_STX7111
tristate "STx7111 support"
depends on SND_STM
default y if CPU_SUBTYPE_STX7111
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
select SND_STM_CONV_DAC_MEMORY_MAPPED
select SND_STM_CONV_I2S_TO_SPDIF
help
Select this to get ALSA driver for STx7111 chip.
If you compile this driver as a module, it will be
called snd-stm-stx7111.ko
config SND_STM_STX7141
tristate "STx7141 support"
depends on SND_STM
default y if CPU_SUBTYPE_STX7141
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
select SND_STM_CONV_DAC_MEMORY_MAPPED
select SND_STM_CONV_I2S_TO_SPDIF
help
Select this to get ALSA driver for STx7141 chip.
If you compile this driver as a module, it will be
called snd-stm-stx7141.ko
config SND_STM_STX7200
tristate "STx7200 support"
depends on SND_STM
default y if CPU_SUBTYPE_STX7200
select SND_STM_PCM_PLAYER
select SND_STM_PCM_READER
select SND_STM_SPDIF_PLAYER
select SND_STM_CONV_DAC_MEMORY_MAPPED
select SND_STM_CONV_I2S_TO_SPDIF
help
Select this to get ALSA driver for STx7200 chip.
If you compile this driver as a module, it will be
called snd-stm-stx7111.ko
comment "External converters"
depends on SND_STM
config SND_STM_CONV_DUMMY
tristate "Dummy converter driver"
depends on SND_STM
help
This provides dummy converter driver to configure
digital audio interface format (I2S, Left-Justified,
Right-Justified). See include/sound/stm.h for
more information.
If compiled as module, it will be called
snd-stm-conv-dummy.ko.
config SND_STM_CONV_EPLD
tristate "Generic EPLD-controlled converter driver"
depends on SND_STM
depends on SH_ST_EPLD
help
This provides generic driver for EPLD-controlled
converters. See include/sound/stm.h for informations
how to define EPLD offsets & values.
If compiled as module, it will be called
snd-stm-conv-epld.ko.
config SND_STM_CONV_GPIO
tristate "Generic GPIO-controlled converter driver"
depends on SND_STM
help
This provides generic driver for GPIO-controlled
converters. See include/sound/stm.h for informations
how to define GPIO line numbers.
If compiled as module, it will be called
snd-stm-conv-gpio.ko.
config SND_STM_CONV_I2C
tristate "Generic I2C-controlled converter driver"
depends on SND_STM && I2C
help
This provides generic driver for I2C-controlled
converters. See include/sound/stm.h for informations
how to define required I2C addresses & commands.
If compiled as module, it will be called
snd-stm-conv-i2c.ko.

63
kernel/sound/stm/Makefile Normal file
View File

@@ -0,0 +1,63 @@
# Core
obj-$(CONFIG_SND_STM) += snd-stm-core.o
snd-stm-core-y := core.o clock.o conv.o
obj-$(CONFIG_SND_STM_PCM_PLAYER) += snd-stm-pcm-player.o
snd-stm-pcm-player-y := pcm_player.o
obj-$(CONFIG_SND_STM_PCM_READER) += snd-stm-pcm-reader.o
snd-stm-pcm-reader-y := pcm_reader.o
obj-$(CONFIG_SND_STM_SPDIF_PLAYER) += snd-stm-spdif-player.o
snd-stm-spdif-player-y := spdif_player.o
obj-$(CONFIG_SND_STM_CONV_I2S_TO_SPDIF) += snd-stm-conv-i2sspdif.o
snd-stm-conv-i2sspdif-y := conv_i2sspdif.o
obj-$(CONFIG_SND_STM_CONV_DAC_MEMORY_MAPPED) += snd-stm-conv-dac-mem.o
snd-stm-conv-dac-mem-y := conv_dac_mem.o
obj-$(CONFIG_SND_STM_CONV_DAC_SYSCONF) += snd-stm-conv-dac-sc.o
snd-stm-conv-dac-sc-y := conv_dac_sc.o
# SOCs
obj-$(CONFIG_SND_STM_FLI7510) += snd-stm-fli7510.o
snd-stm-fli7510-y := fli7510.o
obj-$(CONFIG_SND_STM_STX7100) += snd-stm-stx7100.o
snd-stm-stx7100-y := stx7100.o
obj-$(CONFIG_SND_STM_STX7105) += snd-stm-stx7105.o
snd-stm-stx7105-y := stx7105.o
obj-$(CONFIG_SND_STM_STX7108) += snd-stm-stx7108.o
snd-stm-stx7108-y := stx7108.o
obj-$(CONFIG_SND_STM_STX7111) += snd-stm-stx7111.o
snd-stm-stx7111-y := stx7111.o
obj-$(CONFIG_SND_STM_STX7141) += snd-stm-stx7141.o
snd-stm-stx7141-y := stx7141.o
obj-$(CONFIG_SND_STM_STX7200) += snd-stm-stx7200.o
snd-stm-stx7200-y := stx7200.o
# External converters
obj-$(CONFIG_SND_STM_CONV_DUMMY) += snd-stm-conv-dummy.o
snd-stm-conv-dummy-y := conv_dummy.o
obj-$(CONFIG_SND_STM_CONV_EPLD) += snd-stm-conv-epld.o
snd-stm-conv-epld-y := conv_epld.o
obj-$(CONFIG_SND_STM_CONV_GPIO) += snd-stm-conv-gpio.o
snd-stm-conv-gpio-y := conv_gpio.o
obj-$(CONFIG_SND_STM_CONV_I2C) += snd-stm-conv-i2c.o
snd-stm-conv-i2c-y := conv_i2c.o

379
kernel/sound/stm/clock.c Normal file
View File

@@ -0,0 +1,379 @@
/*
* STMicroelectronics SOC audio clocks wrapper
*
* Copyright (c) 2010-2011 STMicroelectronics Limited
*
* Authors: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <asm/clock.h>
#include <asm/div64.h>
#include <sound/core.h>
#define COMPONENT clock
#include "common.h"
extern int snd_stm_debug_level;
#define to_snd_stm_clk(clk) \
container_of(clk, struct snd_stm_clk, clk)
struct snd_stm_clk_parent {
struct list_head list;
struct clk *parent;
int children_count, ref_count;
};
struct snd_stm_clk {
struct clk clk;
struct snd_stm_clk_parent *parent;
unsigned long nominal_rate;
int adjustment; /* Difference from the nominal rate, in ppm */
unsigned int enabled:1;
snd_stm_magic_field;
};
/* Adjustment ALSA control */
static int snd_stm_clk_adjustment_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = -999999;
uinfo->value.integer.max = 1000000;
return 0;
}
static int snd_stm_clk_adjustment_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_stm_clk *snd_stm_clk = snd_kcontrol_chip(kcontrol);
snd_stm_printd(1, "%s(kcontrol=0x%p, ucontrol=0x%p)\n",
__func__, kcontrol, ucontrol);
BUG_ON(!snd_stm_clk);
BUG_ON(!snd_stm_magic_valid(snd_stm_clk));
ucontrol->value.integer.value[0] = snd_stm_clk->adjustment;
return 0;
}
static int snd_stm_clk_adjustment_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_stm_clk *snd_stm_clk = snd_kcontrol_chip(kcontrol);
int old_adjustement;
snd_stm_printd(1, "%s(kcontrol=0x%p, ucontrol=0x%p)\n",
__func__, kcontrol, ucontrol);
BUG_ON(!snd_stm_clk);
BUG_ON(!snd_stm_magic_valid(snd_stm_clk));
BUG_ON(ucontrol->value.integer.value[0] < -999999);
BUG_ON(ucontrol->value.integer.value[0] > 1000000);
old_adjustement = snd_stm_clk->adjustment;
snd_stm_clk->adjustment = ucontrol->value.integer.value[0];
if (snd_stm_clk->enabled && clk_set_rate(&snd_stm_clk->clk,
snd_stm_clk->nominal_rate) < 0)
return -EINVAL;
return old_adjustement != snd_stm_clk->adjustment;
}
static struct snd_kcontrol_new snd_stm_clk_adjustment_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Oversampling Freq. Adjustment",
.info = snd_stm_clk_adjustment_info,
.get = snd_stm_clk_adjustment_get,
.put = snd_stm_clk_adjustment_put,
};
/* Clock interface */
static LIST_HEAD(snd_stm_clk_parents);
static DEFINE_SPINLOCK(snd_stm_clk_parents_lock);
int snd_stm_clk_enable(struct clk *clk)
{
int result = -EINVAL;
struct snd_stm_clk *snd_stm_clk;
snd_stm_printd(1, "%s(clk=%p)\n", __func__, clk);
BUG_ON(!clk);
snd_stm_clk = to_snd_stm_clk(clk);
BUG_ON(!snd_stm_magic_valid(snd_stm_clk));
spin_lock(&snd_stm_clk_parents_lock);
if (snd_stm_clk->parent->ref_count++ == 0) {
result = clk_enable(clk->parent);
if (result == 0)
snd_stm_clk->enabled = 1;
else
snd_stm_clk->parent->ref_count--;
}
BUG_ON(snd_stm_clk->parent->ref_count >
snd_stm_clk->parent->children_count);
spin_unlock(&snd_stm_clk_parents_lock);
return result;
}
int snd_stm_clk_disable(struct clk *clk)
{
struct snd_stm_clk *snd_stm_clk;
snd_stm_printd(1, "%s(clk=%p)\n", __func__, clk);
BUG_ON(!clk);
snd_stm_clk = to_snd_stm_clk(clk);
BUG_ON(!snd_stm_magic_valid(snd_stm_clk));
spin_lock(&snd_stm_clk_parents_lock);
if (--snd_stm_clk->parent->ref_count == 0) {
clk_disable(clk->parent);
snd_stm_clk->enabled = 0;
}
BUG_ON(snd_stm_clk->parent->ref_count < 0);
spin_unlock(&snd_stm_clk_parents_lock);
return 0;
}
int snd_stm_clk_set_rate(struct clk *clk, unsigned long rate)
{
struct snd_stm_clk *snd_stm_clk;
int rate_adjusted, rate_achieved;
int delta;
snd_stm_printd(1, "%s(clk=%p, rate=%lu)\n", __func__, clk, rate);
BUG_ON(!clk);
snd_stm_clk = to_snd_stm_clk(clk);
BUG_ON(!snd_stm_magic_valid(snd_stm_clk));
/* User must enable the clock first */
if (!snd_stm_clk->enabled)
return -EAGAIN;
/* a
* F = f + --------- * f = f + d
* 1000000
*
* a
* d = --------- * f
* 1000000
*
* where:
* f - nominal rate
* a - adjustment in ppm (parts per milion)
* F - rate to be set in synthesizer
* d - delta (difference) between f and F
*/
if (snd_stm_clk->adjustment < 0) {
/* div64_64 operates on unsigned values... */
delta = -1;
snd_stm_clk->adjustment = -snd_stm_clk->adjustment;
} else {
delta = 1;
}
/* 500000 ppm is 0.5, which is used to round up values */
delta *= (int)div64_u64((uint64_t)rate *
(uint64_t)snd_stm_clk->adjustment + 500000,
1000000);
rate_adjusted = rate + delta;
snd_stm_printd(1, "Setting clock '%s' to rate %d.\n",
clk->parent->name, rate_adjusted);
/* adjusted rate should never be == 0 */
BUG_ON(rate_adjusted == 0);
if (clk_set_rate(snd_stm_clk->clk.parent, rate_adjusted) < 0) {
snd_stm_printe("Failed to set rate %d on clock '%s'!\n",
rate_adjusted, clk->parent->name);
return -EINVAL;
}
rate_achieved = clk_get_rate(clk->parent);
snd_stm_printd(1, "Achieved rate %d.\n", rate_achieved);
/* using ALSA's adjustment control, we can modify the rate to be up to
twice as much as requested, but no more */
BUG_ON(rate_achieved > 2*rate);
delta = rate_achieved - rate;
if (delta < 0) {
/* div64_64 operates on unsigned values... */
delta = -delta;
snd_stm_clk->adjustment = -1;
} else {
snd_stm_clk->adjustment = 1;
}
/* frequency/2 is added to round up result */
snd_stm_clk->adjustment *= (int)div64_u64((uint64_t)delta * 1000000 +
rate / 2, rate);
snd_stm_clk->nominal_rate = rate;
clk->rate = rate_achieved;
return 0;
}
static struct clk_ops snd_stm_clk_ops = {
.enable = snd_stm_clk_enable,
.disable = snd_stm_clk_disable,
.set_rate = snd_stm_clk_set_rate,
};
struct clk *snd_stm_clk_get(struct device *dev, const char *id,
struct snd_card *card, int card_device)
{
struct snd_stm_clk *snd_stm_clk;
struct snd_stm_clk_parent *snd_stm_clk_parent;
snd_stm_printd(0, "%s(dev=%p('%s'), id='%s')\n",
__func__, dev, dev_name(dev), id);
snd_stm_clk = kzalloc(sizeof(*snd_stm_clk), GFP_KERNEL);
if (!snd_stm_clk) {
snd_stm_printe("No memory for '%s'('%s') clock!\n",
dev_name(dev), id);
goto error_kzalloc_clk;
}
snd_stm_magic_set(snd_stm_clk);
snd_stm_clk->clk.ops = &snd_stm_clk_ops;
snd_stm_clk->clk.name = dev_name(dev);
/* Get the parent clock */
snd_stm_clk->clk.parent = clk_get(dev, id);
if (!snd_stm_clk->clk.parent || IS_ERR(snd_stm_clk->clk.parent)) {
snd_stm_printe("Can't get '%s' clock ('%s's parent)!\n",
id, dev_name(dev));
goto error_clk_get;
}
/* Find the parent clock description... */
spin_lock(&snd_stm_clk_parents_lock);
list_for_each_entry(snd_stm_clk_parent, &snd_stm_clk_parents, list) {
if (snd_stm_clk_parent->parent == snd_stm_clk->clk.parent) {
snd_stm_clk->parent = snd_stm_clk_parent;
snd_stm_clk_parent->children_count++;
break;
}
}
spin_unlock(&snd_stm_clk_parents_lock);
/* ... or allocate it now */
if (!snd_stm_clk->parent) {
snd_stm_clk->parent = kzalloc(sizeof(*snd_stm_clk->parent),
GFP_KERNEL);
if (!snd_stm_clk->parent) {
snd_stm_printe("No memory for '%s'('%s') parent!\n",
dev_name(dev), id);
goto error_kzalloc_parent;
}
snd_stm_clk->parent->parent = snd_stm_clk->clk.parent;
snd_stm_clk->parent->children_count = 1;
spin_lock(&snd_stm_clk_parents_lock);
list_add_tail(&snd_stm_clk->parent->list, &snd_stm_clk_parents);
spin_unlock(&snd_stm_clk_parents_lock);
}
/* Register the clock "wrapper" */
if (clk_register(&snd_stm_clk->clk) < 0) {
snd_stm_printe("Failed to register '%s'('%s')\n",
dev_name(dev), id);
goto error_clk_register;
}
/* Rate adjustment ALSA control */
snd_stm_clk_adjustment_ctl.device = card_device;
if (snd_ctl_add(card, snd_ctl_new1(&snd_stm_clk_adjustment_ctl,
snd_stm_clk)) < 0) {
snd_stm_printe("Failed to add '%s'('%s') clock ALSA control!\n",
dev_name(dev), id);
goto error_snd_ctl_add;
}
snd_stm_clk_adjustment_ctl.index++;
return &snd_stm_clk->clk;
error_snd_ctl_add:
clk_unregister(&snd_stm_clk->clk);
error_clk_register:
spin_lock(&snd_stm_clk_parents_lock);
if (--snd_stm_clk->parent->children_count == 0)
kfree(snd_stm_clk->parent);
spin_unlock(&snd_stm_clk_parents_lock);
error_kzalloc_parent:
clk_put(snd_stm_clk->clk.parent);
error_clk_get:
snd_stm_magic_clear(snd_stm_clk);
kfree(snd_stm_clk);
error_kzalloc_clk:
return NULL;
}
void snd_stm_clk_put(struct clk *clk)
{
struct snd_stm_clk *snd_stm_clk;
snd_stm_printd(0, "%s(clk=%p)\n", __func__, clk);
BUG_ON(!clk);
snd_stm_clk = to_snd_stm_clk(clk);
BUG_ON(!snd_stm_magic_valid(snd_stm_clk));
clk_unregister(clk);
spin_lock(&snd_stm_clk_parents_lock);
if (--snd_stm_clk->parent->children_count == 0)
kfree(snd_stm_clk->parent);
spin_unlock(&snd_stm_clk_parents_lock);
clk_put(clk->parent);
snd_stm_magic_clear(snd_stm_clk);
kfree(snd_stm_clk);
}

220
kernel/sound/stm/common.h Normal file
View File

@@ -0,0 +1,220 @@
/*
* STMicroelectronics' System-on-Chips audio subsystem commons
*/
#ifndef __SOUND_STM_COMMON_H
#define __SOUND_STM_COMMON_H
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/platform_device.h>
#include <linux/stringify.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/stm.h>
/*
* ALSA card management
*/
int snd_stm_card_register(void);
int snd_stm_card_is_registered(void);
struct snd_card *snd_stm_card_get(void);
/*
* Converters (DAC, ADC, I2S-SPDIF etc.) control interface
*/
struct snd_stm_conv_source;
struct snd_stm_conv_group;
struct snd_stm_conv_converter;
struct snd_stm_conv_source *snd_stm_conv_register_source(struct bus_type *bus,
const char *bus_id, int channels_num,
struct snd_card *card, int card_device);
int snd_stm_conv_unregister_source(struct snd_stm_conv_source *source);
int snd_stm_conv_get_card_device(struct snd_stm_conv_converter *converter);
struct snd_stm_conv_group *snd_stm_conv_request_group(
struct snd_stm_conv_source *source);
int snd_stm_conv_release_group(struct snd_stm_conv_group *group);
const char *snd_stm_conv_get_name(struct snd_stm_conv_group *group);
unsigned int snd_stm_conv_get_format(struct snd_stm_conv_group *group);
int snd_stm_conv_get_oversampling(struct snd_stm_conv_group *group);
int snd_stm_conv_enable(struct snd_stm_conv_group *group,
int channel_from, int channel_to);
int snd_stm_conv_disable(struct snd_stm_conv_group *group);
int snd_stm_conv_mute(struct snd_stm_conv_group *group);
int snd_stm_conv_unmute(struct snd_stm_conv_group *group);
int snd_stm_conv_init(void);
void snd_stm_conv_exit(void);
/*
* Clocks control interface
*/
struct clk *snd_stm_clk_get(struct device *dev, const char *id,
struct snd_card *card, int card_device);
void snd_stm_clk_put(struct clk *clk);
/*
* PCM buffer memory management
*/
struct snd_stm_buffer;
struct snd_stm_buffer *snd_stm_buffer_create(struct snd_pcm *pcm,
struct device *device, int prealloc_size);
void snd_stm_buffer_dispose(struct snd_stm_buffer *buffer);
int snd_stm_buffer_is_allocated(struct snd_stm_buffer *buffer);
int snd_stm_buffer_alloc(struct snd_stm_buffer *buffer,
struct snd_pcm_substream *substream, int size);
void snd_stm_buffer_free(struct snd_stm_buffer *buffer);
int snd_stm_buffer_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area);
/*
* Common ALSA controls routines
*/
int snd_stm_ctl_boolean_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_stm_ctl_iec958_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_stm_ctl_iec958_mask_get_con(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_stm_ctl_iec958_mask_get_pro(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_stm_iec958_cmp(const struct snd_aes_iec958 *a,
const struct snd_aes_iec958 *b);
/*
* Common ALSA parameters constraints
*/
int snd_stm_pcm_transfer_bytes(unsigned int bytes_per_frame,
unsigned int max_transfer_bytes);
int snd_stm_pcm_hw_constraint_transfer_bytes(struct snd_pcm_runtime *runtime,
unsigned int max_transfer_bytes);
/*
* ALSA procfs additional entries
*/
int snd_stm_info_register(struct snd_info_entry **entry,
const char *name,
void (read)(struct snd_info_entry *, struct snd_info_buffer *),
void *private_data);
void snd_stm_info_unregister(struct snd_info_entry *entry);
/*
* Resources management
*/
int snd_stm_memory_request(struct platform_device *pdev,
struct resource **mem_region, void **base_address);
void snd_stm_memory_release(struct resource *mem_region,
void *base_address);
int snd_stm_irq_request(struct platform_device *pdev,
unsigned int *irq, irq_handler_t handler, void *dev_id);
#define snd_stm_irq_release(irq, dev_id) free_irq(irq, dev_id)
int snd_stm_fdma_request(struct platform_device *pdev, int *channel);
#define snd_stm_fdma_release(channel) free_dma(channel)
/*
* Debug features
*/
/* Data dump functions */
void snd_stm_hex_dump(void *data, int size);
void snd_stm_iec958_dump(const struct snd_aes_iec958 *vuc);
/* Debug messages */
#if defined(CONFIG_SND_DEBUG) || defined(DEBUG)
#define snd_stm_printd(level, format, args...) \
do { \
if (level <= snd_stm_debug_level) \
printk(KERN_DEBUG "%s:%d: " format, \
__FILE__, __LINE__, ##args); \
} while (0)
#else
#define snd_stm_printd(...) /* nothing */
#endif
/* Error messages */
#define snd_stm_printe(format, args...) \
printk(KERN_ERR "%s:%d: " format, __FILE__, __LINE__, ##args)
/* Magic value checking in device structures */
#if defined(CONFIG_SND_DEBUG) || defined(DEBUG)
#ifndef snd_stm_magic
#define snd_stm_magic 0xf00d
#endif
enum snd_stm_magic_enum {
snd_stm_magic_good = 0x600d0000 | snd_stm_magic,
snd_stm_magic_bad = 0xbaad0000 | snd_stm_magic,
};
#define snd_stm_magic_field \
enum snd_stm_magic_enum __snd_stm_magic
#define snd_stm_magic_set(object) \
((object)->__snd_stm_magic) = snd_stm_magic_good
#define snd_stm_magic_clear(object) \
((object)->__snd_stm_magic) = snd_stm_magic_bad
#define snd_stm_magic_valid(object) \
((object)->__snd_stm_magic == snd_stm_magic_good)
#else
#define snd_stm_magic_field
#define snd_stm_magic_set(object)
#define snd_stm_magic_clear(object)
#define snd_stm_magic_valid(object) 1
#endif
#endif

1123
kernel/sound/stm/conv.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,368 @@
/*
* STMicroelectronics System-on-Chips' internal (memory mapped)
* audio DAC driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/stm.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Registers definitions
*/
#define ADAC_CTRL(base) ((base) + 0x00)
#define NRST 0
#define NRST__MASK (1 << NRST)
#define NRST__RESET (0 << NRST)
#define NRST__NORMAL (1 << NRST)
#define MODE 1
#define MODE__DEFAULT (0 << MODE)
#define NSB 3
#define NSB__MASK (1 << NSB)
#define NSB__POWER_DOWN (0 << NSB)
#define NSB__NORMAL (1 << NSB)
#define SOFTMUTE 4
#define SOFTMUTE__MASK (1 << SOFTMUTE)
#define SOFTMUTE__NORMAL (0 << SOFTMUTE)
#define SOFTMUTE__MUTE (1 << SOFTMUTE)
#define PDNANA 5
#define PDNANA__POWER_DOWN (0 << PDNANA)
#define PDNANA__NORMAL (1 << PDNANA)
#define PDNBG 6
#define PDNBG__POWER_DOWN (0 << PDNBG)
#define PDNBG__NORMAL (1 << PDNBG)
/*
* Internal DAC instance structure
*/
struct snd_stm_conv_dac_mem {
/* System informations */
struct snd_stm_conv_converter *converter;
const char *dev_name;
/* Resources */
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
/*
* Converter interface implementation
*/
static unsigned int snd_stm_conv_dac_mem_get_format(void *priv)
{
snd_stm_printd(1, "snd_stm_conv_dac_mem_get_format(priv=%p)\n", priv);
return SND_STM_FORMAT__I2S | SND_STM_FORMAT__SUBFRAME_32_BITS;
}
static int snd_stm_conv_dac_mem_get_oversampling(void *priv)
{
snd_stm_printd(1, "snd_stm_conv_dac_mem_get_oversampling(priv=%p)\n",
priv);
return 256;
}
static int snd_stm_conv_dac_mem_set_enabled(int enabled, void *priv)
{
struct snd_stm_conv_dac_mem *conv_dac_mem = priv;
unsigned long value;
snd_stm_printd(1, "snd_stm_conv_dac_mem_set_enabled(enabled=%d, "
"priv=%p)\n", enabled, priv);
BUG_ON(!conv_dac_mem);
BUG_ON(!snd_stm_magic_valid(conv_dac_mem));
snd_stm_printd(1, "%sabling DAC %s's digital part.\n",
enabled ? "En" : "Dis", conv_dac_mem->dev_name);
value = readl(ADAC_CTRL(conv_dac_mem->base));
value &= ~(NSB__MASK | NRST__MASK);
if (enabled)
value |= NSB__NORMAL | NRST__NORMAL;
else
value |= NSB__POWER_DOWN | NRST__RESET;
writel(value, ADAC_CTRL(conv_dac_mem->base));
return 0;
}
static int snd_stm_conv_dac_mem_set_muted(int muted, void *priv)
{
struct snd_stm_conv_dac_mem *conv_dac_mem = priv;
unsigned long value;
snd_stm_printd(1, "snd_stm_conv_dac_mem_set_muted(muted=%d, priv=%p)\n",
muted, priv);
BUG_ON(!conv_dac_mem);
BUG_ON(!snd_stm_magic_valid(conv_dac_mem));
snd_stm_printd(1, "%suting DAC %s.\n", muted ? "M" : "Unm",
conv_dac_mem->dev_name);
value = readl(ADAC_CTRL(conv_dac_mem->base));
value &= ~SOFTMUTE__MASK;
if (muted)
value |= SOFTMUTE__MUTE;
else
value |= SOFTMUTE__NORMAL;
writel(value, ADAC_CTRL(conv_dac_mem->base));
return 0;
}
static struct snd_stm_conv_ops snd_stm_conv_dac_mem_ops = {
.get_format = snd_stm_conv_dac_mem_get_format,
.get_oversampling = snd_stm_conv_dac_mem_get_oversampling,
.set_enabled = snd_stm_conv_dac_mem_set_enabled,
.set_muted = snd_stm_conv_dac_mem_set_muted,
};
/*
* ALSA lowlevel device implementation
*/
static void snd_stm_conv_dac_mem_read_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_conv_dac_mem *conv_dac_mem =
entry->private_data;
BUG_ON(!conv_dac_mem);
BUG_ON(!snd_stm_magic_valid(conv_dac_mem));
snd_iprintf(buffer, "--- %s ---\n", conv_dac_mem->dev_name);
snd_iprintf(buffer, "base = 0x%p\n", conv_dac_mem->base);
snd_iprintf(buffer, "ADAC_CTRL (0x%p) = 0x%08x\n",
ADAC_CTRL(conv_dac_mem->base),
readl(ADAC_CTRL(conv_dac_mem->base)));
snd_iprintf(buffer, "\n");
}
static int snd_stm_conv_dac_mem_register(struct snd_device *snd_device)
{
struct snd_stm_conv_dac_mem *conv_dac_mem =
snd_device->device_data;
BUG_ON(!conv_dac_mem);
BUG_ON(!snd_stm_magic_valid(conv_dac_mem));
/* Initialize DAC with digital part down, analog up and muted */
writel(NRST__RESET | MODE__DEFAULT | NSB__POWER_DOWN |
SOFTMUTE__MUTE | PDNANA__NORMAL | PDNBG__NORMAL,
ADAC_CTRL(conv_dac_mem->base));
/* Additional procfs info */
snd_stm_info_register(&conv_dac_mem->proc_entry,
conv_dac_mem->dev_name,
snd_stm_conv_dac_mem_read_info,
conv_dac_mem);
return 0;
}
static int __exit snd_stm_conv_dac_mem_disconnect(struct snd_device *snd_device)
{
struct snd_stm_conv_dac_mem *conv_dac_mem =
snd_device->device_data;
BUG_ON(!conv_dac_mem);
BUG_ON(!snd_stm_magic_valid(conv_dac_mem));
/* Remove procfs entry */
snd_stm_info_unregister(conv_dac_mem->proc_entry);
/* Global power done & mute mode */
writel(NRST__RESET | MODE__DEFAULT | NSB__POWER_DOWN |
SOFTMUTE__MUTE | PDNANA__POWER_DOWN | PDNBG__POWER_DOWN,
ADAC_CTRL(conv_dac_mem->base));
return 0;
}
static struct snd_device_ops snd_stm_conv_dac_mem_snd_device_ops = {
.dev_register = snd_stm_conv_dac_mem_register,
.dev_disconnect = snd_stm_conv_dac_mem_disconnect,
};
/*
* Platform driver routines
*/
static int snd_stm_conv_dac_mem_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_conv_dac_mem_info *conv_dac_mem_info =
pdev->dev.platform_data;
struct snd_stm_conv_dac_mem *conv_dac_mem;
struct snd_card *card = snd_stm_card_get();
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!card);
BUG_ON(!conv_dac_mem_info);
conv_dac_mem = kzalloc(sizeof(*conv_dac_mem), GFP_KERNEL);
if (!conv_dac_mem) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(conv_dac_mem);
conv_dac_mem->dev_name = dev_name(&pdev->dev);
/* Get resources */
result = snd_stm_memory_request(pdev, &conv_dac_mem->mem_region,
&conv_dac_mem->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Get connections */
BUG_ON(!conv_dac_mem_info->source_bus_id);
snd_stm_printd(0, "This DAC is attached to PCM player '%s'.\n",
conv_dac_mem_info->source_bus_id);
conv_dac_mem->converter = snd_stm_conv_register_converter(
"Analog Output",
&snd_stm_conv_dac_mem_ops, conv_dac_mem,
&platform_bus_type, conv_dac_mem_info->source_bus_id,
conv_dac_mem_info->channel_from,
conv_dac_mem_info->channel_to, NULL);
if (!conv_dac_mem->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
goto error_attach;
}
/* Create ALSA lowlevel device*/
result = snd_device_new(card, SNDRV_DEV_LOWLEVEL, conv_dac_mem,
&snd_stm_conv_dac_mem_snd_device_ops);
if (result < 0) {
snd_stm_printe("ALSA low level device creation failed!\n");
goto error_device;
}
/* Done now */
platform_set_drvdata(pdev, conv_dac_mem);
return 0;
error_device:
error_attach:
snd_stm_memory_release(conv_dac_mem->mem_region,
conv_dac_mem->base);
error_memory_request:
snd_stm_magic_clear(conv_dac_mem);
kfree(conv_dac_mem);
error_alloc:
return result;
}
static int snd_stm_conv_dac_mem_remove(struct platform_device *pdev)
{
struct snd_stm_conv_dac_mem *conv_dac_mem = platform_get_drvdata(pdev);
BUG_ON(!conv_dac_mem);
BUG_ON(!snd_stm_magic_valid(conv_dac_mem));
snd_stm_conv_unregister_converter(conv_dac_mem->converter);
snd_stm_memory_release(conv_dac_mem->mem_region,
conv_dac_mem->base);
snd_stm_magic_clear(conv_dac_mem);
kfree(conv_dac_mem);
return 0;
}
static struct platform_driver snd_stm_conv_dac_mem_driver = {
.driver.name = "snd_conv_dac_mem",
.probe = snd_stm_conv_dac_mem_probe,
.remove = snd_stm_conv_dac_mem_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_dac_mem_init(void)
{
return platform_driver_register(&snd_stm_conv_dac_mem_driver);
}
static void __exit snd_stm_conv_dac_mem_exit(void)
{
platform_driver_unregister(&snd_stm_conv_dac_mem_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics memory-mapped audio DAC driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_dac_mem_init);
module_exit(snd_stm_conv_dac_mem_exit);

View File

@@ -0,0 +1,334 @@
/*
* STMicroelectronics System-on-Chips' internal (sysconf controlled)
* audio DAC driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/stm/sysconf.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/stm.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Hardware-related definitions
*/
#define FORMAT (SND_STM_FORMAT__I2S | SND_STM_FORMAT__SUBFRAME_32_BITS)
#define OVERSAMPLING 256
/*
* Internal DAC instance structure
*/
struct snd_stm_conv_dac_sc {
/* System informations */
struct snd_stm_conv_converter *converter;
const char *bus_id;
/* Control bits */
struct sysconf_field *nrst;
struct sysconf_field *mode;
struct sysconf_field *nsb;
struct sysconf_field *softmute;
struct sysconf_field *pdana;
struct sysconf_field *pndbg;
snd_stm_magic_field;
};
/*
* Converter interface implementation
*/
static unsigned int snd_stm_conv_dac_sc_get_format(void *priv)
{
snd_stm_printd(1, "snd_stm_conv_dac_sc_get_format(priv=%p)\n", priv);
return FORMAT;
}
static int snd_stm_conv_dac_sc_get_oversampling(void *priv)
{
snd_stm_printd(1, "snd_stm_conv_dac_sc_get_oversampling(priv=%p)\n",
priv);
return OVERSAMPLING;
}
static int snd_stm_conv_dac_sc_set_enabled(int enabled, void *priv)
{
struct snd_stm_conv_dac_sc *conv_dac_sc = priv;
snd_stm_printd(1, "snd_stm_conv_dac_sc_set_enabled(enabled=%d, "
"priv=%p)\n", enabled, priv);
BUG_ON(!conv_dac_sc);
BUG_ON(!snd_stm_magic_valid(conv_dac_sc));
snd_stm_printd(1, "%sabling DAC %s's digital part.\n",
enabled ? "En" : "Dis", conv_dac_sc->bus_id);
if (enabled) {
sysconf_write(conv_dac_sc->nsb, 1); /* NORMAL */
sysconf_write(conv_dac_sc->nrst, 1); /* NORMAL */
} else {
sysconf_write(conv_dac_sc->nrst, 0); /* RESET */
sysconf_write(conv_dac_sc->nsb, 0); /* POWER_DOWN */
}
return 0;
}
static int snd_stm_conv_dac_sc_set_muted(int muted, void *priv)
{
struct snd_stm_conv_dac_sc *conv_dac_sc = priv;
snd_stm_printd(1, "snd_stm_conv_dac_sc_set_muted(muted=%d, priv=%p)\n",
muted, priv);
BUG_ON(!conv_dac_sc);
BUG_ON(!snd_stm_magic_valid(conv_dac_sc));
snd_stm_printd(1, "%suting DAC %s.\n", muted ? "M" : "Unm",
conv_dac_sc->bus_id);
if (muted)
sysconf_write(conv_dac_sc->softmute, 1); /* MUTE */
else
sysconf_write(conv_dac_sc->softmute, 0); /* NORMAL */
return 0;
}
static struct snd_stm_conv_ops snd_stm_conv_dac_sc_ops = {
.get_format = snd_stm_conv_dac_sc_get_format,
.get_oversampling = snd_stm_conv_dac_sc_get_oversampling,
.set_enabled = snd_stm_conv_dac_sc_set_enabled,
.set_muted = snd_stm_conv_dac_sc_set_muted,
};
/*
* ALSA lowlevel device implementation
*/
static int snd_stm_conv_dac_sc_register(struct snd_device *snd_device)
{
struct snd_stm_conv_dac_sc *conv_dac_sc =
snd_device->device_data;
BUG_ON(!conv_dac_sc);
BUG_ON(!snd_stm_magic_valid(conv_dac_sc));
/* Initialize DAC with digital part down, analog up and muted */
sysconf_write(conv_dac_sc->nrst, 0); /* RESET */
sysconf_write(conv_dac_sc->mode, 0); /* DEFAULT */
sysconf_write(conv_dac_sc->nsb, 0); /* POWER_DOWN */
sysconf_write(conv_dac_sc->softmute, 1); /* MUTE */
sysconf_write(conv_dac_sc->pdana, 1); /* NORMAL */
sysconf_write(conv_dac_sc->pndbg, 1); /* NORMAL */
return 0;
}
static int __exit snd_stm_conv_dac_sc_disconnect(struct snd_device *snd_device)
{
struct snd_stm_conv_dac_sc *conv_dac_sc =
snd_device->device_data;
BUG_ON(!conv_dac_sc);
BUG_ON(!snd_stm_magic_valid(conv_dac_sc));
/* Global power done & mute mode */
sysconf_write(conv_dac_sc->nrst, 0); /* RESET */
sysconf_write(conv_dac_sc->mode, 0); /* DEFAULT */
sysconf_write(conv_dac_sc->nsb, 0); /* POWER_DOWN */
sysconf_write(conv_dac_sc->softmute, 1); /* MUTE */
sysconf_write(conv_dac_sc->pdana, 0); /* POWER_DOWN */
sysconf_write(conv_dac_sc->pndbg, 0); /* POWER_DOWN */
return 0;
}
static struct snd_device_ops snd_stm_conv_dac_sc_snd_device_ops = {
.dev_register = snd_stm_conv_dac_sc_register,
.dev_disconnect = snd_stm_conv_dac_sc_disconnect,
};
/*
* Platform driver routines
*/
static int snd_stm_conv_dac_sc_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_conv_dac_sc_info *info =
pdev->dev.platform_data;
struct snd_stm_conv_dac_sc *conv_dac_sc;
struct snd_card *card = snd_stm_card_get();
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!card);
BUG_ON(!info);
conv_dac_sc = kzalloc(sizeof(*conv_dac_sc), GFP_KERNEL);
if (!conv_dac_sc) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(conv_dac_sc);
conv_dac_sc->bus_id = dev_name(&pdev->dev);
/* Get resources */
conv_dac_sc->nrst = sysconf_claim(info->nrst.group, info->nrst.num,
info->nrst.lsb, info->nrst.msb, "NRST");
BUG_ON(!conv_dac_sc->nrst);
conv_dac_sc->mode = sysconf_claim(info->mode.group, info->mode.num,
info->mode.lsb, info->mode.msb, "MODE");
BUG_ON(!conv_dac_sc->mode);
conv_dac_sc->nsb = sysconf_claim(info->nsb.group, info->nsb.num,
info->nsb.lsb, info->nsb.msb, "NSB");
BUG_ON(!conv_dac_sc->nsb);
conv_dac_sc->softmute = sysconf_claim(info->softmute.group,
info->softmute.num, info->softmute.lsb,
info->softmute.msb, "SOFTMUTE");
BUG_ON(!conv_dac_sc->softmute);
conv_dac_sc->pdana = sysconf_claim(info->pdana.group, info->pdana.num,
info->pdana.lsb, info->pdana.msb, "PDANA");
BUG_ON(!conv_dac_sc->pdana);
conv_dac_sc->pndbg = sysconf_claim(info->pndbg.group, info->pndbg.num,
info->pndbg.lsb, info->pndbg.msb, "PNDBG");
BUG_ON(!conv_dac_sc->pndbg);
/* Get connections */
BUG_ON(!info->source_bus_id);
snd_stm_printd(0, "This DAC is attached to PCM player '%s'.\n",
info->source_bus_id);
conv_dac_sc->converter = snd_stm_conv_register_converter(
"Analog Output", &snd_stm_conv_dac_sc_ops, conv_dac_sc,
&platform_bus_type, info->source_bus_id,
info->channel_from, info->channel_to, NULL);
if (!conv_dac_sc->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
goto error_attach;
}
/* Create ALSA lowlevel device*/
result = snd_device_new(card, SNDRV_DEV_LOWLEVEL, conv_dac_sc,
&snd_stm_conv_dac_sc_snd_device_ops);
if (result < 0) {
snd_stm_printe("ALSA low level device creation failed!\n");
goto error_device;
}
/* Done now */
platform_set_drvdata(pdev, conv_dac_sc);
return 0;
error_device:
error_attach:
snd_stm_magic_clear(conv_dac_sc);
kfree(conv_dac_sc);
error_alloc:
return result;
}
static int snd_stm_conv_dac_sc_remove(struct platform_device *pdev)
{
struct snd_stm_conv_dac_sc *conv_dac_sc = platform_get_drvdata(pdev);
BUG_ON(!conv_dac_sc);
BUG_ON(!snd_stm_magic_valid(conv_dac_sc));
snd_stm_conv_unregister_converter(conv_dac_sc->converter);
sysconf_release(conv_dac_sc->nrst);
sysconf_release(conv_dac_sc->mode);
sysconf_release(conv_dac_sc->nsb);
sysconf_release(conv_dac_sc->softmute);
sysconf_release(conv_dac_sc->pdana);
sysconf_release(conv_dac_sc->pndbg);
snd_stm_magic_clear(conv_dac_sc);
kfree(conv_dac_sc);
return 0;
}
static struct platform_driver snd_stm_conv_dac_sc_driver = {
.driver.name = "snd_conv_dac_sc",
.probe = snd_stm_conv_dac_sc_probe,
.remove = snd_stm_conv_dac_sc_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_dac_sc_init(void)
{
return platform_driver_register(&snd_stm_conv_dac_sc_driver);
}
static void __exit snd_stm_conv_dac_sc_exit(void)
{
platform_driver_unregister(&snd_stm_conv_dac_sc_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics sysconf-based audio DAC driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_dac_sc_init);
module_exit(snd_stm_conv_dac_sc_exit);

View File

@@ -0,0 +1,170 @@
/*
* STMicroelectronics System-on-Chips' dummy DAC driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <sound/stm.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Dummy converter instance structure
*/
struct snd_stm_conv_dummy {
struct snd_stm_conv_converter *converter;
struct snd_stm_conv_dummy_info *info;
snd_stm_magic_field;
};
/*
* Converter interface implementation
*/
static unsigned int snd_stm_conv_dummy_get_format(void *priv)
{
struct snd_stm_conv_dummy *conv_dummy = priv;
snd_stm_printd(1, "snd_stm_conv_dummy_get_format(priv=%p)\n", priv);
BUG_ON(!conv_dummy);
BUG_ON(!snd_stm_magic_valid(conv_dummy));
return conv_dummy->info->format;
}
static int snd_stm_conv_dummy_get_oversampling(void *priv)
{
struct snd_stm_conv_dummy *conv_dummy = priv;
snd_stm_printd(1, "snd_stm_conv_dummy_get_oversampling(priv=%p)\n",
priv);
BUG_ON(!conv_dummy);
BUG_ON(!snd_stm_magic_valid(conv_dummy));
return conv_dummy->info->oversampling;
}
static struct snd_stm_conv_ops snd_stm_conv_dummy_ops = {
.get_format = snd_stm_conv_dummy_get_format,
.get_oversampling = snd_stm_conv_dummy_get_oversampling,
};
/*
* Platform driver routines
*/
static int snd_stm_conv_dummy_probe(struct platform_device *pdev)
{
struct snd_stm_conv_dummy *conv_dummy;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!pdev->dev.platform_data);
conv_dummy = kzalloc(sizeof(*conv_dummy), GFP_KERNEL);
if (!conv_dummy) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
return -ENOMEM;
}
snd_stm_magic_set(conv_dummy);
conv_dummy->info = pdev->dev.platform_data;
snd_stm_printd(0, "This dummy DAC is attached to PCM player '%s'.\n",
conv_dummy->info->source_bus_id);
conv_dummy->converter = snd_stm_conv_register_converter(
conv_dummy->info->group,
&snd_stm_conv_dummy_ops, conv_dummy,
&platform_bus_type, conv_dummy->info->source_bus_id,
conv_dummy->info->channel_from,
conv_dummy->info->channel_to, NULL);
if (!conv_dummy->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
return -EINVAL;
}
/* Done now */
platform_set_drvdata(pdev, conv_dummy);
return 0;
}
static int snd_stm_conv_dummy_remove(struct platform_device *pdev)
{
struct snd_stm_conv_dummy *conv_dummy = platform_get_drvdata(pdev);
BUG_ON(!conv_dummy);
BUG_ON(!snd_stm_magic_valid(conv_dummy));
snd_stm_conv_unregister_converter(conv_dummy->converter);
snd_stm_magic_clear(conv_dummy);
kfree(conv_dummy);
return 0;
}
static struct platform_driver snd_stm_conv_dummy_driver = {
.driver.name = "snd_conv_dummy",
.probe = snd_stm_conv_dummy_probe,
.remove = snd_stm_conv_dummy_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_dummy_init(void)
{
return platform_driver_register(&snd_stm_conv_dummy_driver);
}
static void __exit snd_stm_conv_dummy_exit(void)
{
platform_driver_unregister(&snd_stm_conv_dummy_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics dummy audio converter driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_dummy_init);
module_exit(snd_stm_conv_dummy_exit);

View File

@@ -0,0 +1,318 @@
/*
* STMicroelectronics System-on-Chips' EPLD-controlled ADC/DAC driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/stm.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Internal DAC instance structure
*/
struct snd_stm_conv_epld {
/* System informations */
const char *dev_name;
struct snd_stm_conv_converter *converter;
struct snd_stm_conv_epld_info *info;
struct snd_stm_conv_ops ops;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
/*
* EPLD access implementation
*/
/* Defined in arch/sh/boards/st/common/epld.c */
void epld_write(unsigned long value, unsigned long offset);
unsigned long epld_read(unsigned long offset);
static void snd_stm_conv_epld_set(unsigned long offset,
unsigned long mask, unsigned long value)
{
unsigned long reg = epld_read(offset);
reg &= ~mask;
reg |= value;
epld_write(reg, offset);
}
/*
* Converter interface implementation
*/
static unsigned int snd_stm_conv_epld_get_format(void *priv)
{
struct snd_stm_conv_epld *conv_epld = priv;
snd_stm_printd(1, "snd_stm_conv_epld_get_format(priv=%p)\n", priv);
BUG_ON(!conv_epld);
BUG_ON(!snd_stm_magic_valid(conv_epld));
return conv_epld->info->format;
}
static int snd_stm_conv_epld_get_oversampling(void *priv)
{
struct snd_stm_conv_epld *conv_epld = priv;
snd_stm_printd(1, "snd_stm_conv_epld_get_oversampling(priv=%p)\n",
priv);
BUG_ON(!conv_epld);
BUG_ON(!snd_stm_magic_valid(conv_epld));
return conv_epld->info->oversampling;
}
static int snd_stm_conv_epld_set_enabled(int enabled, void *priv)
{
struct snd_stm_conv_epld *conv_epld = priv;
snd_stm_printd(1, "snd_stm_conv_epld_enable(enabled=%d, priv=%p)\n",
enabled, priv);
BUG_ON(!conv_epld);
BUG_ON(!snd_stm_magic_valid(conv_epld));
BUG_ON(!conv_epld->info->enable_supported);
snd_stm_printd(1, "%sabling DAC %s's.\n", enabled ? "En" : "Dis",
conv_epld->dev_name);
snd_stm_conv_epld_set(conv_epld->info->enable_offset,
conv_epld->info->enable_mask,
enabled ? conv_epld->info->enable_value :
conv_epld->info->disable_value);
return 0;
}
static int snd_stm_conv_epld_set_muted(int muted, void *priv)
{
struct snd_stm_conv_epld *conv_epld = priv;
snd_stm_printd(1, "snd_stm_conv_epld_set_muted(muted=%d, priv=%p)\n",
muted, priv);
BUG_ON(!conv_epld);
BUG_ON(!snd_stm_magic_valid(conv_epld));
BUG_ON(!conv_epld->info->mute_supported);
snd_stm_printd(1, "%suting DAC %s.\n", muted ? "M" : "Unm",
conv_epld->dev_name);
snd_stm_conv_epld_set(conv_epld->info->mute_offset,
conv_epld->info->mute_mask,
muted ? conv_epld->info->mute_value :
conv_epld->info->unmute_value);
return 0;
}
/*
* Procfs status callback
*/
#define DUMP_EPLD(offset) snd_iprintf(buffer, "EPLD[0x%08x] = 0x%08lx\n", \
offset, epld_read(offset));
static void snd_stm_conv_epld_read_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_conv_epld *conv_epld = entry->private_data;
BUG_ON(!conv_epld);
BUG_ON(!snd_stm_magic_valid(conv_epld));
snd_iprintf(buffer, "--- %s ---\n", conv_epld->dev_name);
if (conv_epld->info->enable_supported)
DUMP_EPLD(conv_epld->info->enable_offset);
if (conv_epld->info->mute_supported)
DUMP_EPLD(conv_epld->info->mute_offset);
snd_iprintf(buffer, "\n");
}
/*
* Platform driver routines
*/
static int snd_stm_conv_epld_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_conv_epld *conv_epld;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!pdev->dev.platform_data);
conv_epld = kzalloc(sizeof(*conv_epld), GFP_KERNEL);
if (!conv_epld) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(conv_epld);
conv_epld->dev_name = dev_name(&pdev->dev);
conv_epld->info = pdev->dev.platform_data;
conv_epld->ops.get_format = snd_stm_conv_epld_get_format;
conv_epld->ops.get_oversampling = snd_stm_conv_epld_get_oversampling;
if (conv_epld->info->enable_supported)
conv_epld->ops.set_enabled = snd_stm_conv_epld_set_enabled;
if (conv_epld->info->mute_supported)
conv_epld->ops.set_muted = snd_stm_conv_epld_set_muted;
/* Get connections */
BUG_ON(!conv_epld->info->source_bus_id);
snd_stm_printd(0, "This DAC is attached to PCM player '%s'.\n",
conv_epld->info->source_bus_id);
conv_epld->converter = snd_stm_conv_register_converter(
conv_epld->info->group, &conv_epld->ops, conv_epld,
&platform_bus_type, conv_epld->info->source_bus_id,
conv_epld->info->channel_from,
conv_epld->info->channel_to, NULL);
if (!conv_epld->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
result = -EINVAL;
goto error_attach;
}
/* Initialize converter as muted & disabled */
if (conv_epld->info->enable_supported)
snd_stm_conv_epld_set(conv_epld->info->enable_offset,
conv_epld->info->enable_mask,
conv_epld->info->disable_value);
if (conv_epld->info->mute_supported)
snd_stm_conv_epld_set(conv_epld->info->mute_offset,
conv_epld->info->mute_mask,
conv_epld->info->mute_value);
/* Additional procfs info */
snd_stm_info_register(&conv_epld->proc_entry,
conv_epld->dev_name,
snd_stm_conv_epld_read_info,
conv_epld);
/* Done now */
platform_set_drvdata(pdev, conv_epld);
return 0;
error_attach:
snd_stm_magic_clear(conv_epld);
kfree(conv_epld);
error_alloc:
return result;
}
static int snd_stm_conv_epld_remove(struct platform_device *pdev)
{
struct snd_stm_conv_epld *conv_epld = platform_get_drvdata(pdev);
BUG_ON(!conv_epld);
BUG_ON(!snd_stm_magic_valid(conv_epld));
snd_stm_conv_unregister_converter(conv_epld->converter);
/* Remove procfs entry */
snd_stm_info_unregister(conv_epld->proc_entry);
/* Muting and disabling - just to be sure ;-) */
if (conv_epld->info->enable_supported)
snd_stm_conv_epld_set(conv_epld->info->enable_offset,
conv_epld->info->enable_mask,
conv_epld->info->disable_value);
if (conv_epld->info->mute_supported)
snd_stm_conv_epld_set(conv_epld->info->mute_offset,
conv_epld->info->mute_mask,
conv_epld->info->mute_value);
snd_stm_magic_clear(conv_epld);
kfree(conv_epld);
return 0;
}
static struct platform_driver snd_stm_conv_epld_driver = {
.driver.name = "snd_conv_epld",
.probe = snd_stm_conv_epld_probe,
.remove = snd_stm_conv_epld_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_epld_init(void)
{
return platform_driver_register(&snd_stm_conv_epld_driver);
}
static void __exit snd_stm_conv_epld_exit(void)
{
platform_driver_unregister(&snd_stm_conv_epld_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics EPLD-controlled audio converter driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_epld_init);
module_exit(snd_stm_conv_epld_exit);

View File

@@ -0,0 +1,417 @@
/*
* STMicroelectronics System-on-Chips' GPIO-controlled ADC/DAC driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/stm.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Internal DAC instance structure
*/
struct snd_stm_conv_gpio {
/* System informations */
const char *dev_name;
struct snd_stm_conv_converter *converter;
struct snd_stm_conv_gpio_info *info;
struct snd_stm_conv_ops ops;
/* Runtime data */
int may_sleep;
struct work_struct work; /* Used if may_sleep */
int work_enable_value;
int work_mute_value;
spinlock_t work_lock; /* Protects work_*_value */
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
/*
* Sleeping-safe GPIO access implementation
*/
static void snd_stm_conv_gpio_work(struct work_struct *work)
{
struct snd_stm_conv_gpio *conv_gpio = container_of(work,
struct snd_stm_conv_gpio, work);
int enable_value, mute_value;
snd_stm_printd(1, "snd_stm_conv_gpio_work(work=%p)\n", work);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
spin_lock(&conv_gpio->work_lock);
enable_value = conv_gpio->work_enable_value;
conv_gpio->work_enable_value = -1;
mute_value = conv_gpio->work_mute_value;
conv_gpio->work_mute_value = -1;
spin_unlock(&conv_gpio->work_lock);
if (enable_value != -1)
gpio_set_value_cansleep(conv_gpio->info->enable_gpio,
enable_value);
if (mute_value != -1)
gpio_set_value_cansleep(conv_gpio->info->mute_gpio,
mute_value);
}
static void snd_stm_conv_gpio_set_value(struct snd_stm_conv_gpio *conv_gpio,
int enable_not_mute, int value)
{
snd_stm_printd(1, "snd_stm_conv_gpio_set_value(conv_gpio=%p, "
"enable_not_mute=%d, value=%d)\n",
conv_gpio, enable_not_mute, value);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
if (conv_gpio->may_sleep) {
spin_lock(&conv_gpio->work_lock);
if (enable_not_mute)
conv_gpio->work_enable_value = value;
else
conv_gpio->work_mute_value = value;
schedule_work(&conv_gpio->work);
spin_unlock(&conv_gpio->work_lock);
} else {
gpio_set_value(enable_not_mute ? conv_gpio->info->enable_gpio :
conv_gpio->info->mute_gpio, value);
}
}
/*
* Converter interface implementation
*/
static unsigned int snd_stm_conv_gpio_get_format(void *priv)
{
struct snd_stm_conv_gpio *conv_gpio = priv;
snd_stm_printd(1, "snd_stm_conv_gpio_get_format(priv=%p)\n", priv);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
return conv_gpio->info->format;
}
static int snd_stm_conv_gpio_get_oversampling(void *priv)
{
struct snd_stm_conv_gpio *conv_gpio = priv;
snd_stm_printd(1, "snd_stm_conv_gpio_get_oversampling(priv=%p)\n",
priv);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
return conv_gpio->info->oversampling;
}
static int snd_stm_conv_gpio_set_enabled(int enabled, void *priv)
{
struct snd_stm_conv_gpio *conv_gpio = priv;
snd_stm_printd(1, "snd_stm_conv_gpio_enable(enabled=%d, priv=%p)\n",
enabled, priv);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
BUG_ON(!conv_gpio->info->enable_supported);
snd_stm_printd(1, "%sabling DAC %s's.\n", enabled ? "En" : "Dis",
conv_gpio->dev_name);
snd_stm_conv_gpio_set_value(conv_gpio, 1,
enabled ? conv_gpio->info->enable_value :
!conv_gpio->info->enable_value);
return 0;
}
static int snd_stm_conv_gpio_set_muted(int muted, void *priv)
{
struct snd_stm_conv_gpio *conv_gpio = priv;
snd_stm_printd(1, "snd_stm_conv_gpio_set_muted(muted=%d, priv=%p)\n",
muted, priv);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
BUG_ON(!conv_gpio->info->mute_supported);
snd_stm_printd(1, "%suting DAC %s.\n", muted ? "M" : "Unm",
conv_gpio->dev_name);
snd_stm_conv_gpio_set_value(conv_gpio, 0,
muted ? conv_gpio->info->mute_value :
!conv_gpio->info->mute_value);
return 0;
}
/*
* Procfs status callback
*/
static void snd_stm_conv_gpio_read_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_conv_gpio *conv_gpio = entry->private_data;
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
snd_iprintf(buffer, "--- %s ---\n", conv_gpio->dev_name);
snd_iprintf(buffer, "enable_gpio(%d) = %d\n",
conv_gpio->info->enable_gpio,
gpio_get_value(conv_gpio->info->enable_gpio));
if (conv_gpio->info->mute_supported)
snd_iprintf(buffer, "mute_gpio(%d) = %d\n",
conv_gpio->info->mute_gpio,
gpio_get_value(conv_gpio->info->mute_gpio));
snd_iprintf(buffer, "\n");
}
/*
* Platform driver routines
*/
static int snd_stm_conv_gpio_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_conv_gpio *conv_gpio;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!pdev->dev.platform_data);
conv_gpio = kzalloc(sizeof(*conv_gpio), GFP_KERNEL);
if (!conv_gpio) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(conv_gpio);
conv_gpio->dev_name = dev_name(&pdev->dev);
conv_gpio->info = pdev->dev.platform_data;
conv_gpio->ops.get_format = snd_stm_conv_gpio_get_format;
conv_gpio->ops.get_oversampling = snd_stm_conv_gpio_get_oversampling;
if (conv_gpio->info->enable_supported)
conv_gpio->ops.set_enabled = snd_stm_conv_gpio_set_enabled;
if (conv_gpio->info->mute_supported)
conv_gpio->ops.set_muted = snd_stm_conv_gpio_set_muted;
/* Get connections */
BUG_ON(!conv_gpio->info->source_bus_id);
snd_stm_printd(0, "This DAC is attached to PCM player '%s'.\n",
conv_gpio->info->source_bus_id);
conv_gpio->converter = snd_stm_conv_register_converter(
conv_gpio->info->group, &conv_gpio->ops, conv_gpio,
&platform_bus_type, conv_gpio->info->source_bus_id,
conv_gpio->info->channel_from,
conv_gpio->info->channel_to, NULL);
if (!conv_gpio->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
result = -EINVAL;
goto error_attach;
}
/* Reserve & initialize GPIO lines (enabled & mute) */
if (conv_gpio->info->enable_supported) {
result = gpio_request(conv_gpio->info->enable_gpio,
conv_gpio->dev_name);
if (result != 0) {
snd_stm_printe("Can't reserve 'enable' GPIO line!\n");
goto error_gpio_request_enable;
}
if (gpio_direction_output(conv_gpio->info->enable_gpio,
!conv_gpio->info->enable_value) != 0) {
snd_stm_printe("Can't set 'enable' GPIO line as "
"output!\n");
goto error_gpio_direction_output_enable;
}
conv_gpio->may_sleep = gpio_cansleep(
conv_gpio->info->enable_gpio);
}
if (conv_gpio->info->mute_supported) {
result = gpio_request(conv_gpio->info->mute_gpio,
conv_gpio->dev_name);
if (result != 0) {
snd_stm_printe("Can't reserve 'mute' GPIO line!\n");
goto error_gpio_request_mute;
}
if (gpio_direction_output(conv_gpio->info->mute_gpio,
conv_gpio->info->mute_value) != 0) {
snd_stm_printe("Can't set 'mute' GPIO line as output!"
"\n");
goto error_gpio_direction_output_mute;
}
conv_gpio->may_sleep |= gpio_cansleep(
conv_gpio->info->mute_gpio);
}
if (conv_gpio->may_sleep) {
INIT_WORK(&conv_gpio->work, snd_stm_conv_gpio_work);
spin_lock_init(&conv_gpio->work_lock);
conv_gpio->work_enable_value = -1;
conv_gpio->work_mute_value = -1;
}
/* Additional procfs info */
snd_stm_info_register(&conv_gpio->proc_entry,
conv_gpio->dev_name,
snd_stm_conv_gpio_read_info,
conv_gpio);
/* Done now */
platform_set_drvdata(pdev, conv_gpio);
return 0;
error_gpio_direction_output_mute:
if (conv_gpio->info->mute_supported)
gpio_free(conv_gpio->info->mute_gpio);
error_gpio_request_mute:
error_gpio_direction_output_enable:
if (conv_gpio->info->enable_supported)
gpio_free(conv_gpio->info->enable_gpio);
error_gpio_request_enable:
snd_stm_conv_unregister_converter(conv_gpio->converter);
error_attach:
snd_stm_magic_clear(conv_gpio);
kfree(conv_gpio);
error_alloc:
return result;
}
static int snd_stm_conv_gpio_remove(struct platform_device *pdev)
{
struct snd_stm_conv_gpio *conv_gpio = platform_get_drvdata(pdev);
BUG_ON(!conv_gpio);
BUG_ON(!snd_stm_magic_valid(conv_gpio));
snd_stm_conv_unregister_converter(conv_gpio->converter);
/* Remove procfs entry */
snd_stm_info_unregister(conv_gpio->proc_entry);
/* Wait for the possibly scheduled work... */
if (conv_gpio->may_sleep)
flush_scheduled_work();
/* Muting and disabling - just to be sure ;-) */
if (conv_gpio->info->mute_supported) {
gpio_set_value_cansleep(conv_gpio->info->mute_gpio,
conv_gpio->info->mute_value);
gpio_free(conv_gpio->info->mute_gpio);
}
if (conv_gpio->info->enable_supported) {
gpio_set_value_cansleep(conv_gpio->info->enable_gpio,
!conv_gpio->info->enable_value);
gpio_free(conv_gpio->info->enable_gpio);
}
snd_stm_magic_clear(conv_gpio);
kfree(conv_gpio);
return 0;
}
static struct platform_driver snd_stm_conv_gpio_driver = {
.driver.name = "snd_conv_gpio",
.probe = snd_stm_conv_gpio_probe,
.remove = snd_stm_conv_gpio_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_gpio_init(void)
{
return platform_driver_register(&snd_stm_conv_gpio_driver);
}
static void __exit snd_stm_conv_gpio_exit(void)
{
platform_driver_unregister(&snd_stm_conv_gpio_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics GPIO-controlled audio converter driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_gpio_init);
module_exit(snd_stm_conv_gpio_exit);

367
kernel/sound/stm/conv_i2c.c Normal file
View File

@@ -0,0 +1,367 @@
/*
* STMicroelectronics System-on-Chips' I2C-controlled ADC/DAC driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/stm.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Internal converter instance structure
*/
struct snd_stm_conv_i2c {
/* System informations */
struct i2c_client *client;
struct snd_stm_conv_converter *converter;
struct snd_stm_conv_i2c_info *info;
struct snd_stm_conv_ops ops;
/* Runtime data */
struct work_struct work;
int work_enable_value;
int work_mute_value;
spinlock_t work_lock; /* Protects work_*_value */
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
/*
* Implementation
*/
static void snd_stm_conv_i2c_work(struct work_struct *work)
{
struct snd_stm_conv_i2c *conv_i2c = container_of(work,
struct snd_stm_conv_i2c, work);
int enable_value, mute_value;
const char *cmd;
int cmd_len;
snd_stm_printd(1, "snd_stm_conv_i2c_work(work=%p)\n", work);
BUG_ON(!conv_i2c);
BUG_ON(!snd_stm_magic_valid(conv_i2c));
spin_lock(&conv_i2c->work_lock);
enable_value = conv_i2c->work_enable_value;
conv_i2c->work_enable_value = -1;
mute_value = conv_i2c->work_mute_value;
conv_i2c->work_mute_value = -1;
spin_unlock(&conv_i2c->work_lock);
cmd = NULL;
if (enable_value == 1) {
cmd = conv_i2c->info->enable_cmd;
cmd_len = conv_i2c->info->enable_cmd_len;
} else if (enable_value == 0) {
cmd = conv_i2c->info->disable_cmd;
cmd_len = conv_i2c->info->disable_cmd_len;
}
if (cmd) {
int result = i2c_master_send(conv_i2c->client, cmd, cmd_len);
if (result != cmd_len)
snd_stm_printe("WARNING! Failed to %sable I2C converter"
" '%s'! (%d)\n",
enable_value ? "en" : "dis",
dev_name(&conv_i2c->client->dev),
result);
}
cmd = NULL;
if (mute_value == 1) {
cmd = conv_i2c->info->mute_cmd;
cmd_len = conv_i2c->info->mute_cmd_len;
} else if (mute_value == 0) {
cmd = conv_i2c->info->unmute_cmd;
cmd_len = conv_i2c->info->unmute_cmd_len;
}
if (cmd) {
int result = i2c_master_send(conv_i2c->client, cmd, cmd_len);
if (result != cmd_len)
snd_stm_printe("WARNING! Failed to %smute I2C converter"
" '%s'! (%d)\n", mute_value ? "" : "un",
dev_name(&conv_i2c->client->dev),
result);
}
}
static unsigned int snd_stm_conv_i2c_get_format(void *priv)
{
struct snd_stm_conv_i2c *conv_i2c = priv;
snd_stm_printd(1, "snd_stm_conv_i2c_get_format(priv=%p)\n", priv);
BUG_ON(!conv_i2c);
BUG_ON(!snd_stm_magic_valid(conv_i2c));
return conv_i2c->info->format;
}
static int snd_stm_conv_i2c_get_oversampling(void *priv)
{
struct snd_stm_conv_i2c *conv_i2c = priv;
snd_stm_printd(1, "snd_stm_conv_i2c_get_oversampling(priv=%p)\n",
priv);
BUG_ON(!conv_i2c);
BUG_ON(!snd_stm_magic_valid(conv_i2c));
return conv_i2c->info->oversampling;
}
static int snd_stm_conv_i2c_set_enabled(int enabled, void *priv)
{
struct snd_stm_conv_i2c *conv_i2c = priv;
snd_stm_printd(1, "snd_stm_conv_i2c_enable(enabled=%d, priv=%p)\n",
enabled, priv);
BUG_ON(!conv_i2c);
BUG_ON(!snd_stm_magic_valid(conv_i2c));
BUG_ON(!conv_i2c->info->enable_supported);
snd_stm_printd(1, "%sabling DAC %s's.\n", enabled ? "En" : "Dis",
dev_name(&conv_i2c->client->dev));
spin_lock(&conv_i2c->work_lock);
conv_i2c->work_enable_value = enabled;
schedule_work(&conv_i2c->work);
spin_unlock(&conv_i2c->work_lock);
return 0;
}
static int snd_stm_conv_i2c_set_muted(int muted, void *priv)
{
struct snd_stm_conv_i2c *conv_i2c = priv;
snd_stm_printd(1, "snd_stm_conv_i2c_set_muted(muted=%d, priv=%p)\n",
muted, priv);
BUG_ON(!conv_i2c);
BUG_ON(!snd_stm_magic_valid(conv_i2c));
BUG_ON(!conv_i2c->info->mute_supported);
snd_stm_printd(1, "%suting DAC %s.\n", muted ? "M" : "Unm",
dev_name(&conv_i2c->client->dev));
spin_lock(&conv_i2c->work_lock);
conv_i2c->work_mute_value = muted;
schedule_work(&conv_i2c->work);
spin_unlock(&conv_i2c->work_lock);
return 0;
}
/*
* I2C driver routines
*/
static int snd_stm_conv_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int result = 0;
struct snd_stm_conv_i2c *conv_i2c;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&client->dev));
BUG_ON(!client->dev.platform_data);
conv_i2c = kzalloc(sizeof(*conv_i2c), GFP_KERNEL);
if (!conv_i2c) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(conv_i2c);
conv_i2c->client = client;
conv_i2c->info = client->dev.platform_data;
conv_i2c->ops.get_format = snd_stm_conv_i2c_get_format;
conv_i2c->ops.get_oversampling = snd_stm_conv_i2c_get_oversampling;
if (conv_i2c->info->enable_supported)
conv_i2c->ops.set_enabled = snd_stm_conv_i2c_set_enabled;
if (conv_i2c->info->mute_supported)
conv_i2c->ops.set_muted = snd_stm_conv_i2c_set_muted;
/* Call the user-provided init function */
if (conv_i2c->info->init) {
result = conv_i2c->info->init(client, conv_i2c->info->priv);
if (result != 0) {
snd_stm_printe("User's init function failed for I2C "
"converter %s! (%d)\n",
dev_name(&conv_i2c->client->dev),
result);
goto error_init;
}
}
/* Get connections */
BUG_ON(!conv_i2c->info->source_bus_id);
snd_stm_printd(0, "This converter is attached to '%s'.\n",
conv_i2c->info->source_bus_id);
conv_i2c->converter = snd_stm_conv_register_converter(
conv_i2c->info->group, &conv_i2c->ops, conv_i2c,
&platform_bus_type, conv_i2c->info->source_bus_id,
conv_i2c->info->channel_from,
conv_i2c->info->channel_to, NULL);
if (!conv_i2c->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
result = -EINVAL;
goto error_attach;
}
/* Initialize the converter */
INIT_WORK(&conv_i2c->work, snd_stm_conv_i2c_work);
spin_lock_init(&conv_i2c->work_lock);
conv_i2c->work_enable_value = -1;
conv_i2c->work_mute_value = -1;
if (conv_i2c->info->enable_supported) {
result = i2c_master_send(client, conv_i2c->info->disable_cmd,
conv_i2c->info->disable_cmd_len);
if (result != conv_i2c->info->disable_cmd_len) {
snd_stm_printe("Failed to disable I2C converter '%s'!"
" (%d)\n",
dev_name(&conv_i2c->client->dev),
result);
goto error_set_enabled;
}
}
if (conv_i2c->info->mute_supported) {
result = i2c_master_send(client, conv_i2c->info->mute_cmd,
conv_i2c->info->mute_cmd_len);
if (result != conv_i2c->info->mute_cmd_len) {
snd_stm_printe("Failed to mute I2C converter '%s'!"
" (%d)\n",
dev_name(&conv_i2c->client->dev),
result);
goto error_set_muted;
}
}
/* Done now */
i2c_set_clientdata(client, conv_i2c);
return 0;
error_set_muted:
error_set_enabled:
snd_stm_conv_unregister_converter(conv_i2c->converter);
error_attach:
error_init:
snd_stm_magic_clear(conv_i2c);
kfree(conv_i2c);
error_alloc:
return result;
}
static int snd_stm_conv_i2c_remove(struct i2c_client *client)
{
struct snd_stm_conv_i2c *conv_i2c = i2c_get_clientdata(client);
BUG_ON(!conv_i2c);
BUG_ON(!snd_stm_magic_valid(conv_i2c));
snd_stm_conv_unregister_converter(conv_i2c->converter);
/* Wait for the possibly scheduled work... */
flush_scheduled_work();
/* Muting and disabling - just to be sure ;-) */
if (conv_i2c->info->mute_supported)
i2c_master_send(client, conv_i2c->info->mute_cmd,
conv_i2c->info->mute_cmd_len);
if (conv_i2c->info->enable_supported)
i2c_master_send(client, conv_i2c->info->disable_cmd,
conv_i2c->info->disable_cmd_len);
snd_stm_magic_clear(conv_i2c);
kfree(conv_i2c);
return 0;
}
static struct i2c_driver snd_stm_conv_i2c_driver = {
.driver.name = "snd_conv_i2c",
.probe = snd_stm_conv_i2c_probe,
.remove = snd_stm_conv_i2c_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_i2c_init(void)
{
return i2c_add_driver(&snd_stm_conv_i2c_driver);
}
static void __exit snd_stm_conv_i2c_exit(void)
{
i2c_del_driver(&snd_stm_conv_i2c_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics I2C-controlled audio converter driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_i2c_init);
module_exit(snd_stm_conv_i2c_exit);

View File

@@ -0,0 +1,689 @@
/*
* STMicroelectronics System-on-Chips' I2S to SPDIF converter driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/stm.h>
#include "common.h"
#include "reg_aud_spdifpc.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Hardware-related definitions
*/
#define DEFAULT_OVERSAMPLING 128
/*
* Converter instance structure
*/
struct snd_stm_conv_i2sspdif {
/* System informations */
struct snd_stm_conv_converter *converter;
struct snd_stm_conv_i2sspdif_info *info;
struct device *device;
int index; /* ALSA controls index */
int ver; /* IP version, used by register access macros */
/* Resources */
struct resource *mem_region;
void *base;
/* Default configuration */
struct snd_aes_iec958 iec958_default;
spinlock_t iec958_default_lock; /* Protects iec958_default */
/* Runtime data */
int enabled;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
/*
* Internal routines
*/
/* Such a empty (zeroed) structure is pretty useful later... ;-) */
static struct snd_aes_iec958 snd_stm_conv_i2sspdif_iec958_zeroed;
#define CHA_STA_TRIES 50000
static int snd_stm_conv_i2sspdif_iec958_set(struct snd_stm_conv_i2sspdif
*conv_i2sspdif, struct snd_aes_iec958 *iec958)
{
int i, j, ok;
unsigned long status[6];
snd_stm_printd(1, "snd_stm_conv_i2sspdif_iec958_set(conv_i2sspdif=%p"
", iec958=%p)\n", conv_i2sspdif, iec958);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
/* I2S to SPDIF converter should be used only for playing
* PCM (non compressed) data, so validity bit should be always
* zero... (it means "valid linear PCM data") */
set__AUD_SPDIFPC_VAL__VALIDITY_BITS(conv_i2sspdif, 0);
/* Well... User data bit... Frankly speaking there is no way
* of correctly setting them with a mechanism provided by
* converter hardware, so it is better not to do this at all... */
set__AUD_SPDIFPC_DATA__USER_DATA_BITS(conv_i2sspdif, 0);
BUG_ON(memcmp(snd_stm_conv_i2sspdif_iec958_zeroed.subcode,
iec958->subcode, sizeof(iec958->subcode)) != 0);
if (conv_i2sspdif->ver < 4) {
/* Converter hardware by default puts every single bit of
* status to separate SPDIF subframe (instead of putting
* the same bit to both left and right subframes).
* So we have to prepare a "duplicated" version of
* status bits... Note that in such way status will be
* transmitted twice in every block! This is definitely
* out of spec, but fortunately most of receivers pay
* attention only to first 36 bits... */
for (i = 0; i < 6; i++) {
unsigned long word = 0;
for (j = 1; j >= 0; j--) {
unsigned char byte = iec958->status[i * 2 + j];
int k;
for (k = 0; k < 8; k++) {
word |= ((byte & 0x80) != 0);
if (!(j == 0 && k == 7)) {
word <<= 2;
byte <<= 1;
}
}
}
status[i] = word | (word << 1);
}
} else {
/* Fortunately in some hardware there is a "sane" mode
* of channel status registers operation... :-) */
for (i = 0; i < 6; i++)
status[i] = iec958->status[i * 4] |
iec958->status[i * 4 + 1] << 8 |
iec958->status[i * 4 + 2] << 16 |
iec958->status[i * 4 + 3] << 24;
}
/* Set converter's channel status registers - they are realised
* in such a ridiculous way that write to them is enabled only
* in (about) 300us time window after CHL_STS_BUFF_EMPTY bit
* is asserted... And this happens once every 2ms (only when
* converter is enabled and gets data...) */
ok = 0;
for (i = 0; i < CHA_STA_TRIES; i++) {
if (get__AUD_SPDIFPC_STA__CHL_STS_BUFF_EMPTY(conv_i2sspdif)) {
for (j = 0; j < 6; j++)
set__AUD_SPDIFPC_CHA_STA(conv_i2sspdif, j,
status[j]);
ok = 1;
for (j = 0; j < 6; j++)
if (get__AUD_SPDIFPC_CHA_STA(conv_i2sspdif,
j) != status[j]) {
ok = 0;
break;
}
if (ok)
break;
}
}
if (!ok) {
snd_stm_printe("WARNING! Failed to set channel status registers"
" for converter %s! (tried %d times)\n",
dev_name(conv_i2sspdif->device), i);
return -EINVAL;
}
snd_stm_printd(1, "Channel status registers set successfully "
"in %i tries.\n", i);
/* Set SPDIF player's VUC registers (these are used only
* for mute data formatting, and it should never happen ;-) */
set__AUD_SPDIFPC_SUV__VAL_LEFT(conv_i2sspdif, 0);
set__AUD_SPDIFPC_SUV__VAL_RIGHT(conv_i2sspdif, 0);
set__AUD_SPDIFPC_SUV__DATA_LEFT(conv_i2sspdif, 0);
set__AUD_SPDIFPC_SUV__DATA_RIGHT(conv_i2sspdif, 0);
/* And this time the problem is that SPDIF player lets
* to set only first 36 bits of channel status bits...
* Hopefully no one needs more ever ;-) And well - at least
* it puts channel status bits to both subframes :-) */
status[0] = iec958->status[0] | iec958->status[1] << 8 |
iec958->status[2] << 16 | iec958->status[3] << 24;
set__AUD_SPDIFPC_CL1__CHANNEL_STATUS(conv_i2sspdif, status[0]);
set__AUD_SPDIFPC_SUV__CH_STA_LEFT(conv_i2sspdif,
iec958->status[4] & 0xf);
set__AUD_SPDIFPC_CR1__CH_STA(conv_i2sspdif, status[0]);
set__AUD_SPDIFPC_SUV__CH_STA_RIGHT(conv_i2sspdif,
iec958->status[4] & 0xf);
return 0;
}
static int snd_stm_conv_i2sspdif_oversampling(struct snd_stm_conv_i2sspdif
*conv_i2sspdif)
{
snd_stm_printd(1, "snd_stm_conv_i2sspdif_oversampling("
"conv_i2sspdif=%p)\n", conv_i2sspdif);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
return DEFAULT_OVERSAMPLING;
}
static int snd_stm_conv_i2sspdif_enable(struct snd_stm_conv_i2sspdif
*conv_i2sspdif)
{
int oversampling;
struct snd_aes_iec958 iec958;
snd_stm_printd(1, "snd_stm_conv_i2sspdif_enable(conv_i2sspdif=%p)\n",
conv_i2sspdif);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
BUG_ON(conv_i2sspdif->enabled);
oversampling = snd_stm_conv_i2sspdif_oversampling(conv_i2sspdif);
BUG_ON(oversampling <= 0);
BUG_ON((oversampling % 128) != 0);
set__AUD_SPDIFPC_CFG(conv_i2sspdif,
mask__AUD_SPDIFPC_CFG__DEVICE_EN__ENABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__SW_RESET__RUNNING(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__FIFO_EN__ENABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__AUDIO_WORD_SIZE__24_BITS(conv_i2sspdif)
| mask__AUD_SPDIFPC_CFG__REQ_ACK_EN__ENABLED(conv_i2sspdif));
set__AUD_SPDIFPC_CTRL(conv_i2sspdif,
mask__AUD_SPDIFPC_CTRL__OPERATION__PCM(conv_i2sspdif) |
mask__AUD_SPDIFPC_CTRL__ROUNDING__NO_ROUNDING(conv_i2sspdif));
set__AUD_SPDIFPC_CTRL__DIVIDER(conv_i2sspdif, oversampling / 128);
/* Full channel status processing - an undocumented feature that
* exists in some hardware... Normally channel status registers
* provides bits for each subframe, so only for 96 frames (a half
* of SPDIF block) - pathetic! ;-) Setting bit 6 of config register
* enables a mode in which channel status bits in L/R subframes
* are identical, and whole block is served... */
if (conv_i2sspdif->ver >= 4)
set__AUD_SPDIFPC_CFG__CHA_STA_BITS__FRAME(conv_i2sspdif);
spin_lock(&conv_i2sspdif->iec958_default_lock);
iec958 = conv_i2sspdif->iec958_default;
spin_unlock(&conv_i2sspdif->iec958_default_lock);
if (snd_stm_conv_i2sspdif_iec958_set(conv_i2sspdif, &iec958) != 0)
snd_stm_printe("WARNING! Can't set channel status "
"registers!\n");
conv_i2sspdif->enabled = 1;
return 0;
}
static int snd_stm_conv_i2sspdif_disable(struct snd_stm_conv_i2sspdif
*conv_i2sspdif)
{
snd_stm_printd(1, "snd_stm_conv_i2sspdif_disable(conv_i2sspdif=%p)\n",
conv_i2sspdif);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
BUG_ON(!conv_i2sspdif->enabled);
if (snd_stm_conv_i2sspdif_iec958_set(conv_i2sspdif,
&snd_stm_conv_i2sspdif_iec958_zeroed) != 0)
snd_stm_printe("WARNING! Failed to clear channel status "
"registers!\n");
set__AUD_SPDIFPC_CFG(conv_i2sspdif,
mask__AUD_SPDIFPC_CFG__DEVICE_EN__DISABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__SW_RESET__RESET(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__FIFO_EN__DISABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__REQ_ACK_EN__DISABLED(conv_i2sspdif));
set__AUD_SPDIFPC_CTRL(conv_i2sspdif,
mask__AUD_SPDIFPC_CTRL__OPERATION__OFF(conv_i2sspdif));
conv_i2sspdif->enabled = 0;
return 0;
}
/*
* Converter interface implementation
*/
static unsigned int snd_stm_conv_i2sspdif_get_format(void *priv)
{
snd_stm_printd(1, "snd_stm_conv_i2sspdif_get_format(priv=%p)\n", priv);
return SND_STM_FORMAT__I2S | SND_STM_FORMAT__SUBFRAME_32_BITS;
}
static int snd_stm_conv_i2sspdif_get_oversampling(void *priv)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif = priv;
snd_stm_printd(1, "snd_stm_conv_i2sspdif_get_oversampling(priv=%p)\n",
priv);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
return snd_stm_conv_i2sspdif_oversampling(conv_i2sspdif);
}
static int snd_stm_conv_i2sspdif_set_enabled(int enabled, void *priv)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif = priv;
snd_stm_printd(1, "snd_stm_conv_i2sspdif_set_enabled(enabled=%d, "
"priv=%p)\n", enabled, priv);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
snd_stm_printd(1, "%sabling I2S to SPDIF converter '%s'.\n",
enabled ? "En" : "Dis",
dev_name(conv_i2sspdif->device));
if (enabled)
return snd_stm_conv_i2sspdif_enable(conv_i2sspdif);
else
return snd_stm_conv_i2sspdif_disable(conv_i2sspdif);
}
static struct snd_stm_conv_ops snd_stm_conv_i2sspdif_ops = {
.get_format = snd_stm_conv_i2sspdif_get_format,
.get_oversampling = snd_stm_conv_i2sspdif_get_oversampling,
.set_enabled = snd_stm_conv_i2sspdif_set_enabled,
};
/*
* ALSA controls
*/
static int snd_stm_conv_i2sspdif_ctl_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif =
snd_kcontrol_chip(kcontrol);
snd_stm_printd(1, "snd_stm_conv_i2sspdif_ctl_default_get("
"kcontrol=0x%p, ucontrol=0x%p)\n", kcontrol, ucontrol);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
spin_lock(&conv_i2sspdif->iec958_default_lock);
ucontrol->value.iec958 = conv_i2sspdif->iec958_default;
spin_unlock(&conv_i2sspdif->iec958_default_lock);
return 0;
}
static int snd_stm_conv_i2sspdif_ctl_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif =
snd_kcontrol_chip(kcontrol);
int changed = 0;
snd_stm_printd(1, "snd_stm_conv_i2sspdif_ctl_default_put("
"kcontrol=0x%p, ucontrol=0x%p)\n", kcontrol, ucontrol);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
spin_lock(&conv_i2sspdif->iec958_default_lock);
if (snd_stm_iec958_cmp(&conv_i2sspdif->iec958_default,
&ucontrol->value.iec958) != 0) {
conv_i2sspdif->iec958_default = ucontrol->value.iec958;
changed = 1;
}
spin_unlock(&conv_i2sspdif->iec958_default_lock);
return changed;
}
static struct snd_kcontrol_new snd_stm_conv_i2sspdif_ctls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.info = snd_stm_ctl_iec958_info,
.get = snd_stm_conv_i2sspdif_ctl_default_get,
.put = snd_stm_conv_i2sspdif_ctl_default_put,
}, {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
.info = snd_stm_ctl_iec958_info,
.get = snd_stm_ctl_iec958_mask_get_con,
}, {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
.info = snd_stm_ctl_iec958_info,
.get = snd_stm_ctl_iec958_mask_get_pro,
},
};
/*
* ALSA lowlevel device implementation
*/
#define DUMP_REGISTER(r) \
snd_iprintf(buffer, "AUD_SPDIFPC_%s (offset 0x%03x) =" \
" 0x%08x\n", __stringify(r), \
offset__AUD_SPDIFPC_##r(conv_i2sspdif), \
get__AUD_SPDIFPC_##r(conv_i2sspdif))
static void snd_stm_conv_i2sspdif_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif =
entry->private_data;
int i;
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
snd_iprintf(buffer, "--- %s ---\n", dev_name(conv_i2sspdif->device));
snd_iprintf(buffer, "base = 0x%p\n", conv_i2sspdif->base);
DUMP_REGISTER(CFG);
DUMP_REGISTER(STA);
DUMP_REGISTER(IT_EN);
DUMP_REGISTER(ITS);
DUMP_REGISTER(IT_CLR);
DUMP_REGISTER(VAL);
DUMP_REGISTER(DATA);
for (i = 0; i <= 5; i++)
snd_iprintf(buffer, "AUD_SPDIFPC_CHA_STA%d_CHANNEL_STATUS_BITS"
" (offset 0x%03x) = 0x%08x\n", i,
offset__AUD_SPDIFPC_CHA_STA(conv_i2sspdif, i),
get__AUD_SPDIFPC_CHA_STA(conv_i2sspdif, i));
DUMP_REGISTER(CTRL);
DUMP_REGISTER(SPDIFSTA);
DUMP_REGISTER(PAUSE);
DUMP_REGISTER(DATA_BURST);
DUMP_REGISTER(PA_PB);
DUMP_REGISTER(PC_PD);
DUMP_REGISTER(CL1);
DUMP_REGISTER(CR1);
DUMP_REGISTER(SUV);
snd_iprintf(buffer, "\n");
}
static int snd_stm_conv_i2sspdif_register(struct snd_device *snd_device)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif = snd_device->device_data;
int i;
snd_stm_printd(1, "%s(snd_device=0x%p)\n", __func__, snd_device);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
BUG_ON(conv_i2sspdif->enabled);
/* Initialize converter's input & SPDIF player as disabled */
set__AUD_SPDIFPC_CFG(conv_i2sspdif,
mask__AUD_SPDIFPC_CFG__DEVICE_EN__DISABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__SW_RESET__RESET(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__FIFO_EN__DISABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__REQ_ACK_EN__DISABLED(conv_i2sspdif));
set__AUD_SPDIFPC_CTRL(conv_i2sspdif,
mask__AUD_SPDIFPC_CTRL__OPERATION__OFF(conv_i2sspdif));
/* Additional procfs info */
snd_stm_info_register(&conv_i2sspdif->proc_entry,
dev_name(conv_i2sspdif->device),
snd_stm_conv_i2sspdif_dump_registers,
conv_i2sspdif);
/* Create ALSA controls */
for (i = 0; i < ARRAY_SIZE(snd_stm_conv_i2sspdif_ctls); i++) {
int result;
snd_stm_conv_i2sspdif_ctls[i].device =
snd_stm_conv_get_card_device(
conv_i2sspdif->converter);
snd_stm_conv_i2sspdif_ctls[i].index = conv_i2sspdif->index;
result = snd_ctl_add(snd_stm_card_get(),
snd_ctl_new1(&snd_stm_conv_i2sspdif_ctls[i],
conv_i2sspdif));
if (result < 0) {
snd_stm_printe("Failed to add I2S-SPDIF converter "
"ALSA control!\n");
return result;
}
}
return 0;
}
static int snd_stm_conv_i2sspdif_disconnect(struct snd_device *snd_device)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif = snd_device->device_data;
snd_stm_printd(1, "%s(snd_device=0x%p)\n", __func__, snd_device);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
BUG_ON(conv_i2sspdif->enabled);
/* Remove procfs entry */
snd_stm_info_unregister(conv_i2sspdif->proc_entry);
/* Power done mode, just to be sure :-) */
set__AUD_SPDIFPC_CFG(conv_i2sspdif,
mask__AUD_SPDIFPC_CFG__DEVICE_EN__DISABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__SW_RESET__RESET(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__FIFO_EN__DISABLED(conv_i2sspdif) |
mask__AUD_SPDIFPC_CFG__REQ_ACK_EN__DISABLED(conv_i2sspdif));
set__AUD_SPDIFPC_CTRL(conv_i2sspdif,
mask__AUD_SPDIFPC_CTRL__OPERATION__OFF(conv_i2sspdif));
return 0;
}
static struct snd_device_ops snd_stm_conv_i2sspdif_snd_device_ops = {
.dev_register = snd_stm_conv_i2sspdif_register,
.dev_disconnect = snd_stm_conv_i2sspdif_disconnect,
};
/*
* Platform driver routines
*/
static int snd_stm_conv_i2sspdif_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_conv_i2sspdif_info *conv_i2sspdif_info =
pdev->dev.platform_data;
struct snd_stm_conv_i2sspdif *conv_i2sspdif;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!conv_i2sspdif_info);
conv_i2sspdif = kzalloc(sizeof(*conv_i2sspdif), GFP_KERNEL);
if (!conv_i2sspdif) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(conv_i2sspdif);
conv_i2sspdif->ver = conv_i2sspdif_info->ver;
BUG_ON(conv_i2sspdif->ver <= 0);
conv_i2sspdif->info = conv_i2sspdif_info;
conv_i2sspdif->device = &pdev->dev;
spin_lock_init(&conv_i2sspdif->iec958_default_lock);
/* Get resources */
result = snd_stm_memory_request(pdev, &conv_i2sspdif->mem_region,
&conv_i2sspdif->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Get connections */
BUG_ON(!conv_i2sspdif_info->source_bus_id);
snd_stm_printd(0, "This I2S-SPDIF converter is attached to PCM player"
" '%s'.\n",
conv_i2sspdif_info->source_bus_id);
conv_i2sspdif->converter = snd_stm_conv_register_converter(
"HDMI Output",
&snd_stm_conv_i2sspdif_ops, conv_i2sspdif,
&platform_bus_type, conv_i2sspdif_info->source_bus_id,
conv_i2sspdif_info->channel_from,
conv_i2sspdif_info->channel_to,
&conv_i2sspdif->index);
if (!conv_i2sspdif->converter) {
snd_stm_printe("Can't attach to PCM player!\n");
result = -EINVAL;
goto error_attach;
}
/* Create ALSA lowlevel device*/
result = snd_device_new(snd_stm_card_get(), SNDRV_DEV_LOWLEVEL,
conv_i2sspdif, &snd_stm_conv_i2sspdif_snd_device_ops);
if (result < 0) {
snd_stm_printe("ALSA low level device creation failed!\n");
goto error_device;
}
/* Done now */
platform_set_drvdata(pdev, conv_i2sspdif);
return result;
error_device:
error_attach:
snd_stm_memory_release(conv_i2sspdif->mem_region,
conv_i2sspdif->base);
error_memory_request:
snd_stm_magic_clear(conv_i2sspdif);
kfree(conv_i2sspdif);
error_alloc:
return result;
}
static int snd_stm_conv_i2sspdif_remove(struct platform_device *pdev)
{
struct snd_stm_conv_i2sspdif *conv_i2sspdif =
platform_get_drvdata(pdev);
BUG_ON(!conv_i2sspdif);
BUG_ON(!snd_stm_magic_valid(conv_i2sspdif));
snd_stm_conv_unregister_converter(conv_i2sspdif->converter);
snd_stm_memory_release(conv_i2sspdif->mem_region, conv_i2sspdif->base);
snd_stm_magic_clear(conv_i2sspdif);
kfree(conv_i2sspdif);
return 0;
}
static struct platform_driver snd_stm_conv_i2sspdif_driver = {
.driver.name = "snd_conv_i2sspdif",
.probe = snd_stm_conv_i2sspdif_probe,
.remove = snd_stm_conv_i2sspdif_remove,
};
/*
* Initialization
*/
static int __init snd_stm_conv_i2sspdif_init(void)
{
return platform_driver_register(&snd_stm_conv_i2sspdif_driver);
}
static void __exit snd_stm_conv_i2sspdif_exit(void)
{
platform_driver_unregister(&snd_stm_conv_i2sspdif_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics I2S to SPDIF converter driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_conv_i2sspdif_init);
module_exit(snd_stm_conv_i2sspdif_exit);

811
kernel/sound/stm/core.c Normal file
View File

@@ -0,0 +1,811 @@
/*
* Core routines for STMicroelectronics' SoCs audio drivers
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/platform_device.h>
#include <linux/bpa2.h>
#include <linux/stm/stm-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include "common.h"
int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
static int snd_stm_card_index = -1; /* First available index */
module_param_named(index, snd_stm_card_index, int, 0444);
MODULE_PARM_DESC(index, "Index value for STMicroelectronics audio subsystem "
"card.");
static char *snd_stm_card_id;
module_param_named(id, snd_stm_card_id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for STMicroelectronics audio subsystem card.");
/*
* ALSA card management
*/
static struct snd_card *snd_stm_card;
static int snd_stm_card_registered;
int snd_stm_card_register(void)
{
int result;
const char *soc_type = get_cpu_subtype(cpu_data);
BUG_ON(!soc_type);
BUG_ON(!snd_stm_card);
BUG_ON(snd_stm_card_registered);
if (!snd_stm_card_id)
strlcpy(snd_stm_card->id, soc_type, sizeof(snd_stm_card->id));
strlcpy(snd_stm_card->driver, soc_type, sizeof(snd_stm_card->driver));
snprintf(snd_stm_card->shortname, sizeof(snd_stm_card->shortname),
"%s audio subsystem", soc_type);
if (cpu_data->cut_minor < 0)
snprintf(snd_stm_card->longname, sizeof(snd_stm_card->longname),
"STMicroelectronics %s cut %d.x SOC audio "
"subsystem", soc_type, cpu_data->cut_major);
else
snprintf(snd_stm_card->longname, sizeof(snd_stm_card->longname),
"STMicroelectronics %s cut %d.%d SOC audio "
"subsystem", soc_type, cpu_data->cut_major,
cpu_data->cut_minor);
result = snd_card_register(snd_stm_card);
if (result == 0)
snd_stm_card_registered = 1;
return result;
}
EXPORT_SYMBOL(snd_stm_card_register);
int snd_stm_card_is_registered(void)
{
BUG_ON(!snd_stm_card);
return snd_stm_card_registered;
}
EXPORT_SYMBOL(snd_stm_card_is_registered);
struct snd_card *snd_stm_card_get(void)
{
BUG_ON(!snd_stm_card);
return snd_stm_card;
}
EXPORT_SYMBOL(snd_stm_card_get);
/*
* Resources management
*/
int snd_stm_memory_request(struct platform_device *pdev,
struct resource **mem_region, void **base_address)
{
struct resource *resource;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!resource) {
snd_stm_printe("Failed to"
" platform_get_resource(IORESOURCE_MEM)!\n");
return -ENODEV;
}
*mem_region = request_mem_region(resource->start,
resource->end - resource->start + 1, pdev->name);
if (!*mem_region) {
snd_stm_printe("Failed request_mem_region(0x%08x,"
" 0x%08x, '%s')!\n", resource->start,
resource->end - resource->start + 1,
pdev->name);
return -EBUSY;
}
snd_stm_printd(0, "Memory region: 0x%08x-0x%08x\n",
(*mem_region)->start, (*mem_region)->end);
*base_address = ioremap(resource->start,
resource->end - resource->start + 1);
if (!*base_address) {
release_resource(*mem_region);
snd_stm_printe("Failed ioremap!\n");
return -EINVAL;
}
snd_stm_printd(0, "Base address is 0x%p.\n", *base_address);
return 0;
}
EXPORT_SYMBOL(snd_stm_memory_request);
void snd_stm_memory_release(struct resource *mem_region,
void *base_address)
{
iounmap(base_address);
release_resource(mem_region);
}
EXPORT_SYMBOL(snd_stm_memory_release);
int snd_stm_irq_request(struct platform_device *pdev,
unsigned int *irq, irq_handler_t handler, void *dev_id)
{
struct resource *resource;
int result;
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!resource) {
snd_stm_printe("Failed to "
"platform_get_resource(IORESOURCE_IRQ)!\n");
return -ENODEV;
}
snd_stm_printd(0, "IRQ: %u\n", resource->start);
*irq = resource->start;
result = request_irq(*irq, handler, IRQF_DISABLED, pdev->name, dev_id);
if (result != 0) {
snd_stm_printe("Failed request_irq!\n");
return -EINVAL;
}
/* request_irq() enables the interrupt immediately; as it is
* lethal in concurrent audio environment, we want to have
* it disabled for most of the time... */
disable_irq(*irq);
return 0;
}
int snd_stm_fdma_request(struct platform_device *pdev, int *channel)
{
static const char *fdmac_id[] = { STM_DMAC_ID, NULL };
static const char *fdma_cap_lb[] = { STM_DMA_CAP_LOW_BW, NULL };
static const char *fdma_cap_hb[] = { STM_DMA_CAP_HIGH_BW, NULL };
*channel = request_dma_bycap(fdmac_id, fdma_cap_lb, pdev->name);
if (*channel < 0) {
*channel = request_dma_bycap(fdmac_id, fdma_cap_hb, pdev->name);
if (*channel < 0) {
snd_stm_printe("Failed to request_dma_bycap()==%d!\n",
*channel);
return -ENODEV;
}
}
snd_stm_printd(0, "FDMA channel: %d\n", *channel);
return 0;
}
/*
* ALSA procfs additional entries
*/
static struct snd_info_entry *snd_stm_info_root;
int snd_stm_info_create(void)
{
int result = 0;
snd_stm_info_root = snd_info_create_module_entry(THIS_MODULE,
"stm", NULL);
if (snd_stm_info_root) {
snd_stm_info_root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (snd_info_register(snd_stm_info_root) < 0) {
result = -EINVAL;
snd_info_free_entry(snd_stm_info_root);
}
} else {
result = -ENOMEM;
}
return result;
}
void snd_stm_info_dispose(void)
{
if (snd_stm_info_root)
snd_info_free_entry(snd_stm_info_root);
}
int snd_stm_info_register(struct snd_info_entry **entry,
const char *name,
void (read)(struct snd_info_entry *, struct snd_info_buffer *),
void *private_data)
{
int result = 0;
/* Skip the "snd_" prefix, if bus_id has been simply given */
if (strncmp(name, "snd_", 4) == 0)
name += 4;
*entry = snd_info_create_module_entry(THIS_MODULE, name,
snd_stm_info_root);
if (*entry) {
(*entry)->c.text.read = read;
(*entry)->private_data = private_data;
if (snd_info_register(*entry) < 0) {
result = -EINVAL;
snd_info_free_entry(*entry);
}
} else {
result = -EINVAL;
}
return result;
}
EXPORT_SYMBOL(snd_stm_info_register);
void snd_stm_info_unregister(struct snd_info_entry *entry)
{
if (entry)
snd_info_free_entry(entry);
}
EXPORT_SYMBOL(snd_stm_info_unregister);
/*
* PCM buffer memory management
*/
struct snd_stm_buffer {
struct snd_pcm *pcm;
struct bpa2_part *bpa2_part;
int allocated;
struct snd_pcm_substream *substream;
snd_stm_magic_field;
};
#if defined(CONFIG_BPA2)
static char *bpa2_part = CONFIG_SND_STM_BPA2_PARTITION_NAME;
#else
static char *bpa2_part = "";
#endif
module_param(bpa2_part, charp, S_IRUGO);
struct snd_stm_buffer *snd_stm_buffer_create(struct snd_pcm *pcm,
struct device *device, int prealloc_size)
{
struct snd_stm_buffer *buffer;
snd_stm_printd(1, "snd_stm_buffer_init(pcm=%p, prealloc_size=%d)\n",
pcm, prealloc_size);
BUG_ON(!pcm);
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
snd_stm_printe("Can't allocate memory for a buffer "
"description!\n");
return NULL;
}
snd_stm_magic_set(buffer);
buffer->pcm = pcm;
#if defined(CONFIG_BPA2)
buffer->bpa2_part = bpa2_find_part(bpa2_part);
if (buffer->bpa2_part) {
snd_stm_printd(0, "Using BPA2 partition '%s'...\n", bpa2_part);
return buffer;
}
buffer->bpa2_part = bpa2_find_part("bigphysarea");
if (buffer->bpa2_part) {
snd_stm_printd(0, "Using legacy 'bigphysarea' BPA2 "
"partition...\n");
return buffer;
}
#endif
if (snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, device,
prealloc_size, prealloc_size) == 0) {
snd_stm_printd(0, "Using pcm_lib's preallocated buffer "
"(%d bytes)...\n", prealloc_size);
return buffer;
}
snd_stm_printe("Can't provide any memory for buffers!\n");
kfree(buffer);
return NULL;
}
void snd_stm_buffer_dispose(struct snd_stm_buffer *buffer)
{
snd_stm_printd(1, "snd_stm_buffer_dispose(buffer=%p)\n", buffer);
BUG_ON(!buffer);
BUG_ON(!snd_stm_magic_valid(buffer));
BUG_ON(buffer->allocated);
/* snd_pcm_lib__preallocate*-ed buffer is freed automagically */
snd_stm_magic_clear(buffer);
kfree(buffer);
}
int snd_stm_buffer_is_allocated(struct snd_stm_buffer *buffer)
{
snd_stm_printd(1, "snd_stm_buffer_is_allocated(buffer=%p)\n",
buffer);
BUG_ON(!buffer);
BUG_ON(!snd_stm_magic_valid(buffer));
return buffer->allocated;
}
int snd_stm_buffer_alloc(struct snd_stm_buffer *buffer,
struct snd_pcm_substream *substream, int size)
{
snd_stm_printd(1, "snd_stm_buffer_alloc(buffer=%p, substream=%p, "
"size=%d)\n", buffer, substream, size);
BUG_ON(!buffer);
BUG_ON(!snd_stm_magic_valid(buffer));
BUG_ON(buffer->allocated);
BUG_ON(size <= 0);
if (buffer->bpa2_part) {
#if defined(CONFIG_BPA2)
struct snd_pcm_runtime *runtime = substream->runtime;
int pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
runtime->dma_addr = bpa2_alloc_pages(buffer->bpa2_part, pages,
0, GFP_KERNEL);
if (runtime->dma_addr == 0) {
snd_stm_printe("Can't get %d pages from BPA2!\n",
pages);
return -ENOMEM;
}
runtime->dma_bytes = size;
runtime->dma_area = ioremap_nocache(runtime->dma_addr, size);
#else
snd_BUG();
#endif
} else {
if (snd_pcm_lib_malloc_pages(substream, size) < 0) {
snd_stm_printe("Can't allocate pages using pcm_lib!\n");
return -ENOMEM;
}
/* runtime->dma_* are set by snd_pcm_lib_malloc_pages()
* (by snd_pcm_set_runtime_buffer() to be more specific.) */
}
snd_stm_printd(1, "Allocated memory: dma_addr=0x%08x, dma_area=0x%p, "
"dma_bytes=%u\n", substream->runtime->dma_addr,
substream->runtime->dma_area,
substream->runtime->dma_bytes);
buffer->substream = substream;
buffer->allocated = 1;
return 0;
}
void snd_stm_buffer_free(struct snd_stm_buffer *buffer)
{
struct snd_pcm_runtime *runtime;
snd_stm_printd(1, "snd_stm_buffer_free(buffer=%p)\n", buffer);
BUG_ON(!buffer);
BUG_ON(!snd_stm_magic_valid(buffer));
BUG_ON(!buffer->allocated);
runtime = buffer->substream->runtime;
snd_stm_printd(1, "Freeing dma_addr=0x%08x, dma_area=0x%p, "
"dma_bytes=%u\n", runtime->dma_addr,
runtime->dma_area, runtime->dma_bytes);
if (buffer->bpa2_part) {
#if defined(CONFIG_BPA2)
iounmap(runtime->dma_area);
bpa2_free_pages(buffer->bpa2_part, runtime->dma_addr);
runtime->dma_area = NULL;
runtime->dma_addr = 0;
runtime->dma_bytes = 0;
#else
snd_BUG();
#endif
} else {
snd_pcm_lib_free_pages(buffer->substream);
/* runtime->dma_* are cleared by snd_pcm_lib_free_pages()
* (by snd_pcm_set_runtime_buffer() to be more specific.) */
}
buffer->allocated = 0;
buffer->substream = NULL;
}
static int snd_stm_buffer_mmap_fault(struct vm_area_struct *area,
struct vm_fault *vmf)
{
/* No VMA expanding here! */
return VM_FAULT_SIGBUS;
}
static struct vm_operations_struct snd_stm_buffer_mmap_vm_ops = {
.open = snd_pcm_mmap_data_open,
.close = snd_pcm_mmap_data_close,
.fault = snd_stm_buffer_mmap_fault,
};
int snd_stm_buffer_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long map_offset = area->vm_pgoff << PAGE_SHIFT;
unsigned long phys_addr = runtime->dma_addr + map_offset;
unsigned long map_size = area->vm_end - area->vm_start;
unsigned long phys_size = runtime->dma_bytes + PAGE_SIZE -
runtime->dma_bytes % PAGE_SIZE;
snd_stm_printd(1, "snd_stm_buffer_mmap(substream=%p, area=%p)\n",
substream, area);
snd_stm_printd(1, "Mmaping %lu bytes starting from 0x%08lx "
"(dma_addr=0x%08x, dma_size=%u, vm_pgoff=%lu, "
"vm_start=0x%lx, vm_end=0x%lx)...\n", map_size,
phys_addr, runtime->dma_addr, runtime->dma_bytes,
area->vm_pgoff, area->vm_start, area->vm_end);
if (map_size > phys_size) {
snd_stm_printe("Trying to perform mmap larger than buffer!\n");
return -EINVAL;
}
area->vm_ops = &snd_stm_buffer_mmap_vm_ops;
area->vm_private_data = substream;
area->vm_flags |= VM_RESERVED;
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
if (remap_pfn_range(area, area->vm_start, phys_addr >> PAGE_SHIFT,
map_size, area->vm_page_prot) != 0) {
snd_stm_printe("Can't remap buffer!\n");
return -EAGAIN;
}
/* Must be called implicitly here... */
snd_pcm_mmap_data_open(area);
return 0;
}
/*
* Common ALSA parameters constraints
*/
/*
#define FIXED_TRANSFER_BYTES max_transfer_bytes > 16 ? 16 : max_transfer_bytes
#define FIXED_TRANSFER_BYTES max_transfer_bytes
*/
#if defined(FIXED_TRANSFER_BYTES)
int snd_stm_pcm_transfer_bytes(unsigned int bytes_per_frame,
unsigned int max_transfer_bytes)
{
int transfer_bytes = FIXED_TRANSFER_BYTES;
snd_stm_printd(1, "snd_stm_pcm_transfer_bytes(bytes_per_frame=%u, "
"max_transfer_bytes=%u) = %u (FIXED)\n",
bytes_per_frame, max_transfer_bytes, transfer_bytes);
return transfer_bytes;
}
int snd_stm_pcm_hw_constraint_transfer_bytes(struct snd_pcm_runtime *runtime,
unsigned int max_transfer_bytes)
{
return snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
snd_stm_pcm_transfer_bytes(0, max_transfer_bytes));
}
#else
int snd_stm_pcm_transfer_bytes(unsigned int bytes_per_frame,
unsigned int max_transfer_bytes)
{
unsigned int transfer_bytes;
for (transfer_bytes = bytes_per_frame;
transfer_bytes * 2 < max_transfer_bytes;
transfer_bytes *= 2)
;
snd_stm_printd(2, "snd_stm_pcm_transfer_bytes(bytes_per_frame=%u, "
"max_transfer_bytes=%u) = %u\n", bytes_per_frame,
max_transfer_bytes, transfer_bytes);
return transfer_bytes;
}
static int snd_stm_pcm_hw_rule_transfer_bytes(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
int changed = 0;
unsigned int max_transfer_bytes = (unsigned int)rule->private;
struct snd_interval *period_bytes = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES);
struct snd_interval *frame_bits = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_FRAME_BITS);
unsigned int transfer_bytes, n;
transfer_bytes = snd_stm_pcm_transfer_bytes(frame_bits->min / 8,
max_transfer_bytes);
n = period_bytes->min % transfer_bytes;
if (n != 0 || period_bytes->openmin) {
period_bytes->min += transfer_bytes - n;
changed = 1;
}
transfer_bytes = snd_stm_pcm_transfer_bytes(frame_bits->max / 8,
max_transfer_bytes);
n = period_bytes->max % transfer_bytes;
if (n != 0 || period_bytes->openmax) {
period_bytes->max -= n;
changed = 1;
}
if (snd_interval_checkempty(period_bytes)) {
period_bytes->empty = 1;
return -EINVAL;
}
return changed;
}
int snd_stm_pcm_hw_constraint_transfer_bytes(struct snd_pcm_runtime *runtime,
unsigned int max_transfer_bytes)
{
return snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
snd_stm_pcm_hw_rule_transfer_bytes,
(void *)max_transfer_bytes,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
SNDRV_PCM_HW_PARAM_FRAME_BITS, -1);
}
#endif
/*
* Common ALSA controls routines
*/
int snd_stm_ctl_boolean_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
EXPORT_SYMBOL(snd_stm_ctl_boolean_info);
int snd_stm_ctl_iec958_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
int snd_stm_ctl_iec958_mask_get_con(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
IEC958_AES0_NONAUDIO |
IEC958_AES0_CON_NOT_COPYRIGHT |
IEC958_AES0_CON_EMPHASIS |
IEC958_AES0_CON_MODE;
ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
IEC958_AES1_CON_ORIGINAL;
ucontrol->value.iec958.status[2] = IEC958_AES2_CON_SOURCE |
IEC958_AES2_CON_CHANNEL;
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS |
IEC958_AES3_CON_CLOCK;
ucontrol->value.iec958.status[4] = IEC958_AES4_CON_MAX_WORDLEN_24 |
IEC958_AES4_CON_WORDLEN;
return 0;
}
int snd_stm_ctl_iec958_mask_get_pro(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
IEC958_AES0_NONAUDIO |
IEC958_AES0_PRO_EMPHASIS |
IEC958_AES0_PRO_FREQ_UNLOCKED |
IEC958_AES0_PRO_FS;
ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE |
IEC958_AES1_PRO_USERBITS;
ucontrol->value.iec958.status[2] = IEC958_AES2_PRO_SBITS |
IEC958_AES2_PRO_WORDLEN;
return 0;
}
int snd_stm_iec958_cmp(const struct snd_aes_iec958 *a,
const struct snd_aes_iec958 *b)
{
int result;
BUG_ON(!a);
BUG_ON(!b);
result = memcmp(a->status, b->status, sizeof(a->status));
if (result == 0)
result = memcmp(a->subcode, b->subcode, sizeof(a->subcode));
if (result == 0)
result = memcmp(a->dig_subframe, b->dig_subframe,
sizeof(a->dig_subframe));
return result;
}
/*
* Debug features
*/
/* Memory dump function */
void snd_stm_hex_dump(void *data, int size)
{
unsigned char *buffer = data;
char line[57];
int i;
for (i = 0; i < size; i++) {
if (i % 16 == 0)
sprintf(line, "%p", data + i);
sprintf(line + 8 + ((i % 16) * 3), " %02x", *buffer++);
if (i % 16 == 15 || i == size - 1)
printk(KERN_DEBUG "%s\n", line);
}
}
/* IEC958 structure dump */
void snd_stm_iec958_dump(const struct snd_aes_iec958 *vuc)
{
int i;
char line[54];
const unsigned char *data;
printk(KERN_DEBUG " "
"0 1 2 3 4 5 6 7 8 9\n");
data = vuc->status;
for (i = 0; i < 24; i++) {
if (i % 10 == 0)
sprintf(line, "%p status %02d:",
(unsigned char *)vuc + i, i);
sprintf(line + 22 + ((i % 10) * 3), " %02x", *data++);
if (i % 10 == 9 || i == 23)
printk(KERN_DEBUG "%s\n", line);
}
data = vuc->subcode;
for (i = 0; i < 147; i++) {
if (i % 10 == 0)
sprintf(line, "%p subcode %03d:",
(unsigned char *)vuc +
offsetof(struct snd_aes_iec958,
dig_subframe) + i, i);
sprintf(line + 22 + ((i % 10) * 3), " %02x", *data++);
if (i % 10 == 9 || i == 146)
printk(KERN_DEBUG "%s\n", line);
}
printk(KERN_DEBUG "%p dig_subframe: %02x %02x %02x %02x\n",
(unsigned char *)vuc +
offsetof(struct snd_aes_iec958, dig_subframe),
vuc->dig_subframe[0], vuc->dig_subframe[1],
vuc->dig_subframe[2], vuc->dig_subframe[3]);
}
/*
* Core initialization
*/
static int __init snd_stm_core_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
result = snd_card_create(snd_stm_card_index, snd_stm_card_id,
THIS_MODULE, 0, &snd_stm_card);
if (result != 0) {
snd_stm_printe("Failed to create ALSA card!\n");
goto error_card_create;
}
result = snd_stm_info_create();
if (result != 0) {
snd_stm_printe("Procfs info creation failed!\n");
goto error_info;
}
result = snd_stm_conv_init();
if (result != 0) {
snd_stm_printe("Converters infrastructure initialization"
" failed!\n");
goto error_conv;
}
return result;
error_conv:
snd_stm_info_dispose();
error_info:
snd_card_free(snd_stm_card);
error_card_create:
return result;
}
static void __exit snd_stm_core_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
snd_stm_conv_exit();
snd_stm_info_dispose();
snd_card_free(snd_stm_card);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics System-on-Chips' audio core driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_core_init);
module_exit(snd_stm_core_exit);

301
kernel/sound/stm/fli7510.c Normal file
View File

@@ -0,0 +1,301 @@
/*
* STMicrolectronics Freeman 510/520/530/540 (FLI7510/FLI7520/FLI7530/
* FLI7540) audio glue driver
*
* Copyright (c) 2010-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/clock.h>
#include <asm/irq-ilc.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Audio glue driver implementation
*/
#define AUD_CONFIG_REG1(base) ((base) + 0x00)
#define SPDIF_CLK 0
#define SPDIF_CLK__CLK_256FS_FREE_RUN (0 << SPDIF_CLK)
#define SPDIF_CLK__M_CLK_SPDIF (1 << SPDIF_CLK)
#define SPDIF_CLK__M_CLK_HDMI (2 << SPDIF_CLK)
#define SPDIF_CLK__M_CLK_I2S (3 << SPDIF_CLK)
#define SPDIF_CLK__CLK_256FS_DEC_1 (4 << SPDIF_CLK)
#define SPDIF_CLK__CLK_256FS_DEC_2 (5 << SPDIF_CLK)
#define MAIN_CLK 3
#define MAIN_CLK__CLK_256FS_FREE_RUN (0 << MAIN_CLK)
#define MAIN_CLK__M_CLK_MAIN (1 << MAIN_CLK)
#define MAIN_CLK__M_CLK_HDMI (2 << MAIN_CLK)
#define MAIN_CLK__M_CLK_I2S (3 << MAIN_CLK)
#define MAIN_CLK__CLK_256FS_DEC_1 (4 << MAIN_CLK)
#define MAIN_CLK__CLK_256FS_DEC_2 (5 << MAIN_CLK)
#define ENC_CLK 6
#define ENC_CLK__CLK_256FS_FREE_RUN (0 << ENC_CLK)
#define ENC_CLK__M_CLK_ENC (1 << ENC_CLK)
#define ENC_CLK__M_CLK_HDMI (2 << ENC_CLK)
#define ENC_CLK__M_CLK_I2S (3 << ENC_CLK)
#define ENC_CLK__CLK_256FS_DEC_1 (4 << ENC_CLK)
#define ENC_CLK__CLK_256FS_DEC_2 (5 << ENC_CLK)
#define DAC_CLK 9
#define DAC_CLK__CLK_256FS_FREE_RUN (0 << DAC_CLK)
#define DAC_CLK__M_CLK_DAC (1 << DAC_CLK)
#define DAC_CLK__M_CLK_HDMI (2 << DAC_CLK)
#define DAC_CLK__M_CLK_I2S (3 << DAC_CLK)
#define DAC_CLK__CLK_256FS_DEC_1 (4 << DAC_CLK)
#define DAC_CLK__CLK_256FS_DEC_2 (5 << DAC_CLK)
#define ADC_CLK 12
#define ADC_CLK__CLK_256FS_FREE_RUN (0 << ADC_CLK)
#define ADC_CLK__M_CLK_ADC (1 << ADC_CLK)
#define ADC_CLK__M_CLK_HDMI (2 << ADC_CLK)
#define ADC_CLK__M_CLK_I2S (3 << ADC_CLK)
#define ADC_CLK__CLK_256FS_DEC_1 (4 << ADC_CLK)
#define ADC_CLK__CLK_256FS_DEC_2 (5 << ADC_CLK)
#define SPDIF_CLK_DIV2_EN 30
#define SPDIF_CLK_DIV2_EN__DISABLED (0 << SPDIF_CLK_DIV2_EN)
#define SPDIF_CLK_DIV2_EN__ENABLED (1 << SPDIF_CLK_DIV2_EN)
#define SPDIF_IN_PAD_HYST_EN 31
#define SPDIF_IN_PAD_HYST_EN__DISABLED (0 << SPDIF_IN_PAD_HYST_EN)
#define SPDIF_IN_PAD_HYST_EN__ENABLED (1 << SPDIF_IN_PAD_HYST_EN)
#define AUD_CONFIG_REG2(base) ((base) + 0x04)
#define SPDIF 0
#define SPDIF__PLAYER (0 << SPDIF)
#define SPDIF__READER (1 << SPDIF)
#define FLI7510_MAIN_I2S 1
#define FLI7510_MAIN_I2S__PCM_PLAYER_0 (0 << FLI7510_MAIN_I2S)
#define FLI7510_MAIN_I2S__PCM_READER_1 (1 << FLI7510_MAIN_I2S)
#define FLI7510_SEC_I2S 2
#define FLI7510_SEC_I2S__PCM_PLAYER_1 (0 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__PCM_PLAYER_2 (1 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__PCM_PLAYER_3 (2 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__PCM_PLAYER_4 (3 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__PCM_READER_0 (4 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__PCM_READER_3 (5 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__PCM_READER_4 (6 << FLI7510_SEC_I2S)
#define FLI7510_SEC_I2S__AATV_I2S0 (7 << FLI7510_SEC_I2S)
#define FLI7520_MAIN_I2S 1
#define FLI7520_MAIN_I2S__PCM_READER_1 (0 << FLI7520_MAIN_I2S)
#define FLI7520_MAIN_I2S__PCM_READER_5 (1 << FLI7520_MAIN_I2S)
#define FLI7520_MAIN_I2S__PCM_PLAYER_0 (2 << FLI7520_MAIN_I2S)
#define FLI7520_MAIN_I2S__AATV_I2S0 (3 << FLI7520_MAIN_I2S)
#define FLI7520_SEC_I2S 3
#define FLI7520_SEC_I2S__PCM_PLAYER_1 (0 << FLI7520_SEC_I2S)
#define FLI7520_SEC_I2S__PCM_PLAYER_2 (1 << FLI7520_SEC_I2S)
#define FLI7520_SEC_I2S__PCM_PLAYER_4 (3 << FLI7520_SEC_I2S)
#define FLI7520_SEC_I2S__PCM_READER_0 (4 << FLI7520_SEC_I2S)
#define FLI7520_SEC_I2S__PCM_READER_3 (5 << FLI7520_SEC_I2S)
#define FLI7520_SEC_I2S__PCM_READER_4 (6 << FLI7520_SEC_I2S)
#define FLI7520_SEC_I2S__AATV_I2S0 (7 << FLI7520_SEC_I2S)
#define SPDIF_PLAYER_EN 31
#define SPDIF_PLAYER_EN__DISABLED (0 << SPDIF_PLAYER_EN)
#define SPDIF_PLAYER_EN__ENABLED (1 << SPDIF_PLAYER_EN)
struct snd_stm_fli7510_glue {
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
static void snd_stm_fli7510_glue_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_fli7510_glue *fli7510_glue = entry->private_data;
BUG_ON(!fli7510_glue);
BUG_ON(!snd_stm_magic_valid(fli7510_glue));
snd_iprintf(buffer, "--- snd_fli7510_glue ---\n");
snd_iprintf(buffer, "AUD_CONFIG_REG1 (0x%p) = 0x%08x\n",
AUD_CONFIG_REG1(fli7510_glue->base),
readl(AUD_CONFIG_REG1(fli7510_glue->base)));
snd_iprintf(buffer, "AUD_CONFIG_REG2 (0x%p) = 0x%08x\n",
AUD_CONFIG_REG2(fli7510_glue->base),
readl(AUD_CONFIG_REG2(fli7510_glue->base)));
snd_iprintf(buffer, "\n");
}
static int __init snd_stm_fli7510_glue_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_fli7510_glue *fli7510_glue;
unsigned int value;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
fli7510_glue = kzalloc(sizeof(*fli7510_glue), GFP_KERNEL);
if (!fli7510_glue) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(fli7510_glue);
result = snd_stm_memory_request(pdev, &fli7510_glue->mem_region,
&fli7510_glue->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Clocking configuration:
*
* clk_256fs_free_run --> SPDIF clock
*
* clk_256fs_dec_1 -----> MAIN (LS) clock
*
* ,-> ENCODER clock
* clk_256fs_dec_2 --<
* `-> DAC (HP, AUX) clock
*/
value = SPDIF_CLK__CLK_256FS_FREE_RUN;
value |= MAIN_CLK__CLK_256FS_DEC_1;
value |= ENC_CLK__CLK_256FS_DEC_2;
value |= DAC_CLK__CLK_256FS_DEC_2;
value |= SPDIF_CLK_DIV2_EN__DISABLED;
value |= SPDIF_IN_PAD_HYST_EN__DISABLED;
writel(value, AUD_CONFIG_REG1(fli7510_glue->base));
value = SPDIF__PLAYER;
if (cpu_data->type == CPU_FLI7510) {
value |= FLI7510_MAIN_I2S__PCM_PLAYER_0;
value |= FLI7510_SEC_I2S__PCM_PLAYER_1;
} else {
value |= FLI7520_MAIN_I2S__PCM_PLAYER_0;
value |= FLI7520_SEC_I2S__PCM_PLAYER_1;
}
value |= SPDIF_PLAYER_EN__ENABLED;
writel(value, AUD_CONFIG_REG2(fli7510_glue->base));
/* Additional procfs info */
snd_stm_info_register(&fli7510_glue->proc_entry, "fli7510_glue",
snd_stm_fli7510_glue_dump_registers, fli7510_glue);
platform_set_drvdata(pdev, fli7510_glue);
return result;
error_memory_request:
snd_stm_magic_clear(fli7510_glue);
kfree(fli7510_glue);
error_alloc:
return result;
}
static int __exit snd_stm_fli7510_glue_remove(struct platform_device *pdev)
{
struct snd_stm_fli7510_glue *fli7510_glue = platform_get_drvdata(pdev);
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!fli7510_glue);
BUG_ON(!snd_stm_magic_valid(fli7510_glue));
/* Remove procfs entry */
snd_stm_info_unregister(fli7510_glue->proc_entry);
/* Disable audio outputs */
snd_stm_memory_release(fli7510_glue->mem_region, fli7510_glue->base);
snd_stm_magic_clear(fli7510_glue);
kfree(fli7510_glue);
return 0;
}
static struct platform_driver snd_stm_fli7510_glue_driver = {
.driver.name = "snd_fli7510_glue",
.probe = snd_stm_fli7510_glue_probe,
.remove = snd_stm_fli7510_glue_remove,
};
/*
* Audio initialization
*/
static int __init snd_stm_fli7510_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
switch (cpu_data->type) {
case CPU_FLI7510:
case CPU_FLI7520:
case CPU_FLI7530:
case CPU_FLI7540:
break;
default:
snd_stm_printe("Unsupported (non-Freeman) SOC detected!\n");
result = -EINVAL;
goto error_soc_type;
}
result = platform_driver_register(&snd_stm_fli7510_glue_driver);
if (result != 0) {
snd_stm_printe("Failed to register audio glue driver!\n");
goto error_glue_driver_register;
}
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards!\n");
goto error_card_register;
}
return 0;
error_card_register:
platform_driver_unregister(&snd_stm_fli7510_glue_driver);
error_glue_driver_register:
error_soc_type:
return result;
}
static void __exit snd_stm_fli7510_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
platform_driver_unregister(&snd_stm_fli7510_glue_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics Freeman 510/520/530/540 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_fli7510_init);
module_exit(snd_stm_fli7510_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

206
kernel/sound/stm/stx7100.c Normal file
View File

@@ -0,0 +1,206 @@
/*
* STMicrolectronics STx7100/STx7109 audio glue driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Audio glue driver implementation
*/
#define IO_CTRL(base) ((base) + 0x00)
#define PCM_CLK_EN 0
#define PCM_CLK_EN__INPUT (0 << PCM_CLK_EN)
#define PCM_CLK_EN__OUTPUT (1 << PCM_CLK_EN)
#define DATA0_EN 1
#define DATA0_EN__INPUT (0 << DATA0_EN)
#define DATA0_EN__OUTPUT (1 << DATA0_EN)
#define DATA1_EN 2
#define DATA1_EN__INPUT (0 << DATA1_EN)
#define DATA1_EN__OUTPUT (1 << DATA1_EN)
#define SPDIN_EN 3
#define SPDIF_EN__DISABLE (0 << SPDIN_EN)
#define SPDIF_EN__ENABLE (1 << SPDIN_EN)
struct snd_stm_stx7100_glue {
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
static void snd_stm_stx7100_glue_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_stx7100_glue *stx7100_glue = entry->private_data;
BUG_ON(!stx7100_glue);
BUG_ON(!snd_stm_magic_valid(stx7100_glue));
snd_iprintf(buffer, "--- snd_stx7100_glue ---\n");
snd_iprintf(buffer, "IO_CTRL (0x%p) = 0x%08x\n",
IO_CTRL(stx7100_glue->base),
readl(IO_CTRL(stx7100_glue->base)));
snd_iprintf(buffer, "\n");
}
static int __init snd_stm_stx7100_glue_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_stx7100_glue *stx7100_glue;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
stx7100_glue = kzalloc(sizeof(*stx7100_glue), GFP_KERNEL);
if (!stx7100_glue) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(stx7100_glue);
result = snd_stm_memory_request(pdev, &stx7100_glue->mem_region,
&stx7100_glue->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Enable audio outputs */
writel(SPDIF_EN__ENABLE | DATA1_EN__OUTPUT | DATA0_EN__OUTPUT |
PCM_CLK_EN__OUTPUT, IO_CTRL(stx7100_glue->base));
/* Additional procfs info */
snd_stm_info_register(&stx7100_glue->proc_entry, "stx7100_glue",
snd_stm_stx7100_glue_dump_registers, stx7100_glue);
platform_set_drvdata(pdev, stx7100_glue);
return result;
error_memory_request:
snd_stm_magic_clear(stx7100_glue);
kfree(stx7100_glue);
error_alloc:
return result;
}
static int __exit snd_stm_stx7100_glue_remove(struct platform_device *pdev)
{
struct snd_stm_stx7100_glue *stx7100_glue =
platform_get_drvdata(pdev);
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!stx7100_glue);
BUG_ON(!snd_stm_magic_valid(stx7100_glue));
/* Remove procfs entry */
snd_stm_info_unregister(stx7100_glue->proc_entry);
/* Disable audio outputs */
writel(SPDIF_EN__DISABLE | DATA1_EN__INPUT | DATA0_EN__INPUT |
PCM_CLK_EN__INPUT, IO_CTRL(stx7100_glue->base));
snd_stm_memory_release(stx7100_glue->mem_region, stx7100_glue->base);
snd_stm_magic_clear(stx7100_glue);
kfree(stx7100_glue);
return 0;
}
static struct platform_driver snd_stm_stx7100_glue_driver = {
.driver.name = "snd_stx7100_glue",
.probe = snd_stm_stx7100_glue_probe,
.remove = snd_stm_stx7100_glue_remove,
};
/*
* Audio initialization
*/
static int __init snd_stm_stx7100_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
if (cpu_data->type != CPU_STX7100 && cpu_data->type != CPU_STX7109) {
snd_stm_printe("Not supported (other than STx7100 or STx7109)"
" SOC detected!\n");
result = -EINVAL;
goto error_soc_type;
}
result = platform_driver_register(&snd_stm_stx7100_glue_driver);
if (result != 0) {
snd_stm_printe("Failed to register audio glue driver!\n");
goto error_glue_driver_register;
}
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards!\n");
goto error_card_register;
}
return 0;
error_card_register:
platform_driver_unregister(&snd_stm_stx7100_glue_driver);
error_glue_driver_register:
error_soc_type:
return result;
}
static void __exit snd_stm_stx7100_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
platform_driver_unregister(&snd_stm_stx7100_glue_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STx7100/STx7109 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_stx7100_init);
module_exit(snd_stm_stx7100_exit);

208
kernel/sound/stm/stx7105.c Normal file
View File

@@ -0,0 +1,208 @@
/*
* STMicrolectronics STx7105 ans STx7106 audio glue driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Audio glue driver implementation
*/
#define IO_CTRL(base) ((base) + 0x00)
#define PCM_CLK_EN 0
#define PCM_CLK_EN__INPUT (0 << PCM_CLK_EN)
#define PCM_CLK_EN__OUTPUT (1 << PCM_CLK_EN)
#define SPDIFHDMI_EN 3
#define SPDIFHDMI_EN__INPUT (0 << SPDIFHDMI_EN)
#define SPDIFHDMI_EN__OUTPUT (1 << SPDIFHDMI_EN)
#define PCMPLHDMI_EN 5
#define PCMPLHDMI_EN__INPUT (0 << PCMPLHDMI_EN)
#define PCMPLHDMI_EN__OUTPUT (1 << PCMPLHDMI_EN)
#define CLKREC_SEL 9
#define CLKREC_SEL__PCMPLHDMI (0 << CLKREC_SEL)
#define CLKREC_SEL__SPDIFHDMI (1 << CLKREC_SEL)
#define CLKREC_SEL__PCMPL1 (2 << CLKREC_SEL)
struct snd_stm_stx7105_glue {
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
static void snd_stm_stx7105_glue_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_stx7105_glue *stx7105_glue = entry->private_data;
BUG_ON(!stx7105_glue);
BUG_ON(!snd_stm_magic_valid(stx7105_glue));
snd_iprintf(buffer, "--- snd_stx7105_glue ---\n");
snd_iprintf(buffer, "IO_CTRL (0x%p) = 0x%08x\n",
IO_CTRL(stx7105_glue->base),
readl(IO_CTRL(stx7105_glue->base)));
snd_iprintf(buffer, "\n");
}
static int __init snd_stm_stx7105_glue_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_stx7105_glue *stx7105_glue;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
stx7105_glue = kzalloc(sizeof(*stx7105_glue), GFP_KERNEL);
if (!stx7105_glue) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(stx7105_glue);
result = snd_stm_memory_request(pdev, &stx7105_glue->mem_region,
&stx7105_glue->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Enable audio outputs */
writel(PCMPLHDMI_EN__OUTPUT | SPDIFHDMI_EN__OUTPUT |
PCM_CLK_EN__OUTPUT, IO_CTRL(stx7105_glue->base));
/* Additional procfs info */
snd_stm_info_register(&stx7105_glue->proc_entry, "stx7105_glue",
snd_stm_stx7105_glue_dump_registers, stx7105_glue);
platform_set_drvdata(pdev, stx7105_glue);
return result;
error_memory_request:
snd_stm_magic_clear(stx7105_glue);
kfree(stx7105_glue);
error_alloc:
return result;
}
static int __exit snd_stm_stx7105_glue_remove(struct platform_device *pdev)
{
struct snd_stm_stx7105_glue *stx7105_glue = platform_get_drvdata(pdev);
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!stx7105_glue);
BUG_ON(!snd_stm_magic_valid(stx7105_glue));
/* Remove procfs entry */
snd_stm_info_unregister(stx7105_glue->proc_entry);
/* Disable audio outputs */
writel(PCMPLHDMI_EN__INPUT | SPDIFHDMI_EN__INPUT |
PCM_CLK_EN__INPUT, IO_CTRL(stx7105_glue->base));
snd_stm_memory_release(stx7105_glue->mem_region, stx7105_glue->base);
snd_stm_magic_clear(stx7105_glue);
kfree(stx7105_glue);
return 0;
}
static struct platform_driver snd_stm_stx7105_glue_driver = {
.driver.name = "snd_stx7105_glue",
.probe = snd_stm_stx7105_glue_probe,
.remove = snd_stm_stx7105_glue_remove,
};
/*
* Audio initialization
*/
static int __init snd_stm_stx7105_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
if (cpu_data->type != CPU_STX7105 && cpu_data->type != CPU_STX7106) {
snd_stm_printe("Not supported (other than STx7105 or STx7106)"
" SOC detected!\n");
result = -EINVAL;
goto error_soc_type;
}
result = platform_driver_register(&snd_stm_stx7105_glue_driver);
if (result != 0) {
snd_stm_printe("Failed to register audio glue driver!\n");
goto error_glue_driver_register;
}
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards!\n");
goto error_card_register;
}
return 0;
error_card_register:
platform_driver_unregister(&snd_stm_stx7105_glue_driver);
error_glue_driver_register:
error_soc_type:
return result;
}
static void __exit snd_stm_stx7105_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
platform_driver_unregister(&snd_stm_stx7105_glue_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STx7105 and STx7106 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_stx7105_init);
module_exit(snd_stm_stx7105_exit);

111
kernel/sound/stm/stx7108.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* STMicrolectronics STx7108 SoC audio glue driver
*
* Copyright (c) 2010-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <asm/irq-ilc.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/stm/sysconf.h>
#include <linux/stm/stx7108.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* ALSA module parameters
*/
static int index = -1; /* First available index */
static char *id = "STx7108"; /* Default card ID */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for STx7108 audio subsystem "
"card.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for STx7108 audio subsystem card.");
/*
* Audio initialization
*/
static struct sysconf_field *snd_stm_stx7108_pcmp_valid_sel;
static int __init snd_stm_stx7108_init(void)
{
int result;
snd_stm_printd(0, "snd_stm_stx7108_init()\n");
if (cpu_data->type != CPU_STX7108) {
snd_stm_printe("Not supported (other than STx7108) SOC "
"detected!\n");
result = -EINVAL;
goto error_soc_type;
}
/* Route PCM Player #2 to the digital outputs (PIOs) */
snd_stm_stx7108_pcmp_valid_sel = sysconf_claim(SYS_CFG_BANK4,
43, 0, 1, "PCMP_VALID_SEL");
if (!snd_stm_stx7108_pcmp_valid_sel) {
snd_stm_printe("Can't claim required sysconf field!\n");
result = -EBUSY;
goto error_sysconf_claim;
}
sysconf_write(snd_stm_stx7108_pcmp_valid_sel, 2);
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards!\n");
goto error_card_register;
}
return 0;
error_card_register:
sysconf_release(snd_stm_stx7108_pcmp_valid_sel);
error_sysconf_claim:
error_soc_type:
return result;
}
static void __exit snd_stm_stx7108_exit(void)
{
snd_stm_printd(0, "snd_stm_stx7108_exit()\n");
sysconf_release(snd_stm_stx7108_pcmp_valid_sel);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STx7108 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_stx7108_init);
module_exit(snd_stm_stx7108_exit);

208
kernel/sound/stm/stx7111.c Normal file
View File

@@ -0,0 +1,208 @@
/*
* STMicrolectronics STx7111 audio glue driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Audio glue driver implementation
*/
#define IO_CTRL(base) ((base) + 0x00)
#define PCM_CLK_EN 0
#define PCM_CLK_EN__INPUT (0 << PCM_CLK_EN)
#define PCM_CLK_EN__OUTPUT (1 << PCM_CLK_EN)
#define SPDIFHDMI_EN 3
#define SPDIFHDMI_EN__INPUT (0 << SPDIFHDMI_EN)
#define SPDIFHDMI_EN__OUTPUT (1 << SPDIFHDMI_EN)
#define PCMPLHDMI_EN 5
#define PCMPLHDMI_EN__INPUT (0 << PCMPLHDMI_EN)
#define PCMPLHDMI_EN__OUTPUT (1 << PCMPLHDMI_EN)
#define CLKREC_SEL 9
#define CLKREC_SEL__PCMPLHDMI (0 << CLKREC_SEL)
#define CLKREC_SEL__SPDIFHDMI (1 << CLKREC_SEL)
#define CLKREC_SEL__PCMPL1 (2 << CLKREC_SEL)
struct snd_stm_stx7111_glue {
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
static void snd_stm_stx7111_glue_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_stx7111_glue *stx7111_glue = entry->private_data;
BUG_ON(!stx7111_glue);
BUG_ON(!snd_stm_magic_valid(stx7111_glue));
snd_iprintf(buffer, "--- snd_stx7111_glue ---\n");
snd_iprintf(buffer, "IO_CTRL (0x%p) = 0x%08x\n",
IO_CTRL(stx7111_glue->base),
readl(IO_CTRL(stx7111_glue->base)));
snd_iprintf(buffer, "\n");
}
static int __init snd_stm_stx7111_glue_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_stx7111_glue *stx7111_glue;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
stx7111_glue = kzalloc(sizeof(*stx7111_glue), GFP_KERNEL);
if (!stx7111_glue) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(stx7111_glue);
result = snd_stm_memory_request(pdev, &stx7111_glue->mem_region,
&stx7111_glue->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Enable audio outputs */
writel(PCMPLHDMI_EN__OUTPUT | SPDIFHDMI_EN__OUTPUT |
PCM_CLK_EN__OUTPUT, IO_CTRL(stx7111_glue->base));
/* Additional procfs info */
snd_stm_info_register(&stx7111_glue->proc_entry, "stx7111_glue",
snd_stm_stx7111_glue_dump_registers, stx7111_glue);
platform_set_drvdata(pdev, stx7111_glue);
return result;
error_memory_request:
snd_stm_magic_clear(stx7111_glue);
kfree(stx7111_glue);
error_alloc:
return result;
}
static int __exit snd_stm_stx7111_glue_remove(struct platform_device *pdev)
{
struct snd_stm_stx7111_glue *stx7111_glue = platform_get_drvdata(pdev);
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!stx7111_glue);
BUG_ON(!snd_stm_magic_valid(stx7111_glue));
/* Remove procfs entry */
snd_stm_info_unregister(stx7111_glue->proc_entry);
/* Disable audio outputs */
writel(PCMPLHDMI_EN__INPUT | SPDIFHDMI_EN__INPUT |
PCM_CLK_EN__INPUT, IO_CTRL(stx7111_glue->base));
snd_stm_memory_release(stx7111_glue->mem_region, stx7111_glue->base);
snd_stm_magic_clear(stx7111_glue);
kfree(stx7111_glue);
return 0;
}
static struct platform_driver snd_stm_stx7111_glue_driver = {
.driver.name = "snd_stx7111_glue",
.probe = snd_stm_stx7111_glue_probe,
.remove = snd_stm_stx7111_glue_remove,
};
/*
* Audio initialization
*/
static int __init snd_stm_stx7111_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
if (cpu_data->type != CPU_STX7111) {
snd_stm_printe("Not supported (other than STx7111) SOC "
"detected!\n");
result = -EINVAL;
goto error_soc_type;
}
result = platform_driver_register(&snd_stm_stx7111_glue_driver);
if (result != 0) {
snd_stm_printe("Failed to register audio glue driver!\n");
goto error_glue_driver_register;
}
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards!\n");
goto error_card_register;
}
return 0;
error_card_register:
platform_driver_unregister(&snd_stm_stx7111_glue_driver);
error_glue_driver_register:
error_soc_type:
return result;
}
static void __exit snd_stm_stx7111_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
platform_driver_unregister(&snd_stm_stx7111_glue_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STx7111 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_stx7111_init);
module_exit(snd_stm_stx7111_exit);

205
kernel/sound/stm/stx7141.c Normal file
View File

@@ -0,0 +1,205 @@
/*
* STMicrolectronics STx7141 audio glue driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Stephen Gallimore <stephen.gallimore@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <asm/irq-ilc.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* Audio glue driver implementation
*/
#define IO_CTRL(base) ((base) + 0x00)
#define CLKREC_SEL 9
#define CLKREC_SEL__PCMPLHDMI (0 << CLKREC_SEL)
#define CLKREC_SEL__SPDIFHDMI (1 << CLKREC_SEL)
#define CLKREC_SEL__PCMPL1 (2 << CLKREC_SEL)
#define CLKREC_SEL__PCMPL0 (3 << CLKREC_SEL)
#define PCMR1_SCLK_INV_SEL 12
#define PCMR1_SCLK_INV_SEL__NO_INVERSION (0 << PCMR1_SCLK_INV_SEL)
#define PCMR1_SCLK_INV_SEL__INVERSION (1 << PCMR1_SCLK_INV_SEL)
#define PCMR1_LRCLK_RET_SEL 13
#define PCMR1_LRCLK_RET_SEL__NO_RETIMIMG (0 << PCMR1_LRCLK_RET_SEL)
#define PCMR1_LRCLK_RET_SEL__RETIME_BY_1_CYCLE (1 << PCMR1_LRCLK_RET_SEL)
#define PCMR1_SCLK_SEL 14
#define PCMR1_SCLK_SEL__FROM_PAD (0 << PCMR1_SCLK_SEL)
#define PCMR1_SCLK_SEL__FROM_PCM_PLAYER_0 (1 << PCMR1_SCLK_SEL)
#define PCMR1_LRCLK_SEL 15
#define PCMR1_LRCLK_SEL__FROM_PAD (0 << PCMR1_LRCLK_SEL)
#define PCMR1_LRCLK_SEL__FROM_PCM_PLAYER_0 (1 << PCMR1_LRCLK_SEL)
struct snd_stm_stx7141_glue {
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
static void snd_stm_stx7141_glue_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_stx7141_glue *stx7141_glue = entry->private_data;
BUG_ON(!stx7141_glue);
BUG_ON(!snd_stm_magic_valid(stx7141_glue));
snd_iprintf(buffer, "--- snd_stx7141_glue ---\n");
snd_iprintf(buffer, "IO_CTRL (0x%p) = 0x%08x\n",
IO_CTRL(stx7141_glue->base),
readl(IO_CTRL(stx7141_glue->base)));
snd_iprintf(buffer, "\n");
}
static int __init snd_stm_stx7141_glue_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_stx7141_glue *stx7141_glue;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
stx7141_glue = kzalloc(sizeof(*stx7141_glue), GFP_KERNEL);
if (!stx7141_glue) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(stx7141_glue);
result = snd_stm_memory_request(pdev, &stx7141_glue->mem_region,
&stx7141_glue->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Additional procfs info */
snd_stm_info_register(&stx7141_glue->proc_entry, "stx7141_glue",
snd_stm_stx7141_glue_dump_registers, stx7141_glue);
platform_set_drvdata(pdev, stx7141_glue);
return result;
error_memory_request:
snd_stm_magic_clear(stx7141_glue);
kfree(stx7141_glue);
error_alloc:
return result;
}
static int __exit snd_stm_stx7141_glue_remove(struct platform_device *pdev)
{
struct snd_stm_stx7141_glue *stx7141_glue = platform_get_drvdata(pdev);
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!stx7141_glue);
BUG_ON(!snd_stm_magic_valid(stx7141_glue));
/* Remove procfs entry */
snd_stm_info_unregister(stx7141_glue->proc_entry);
snd_stm_memory_release(stx7141_glue->mem_region, stx7141_glue->base);
snd_stm_magic_clear(stx7141_glue);
kfree(stx7141_glue);
return 0;
}
static struct platform_driver snd_stm_stx7141_glue_driver = {
.driver.name = "snd_stx7141_glue",
.probe = snd_stm_stx7141_glue_probe,
.remove = snd_stm_stx7141_glue_remove,
};
/*
* Audio initialization
*/
static int __init snd_stm_stx7141_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
if (cpu_data->type != CPU_STX7141) {
snd_stm_printe("Not supported (other than STx7141) SOC "
"detected!\n");
result = -EINVAL;
goto error_soc_type;
}
result = platform_driver_register(&snd_stm_stx7141_glue_driver);
if (result != 0) {
snd_stm_printe("Failed to register audio glue driver!\n");
goto error_glue_driver_register;
}
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards!\n");
goto error_card_register;
}
return 0;
error_card_register:
platform_driver_unregister(&snd_stm_stx7141_glue_driver);
error_glue_driver_register:
error_soc_type:
return result;
}
static void __exit snd_stm_stx7141_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
platform_driver_unregister(&snd_stm_stx7141_glue_driver);
}
MODULE_AUTHOR("Stephen Gallimore <stephen.gallimore@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STx7141 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_stx7141_init);
module_exit(snd_stm_stx7141_exit);

253
kernel/sound/stm/stx7200.c Normal file
View File

@@ -0,0 +1,253 @@
/*
* STMicrolectronics STx7200 SoC audio glue driver
*
* Copyright (c) 2005-2011 STMicroelectronics Limited
*
* Author: Pawel Moll <pawel.moll@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <asm/irq-ilc.h>
#include <sound/core.h>
#include "common.h"
static int snd_stm_debug_level;
module_param_named(debug, snd_stm_debug_level, int, S_IRUGO | S_IWUSR);
/*
* ALSA module parameters
*/
/* CUT 2+ ONLY! As PCM Reader #1 shares pins with MII1 it may receive
* unwanted traffic if MII1 is actually used to networking,
* or when PCM Player #1 is configured to use these pins. In such
* case one may disable the reader input using this module parameter. */
static int pcm_reader_1_enabled = 1;
module_param(pcm_reader_1_enabled, int, 0444);
MODULE_PARM_DESC(id, "PCM Reader #1 control (not valid for STx7200 cut 1).");
/*
* Audio glue driver implementation
*/
#define IOMUX_CTRL(base) ((base) + 0x00)
#define PCM_CLK_EN 0
#define PCM_CLK_EN__INPUT (0 << PCM_CLK_EN)
#define PCM_CLK_EN__OUTPUT (1 << PCM_CLK_EN)
#define DATA0_EN 1
#define DATA0_EN__INPUT (0 << DATA0_EN)
#define DATA0_EN__OUTPUT (1 << DATA0_EN)
#define DATA1_EN 2
#define DATA1_EN__INPUT (0 << DATA1_EN)
#define DATA1_EN__OUTPUT (1 << DATA1_EN)
#define DATA2_EN 3
#define DATA2_EN__INPUT (0 << DATA2_EN)
#define DATA2_EN__OUTPUT (1 << DATA2_EN)
#define SPDIF_EN 4
#define SPDIF_EN__DISABLE (0 << SPDIF_EN)
#define SPDIF_EN__ENABLE (1 << SPDIF_EN)
#define PCMRDR1_EN 5
#define PCMRDR1_EN__DISABLE (0 << PCMRDR1_EN)
#define PCMRDR1_EN__ENABLE (1 << PCMRDR1_EN)
#define HDMI_CTRL(base) ((base) + 0x04)
#define RECOVERY_CTRL(base) ((base) + 0x08)
struct snd_stm_stx7200_glue {
struct resource *mem_region;
void *base;
struct snd_info_entry *proc_entry;
snd_stm_magic_field;
};
static void snd_stm_stx7200_glue_dump_registers(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_stm_stx7200_glue *stx7200_glue = entry->private_data;
BUG_ON(!stx7200_glue);
BUG_ON(!snd_stm_magic_valid(stx7200_glue));
snd_iprintf(buffer, "--- snd_stx7200_glue ---\n");
snd_iprintf(buffer, "IOMUX_CTRL (0x%p) = 0x%08x\n",
IOMUX_CTRL(stx7200_glue->base),
readl(IOMUX_CTRL(stx7200_glue->base)));
snd_iprintf(buffer, "HDMI_CTRL (0x%p) = 0x%08x\n",
HDMI_CTRL(stx7200_glue->base),
readl(HDMI_CTRL(stx7200_glue->base)));
snd_iprintf(buffer, "RECOVERY_CTRL (0x%p) = 0x%08x\n",
RECOVERY_CTRL(stx7200_glue->base),
readl(RECOVERY_CTRL(stx7200_glue->base)));
snd_iprintf(buffer, "\n");
}
static int __init snd_stm_stx7200_glue_probe(struct platform_device *pdev)
{
int result = 0;
struct snd_stm_stx7200_glue *stx7200_glue;
unsigned long value;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
stx7200_glue = kzalloc(sizeof(*stx7200_glue), GFP_KERNEL);
if (!stx7200_glue) {
snd_stm_printe("Can't allocate memory "
"for a device description!\n");
result = -ENOMEM;
goto error_alloc;
}
snd_stm_magic_set(stx7200_glue);
result = snd_stm_memory_request(pdev, &stx7200_glue->mem_region,
&stx7200_glue->base);
if (result < 0) {
snd_stm_printe("Memory region request failed!\n");
goto error_memory_request;
}
/* Enable audio outputs */
value = SPDIF_EN__ENABLE | DATA2_EN__OUTPUT | DATA1_EN__OUTPUT |
DATA0_EN__OUTPUT | PCM_CLK_EN__OUTPUT;
/* Enable PCM Reader #1 (well, in some cases) */
if (cpu_data->cut_major > 1 && pcm_reader_1_enabled)
value |= PCMRDR1_EN__ENABLE;
writel(value, IOMUX_CTRL(stx7200_glue->base));
/* Additional procfs info */
snd_stm_info_register(&stx7200_glue->proc_entry, "stx7200_glue",
snd_stm_stx7200_glue_dump_registers, stx7200_glue);
platform_set_drvdata(pdev, stx7200_glue);
return result;
error_memory_request:
snd_stm_magic_clear(stx7200_glue);
kfree(stx7200_glue);
error_alloc:
return result;
}
static int __exit snd_stm_stx7200_glue_remove(struct platform_device *pdev)
{
struct snd_stm_stx7200_glue *stx7200_glue = platform_get_drvdata(pdev);
unsigned long value;
snd_stm_printd(0, "%s('%s')\n", __func__, dev_name(&pdev->dev));
BUG_ON(!stx7200_glue);
BUG_ON(!snd_stm_magic_valid(stx7200_glue));
/* Remove procfs entry */
snd_stm_info_unregister(stx7200_glue->proc_entry);
/* Disable audio outputs */
value = SPDIF_EN__DISABLE | DATA2_EN__INPUT | DATA1_EN__INPUT |
DATA0_EN__INPUT | PCM_CLK_EN__INPUT;
/* Disable PCM Reader #1 (well, in some cases) */
if (cpu_data->cut_major > 1 && pcm_reader_1_enabled)
value |= PCMRDR1_EN__DISABLE;
writel(value, IOMUX_CTRL(stx7200_glue->base));
snd_stm_memory_release(stx7200_glue->mem_region, stx7200_glue->base);
snd_stm_magic_clear(stx7200_glue);
kfree(stx7200_glue);
return 0;
}
static struct platform_driver snd_stm_stx7200_glue_driver = {
.driver.name = "snd_stx7200_glue",
.probe = snd_stm_stx7200_glue_probe,
.remove = snd_stm_stx7200_glue_remove,
};
/*
* Audio initialization
*/
#define SET_VER(_info_struct_, _device_, _ver_) \
(((struct _info_struct_ *)_device_.dev.platform_data)->ver = \
_ver_)
static int __init snd_stm_stx7200_init(void)
{
int result;
snd_stm_printd(0, "%s()\n", __func__);
if (cpu_data->type != CPU_STX7200) {
snd_stm_printe("Not supported (other than STx7200) SOC "
"detected!\n");
result = -EINVAL;
goto error_soc_type;
}
result = platform_driver_register(&snd_stm_stx7200_glue_driver);
if (result != 0) {
snd_stm_printe("Failed to register audio glue driver!\n");
goto error_glue_driver_register;
}
result = snd_stm_card_register();
if (result != 0) {
snd_stm_printe("Failed to register ALSA cards (%d)!\n", result);
goto error_card_register;
}
return 0;
error_card_register:
platform_driver_unregister(&snd_stm_stx7200_glue_driver);
error_glue_driver_register:
error_soc_type:
return result;
}
static void __exit snd_stm_stx7200_exit(void)
{
snd_stm_printd(0, "%s()\n", __func__);
platform_driver_unregister(&snd_stm_stx7200_glue_driver);
}
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STx7200 audio driver");
MODULE_LICENSE("GPL");
module_init(snd_stm_stx7200_init);
module_exit(snd_stm_stx7200_exit);