mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
14
sound/aoa/soundbus/Kconfig
Normal file
14
sound/aoa/soundbus/Kconfig
Normal file
|
@ -0,0 +1,14 @@
|
|||
config SND_AOA_SOUNDBUS
|
||||
tristate "Apple Soundbus support"
|
||||
select SND_PCM
|
||||
---help---
|
||||
This option enables the generic driver for the soundbus
|
||||
support on Apple machines.
|
||||
|
||||
It is required for the sound bus implementations.
|
||||
|
||||
config SND_AOA_SOUNDBUS_I2S
|
||||
tristate "I2S bus support"
|
||||
depends on SND_AOA_SOUNDBUS && PCI
|
||||
---help---
|
||||
This option enables support for Apple I2S busses.
|
3
sound/aoa/soundbus/Makefile
Normal file
3
sound/aoa/soundbus/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o
|
||||
snd-aoa-soundbus-objs := core.o sysfs.o
|
||||
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/
|
219
sound/aoa/soundbus/core.c
Normal file
219
sound/aoa/soundbus/core.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* soundbus
|
||||
*
|
||||
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "soundbus.h"
|
||||
|
||||
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Apple Soundbus");
|
||||
|
||||
struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev)
|
||||
{
|
||||
struct device *tmp;
|
||||
|
||||
if (!dev)
|
||||
return NULL;
|
||||
tmp = get_device(&dev->ofdev.dev);
|
||||
if (tmp)
|
||||
return to_soundbus_device(tmp);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soundbus_dev_get);
|
||||
|
||||
void soundbus_dev_put(struct soundbus_dev *dev)
|
||||
{
|
||||
if (dev)
|
||||
put_device(&dev->ofdev.dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soundbus_dev_put);
|
||||
|
||||
static int soundbus_probe(struct device *dev)
|
||||
{
|
||||
int error = -ENODEV;
|
||||
struct soundbus_driver *drv;
|
||||
struct soundbus_dev *soundbus_dev;
|
||||
|
||||
drv = to_soundbus_driver(dev->driver);
|
||||
soundbus_dev = to_soundbus_device(dev);
|
||||
|
||||
if (!drv->probe)
|
||||
return error;
|
||||
|
||||
soundbus_dev_get(soundbus_dev);
|
||||
|
||||
error = drv->probe(soundbus_dev);
|
||||
if (error)
|
||||
soundbus_dev_put(soundbus_dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct soundbus_dev * soundbus_dev;
|
||||
struct platform_device * of;
|
||||
const char *compat;
|
||||
int retval = 0;
|
||||
int cplen, seen = 0;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
soundbus_dev = to_soundbus_device(dev);
|
||||
if (!soundbus_dev)
|
||||
return -ENODEV;
|
||||
|
||||
of = &soundbus_dev->ofdev;
|
||||
|
||||
/* stuff we want to pass to /sbin/hotplug */
|
||||
retval = add_uevent_var(env, "OF_NAME=%s", of->dev.of_node->name);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = add_uevent_var(env, "OF_TYPE=%s", of->dev.of_node->type);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* Since the compatible field can contain pretty much anything
|
||||
* it's not really legal to split it out with commas. We split it
|
||||
* up using a number of environment variables instead. */
|
||||
|
||||
compat = of_get_property(of->dev.of_node, "compatible", &cplen);
|
||||
while (compat && cplen > 0) {
|
||||
int tmp = env->buflen;
|
||||
retval = add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat);
|
||||
if (retval)
|
||||
return retval;
|
||||
compat += env->buflen - tmp;
|
||||
cplen -= env->buflen - tmp;
|
||||
seen += 1;
|
||||
}
|
||||
|
||||
retval = add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = add_uevent_var(env, "MODALIAS=%s", soundbus_dev->modalias);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int soundbus_device_remove(struct device *dev)
|
||||
{
|
||||
struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
|
||||
struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
|
||||
|
||||
if (dev->driver && drv->remove)
|
||||
drv->remove(soundbus_dev);
|
||||
soundbus_dev_put(soundbus_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void soundbus_device_shutdown(struct device *dev)
|
||||
{
|
||||
struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
|
||||
struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
|
||||
|
||||
if (dev->driver && drv->shutdown)
|
||||
drv->shutdown(soundbus_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int soundbus_device_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
|
||||
struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
|
||||
|
||||
if (dev->driver && drv->suspend)
|
||||
return drv->suspend(soundbus_dev, state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int soundbus_device_resume(struct device * dev)
|
||||
{
|
||||
struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
|
||||
struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
|
||||
|
||||
if (dev->driver && drv->resume)
|
||||
return drv->resume(soundbus_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct bus_type soundbus_bus_type = {
|
||||
.name = "aoa-soundbus",
|
||||
.probe = soundbus_probe,
|
||||
.uevent = soundbus_uevent,
|
||||
.remove = soundbus_device_remove,
|
||||
.shutdown = soundbus_device_shutdown,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = soundbus_device_suspend,
|
||||
.resume = soundbus_device_resume,
|
||||
#endif
|
||||
.dev_attrs = soundbus_dev_attrs,
|
||||
};
|
||||
|
||||
int soundbus_add_one(struct soundbus_dev *dev)
|
||||
{
|
||||
static int devcount;
|
||||
|
||||
/* sanity checks */
|
||||
if (!dev->attach_codec ||
|
||||
!dev->ofdev.dev.of_node ||
|
||||
dev->pcmname ||
|
||||
dev->pcmid != -1) {
|
||||
printk(KERN_ERR "soundbus: adding device failed sanity check!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_set_name(&dev->ofdev.dev, "soundbus:%x", ++devcount);
|
||||
dev->ofdev.dev.bus = &soundbus_bus_type;
|
||||
return of_device_register(&dev->ofdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soundbus_add_one);
|
||||
|
||||
void soundbus_remove_one(struct soundbus_dev *dev)
|
||||
{
|
||||
of_device_unregister(&dev->ofdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soundbus_remove_one);
|
||||
|
||||
int soundbus_register_driver(struct soundbus_driver *drv)
|
||||
{
|
||||
/* initialize common driver fields */
|
||||
drv->driver.name = drv->name;
|
||||
drv->driver.bus = &soundbus_bus_type;
|
||||
|
||||
/* register with core */
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soundbus_register_driver);
|
||||
|
||||
void soundbus_unregister_driver(struct soundbus_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soundbus_unregister_driver);
|
||||
|
||||
static int __init soundbus_init(void)
|
||||
{
|
||||
return bus_register(&soundbus_bus_type);
|
||||
}
|
||||
|
||||
static void __exit soundbus_exit(void)
|
||||
{
|
||||
bus_unregister(&soundbus_bus_type);
|
||||
}
|
||||
|
||||
subsys_initcall(soundbus_init);
|
||||
module_exit(soundbus_exit);
|
2
sound/aoa/soundbus/i2sbus/Makefile
Normal file
2
sound/aoa/soundbus/i2sbus/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
|
||||
snd-aoa-i2sbus-objs := core.o pcm.o control.o
|
194
sound/aoa/soundbus/i2sbus/control.c
Normal file
194
sound/aoa/soundbus/i2sbus/control.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* i2sbus driver -- bus control routines
|
||||
*
|
||||
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/macio.h>
|
||||
#include <asm/pmac_feature.h>
|
||||
#include <asm/pmac_pfunc.h>
|
||||
#include <asm/keylargo.h>
|
||||
|
||||
#include "i2sbus.h"
|
||||
|
||||
int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c)
|
||||
{
|
||||
*c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL);
|
||||
if (!*c)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&(*c)->list);
|
||||
|
||||
(*c)->macio = dev->bus->chip;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2sbus_control_destroy(struct i2sbus_control *c)
|
||||
{
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
/* this is serialised externally */
|
||||
int i2sbus_control_add_dev(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = i2sdev->sound.ofdev.dev.of_node;
|
||||
i2sdev->enable = pmf_find_function(np, "enable");
|
||||
i2sdev->cell_enable = pmf_find_function(np, "cell-enable");
|
||||
i2sdev->clock_enable = pmf_find_function(np, "clock-enable");
|
||||
i2sdev->cell_disable = pmf_find_function(np, "cell-disable");
|
||||
i2sdev->clock_disable = pmf_find_function(np, "clock-disable");
|
||||
|
||||
/* if the bus number is not 0 or 1 we absolutely need to use
|
||||
* the platform functions -- there's nothing in Darwin that
|
||||
* would allow seeing a system behind what the FCRs are then,
|
||||
* and I don't want to go parsing a bunch of platform functions
|
||||
* by hand to try finding a system... */
|
||||
if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 &&
|
||||
(!i2sdev->enable ||
|
||||
!i2sdev->cell_enable || !i2sdev->clock_enable ||
|
||||
!i2sdev->cell_disable || !i2sdev->clock_disable)) {
|
||||
pmf_put_function(i2sdev->enable);
|
||||
pmf_put_function(i2sdev->cell_enable);
|
||||
pmf_put_function(i2sdev->clock_enable);
|
||||
pmf_put_function(i2sdev->cell_disable);
|
||||
pmf_put_function(i2sdev->clock_disable);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
list_add(&i2sdev->item, &c->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2sbus_control_remove_dev(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev)
|
||||
{
|
||||
/* this is serialised externally */
|
||||
list_del(&i2sdev->item);
|
||||
if (list_empty(&c->list))
|
||||
i2sbus_control_destroy(c);
|
||||
}
|
||||
|
||||
int i2sbus_control_enable(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev)
|
||||
{
|
||||
struct pmf_args args = { .count = 0 };
|
||||
struct macio_chip *macio = c->macio;
|
||||
|
||||
if (i2sdev->enable)
|
||||
return pmf_call_one(i2sdev->enable, &args);
|
||||
|
||||
if (macio == NULL || macio->base == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
switch (i2sdev->bus_number) {
|
||||
case 0:
|
||||
/* these need to be locked or done through
|
||||
* newly created feature calls! */
|
||||
MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE);
|
||||
break;
|
||||
case 1:
|
||||
MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_ENABLE);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2sbus_control_cell(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev,
|
||||
int enable)
|
||||
{
|
||||
struct pmf_args args = { .count = 0 };
|
||||
struct macio_chip *macio = c->macio;
|
||||
|
||||
switch (enable) {
|
||||
case 0:
|
||||
if (i2sdev->cell_disable)
|
||||
return pmf_call_one(i2sdev->cell_disable, &args);
|
||||
break;
|
||||
case 1:
|
||||
if (i2sdev->cell_enable)
|
||||
return pmf_call_one(i2sdev->cell_enable, &args);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (macio == NULL || macio->base == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
switch (i2sdev->bus_number) {
|
||||
case 0:
|
||||
if (enable)
|
||||
MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
|
||||
else
|
||||
MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE);
|
||||
break;
|
||||
case 1:
|
||||
if (enable)
|
||||
MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
|
||||
else
|
||||
MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CELL_ENABLE);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2sbus_control_clock(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev,
|
||||
int enable)
|
||||
{
|
||||
struct pmf_args args = { .count = 0 };
|
||||
struct macio_chip *macio = c->macio;
|
||||
|
||||
switch (enable) {
|
||||
case 0:
|
||||
if (i2sdev->clock_disable)
|
||||
return pmf_call_one(i2sdev->clock_disable, &args);
|
||||
break;
|
||||
case 1:
|
||||
if (i2sdev->clock_enable)
|
||||
return pmf_call_one(i2sdev->clock_enable, &args);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (macio == NULL || macio->base == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
switch (i2sdev->bus_number) {
|
||||
case 0:
|
||||
if (enable)
|
||||
MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
|
||||
else
|
||||
MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT);
|
||||
break;
|
||||
case 1:
|
||||
if (enable)
|
||||
MACIO_BIS(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
|
||||
else
|
||||
MACIO_BIC(KEYLARGO_FCR1, KL1_I2S1_CLK_ENABLE_BIT);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
463
sound/aoa/soundbus/i2sbus/core.c
Normal file
463
sound/aoa/soundbus/i2sbus/core.c
Normal file
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* i2sbus driver
|
||||
*
|
||||
* Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
|
||||
#include <asm/macio.h>
|
||||
#include <asm/dbdma.h>
|
||||
|
||||
#include "../soundbus.h"
|
||||
#include "i2sbus.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
|
||||
MODULE_DESCRIPTION("Apple Soundbus: I2S support");
|
||||
|
||||
static int force;
|
||||
module_param(force, int, 0444);
|
||||
MODULE_PARM_DESC(force, "Force loading i2sbus even when"
|
||||
" no layout-id property is present");
|
||||
|
||||
static struct of_device_id i2sbus_match[] = {
|
||||
{ .name = "i2s" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, i2sbus_match);
|
||||
|
||||
static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
|
||||
struct dbdma_command_mem *r,
|
||||
int numcmds)
|
||||
{
|
||||
/* one more for rounding, one for branch back, one for stop command */
|
||||
r->size = (numcmds + 3) * sizeof(struct dbdma_cmd);
|
||||
/* We use the PCI APIs for now until the generic one gets fixed
|
||||
* enough or until we get some macio-specific versions
|
||||
*/
|
||||
r->space = dma_zalloc_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
|
||||
r->size, &r->bus_addr, GFP_KERNEL);
|
||||
if (!r->space)
|
||||
return -ENOMEM;
|
||||
|
||||
r->cmds = (void*)DBDMA_ALIGN(r->space);
|
||||
r->bus_cmd_start = r->bus_addr +
|
||||
(dma_addr_t)((char*)r->cmds - (char*)r->space);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
|
||||
struct dbdma_command_mem *r)
|
||||
{
|
||||
if (!r->space) return;
|
||||
|
||||
dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
|
||||
r->size, r->space, r->bus_addr);
|
||||
}
|
||||
|
||||
static void i2sbus_release_dev(struct device *dev)
|
||||
{
|
||||
struct i2sbus_dev *i2sdev;
|
||||
int i;
|
||||
|
||||
i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
|
||||
|
||||
if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
|
||||
if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
|
||||
if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
|
||||
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
|
||||
if (i2sdev->allocated_resource[i])
|
||||
release_and_free_resource(i2sdev->allocated_resource[i]);
|
||||
free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
|
||||
free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
|
||||
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
|
||||
free_irq(i2sdev->interrupts[i], i2sdev);
|
||||
i2sbus_control_remove_dev(i2sdev->control, i2sdev);
|
||||
mutex_destroy(&i2sdev->lock);
|
||||
kfree(i2sdev);
|
||||
}
|
||||
|
||||
static irqreturn_t i2sbus_bus_intr(int irq, void *devid)
|
||||
{
|
||||
struct i2sbus_dev *dev = devid;
|
||||
u32 intreg;
|
||||
|
||||
spin_lock(&dev->low_lock);
|
||||
intreg = in_le32(&dev->intfregs->intr_ctl);
|
||||
|
||||
/* acknowledge interrupt reasons */
|
||||
out_le32(&dev->intfregs->intr_ctl, intreg);
|
||||
|
||||
spin_unlock(&dev->low_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* XXX FIXME: We test the layout_id's here to get the proper way of
|
||||
* mapping in various registers, thanks to bugs in Apple device-trees.
|
||||
* We could instead key off the machine model and the name of the i2s
|
||||
* node (i2s-a). This we'll do when we move it all to macio_asic.c
|
||||
* and have that export items for each sub-node too.
|
||||
*/
|
||||
static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
|
||||
int layout, struct resource *res)
|
||||
{
|
||||
struct device_node *parent;
|
||||
int pindex, rc = -ENXIO;
|
||||
const u32 *reg;
|
||||
|
||||
/* Machines with layout 76 and 36 (K2 based) have a weird device
|
||||
* tree what we need to special case.
|
||||
* Normal machines just fetch the resource from the i2s-X node.
|
||||
* Darwin further divides normal machines into old and new layouts
|
||||
* with a subtely different code path but that doesn't seem necessary
|
||||
* in practice, they just bloated it. In addition, even on our K2
|
||||
* case the i2s-modem node, if we ever want to handle it, uses the
|
||||
* normal layout
|
||||
*/
|
||||
if (layout != 76 && layout != 36)
|
||||
return of_address_to_resource(np, index, res);
|
||||
|
||||
parent = of_get_parent(np);
|
||||
pindex = (index == aoa_resource_i2smmio) ? 0 : 1;
|
||||
rc = of_address_to_resource(parent, pindex, res);
|
||||
if (rc)
|
||||
goto bail;
|
||||
reg = of_get_property(np, "reg", NULL);
|
||||
if (reg == NULL) {
|
||||
rc = -ENXIO;
|
||||
goto bail;
|
||||
}
|
||||
res->start += reg[index * 2];
|
||||
res->end = res->start + reg[index * 2 + 1] - 1;
|
||||
bail:
|
||||
of_node_put(parent);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* FIXME: look at device node refcounting */
|
||||
static int i2sbus_add_dev(struct macio_dev *macio,
|
||||
struct i2sbus_control *control,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct i2sbus_dev *dev;
|
||||
struct device_node *child = NULL, *sound = NULL;
|
||||
struct resource *r;
|
||||
int i, layout = 0, rlen, ok = force;
|
||||
static const char *rnames[] = { "i2sbus: %s (control)",
|
||||
"i2sbus: %s (tx)",
|
||||
"i2sbus: %s (rx)" };
|
||||
static irq_handler_t ints[] = {
|
||||
i2sbus_bus_intr,
|
||||
i2sbus_tx_intr,
|
||||
i2sbus_rx_intr
|
||||
};
|
||||
|
||||
if (strlen(np->name) != 5)
|
||||
return 0;
|
||||
if (strncmp(np->name, "i2s-", 4))
|
||||
return 0;
|
||||
|
||||
dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
i = 0;
|
||||
while ((child = of_get_next_child(np, child))) {
|
||||
if (strcmp(child->name, "sound") == 0) {
|
||||
i++;
|
||||
sound = child;
|
||||
}
|
||||
}
|
||||
if (i == 1) {
|
||||
const u32 *id = of_get_property(sound, "layout-id", NULL);
|
||||
|
||||
if (id) {
|
||||
layout = *id;
|
||||
snprintf(dev->sound.modalias, 32,
|
||||
"sound-layout-%d", layout);
|
||||
ok = 1;
|
||||
} else {
|
||||
id = of_get_property(sound, "device-id", NULL);
|
||||
/*
|
||||
* We probably cannot handle all device-id machines,
|
||||
* so restrict to those we do handle for now.
|
||||
*/
|
||||
if (id && (*id == 22 || *id == 14 || *id == 35 ||
|
||||
*id == 44)) {
|
||||
snprintf(dev->sound.modalias, 32,
|
||||
"aoa-device-id-%d", *id);
|
||||
ok = 1;
|
||||
layout = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* for the time being, until we can handle non-layout-id
|
||||
* things in some fabric, refuse to attach if there is no
|
||||
* layout-id property or we haven't been forced to attach.
|
||||
* When there are two i2s busses and only one has a layout-id,
|
||||
* then this depends on the order, but that isn't important
|
||||
* either as the second one in that case is just a modem. */
|
||||
if (!ok) {
|
||||
kfree(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
spin_lock_init(&dev->low_lock);
|
||||
dev->sound.ofdev.archdata.dma_mask = macio->ofdev.archdata.dma_mask;
|
||||
dev->sound.ofdev.dev.of_node = np;
|
||||
dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.archdata.dma_mask;
|
||||
dev->sound.ofdev.dev.parent = &macio->ofdev.dev;
|
||||
dev->sound.ofdev.dev.release = i2sbus_release_dev;
|
||||
dev->sound.attach_codec = i2sbus_attach_codec;
|
||||
dev->sound.detach_codec = i2sbus_detach_codec;
|
||||
dev->sound.pcmid = -1;
|
||||
dev->macio = macio;
|
||||
dev->control = control;
|
||||
dev->bus_number = np->name[4] - 'a';
|
||||
INIT_LIST_HEAD(&dev->sound.codec_list);
|
||||
|
||||
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
|
||||
dev->interrupts[i] = -1;
|
||||
snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
|
||||
rnames[i], np->name);
|
||||
}
|
||||
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
|
||||
int irq = irq_of_parse_and_map(np, i);
|
||||
if (request_irq(irq, ints[i], 0, dev->rnames[i], dev))
|
||||
goto err;
|
||||
dev->interrupts[i] = irq;
|
||||
}
|
||||
|
||||
|
||||
/* Resource handling is problematic as some device-trees contain
|
||||
* useless crap (ugh ugh ugh). We work around that here by calling
|
||||
* specific functions for calculating the appropriate resources.
|
||||
*
|
||||
* This will all be moved to macio_asic.c at one point
|
||||
*/
|
||||
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
|
||||
if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i]))
|
||||
goto err;
|
||||
/* If only we could use our resource dev->resources[i]...
|
||||
* but request_resource doesn't know about parents and
|
||||
* contained resources...
|
||||
*/
|
||||
dev->allocated_resource[i] =
|
||||
request_mem_region(dev->resources[i].start,
|
||||
resource_size(&dev->resources[i]),
|
||||
dev->rnames[i]);
|
||||
if (!dev->allocated_resource[i]) {
|
||||
printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
r = &dev->resources[aoa_resource_i2smmio];
|
||||
rlen = resource_size(r);
|
||||
if (rlen < sizeof(struct i2s_interface_regs))
|
||||
goto err;
|
||||
dev->intfregs = ioremap(r->start, rlen);
|
||||
|
||||
r = &dev->resources[aoa_resource_txdbdma];
|
||||
rlen = resource_size(r);
|
||||
if (rlen < sizeof(struct dbdma_regs))
|
||||
goto err;
|
||||
dev->out.dbdma = ioremap(r->start, rlen);
|
||||
|
||||
r = &dev->resources[aoa_resource_rxdbdma];
|
||||
rlen = resource_size(r);
|
||||
if (rlen < sizeof(struct dbdma_regs))
|
||||
goto err;
|
||||
dev->in.dbdma = ioremap(r->start, rlen);
|
||||
|
||||
if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
|
||||
goto err;
|
||||
|
||||
if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring,
|
||||
MAX_DBDMA_COMMANDS))
|
||||
goto err;
|
||||
if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring,
|
||||
MAX_DBDMA_COMMANDS))
|
||||
goto err;
|
||||
|
||||
if (i2sbus_control_add_dev(dev->control, dev)) {
|
||||
printk(KERN_ERR "i2sbus: control layer didn't like bus\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (soundbus_add_one(&dev->sound)) {
|
||||
printk(KERN_DEBUG "i2sbus: device registration error!\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* enable this cell */
|
||||
i2sbus_control_cell(dev->control, dev, 1);
|
||||
i2sbus_control_enable(dev->control, dev);
|
||||
i2sbus_control_clock(dev->control, dev, 1);
|
||||
|
||||
return 1;
|
||||
err:
|
||||
for (i=0;i<3;i++)
|
||||
if (dev->interrupts[i] != -1)
|
||||
free_irq(dev->interrupts[i], dev);
|
||||
free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
|
||||
free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
|
||||
if (dev->intfregs) iounmap(dev->intfregs);
|
||||
if (dev->out.dbdma) iounmap(dev->out.dbdma);
|
||||
if (dev->in.dbdma) iounmap(dev->in.dbdma);
|
||||
for (i=0;i<3;i++)
|
||||
if (dev->allocated_resource[i])
|
||||
release_and_free_resource(dev->allocated_resource[i]);
|
||||
mutex_destroy(&dev->lock);
|
||||
kfree(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
|
||||
{
|
||||
struct device_node *np = NULL;
|
||||
int got = 0, err;
|
||||
struct i2sbus_control *control = NULL;
|
||||
|
||||
err = i2sbus_control_init(dev, &control);
|
||||
if (err)
|
||||
return err;
|
||||
if (!control) {
|
||||
printk(KERN_ERR "i2sbus_control_init API breakage\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
while ((np = of_get_next_child(dev->ofdev.dev.of_node, np))) {
|
||||
if (of_device_is_compatible(np, "i2sbus") ||
|
||||
of_device_is_compatible(np, "i2s-modem")) {
|
||||
got += i2sbus_add_dev(dev, control, np);
|
||||
}
|
||||
}
|
||||
|
||||
if (!got) {
|
||||
/* found none, clean up */
|
||||
i2sbus_control_destroy(control);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&dev->ofdev.dev, control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2sbus_remove(struct macio_dev* dev)
|
||||
{
|
||||
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
|
||||
struct i2sbus_dev *i2sdev, *tmp;
|
||||
|
||||
list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
|
||||
soundbus_remove_one(&i2sdev->sound);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
|
||||
{
|
||||
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
|
||||
struct codec_info_item *cii;
|
||||
struct i2sbus_dev* i2sdev;
|
||||
int err, ret = 0;
|
||||
|
||||
list_for_each_entry(i2sdev, &control->list, item) {
|
||||
/* Notify Alsa */
|
||||
if (i2sdev->sound.pcm) {
|
||||
/* Suspend PCM streams */
|
||||
snd_pcm_suspend_all(i2sdev->sound.pcm);
|
||||
}
|
||||
|
||||
/* Notify codecs */
|
||||
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
|
||||
err = 0;
|
||||
if (cii->codec->suspend)
|
||||
err = cii->codec->suspend(cii, state);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
/* wait until streams are stopped */
|
||||
i2sbus_wait_for_stop_both(i2sdev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2sbus_resume(struct macio_dev* dev)
|
||||
{
|
||||
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
|
||||
struct codec_info_item *cii;
|
||||
struct i2sbus_dev* i2sdev;
|
||||
int err, ret = 0;
|
||||
|
||||
list_for_each_entry(i2sdev, &control->list, item) {
|
||||
/* reset i2s bus format etc. */
|
||||
i2sbus_pcm_prepare_both(i2sdev);
|
||||
|
||||
/* Notify codecs so they can re-initialize */
|
||||
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
|
||||
err = 0;
|
||||
if (cii->codec->resume)
|
||||
err = cii->codec->resume(cii);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int i2sbus_shutdown(struct macio_dev* dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct macio_driver i2sbus_drv = {
|
||||
.driver = {
|
||||
.name = "soundbus-i2s",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = i2sbus_match,
|
||||
},
|
||||
.probe = i2sbus_probe,
|
||||
.remove = i2sbus_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = i2sbus_suspend,
|
||||
.resume = i2sbus_resume,
|
||||
#endif
|
||||
.shutdown = i2sbus_shutdown,
|
||||
};
|
||||
|
||||
static int __init soundbus_i2sbus_init(void)
|
||||
{
|
||||
return macio_register_driver(&i2sbus_drv);
|
||||
}
|
||||
|
||||
static void __exit soundbus_i2sbus_exit(void)
|
||||
{
|
||||
macio_unregister_driver(&i2sbus_drv);
|
||||
}
|
||||
|
||||
module_init(soundbus_i2sbus_init);
|
||||
module_exit(soundbus_i2sbus_exit);
|
126
sound/aoa/soundbus/i2sbus/i2sbus.h
Normal file
126
sound/aoa/soundbus/i2sbus/i2sbus.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* i2sbus driver -- private definitions
|
||||
*
|
||||
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
#ifndef __I2SBUS_H
|
||||
#define __I2SBUS_H
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pmac_feature.h>
|
||||
#include <asm/dbdma.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include "../soundbus.h"
|
||||
|
||||
struct i2sbus_control {
|
||||
struct list_head list;
|
||||
struct macio_chip *macio;
|
||||
};
|
||||
|
||||
#define MAX_DBDMA_COMMANDS 32
|
||||
|
||||
struct dbdma_command_mem {
|
||||
dma_addr_t bus_addr;
|
||||
dma_addr_t bus_cmd_start;
|
||||
struct dbdma_cmd *cmds;
|
||||
void *space;
|
||||
int size;
|
||||
u32 running:1;
|
||||
u32 stopping:1;
|
||||
};
|
||||
|
||||
struct pcm_info {
|
||||
u32 created:1, /* has this direction been created with alsa? */
|
||||
active:1; /* is this stream active? */
|
||||
/* runtime information */
|
||||
struct snd_pcm_substream *substream;
|
||||
int current_period;
|
||||
u32 frame_count;
|
||||
struct dbdma_command_mem dbdma_ring;
|
||||
volatile struct dbdma_regs __iomem *dbdma;
|
||||
struct completion *stop_completion;
|
||||
};
|
||||
|
||||
enum {
|
||||
aoa_resource_i2smmio = 0,
|
||||
aoa_resource_txdbdma,
|
||||
aoa_resource_rxdbdma,
|
||||
};
|
||||
|
||||
struct i2sbus_dev {
|
||||
struct soundbus_dev sound;
|
||||
struct macio_dev *macio;
|
||||
struct i2sbus_control *control;
|
||||
volatile struct i2s_interface_regs __iomem *intfregs;
|
||||
|
||||
struct resource resources[3];
|
||||
struct resource *allocated_resource[3];
|
||||
int interrupts[3];
|
||||
char rnames[3][32];
|
||||
|
||||
/* info about currently active substreams */
|
||||
struct pcm_info out, in;
|
||||
snd_pcm_format_t format;
|
||||
unsigned int rate;
|
||||
|
||||
/* list for a single controller */
|
||||
struct list_head item;
|
||||
/* number of bus on controller */
|
||||
int bus_number;
|
||||
/* for use by control layer */
|
||||
struct pmf_function *enable,
|
||||
*cell_enable,
|
||||
*cell_disable,
|
||||
*clock_enable,
|
||||
*clock_disable;
|
||||
|
||||
/* locks */
|
||||
/* spinlock for low-level interrupt locking */
|
||||
spinlock_t low_lock;
|
||||
/* mutex for high-level consistency */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#define soundbus_dev_to_i2sbus_dev(sdev) \
|
||||
container_of(sdev, struct i2sbus_dev, sound)
|
||||
|
||||
/* pcm specific functions */
|
||||
extern int
|
||||
i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
|
||||
struct codec_info *ci, void *data);
|
||||
extern void
|
||||
i2sbus_detach_codec(struct soundbus_dev *dev, void *data);
|
||||
extern irqreturn_t
|
||||
i2sbus_tx_intr(int irq, void *devid);
|
||||
extern irqreturn_t
|
||||
i2sbus_rx_intr(int irq, void *devid);
|
||||
|
||||
extern void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev);
|
||||
extern void i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev);
|
||||
|
||||
/* control specific functions */
|
||||
extern int i2sbus_control_init(struct macio_dev* dev,
|
||||
struct i2sbus_control **c);
|
||||
extern void i2sbus_control_destroy(struct i2sbus_control *c);
|
||||
extern int i2sbus_control_add_dev(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev);
|
||||
extern void i2sbus_control_remove_dev(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev);
|
||||
extern int i2sbus_control_enable(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev);
|
||||
extern int i2sbus_control_cell(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev,
|
||||
int enable);
|
||||
extern int i2sbus_control_clock(struct i2sbus_control *c,
|
||||
struct i2sbus_dev *i2sdev,
|
||||
int enable);
|
||||
#endif /* __I2SBUS_H */
|
187
sound/aoa/soundbus/i2sbus/interface.h
Normal file
187
sound/aoa/soundbus/i2sbus/interface.h
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* i2sbus driver -- interface register definitions
|
||||
*
|
||||
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
#ifndef __I2SBUS_INTERFACE_H
|
||||
#define __I2SBUS_INTERFACE_H
|
||||
|
||||
/* i2s bus control registers, at least what we know about them */
|
||||
|
||||
#define __PAD(m,n) u8 __pad##m[n]
|
||||
#define _PAD(line, n) __PAD(line, n)
|
||||
#define PAD(n) _PAD(__LINE__, (n))
|
||||
struct i2s_interface_regs {
|
||||
__le32 intr_ctl; /* 0x00 */
|
||||
PAD(12);
|
||||
__le32 serial_format; /* 0x10 */
|
||||
PAD(12);
|
||||
__le32 codec_msg_out; /* 0x20 */
|
||||
PAD(12);
|
||||
__le32 codec_msg_in; /* 0x30 */
|
||||
PAD(12);
|
||||
__le32 frame_count; /* 0x40 */
|
||||
PAD(12);
|
||||
__le32 frame_match; /* 0x50 */
|
||||
PAD(12);
|
||||
__le32 data_word_sizes; /* 0x60 */
|
||||
PAD(12);
|
||||
__le32 peak_level_sel; /* 0x70 */
|
||||
PAD(12);
|
||||
__le32 peak_level_in0; /* 0x80 */
|
||||
PAD(12);
|
||||
__le32 peak_level_in1; /* 0x90 */
|
||||
PAD(12);
|
||||
/* total size: 0x100 bytes */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* interrupt register is just a bitfield with
|
||||
* interrupt enable and pending bits */
|
||||
#define I2S_REG_INTR_CTL 0x00
|
||||
# define I2S_INT_FRAME_COUNT (1<<31)
|
||||
# define I2S_PENDING_FRAME_COUNT (1<<30)
|
||||
# define I2S_INT_MESSAGE_FLAG (1<<29)
|
||||
# define I2S_PENDING_MESSAGE_FLAG (1<<28)
|
||||
# define I2S_INT_NEW_PEAK (1<<27)
|
||||
# define I2S_PENDING_NEW_PEAK (1<<26)
|
||||
# define I2S_INT_CLOCKS_STOPPED (1<<25)
|
||||
# define I2S_PENDING_CLOCKS_STOPPED (1<<24)
|
||||
# define I2S_INT_EXTERNAL_SYNC_ERROR (1<<23)
|
||||
# define I2S_PENDING_EXTERNAL_SYNC_ERROR (1<<22)
|
||||
# define I2S_INT_EXTERNAL_SYNC_OK (1<<21)
|
||||
# define I2S_PENDING_EXTERNAL_SYNC_OK (1<<20)
|
||||
# define I2S_INT_NEW_SAMPLE_RATE (1<<19)
|
||||
# define I2S_PENDING_NEW_SAMPLE_RATE (1<<18)
|
||||
# define I2S_INT_STATUS_FLAG (1<<17)
|
||||
# define I2S_PENDING_STATUS_FLAG (1<<16)
|
||||
|
||||
/* serial format register is more interesting :)
|
||||
* It contains:
|
||||
* - clock source
|
||||
* - MClk divisor
|
||||
* - SClk divisor
|
||||
* - SClk master flag
|
||||
* - serial format (sony, i2s 64x, i2s 32x, dav, silabs)
|
||||
* - external sample frequency interrupt (don't understand)
|
||||
* - external sample frequency
|
||||
*/
|
||||
#define I2S_REG_SERIAL_FORMAT 0x10
|
||||
/* clock source. You get either 18.432, 45.1584 or 49.1520 MHz */
|
||||
# define I2S_SF_CLOCK_SOURCE_SHIFT 30
|
||||
# define I2S_SF_CLOCK_SOURCE_MASK (3<<I2S_SF_CLOCK_SOURCE_SHIFT)
|
||||
# define I2S_SF_CLOCK_SOURCE_18MHz (0<<I2S_SF_CLOCK_SOURCE_SHIFT)
|
||||
# define I2S_SF_CLOCK_SOURCE_45MHz (1<<I2S_SF_CLOCK_SOURCE_SHIFT)
|
||||
# define I2S_SF_CLOCK_SOURCE_49MHz (2<<I2S_SF_CLOCK_SOURCE_SHIFT)
|
||||
/* also, let's define the exact clock speeds here, in Hz */
|
||||
#define I2S_CLOCK_SPEED_18MHz 18432000
|
||||
#define I2S_CLOCK_SPEED_45MHz 45158400
|
||||
#define I2S_CLOCK_SPEED_49MHz 49152000
|
||||
/* MClk is the clock that drives the codec, usually called its 'system clock'.
|
||||
* It is derived by taking only every 'divisor' tick of the clock.
|
||||
*/
|
||||
# define I2S_SF_MCLKDIV_SHIFT 24
|
||||
# define I2S_SF_MCLKDIV_MASK (0x1F<<I2S_SF_MCLKDIV_SHIFT)
|
||||
# define I2S_SF_MCLKDIV_1 (0x14<<I2S_SF_MCLKDIV_SHIFT)
|
||||
# define I2S_SF_MCLKDIV_3 (0x13<<I2S_SF_MCLKDIV_SHIFT)
|
||||
# define I2S_SF_MCLKDIV_5 (0x12<<I2S_SF_MCLKDIV_SHIFT)
|
||||
# define I2S_SF_MCLKDIV_14 (0x0E<<I2S_SF_MCLKDIV_SHIFT)
|
||||
# define I2S_SF_MCLKDIV_OTHER(div) (((div/2-1)<<I2S_SF_MCLKDIV_SHIFT)&I2S_SF_MCLKDIV_MASK)
|
||||
static inline int i2s_sf_mclkdiv(int div, int *out)
|
||||
{
|
||||
int d;
|
||||
|
||||
switch(div) {
|
||||
case 1: *out |= I2S_SF_MCLKDIV_1; return 0;
|
||||
case 3: *out |= I2S_SF_MCLKDIV_3; return 0;
|
||||
case 5: *out |= I2S_SF_MCLKDIV_5; return 0;
|
||||
case 14: *out |= I2S_SF_MCLKDIV_14; return 0;
|
||||
default:
|
||||
if (div%2) return -1;
|
||||
d = div/2-1;
|
||||
if (d == 0x14 || d == 0x13 || d == 0x12 || d == 0x0E)
|
||||
return -1;
|
||||
*out |= I2S_SF_MCLKDIV_OTHER(div);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* SClk is the clock that drives the i2s wire bus. Note that it is
|
||||
* derived from the MClk above by taking only every 'divisor' tick
|
||||
* of MClk.
|
||||
*/
|
||||
# define I2S_SF_SCLKDIV_SHIFT 20
|
||||
# define I2S_SF_SCLKDIV_MASK (0xF<<I2S_SF_SCLKDIV_SHIFT)
|
||||
# define I2S_SF_SCLKDIV_1 (8<<I2S_SF_SCLKDIV_SHIFT)
|
||||
# define I2S_SF_SCLKDIV_3 (9<<I2S_SF_SCLKDIV_SHIFT)
|
||||
# define I2S_SF_SCLKDIV_OTHER(div) (((div/2-1)<<I2S_SF_SCLKDIV_SHIFT)&I2S_SF_SCLKDIV_MASK)
|
||||
static inline int i2s_sf_sclkdiv(int div, int *out)
|
||||
{
|
||||
int d;
|
||||
|
||||
switch(div) {
|
||||
case 1: *out |= I2S_SF_SCLKDIV_1; return 0;
|
||||
case 3: *out |= I2S_SF_SCLKDIV_3; return 0;
|
||||
default:
|
||||
if (div%2) return -1;
|
||||
d = div/2-1;
|
||||
if (d == 8 || d == 9) return -1;
|
||||
*out |= I2S_SF_SCLKDIV_OTHER(div);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
# define I2S_SF_SCLK_MASTER (1<<19)
|
||||
/* serial format is the way the data is put to the i2s wire bus */
|
||||
# define I2S_SF_SERIAL_FORMAT_SHIFT 16
|
||||
# define I2S_SF_SERIAL_FORMAT_MASK (7<<I2S_SF_SERIAL_FORMAT_SHIFT)
|
||||
# define I2S_SF_SERIAL_FORMAT_SONY (0<<I2S_SF_SERIAL_FORMAT_SHIFT)
|
||||
# define I2S_SF_SERIAL_FORMAT_I2S_64X (1<<I2S_SF_SERIAL_FORMAT_SHIFT)
|
||||
# define I2S_SF_SERIAL_FORMAT_I2S_32X (2<<I2S_SF_SERIAL_FORMAT_SHIFT)
|
||||
# define I2S_SF_SERIAL_FORMAT_I2S_DAV (4<<I2S_SF_SERIAL_FORMAT_SHIFT)
|
||||
# define I2S_SF_SERIAL_FORMAT_I2S_SILABS (5<<I2S_SF_SERIAL_FORMAT_SHIFT)
|
||||
/* unknown */
|
||||
# define I2S_SF_EXT_SAMPLE_FREQ_INT_SHIFT 12
|
||||
# define I2S_SF_EXT_SAMPLE_FREQ_INT_MASK (0xF<<I2S_SF_SAMPLE_FREQ_INT_SHIFT)
|
||||
/* probably gives external frequency? */
|
||||
# define I2S_SF_EXT_SAMPLE_FREQ_MASK 0xFFF
|
||||
|
||||
/* used to send codec messages, but how isn't clear */
|
||||
#define I2S_REG_CODEC_MSG_OUT 0x20
|
||||
|
||||
/* used to receive codec messages, but how isn't clear */
|
||||
#define I2S_REG_CODEC_MSG_IN 0x30
|
||||
|
||||
/* frame count reg isn't clear to me yet, but probably useful */
|
||||
#define I2S_REG_FRAME_COUNT 0x40
|
||||
|
||||
/* program to some value, and get interrupt if frame count reaches it */
|
||||
#define I2S_REG_FRAME_MATCH 0x50
|
||||
|
||||
/* this register describes how the bus transfers data */
|
||||
#define I2S_REG_DATA_WORD_SIZES 0x60
|
||||
/* number of interleaved input channels */
|
||||
# define I2S_DWS_NUM_CHANNELS_IN_SHIFT 24
|
||||
# define I2S_DWS_NUM_CHANNELS_IN_MASK (0x1F<<I2S_DWS_NUM_CHANNELS_IN_SHIFT)
|
||||
/* word size of input data */
|
||||
# define I2S_DWS_DATA_IN_SIZE_SHIFT 16
|
||||
# define I2S_DWS_DATA_IN_16BIT (0<<I2S_DWS_DATA_IN_SIZE_SHIFT)
|
||||
# define I2S_DWS_DATA_IN_24BIT (3<<I2S_DWS_DATA_IN_SIZE_SHIFT)
|
||||
/* number of interleaved output channels */
|
||||
# define I2S_DWS_NUM_CHANNELS_OUT_SHIFT 8
|
||||
# define I2S_DWS_NUM_CHANNELS_OUT_MASK (0x1F<<I2S_DWS_NUM_CHANNELS_OUT_SHIFT)
|
||||
/* word size of output data */
|
||||
# define I2S_DWS_DATA_OUT_SIZE_SHIFT 0
|
||||
# define I2S_DWS_DATA_OUT_16BIT (0<<I2S_DWS_DATA_OUT_SIZE_SHIFT)
|
||||
# define I2S_DWS_DATA_OUT_24BIT (3<<I2S_DWS_DATA_OUT_SIZE_SHIFT)
|
||||
|
||||
|
||||
/* unknown */
|
||||
#define I2S_REG_PEAK_LEVEL_SEL 0x70
|
||||
|
||||
/* unknown */
|
||||
#define I2S_REG_PEAK_LEVEL_IN0 0x80
|
||||
|
||||
/* unknown */
|
||||
#define I2S_REG_PEAK_LEVEL_IN1 0x90
|
||||
|
||||
#endif /* __I2SBUS_INTERFACE_H */
|
1064
sound/aoa/soundbus/i2sbus/pcm.c
Normal file
1064
sound/aoa/soundbus/i2sbus/pcm.c
Normal file
File diff suppressed because it is too large
Load diff
204
sound/aoa/soundbus/soundbus.h
Normal file
204
sound/aoa/soundbus/soundbus.h
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* soundbus generic definitions
|
||||
*
|
||||
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* GPL v2, can be found in COPYING.
|
||||
*/
|
||||
#ifndef __SOUNDBUS_H
|
||||
#define __SOUNDBUS_H
|
||||
|
||||
#include <linux/of_device.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
|
||||
/* When switching from master to slave or the other way around,
|
||||
* you don't want to have the codec chip acting as clock source
|
||||
* while the bus still is.
|
||||
* More importantly, while switch from slave to master, you need
|
||||
* to turn off the chip's master function first, but then there's
|
||||
* no clock for a while and other chips might reset, so we notify
|
||||
* their drivers after having switched.
|
||||
* The constants here are codec-point of view, so when we switch
|
||||
* the soundbus to master we tell the codec we're going to switch
|
||||
* and give it CLOCK_SWITCH_PREPARE_SLAVE!
|
||||
*/
|
||||
enum clock_switch {
|
||||
CLOCK_SWITCH_PREPARE_SLAVE,
|
||||
CLOCK_SWITCH_PREPARE_MASTER,
|
||||
CLOCK_SWITCH_SLAVE,
|
||||
CLOCK_SWITCH_MASTER,
|
||||
CLOCK_SWITCH_NOTIFY,
|
||||
};
|
||||
|
||||
/* information on a transfer the codec can take */
|
||||
struct transfer_info {
|
||||
u64 formats; /* SNDRV_PCM_FMTBIT_* */
|
||||
unsigned int rates; /* SNDRV_PCM_RATE_* */
|
||||
/* flags */
|
||||
u32 transfer_in:1, /* input = 1, output = 0 */
|
||||
must_be_clock_source:1;
|
||||
/* for codecs to distinguish among their TIs */
|
||||
int tag;
|
||||
};
|
||||
|
||||
struct codec_info_item {
|
||||
struct codec_info *codec;
|
||||
void *codec_data;
|
||||
struct soundbus_dev *sdev;
|
||||
/* internal, to be used by the soundbus provider */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* for prepare, where the codecs need to know
|
||||
* what we're going to drive the bus with */
|
||||
struct bus_info {
|
||||
/* see below */
|
||||
int sysclock_factor;
|
||||
int bus_factor;
|
||||
};
|
||||
|
||||
/* information on the codec itself, plus function pointers */
|
||||
struct codec_info {
|
||||
/* the module this lives in */
|
||||
struct module *owner;
|
||||
|
||||
/* supported transfer possibilities, array terminated by
|
||||
* formats or rates being 0. */
|
||||
struct transfer_info *transfers;
|
||||
|
||||
/* Master clock speed factor
|
||||
* to be used (master clock speed = sysclock_factor * sampling freq)
|
||||
* Unused if the soundbus provider has no such notion.
|
||||
*/
|
||||
int sysclock_factor;
|
||||
|
||||
/* Bus factor, bus clock speed = bus_factor * sampling freq)
|
||||
* Unused if the soundbus provider has no such notion.
|
||||
*/
|
||||
int bus_factor;
|
||||
|
||||
/* operations */
|
||||
/* clock switching, see above */
|
||||
int (*switch_clock)(struct codec_info_item *cii,
|
||||
enum clock_switch clock);
|
||||
|
||||
/* called for each transfer_info when the user
|
||||
* opens the pcm device to determine what the
|
||||
* hardware can support at this point in time.
|
||||
* That can depend on other user-switchable controls.
|
||||
* Return 1 if usable, 0 if not.
|
||||
* out points to another instance of a transfer_info
|
||||
* which is initialised to the values in *ti, and
|
||||
* it's format and rate values can be modified by
|
||||
* the callback if it is necessary to further restrict
|
||||
* the formats that can be used at the moment, for
|
||||
* example when one codec has multiple logical codec
|
||||
* info structs for multiple inputs.
|
||||
*/
|
||||
int (*usable)(struct codec_info_item *cii,
|
||||
struct transfer_info *ti,
|
||||
struct transfer_info *out);
|
||||
|
||||
/* called when pcm stream is opened, probably not implemented
|
||||
* most of the time since it isn't too useful */
|
||||
int (*open)(struct codec_info_item *cii,
|
||||
struct snd_pcm_substream *substream);
|
||||
|
||||
/* called when the pcm stream is closed, at this point
|
||||
* the user choices can all be unlocked (see below) */
|
||||
int (*close)(struct codec_info_item *cii,
|
||||
struct snd_pcm_substream *substream);
|
||||
|
||||
/* if the codec must forbid some user choices because
|
||||
* they are not valid with the substream/transfer info,
|
||||
* it must do so here. Example: no digital output for
|
||||
* incompatible framerate, say 8KHz, on Onyx.
|
||||
* If the selected stuff in the substream is NOT
|
||||
* compatible, you have to reject this call! */
|
||||
int (*prepare)(struct codec_info_item *cii,
|
||||
struct bus_info *bi,
|
||||
struct snd_pcm_substream *substream);
|
||||
|
||||
/* start() is called before data is pushed to the codec.
|
||||
* Note that start() must be atomic! */
|
||||
int (*start)(struct codec_info_item *cii,
|
||||
struct snd_pcm_substream *substream);
|
||||
|
||||
/* stop() is called after data is no longer pushed to the codec.
|
||||
* Note that stop() must be atomic! */
|
||||
int (*stop)(struct codec_info_item *cii,
|
||||
struct snd_pcm_substream *substream);
|
||||
|
||||
int (*suspend)(struct codec_info_item *cii, pm_message_t state);
|
||||
int (*resume)(struct codec_info_item *cii);
|
||||
};
|
||||
|
||||
/* information on a soundbus device */
|
||||
struct soundbus_dev {
|
||||
/* the bus it belongs to */
|
||||
struct list_head onbuslist;
|
||||
|
||||
/* the of device it represents */
|
||||
struct platform_device ofdev;
|
||||
|
||||
/* what modules go by */
|
||||
char modalias[32];
|
||||
|
||||
/* These fields must be before attach_codec can be called.
|
||||
* They should be set by the owner of the alsa card object
|
||||
* that is needed, and whoever sets them must make sure
|
||||
* that they are unique within that alsa card object. */
|
||||
char *pcmname;
|
||||
int pcmid;
|
||||
|
||||
/* this is assigned by the soundbus provider in attach_codec */
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
/* operations */
|
||||
/* attach a codec to this soundbus, give the alsa
|
||||
* card object the PCMs for this soundbus should be in.
|
||||
* The 'data' pointer must be unique, it is used as the
|
||||
* key for detach_codec(). */
|
||||
int (*attach_codec)(struct soundbus_dev *dev, struct snd_card *card,
|
||||
struct codec_info *ci, void *data);
|
||||
void (*detach_codec)(struct soundbus_dev *dev, void *data);
|
||||
/* TODO: suspend/resume */
|
||||
|
||||
/* private for the soundbus provider */
|
||||
struct list_head codec_list;
|
||||
u32 have_out:1, have_in:1;
|
||||
};
|
||||
#define to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev.dev)
|
||||
#define of_to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev)
|
||||
|
||||
extern int soundbus_add_one(struct soundbus_dev *dev);
|
||||
extern void soundbus_remove_one(struct soundbus_dev *dev);
|
||||
|
||||
extern struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev);
|
||||
extern void soundbus_dev_put(struct soundbus_dev *dev);
|
||||
|
||||
struct soundbus_driver {
|
||||
char *name;
|
||||
struct module *owner;
|
||||
|
||||
/* we don't implement any matching at all */
|
||||
|
||||
int (*probe)(struct soundbus_dev* dev);
|
||||
int (*remove)(struct soundbus_dev* dev);
|
||||
|
||||
int (*suspend)(struct soundbus_dev* dev, pm_message_t state);
|
||||
int (*resume)(struct soundbus_dev* dev);
|
||||
int (*shutdown)(struct soundbus_dev* dev);
|
||||
|
||||
struct device_driver driver;
|
||||
};
|
||||
#define to_soundbus_driver(drv) container_of(drv,struct soundbus_driver, driver)
|
||||
|
||||
extern int soundbus_register_driver(struct soundbus_driver *drv);
|
||||
extern void soundbus_unregister_driver(struct soundbus_driver *drv);
|
||||
|
||||
extern struct device_attribute soundbus_dev_attrs[];
|
||||
|
||||
#endif /* __SOUNDBUS_H */
|
42
sound/aoa/soundbus/sysfs.c
Normal file
42
sound/aoa/soundbus/sysfs.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/stat.h>
|
||||
/* FIX UP */
|
||||
#include "soundbus.h"
|
||||
|
||||
#define soundbus_config_of_attr(field, format_string) \
|
||||
static ssize_t \
|
||||
field##_show (struct device *dev, struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct soundbus_dev *mdev = to_soundbus_device (dev); \
|
||||
return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \
|
||||
}
|
||||
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct soundbus_dev *sdev = to_soundbus_device(dev);
|
||||
struct platform_device *of = &sdev->ofdev;
|
||||
int length;
|
||||
|
||||
if (*sdev->modalias) {
|
||||
strlcpy(buf, sdev->modalias, sizeof(sdev->modalias) + 1);
|
||||
strcat(buf, "\n");
|
||||
length = strlen(buf);
|
||||
} else {
|
||||
length = sprintf(buf, "of:N%sT%s\n",
|
||||
of->dev.of_node->name, of->dev.of_node->type);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
soundbus_config_of_attr (name, "%s\n");
|
||||
soundbus_config_of_attr (type, "%s\n");
|
||||
|
||||
struct device_attribute soundbus_dev_attrs[] = {
|
||||
__ATTR_RO(name),
|
||||
__ATTR_RO(type),
|
||||
__ATTR_RO(modalias),
|
||||
__ATTR_NULL
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue