linux/sound/soc/omap/omap-mcpdm.c
Linus Torvalds f5a246eab9 Sound updates for 3.7-rc1
This contains pretty many small commits covering fairly large range of
 files in sound/ directory.  Partly because of additional API support
 and partly because of constantly developed ASoC and ARM stuff.
 
 Some highlights:
 
 - Introduced the helper function and documentation for exposing the
   channel map via control API, as discussed in Plumbers; most of PCI
   drivers are covered, will follow more drivers later
 
 - Most of drivers have been replaced with the new PM callbacks (if
   the bus is supported)
 
 - HD-audio controller got the support of runtime PM and the support of
   D3 clock-stop.  Also changing the power_save option in sysfs kicks
   off immediately to enable / disable the power-save mode.
 
 - Another significant code change in HD-audio is the rewrite of
   firmware loading code.  Other than that, most of changes in HD-audio
   are continued cleanups and standardization for the generic auto
   parser and bug fixes (HBR, device-specific fixups), in addition to
   the support of channel-map API.
 
 - Addition of ASoC bindings for the compressed API, used by the
   mid-x86 drivers.
 
 - Lots of cleanups and API refreshes for ASoC codec drivers and
   DaVinci.
 
 - Conversion of OMAP to dmaengine.
 
 - New machine driver for Wolfson Microelectronics Bells.
 
 - New CODEC driver for Wolfson Microelectronics WM0010.
 
 - Enhancements to the ux500 and wm2000 drivers
 
 - A new driver for DA9055 and the support for regulator bypass mode.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.19 (GNU/Linux)
 
 iQIcBAABAgAGBQJQcpeWAAoJEGwxgFQ9KSmkpi4P/2etDDz5aEkEHNa1l4xEmFcm
 ymiGTgjaalqpUAVbM/gYx9G59EFMEbzUl1BHAqE5La4wO/v9lNPb+VrdUo+B+NZ7
 WSxIPWcNqdinSuoSqyYPjoPMVnhs3EMtNOqmf4jm1JOvdqA+4rO29xQVAqK/5Gfu
 LpMOyPiRi5ODnbQ1BOIWwpKICioY/mLwGJudK3z0i/fYVA7gLub20f+w+sOjKIA4
 wmwQAMTjAR798Cg/tVy4fQmf4SLw+c2nIgGe/PD+2gVlGXLNKBrJfMonHPTbmwKu
 lmJO/EtnijNOnpbn6up7ryUQ9cSoZAUZOfdIOgmAeQgQ/LWR0f+zf2IQehSPwrul
 g6hqOnQI2DNN7ugT3cYVbYnsh56TjyhnxhhxZgkapqh706QkqHGyKJNMRetzuXmP
 1O//MnZJrFQWd6sOKLlTL2ZzRvnxEJcNVGaE6bbwZTfQMtPeo9l1842uIq1dLUtG
 VxZb/svKUkMXv4is1dwUYUkpDsKxsgMEmabmuovceGf2N7jj/irkXgqxf6LWkaY1
 JQ7ZFWUJyDzEMXRaFfzdGO15T532CfB84wvFX5xoPMwMste2AA7QuybFBVstXhKu
 AtKNDgRJFUTlnLIxydpPBWdWH3UJdEaFwwsSfuNKI8OmmGKhWC/aP83k4hzueu9H
 KYLvY/0ObMSMqiwh/ndQ
 =uNqD
 -----END PGP SIGNATURE-----

Merge tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "This contains pretty many small commits covering fairly large range of
  files in sound/ directory.  Partly because of additional API support
  and partly because of constantly developed ASoC and ARM stuff.

  Some highlights:

   - Introduced the helper function and documentation for exposing the
     channel map via control API, as discussed in Plumbers; most of PCI
     drivers are covered, will follow more drivers later

   - Most of drivers have been replaced with the new PM callbacks (if
     the bus is supported)

   - HD-audio controller got the support of runtime PM and the support
     of D3 clock-stop.  Also changing the power_save option in sysfs
     kicks off immediately to enable / disable the power-save mode.

   - Another significant code change in HD-audio is the rewrite of
     firmware loading code.  Other than that, most of changes in
     HD-audio are continued cleanups and standardization for the generic
     auto parser and bug fixes (HBR, device-specific fixups), in
     addition to the support of channel-map API.

   - Addition of ASoC bindings for the compressed API, used by the
     mid-x86 drivers.

   - Lots of cleanups and API refreshes for ASoC codec drivers and
     DaVinci.

   - Conversion of OMAP to dmaengine.

   - New machine driver for Wolfson Microelectronics Bells.

   - New CODEC driver for Wolfson Microelectronics WM0010.

   - Enhancements to the ux500 and wm2000 drivers

   - A new driver for DA9055 and the support for regulator bypass mode."

Fix up various arm soc header file reorg conflicts.

* tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (339 commits)
  ALSA: hda - Add new codec ALC283 ALC290 support
  ALSA: hda - avoid unneccesary indices on "Headphone Jack" controls
  ALSA: hda - fix indices on boost volume on Conexant
  ALSA: aloop - add locking to timer access
  ALSA: hda - Fix hang caused by race during suspend.
  sound: Remove unnecessary semicolon
  ALSA: hda/realtek - Fix detection of ALC271X codec
  ALSA: hda - Add inverted internal mic quirk for Lenovo IdeaPad U310
  ALSA: hda - make Realtek/Sigmatel/Conexant use the generic unsol event
  ALSA: hda - make a generic unsol event handler
  ASoC: codecs: Add DA9055 codec driver
  ASoC: eukrea-tlv320: Convert it to platform driver
  ALSA: ASoC: add DT bindings for CS4271
  ASoC: wm_hubs: Ensure volume updates are handled during class W startup
  ASoC: wm5110: Adding missing volume update bits
  ASoC: wm5110: Add OUT3R support
  ASoC: wm5110: Add AEC loopback support
  ASoC: wm5110: Rename EPOUT to HPOUT3
  ASoC: arizona: Add more clock rates
  ASoC: arizona: Add more DSP options for mixer input muxes
  ...
2012-10-09 07:07:14 +09:00

524 lines
14 KiB
C

/*
* omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port
*
* Copyright (C) 2009 - 2011 Texas Instruments
*
* Author: Misael Lopez Cruz <misael.lopez@ti.com>
* Contact: Jorge Eduardo Candelaria <x0107209@ti.com>
* Margarita Olaya <magi.olaya@ti.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <plat/omap_hwmod.h>
#include "omap-mcpdm.h"
#include "omap-pcm.h"
#define OMAP44XX_MCPDM_L3_BASE 0x49032000
struct omap_mcpdm {
struct device *dev;
unsigned long phys_base;
void __iomem *io_base;
int irq;
struct mutex mutex;
/* channel data */
u32 dn_channels;
u32 up_channels;
/* McPDM FIFO thresholds */
u32 dn_threshold;
u32 up_threshold;
/* McPDM dn offsets for rx1, and 2 channels */
u32 dn_rx_offset;
};
/*
* Stream DMA parameters
*/
static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
{
.name = "Audio playback",
},
{
.name = "Audio capture",
},
};
static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val)
{
__raw_writel(val, mcpdm->io_base + reg);
}
static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg)
{
return __raw_readl(mcpdm->io_base + reg);
}
#ifdef DEBUG
static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm)
{
dev_dbg(mcpdm->dev, "***********************\n");
dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW));
dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS));
dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET));
dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR));
dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN));
dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET));
dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR));
dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN));
dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL));
dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA));
dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA));
dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN));
dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP));
dev_dbg(mcpdm->dev, "***********************\n");
}
#else
static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {}
#endif
/*
* Enables the transfer through the PDM interface to/from the Phoenix
* codec by enabling the corresponding UP or DN channels.
*/
static void omap_mcpdm_start(struct omap_mcpdm *mcpdm)
{
u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
ctrl |= mcpdm->dn_channels | mcpdm->up_channels;
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
}
/*
* Disables the transfer through the PDM interface to/from the Phoenix
* codec by disabling the corresponding UP or DN channels.
*/
static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm)
{
u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
ctrl &= ~(mcpdm->dn_channels | mcpdm->up_channels);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
}
/*
* Is the physical McPDM interface active.
*/
static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
{
return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) &
(MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK);
}
/*
* Configures McPDM uplink, and downlink for audio.
* This function should be called before omap_mcpdm_start.
*/
static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
{
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
/* Enable DN RX1/2 offset cancellation feature, if configured */
if (mcpdm->dn_rx_offset) {
u32 dn_offset = mcpdm->dn_rx_offset;
omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN);
omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
}
omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, mcpdm->dn_threshold);
omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, mcpdm->up_threshold);
omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET,
MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE);
}
/*
* Cleans McPDM uplink, and downlink configuration.
* This function should be called when the stream is closed.
*/
static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm)
{
/* Disable irq request generation for downlink */
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL);
/* Disable DMA request generation for downlink */
omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE);
/* Disable irq request generation for uplink */
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
/* Disable DMA request generation for uplink */
omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE);
/* Disable RX1/2 offset cancellation */
if (mcpdm->dn_rx_offset)
omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0);
}
static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
{
struct omap_mcpdm *mcpdm = dev_id;
int irq_status;
irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS);
/* Acknowledge irq event */
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status);
if (irq_status & MCPDM_DN_IRQ_FULL)
dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n");
if (irq_status & MCPDM_DN_IRQ_EMPTY)
dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n");
if (irq_status & MCPDM_DN_IRQ)
dev_dbg(mcpdm->dev, "DN (playback) write request\n");
if (irq_status & MCPDM_UP_IRQ_FULL)
dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n");
if (irq_status & MCPDM_UP_IRQ_EMPTY)
dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n");
if (irq_status & MCPDM_UP_IRQ)
dev_dbg(mcpdm->dev, "UP (capture) write request\n");
return IRQ_HANDLED;
}
static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
mutex_lock(&mcpdm->mutex);
if (!dai->active) {
/* Enable watch dog for ES above ES 1.0 to avoid saturation */
if (omap_rev() != OMAP4430_REV_ES1_0) {
u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL,
ctrl | MCPDM_WD_EN);
}
omap_mcpdm_open_streams(mcpdm);
}
mutex_unlock(&mcpdm->mutex);
snd_soc_dai_set_dma_data(dai, substream,
&omap_mcpdm_dai_dma_params[substream->stream]);
return 0;
}
static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
mutex_lock(&mcpdm->mutex);
if (!dai->active) {
if (omap_mcpdm_active(mcpdm)) {
omap_mcpdm_stop(mcpdm);
omap_mcpdm_close_streams(mcpdm);
}
}
mutex_unlock(&mcpdm->mutex);
}
static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
int stream = substream->stream;
struct omap_pcm_dma_data *dma_data;
int channels;
int link_mask = 0;
channels = params_channels(params);
switch (channels) {
case 5:
if (stream == SNDRV_PCM_STREAM_CAPTURE)
/* up to 3 channels for capture */
return -EINVAL;
link_mask |= 1 << 4;
case 4:
if (stream == SNDRV_PCM_STREAM_CAPTURE)
/* up to 3 channels for capture */
return -EINVAL;
link_mask |= 1 << 3;
case 3:
link_mask |= 1 << 2;
case 2:
link_mask |= 1 << 1;
case 1:
link_mask |= 1 << 0;
break;
default:
/* unsupported number of channels */
return -EINVAL;
}
dma_data = snd_soc_dai_get_dma_data(dai, substream);
/* Configure McPDM channels, and DMA packet size */
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
mcpdm->dn_channels = link_mask << 3;
dma_data->packet_size =
(MCPDM_DN_THRES_MAX - mcpdm->dn_threshold) * channels;
} else {
mcpdm->up_channels = link_mask << 0;
dma_data->packet_size = mcpdm->up_threshold * channels;
}
return 0;
}
static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
if (!omap_mcpdm_active(mcpdm)) {
omap_mcpdm_start(mcpdm);
omap_mcpdm_reg_dump(mcpdm);
}
return 0;
}
static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
.startup = omap_mcpdm_dai_startup,
.shutdown = omap_mcpdm_dai_shutdown,
.hw_params = omap_mcpdm_dai_hw_params,
.prepare = omap_mcpdm_prepare,
};
static int omap_mcpdm_probe(struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
int ret;
pm_runtime_enable(mcpdm->dev);
/* Disable lines while request is ongoing */
pm_runtime_get_sync(mcpdm->dev);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
0, "McPDM", (void *)mcpdm);
pm_runtime_put_sync(mcpdm->dev);
if (ret) {
dev_err(mcpdm->dev, "Request for IRQ failed\n");
pm_runtime_disable(mcpdm->dev);
}
/* Configure McPDM threshold values */
mcpdm->dn_threshold = 2;
mcpdm->up_threshold = MCPDM_UP_THRES_MAX - 3;
return ret;
}
static int omap_mcpdm_remove(struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
free_irq(mcpdm->irq, (void *)mcpdm);
pm_runtime_disable(mcpdm->dev);
return 0;
}
#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
static struct snd_soc_dai_driver omap_mcpdm_dai = {
.probe = omap_mcpdm_probe,
.remove = omap_mcpdm_remove,
.probe_order = SND_SOC_COMP_ORDER_LATE,
.remove_order = SND_SOC_COMP_ORDER_EARLY,
.playback = {
.channels_min = 1,
.channels_max = 5,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
.sig_bits = 24,
},
.capture = {
.channels_min = 1,
.channels_max = 3,
.rates = OMAP_MCPDM_RATES,
.formats = OMAP_MCPDM_FORMATS,
.sig_bits = 24,
},
.ops = &omap_mcpdm_dai_ops,
};
void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
u8 rx1, u8 rx2)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2);
}
EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets);
static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
{
struct omap_mcpdm *mcpdm;
struct resource *res;
mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL);
if (!mcpdm)
return -ENOMEM;
platform_set_drvdata(pdev, mcpdm);
mutex_init(&mcpdm->mutex);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
if (res == NULL)
return -ENOMEM;
omap_mcpdm_dai_dma_params[0].port_addr = res->start + MCPDM_REG_DN_DATA;
omap_mcpdm_dai_dma_params[1].port_addr = res->start + MCPDM_REG_UP_DATA;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "dn_link");
if (!res)
return -ENODEV;
omap_mcpdm_dai_dma_params[0].dma_req = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "up_link");
if (!res)
return -ENODEV;
omap_mcpdm_dai_dma_params[1].dma_req = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
if (res == NULL)
return -ENOMEM;
if (!devm_request_mem_region(&pdev->dev, res->start,
resource_size(res), "McPDM"))
return -EBUSY;
mcpdm->io_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!mcpdm->io_base)
return -ENOMEM;
mcpdm->irq = platform_get_irq(pdev, 0);
if (mcpdm->irq < 0)
return mcpdm->irq;
mcpdm->dev = &pdev->dev;
return snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
}
static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0;
}
static const struct of_device_id omap_mcpdm_of_match[] = {
{ .compatible = "ti,omap4-mcpdm", },
{ }
};
MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match);
static struct platform_driver asoc_mcpdm_driver = {
.driver = {
.name = "omap-mcpdm",
.owner = THIS_MODULE,
.of_match_table = omap_mcpdm_of_match,
},
.probe = asoc_mcpdm_probe,
.remove = __devexit_p(asoc_mcpdm_remove),
};
module_platform_driver(asoc_mcpdm_driver);
MODULE_ALIAS("platform:omap-mcpdm");
MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
MODULE_DESCRIPTION("OMAP PDM SoC Interface");
MODULE_LICENSE("GPL");