Commit 4a161d23 authored by Manuel Lauss's avatar Manuel Lauss Committed by Jaroslav Kysela

ALSA: ASoC: Au12x0/Au1550 PSC Audio support

Audio for Au12x0/Au1550 PSCs in AC97 and I2S mode, for ASoC v1 framework.

- DBDMA, AC97 and I2S drivers
- sample AC97 machine code (Db1200)
Signed-off-by: default avatarManuel Lauss <mano@roarinelk.homelinux.net>
Signed-off-by: default avatarLiam Girdwood <lg@opensource.wolfsonmicro.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent bf415345
...@@ -204,6 +204,14 @@ typedef struct psc_i2s { ...@@ -204,6 +204,14 @@ typedef struct psc_i2s {
u32 psc_i2sudf; u32 psc_i2sudf;
} psc_i2s_t; } psc_i2s_t;
#define PSC_I2SCFG_OFFSET 0x08
#define PSC_I2SMASK_OFFSET 0x0C
#define PSC_I2SPCR_OFFSET 0x10
#define PSC_I2SSTAT_OFFSET 0x14
#define PSC_I2SEVENT_OFFSET 0x18
#define PSC_I2SRXTX_OFFSET 0x1C
#define PSC_I2SUDF_OFFSET 0x20
/* I2S Config Register. */ /* I2S Config Register. */
#define PSC_I2SCFG_RT_MASK (3 << 30) #define PSC_I2SCFG_RT_MASK (3 << 30)
#define PSC_I2SCFG_RT_FIFO1 (0 << 30) #define PSC_I2SCFG_RT_FIFO1 (0 << 30)
......
...@@ -24,6 +24,7 @@ config SND_SOC_AC97_BUS ...@@ -24,6 +24,7 @@ config SND_SOC_AC97_BUS
# All the supported Soc's # All the supported Soc's
source "sound/soc/at32/Kconfig" source "sound/soc/at32/Kconfig"
source "sound/soc/at91/Kconfig" source "sound/soc/at91/Kconfig"
source "sound/soc/au1x/Kconfig"
source "sound/soc/pxa/Kconfig" source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig" source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig" source "sound/soc/sh/Kconfig"
......
...@@ -2,4 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o ...@@ -2,4 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += omap/ au1x/
##
## Au1200/Au1550 PSC + DBDMA
##
config SND_SOC_AU1XPSC
tristate "SoC Audio for Au1200/Au1250/Au1550"
depends on SOC_AU1200 || SOC_AU1550
help
This option enables support for the Programmable Serial
Controllers in AC97 and I2S mode, and the Descriptor-Based DMA
Controller (DBDMA) as found on the Au1200/Au1250/Au1550 SoC.
config SND_SOC_AU1XPSC_I2S
tristate
config SND_SOC_AU1XPSC_AC97
tristate
select AC97_BUS
select SND_AC97_CODEC
select SND_SOC_AC97_BUS
##
## Boards
##
config SND_SOC_SAMPLE_PSC_AC97
tristate "Sample Au12x0/Au1550 PSC AC97 sound machine"
depends on SND_SOC_AU1XPSC
select SND_SOC_AU1XPSC_AC97
select SND_SOC_AC97_CODEC
help
This is a sample AC97 sound machine for use in Au12x0/Au1550
based systems which have audio on PSC1 (e.g. Db1200 demoboard).
# Au1200/Au1550 PSC audio
snd-soc-au1xpsc-dbdma-objs := dbdma2.o
snd-soc-au1xpsc-i2s-objs := psc-i2s.o
snd-soc-au1xpsc-ac97-objs := psc-ac97.o
obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
# Boards
snd-soc-sample-ac97-objs := sample-ac97.o
obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o
This diff is collapsed.
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <mano@roarinelk.homelinux.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Au1xxx-PSC AC97 glue.
*
* NOTE: all of these drivers can only work with a SINGLE instance
* of a PSC. Multiple independent audio devices are impossible
* with ASoC v1.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/suspend.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include "psc.h"
#define AC97_DIR \
(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
#define AC97_RATES \
SNDRV_PCM_RATE_8000_48000
#define AC97_FMTS \
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
#define AC97PCR_START(stype) \
((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
#define AC97PCR_STOP(stype) \
((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
#define AC97PCR_CLRFIFO(stype) \
((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
/* AC97 controller reads codec register */
static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned short data, tmo;
au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
au_sync();
tmo = 1000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
udelay(2);
if (!tmo)
data = 0xffff;
else
data = au_readl(AC97_CDC(pscdata)) & 0xffff;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
return data;
}
/* AC97 controller writes to codec register */
static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned int tmo;
au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
au_sync();
tmo = 1000;
while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
au_sync();
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
au_sync();
}
/* AC97 controller asserts a warm reset */
static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
au_sync();
msleep(10);
au_writel(0, AC97_RST(pscdata));
au_sync();
}
static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
int i;
/* disable PSC during cold reset */
au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata));
au_sync();
/* issue cold reset */
au_writel(PSC_AC97RST_RST, AC97_RST(pscdata));
au_sync();
msleep(500);
au_writel(0, AC97_RST(pscdata));
au_sync();
/* enable PSC */
au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata));
au_sync();
/* wait for PSC to indicate it's ready */
i = 100000;
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
au_sync();
if (i == 0) {
printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
return;
}
/* enable the ac97 function */
au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
/* wait for AC97 core to become ready */
i = 100000;
while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
au_sync();
if (i == 0)
printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
}
/* AC97 controller operations */
struct snd_ac97_bus_ops soc_ac97_ops = {
.read = au1xpsc_ac97_read,
.write = au1xpsc_ac97_write,
.reset = au1xpsc_ac97_cold_reset,
.warm_reset = au1xpsc_ac97_warm_reset,
};
EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
unsigned long r, stat;
int chans, stype = SUBSTREAM_TYPE(substream);
chans = params_channels(params);
r = au_readl(AC97_CFG(pscdata));
stat = au_readl(AC97_STAT(pscdata));
/* already active? */
if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) {
/* reject parameters not currently set up */
if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) ||
(pscdata->rate != params_rate(params)))
return -EINVAL;
} else {
/* disable AC97 device controller first */
au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
r &= ~PSC_AC97CFG_LEN_MASK;
r |= PSC_AC97CFG_SET_LEN(params->msbits);
/* channels: enable slots for front L/R channel */
if (stype == PCM_TX) {
r &= ~PSC_AC97CFG_TXSLOT_MASK;
r |= PSC_AC97CFG_TXSLOT_ENA(3);
r |= PSC_AC97CFG_TXSLOT_ENA(4);
} else {
r &= ~PSC_AC97CFG_RXSLOT_MASK;
r |= PSC_AC97CFG_RXSLOT_ENA(3);
r |= PSC_AC97CFG_RXSLOT_ENA(4);
}
/* finally enable the AC97 controller again */
au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
au_sync();
pscdata->cfg = r;
pscdata->rate = params_rate(params);
}
return 0;
}
static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
int cmd)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
int ret, stype = SUBSTREAM_TYPE(substream);
ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
au_sync();
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
au_sync();
break;
default:
ret = -EINVAL;
}
return ret;
}
static int au1xpsc_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
int ret;
struct resource *r;
unsigned long sel;
if (au1xpsc_ac97_workdata)
return -EBUSY;
au1xpsc_ac97_workdata =
kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!au1xpsc_ac97_workdata)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENODEV;
goto out0;
}
ret = -EBUSY;
au1xpsc_ac97_workdata->ioarea =
request_mem_region(r->start, r->end - r->start + 1,
"au1xpsc_ac97");
if (!au1xpsc_ac97_workdata->ioarea)
goto out0;
au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
if (!au1xpsc_ac97_workdata->mmio)
goto out1;
/* configuration: max dma trigger threshold, enable ac97 */
au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
PSC_AC97CFG_TT_FIFO8 |
PSC_AC97CFG_DE_ENABLE;
/* preserve PSC clock source set up by platform (dev.platform_data
* is already occupied by soc layer)
*/
sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
au_sync();
au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
au_sync();
/* next up: cold reset. Dont check for PSC-ready now since
* there may not be any codec clock yet.
*/
return 0;
out1:
release_resource(au1xpsc_ac97_workdata->ioarea);
kfree(au1xpsc_ac97_workdata->ioarea);
out0:
kfree(au1xpsc_ac97_workdata);
au1xpsc_ac97_workdata = NULL;
return ret;
}
static void au1xpsc_ac97_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
/* disable PSC completely */
au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
au_sync();
iounmap(au1xpsc_ac97_workdata->mmio);
release_resource(au1xpsc_ac97_workdata->ioarea);
kfree(au1xpsc_ac97_workdata->ioarea);
kfree(au1xpsc_ac97_workdata);
au1xpsc_ac97_workdata = NULL;
}
static int au1xpsc_ac97_suspend(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
/* save interesting registers and disable PSC */
au1xpsc_ac97_workdata->pm[0] =
au_readl(PSC_SEL(au1xpsc_ac97_workdata));
au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
au_sync();
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
au_sync();
return 0;
}
static int au1xpsc_ac97_resume(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
/* restore PSC clock config */
au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
PSC_SEL(au1xpsc_ac97_workdata));
au_sync();
/* after this point the ac97 core will cold-reset the codec.
* During cold-reset the PSC is reinitialized and the last
* configuration set up in hw_params() is restored.
*/
return 0;
}
struct snd_soc_dai au1xpsc_ac97_dai = {
.name = "au1xpsc_ac97",
.type = SND_SOC_DAI_AC97,
.probe = au1xpsc_ac97_probe,
.remove = au1xpsc_ac97_remove,
.suspend = au1xpsc_ac97_suspend,
.resume = au1xpsc_ac97_resume,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
.channels_min = 2,
.channels_max = 2,
},
.ops = {
.trigger = au1xpsc_ac97_trigger,
.hw_params = au1xpsc_ac97_hw_params,
},
};
EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
static int __init au1xpsc_ac97_init(void)
{
au1xpsc_ac97_workdata = NULL;
return 0;
}
static void __exit au1xpsc_ac97_exit(void)
{
}
module_init(au1xpsc_ac97_init);
module_exit(au1xpsc_ac97_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
This diff is collapsed.
/*
* Au12x0/Au1550 PSC ALSA ASoC audio support.
*
* (c) 2007-2008 MSC Vertriebsges.m.b.H.,
* Manuel Lauss <mano@roarinelk.homelinux.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* NOTE: all of these drivers can only work with a SINGLE instance
* of a PSC. Multiple independent audio devices are impossible
* with ASoC v1.
*/
#ifndef _AU1X_PCM_H
#define _AU1X_PCM_H
extern struct snd_soc_dai au1xpsc_ac97_dai;
extern struct snd_soc_dai au1xpsc_i2s_dai;
extern struct snd_soc_platform au1xpsc_soc_platform;
extern struct snd_ac97_bus_ops soc_ac97_ops;
struct au1xpsc_audio_data {
void __iomem *mmio;
unsigned long cfg;
unsigned long rate;
unsigned long pm[2];
struct resource *ioarea;
};
#define PCM_TX 0
#define PCM_RX 1
#define SUBSTREAM_TYPE(substream) \
((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
/* easy access macros */
#define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
#define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
#define I2S_STAT(x) ((unsigned long)((x)->mmio) + PSC_I2SSTAT_OFFSET)
#define I2S_CFG(x) ((unsigned long)((x)->mmio) + PSC_I2SCFG_OFFSET)
#define I2S_PCR(x) ((unsigned long)((x)->mmio) + PSC_I2SPCR_OFFSET)
#define AC97_CFG(x) ((unsigned long)((x)->mmio) + PSC_AC97CFG_OFFSET)
#define AC97_CDC(x) ((unsigned long)((x)->mmio) + PSC_AC97CDC_OFFSET)
#define AC97_EVNT(x) ((unsigned long)((x)->mmio) + PSC_AC97EVNT_OFFSET)
#define AC97_PCR(x) ((unsigned long)((x)->mmio) + PSC_AC97PCR_OFFSET)
#define AC97_RST(x) ((unsigned long)((x)->mmio) + PSC_AC97RST_OFFSET)
#define AC97_STAT(x) ((unsigned long)((x)->mmio) + PSC_AC97STAT_OFFSET)
#endif
/*
* Sample Au12x0/Au1550 PSC AC97 sound machine.
*
* Copyright (c) 2007-2008 Manuel Lauss <mano@roarinelk.homelinux.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms outlined in the file COPYING at the root of this
* source archive.
*
* This is a very generic AC97 sound machine driver for boards which
* have (AC97) audio at PSC1 (e.g. DB1200 demoboards).
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include "../codecs/ac97.h"
#include "psc.h"
static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec)
{
snd_soc_dapm_sync(codec);
return 0;
}
static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai = &au1xpsc_ac97_dai, /* see psc-ac97.c */
.codec_dai = &ac97_dai, /* see codecs/ac97.c */
.init = au1xpsc_sample_ac97_init,
.ops = NULL,
};
static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
.name = "Au1xxx PSC AC97 Audio",
.dai_link = &au1xpsc_sample_ac97_dai,
.num_links = 1,
};
static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
.machine = &au1xpsc_sample_ac97_machine,
.platform = &au1xpsc_soc_platform, /* see dbdma2.c */
.codec_dev = &soc_codec_dev_ac97,
};
static struct resource au1xpsc_psc1_res[] = {
[0] = {
.start = CPHYSADDR(PSC1_BASE_ADDR),
.end = CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff,
.flags = IORESOURCE_MEM,
},
[1] = {
#ifdef CONFIG_SOC_AU1200
.start = AU1200_PSC1_INT,
.end = AU1200_PSC1_INT,
#elif defined(CONFIG_SOC_AU1550)
.start = AU1550_PSC1_INT,
.end = AU1550_PSC1_INT,
#endif
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = DSCR_CMD0_PSC1_TX,
.end = DSCR_CMD0_PSC1_TX,
.flags = IORESOURCE_DMA,
},
[3] = {
.start = DSCR_CMD0_PSC1_RX,
.end = DSCR_CMD0_PSC1_RX,
.flags = IORESOURCE_DMA,
},
};
static struct platform_device *au1xpsc_sample_ac97_dev;
static int __init au1xpsc_sample_ac97_load(void)
{
int ret;
#ifdef CONFIG_SOC_AU1200
unsigned long io;
/* modify sys_pinfunc for AC97 on PSC1 */
io = au_readl(SYS_PINFUNC);
io |= SYS_PINFUNC_P1C;
io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B);
au_writel(io, SYS_PINFUNC);
au_sync();
#endif
ret = -ENOMEM;
/* setup PSC clock source for AC97 part: external clock provided
* by codec. The psc-ac97.c driver depends on this setting!
*/
au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET);
au_sync();
au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1);
if (!au1xpsc_sample_ac97_dev)
goto out;
au1xpsc_sample_ac97_dev->resource =
kmemdup(au1xpsc_psc1_res, sizeof(struct resource) *
ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL);
au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res);
au1xpsc_sample_ac97_dev->id = 1;
platform_set_drvdata(au1xpsc_sample_ac97_dev,
&au1xpsc_sample_ac97_devdata);
au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev;
ret = platform_device_add(au1xpsc_sample_ac97_dev);
if (ret) {
platform_device_put(au1xpsc_sample_ac97_dev);
au1xpsc_sample_ac97_dev = NULL;
}
out:
return ret;
}
static void __exit au1xpsc_sample_ac97_exit(void)
{
platform_device_unregister(au1xpsc_sample_ac97_dev);
}
module_init(au1xpsc_sample_ac97_load);
module_exit(au1xpsc_sample_ac97_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment