blob: 0dd0b2dc3a9a14ff8a41e81de8af01ce9b0ba4d9 [file] [log] [blame]
/*
*
* Marvell Orion Alsa Sound driver
*
* Author: Maen Suleiman
* Copyright (C) 2008 Marvell Ltd.
*
*
* 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.
*
*/
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <sound/driver.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/asoundef.h>
#include <sound/asound.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <asm/sizes.h>
#include "mv88fx-pcm.h"
/*#define AUDIO_REG_BASE 0x0*/
#include "audio/mvAudio.h"
#include "ctrlEnv/sys/mvSysAudio.h"
int cs42l51_init(void);
int mv88fx_snd_hw_init(struct mv88fx_snd_chip *chip)
{
if((chip->audio_info->i2s_rec) || (chip->audio_info->i2s_play)) {
/* Clear ints */
cs42l51_init();
}
mv88fx_snd_writel(chip, MV_AUDIO_INT_CAUSE_REG(0), 0xffffffff);
mv88fx_snd_writel(chip, MV_AUDIO_INT_MASK_REG(0), 0);
mv88fx_snd_writel(chip, MV_AUDIO_SPDIF_REC_INT_CAUSE_MASK_REG(0),
0);
if (MV_OK != mvAudioInit())
return EIO;
/* Disable all playback/recording */
mv88fx_snd_bitreset(chip, MV_AUDIO_PLAYBACK_CTRL_REG(0),
(APCR_PLAY_I2S_ENABLE_MASK |
APCR_PLAY_SPDIF_ENABLE_MASK));
mv88fx_snd_bitreset(chip, MV_AUDIO_RECORD_CTRL_REG(0),
(ARCR_RECORD_SPDIF_EN_MASK |
ARCR_RECORD_I2S_EN_MASK));
if (MV_OK != mvSPDIFRecordTclockSet(0)) {
mv88fx_snd_debug("Marvell ALSA driver ERR. mvSPDIFRecordTclockSet failed\n");
return -ENOMEM;
}
return 0;
}
int mv88fx_snd_hw_playback_set(struct mv88fx_snd_chip *chip)
{
struct mv88fx_snd_stream *audio_stream =
chip->stream[PLAYBACK];
struct snd_pcm_substream *substream = audio_stream->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
MV_AUDIO_PLAYBACK_CTRL pcm_play_ctrl;
MV_I2S_PLAYBACK_CTRL i2s_play_ctrl;
MV_SPDIF_PLAYBACK_CTRL spdif_play_ctrl;
MV_AUDIO_FREQ_DATA dco_ctrl;
dco_ctrl.offset = chip->dco_ctrl_offst;
switch(audio_stream->rate) {
case 44100:
dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH;
break;
case 48000:
dco_ctrl.baseFreq = AUDIO_FREQ_48KH;
break;
case 96000:
dco_ctrl.baseFreq = AUDIO_FREQ_96KH;
break;
default:
snd_printk("Requested rate %d is not supported\n",
runtime->rate); return -1;
}
pcm_play_ctrl.burst = (chip->burst == 128)?AUDIO_128BYTE_BURST:
AUDIO_32BYTE_BURST;
pcm_play_ctrl.loopBack = chip->loopback;
if (audio_stream->stereo) {
pcm_play_ctrl.monoMode = AUDIO_PLAY_MONO_OFF;
}
else {
switch (audio_stream->mono_mode) {
case MONO_LEFT:
pcm_play_ctrl.monoMode = AUDIO_PLAY_LEFT_MONO;
break;
case MONO_RIGHT:
pcm_play_ctrl.monoMode = AUDIO_PLAY_RIGHT_MONO;
break;
case MONO_BOTH:
default:
pcm_play_ctrl.monoMode = AUDIO_PLAY_BOTH_MONO;
break;
}
}
if (audio_stream->format == SAMPLE_16IN16) {
pcm_play_ctrl.sampleSize = SAMPLE_16BIT;
i2s_play_ctrl.sampleSize = SAMPLE_16BIT;
}
else if (audio_stream->format == SAMPLE_24IN32) {
pcm_play_ctrl.sampleSize = SAMPLE_24BIT;
i2s_play_ctrl.sampleSize = SAMPLE_24BIT;
}
else if (audio_stream->format == SAMPLE_32IN32) {
pcm_play_ctrl.sampleSize = SAMPLE_32BIT;
i2s_play_ctrl.sampleSize = SAMPLE_32BIT;
}
else {
snd_printk("Requested format %d is not supported\n", runtime->format);
return -1;
}
/* buffer and period sizes in frame */
pcm_play_ctrl.bufferPhyBase = audio_stream->dma_addr;
pcm_play_ctrl.bufferSize = audio_stream->dma_size;
pcm_play_ctrl.intByteCount = audio_stream->period_size;
/* I2S playback streem stuff */
/*i2s_play_ctrl.sampleSize = pcm_play_ctrl.sampleSize;*/
i2s_play_ctrl.justification = I2S_JUSTIFIED;
i2s_play_ctrl.sendLastFrame = 0;
spdif_play_ctrl.nonPcm = MV_FALSE;
spdif_play_ctrl.validity = chip->ch_stat_valid;
if (audio_stream->stat_mem) {
spdif_play_ctrl.userBitsFromMemory = MV_TRUE;
spdif_play_ctrl.validityFromMemory = MV_TRUE;
spdif_play_ctrl.blockStartInternally = MV_FALSE;
} else {
spdif_play_ctrl.userBitsFromMemory = MV_FALSE;
spdif_play_ctrl.validityFromMemory = MV_FALSE;
spdif_play_ctrl.blockStartInternally = MV_TRUE;
}
/* If this is non-PCM sound, mute I2S channel */
spin_lock_irq(&chip->reg_lock);
if (!(mv88fx_snd_readl(chip, MV_AUDIO_PLAYBACK_CTRL_REG(0)) &
(APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK))) {
if (MV_OK != mvAudioDCOCtrlSet(0, &dco_ctrl)) {
snd_printk("Failed to initialize DCO clock control.\n");
return -1;
}
}
if (audio_stream->clock_src == DCO_CLOCK)
while ((mv88fx_snd_readl(chip, MV_AUDIO_SPCR_DCO_STATUS_REG(0)) &
ASDSR_DCO_LOCK_MASK) == 0);
else if (audio_stream->clock_src == SPCR_CLOCK)
while ((mv88fx_snd_readl(chip, MV_AUDIO_SPCR_DCO_STATUS_REG(0)) &
ASDSR_SPCR_LOCK_MASK) == 0);
if (MV_OK != mvAudioPlaybackControlSet(0, &pcm_play_ctrl)) {
snd_printk("Failed to initialize PCM playback control.\n");
return -1;
}
if (MV_OK != mvI2SPlaybackCtrlSet(0, &i2s_play_ctrl)) {
snd_printk("Failed to initialize I2S playback control.\n");
return -1;
}
mvSPDIFPlaybackCtrlSet(0, &spdif_play_ctrl);
spin_unlock_irq(&chip->reg_lock);
#if 0
mv88fx_snd_debug("runtime->xfer_align=%d\n",(int)runtime->xfer_align);
mv88fx_snd_debug("runtime->format=%d\n",runtime->format);
mv88fx_snd_debug("runtime->channels=%d\n",runtime->channels);
mv88fx_snd_debug("runtime->rate=%d\n",runtime->rate);
mv88fx_snd_debug("runtime->dma_addr=0x%x\n",runtime->dma_addr);
mv88fx_snd_debug("runtime->dma_area=0x%x\n",(unsigned int)runtime->dma_area);
mv88fx_snd_debug("runtime->frame_bits=0x%x\n",runtime->frame_bits);
mv88fx_snd_debug("runtime->period_size=0x%x\n",
(unsigned int)runtime->period_size);
mv88fx_snd_debug("runtime->buffer_size=0x%x\n",
(unsigned int)runtime->buffer_size);
mv88fx_snd_debug("bufferPhyBase=0x%x\n",runtime->dma_addr);
mv88fx_snd_debug("bufferSize=0x%x\n",pcm_play_ctrl.bufferSize);
mv88fx_snd_debug("intByteCount=0x%x.\n",pcm_play_ctrl.intByteCount);
#endif
return 0;
}
int mv88fx_snd_hw_capture_set(struct mv88fx_snd_chip *chip)
{
struct mv88fx_snd_stream *audio_stream =
chip->stream[CAPTURE];
struct snd_pcm_substream *substream = audio_stream->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
MV_AUDIO_RECORD_CTRL pcm_rec_ctrl;
MV_I2S_RECORD_CTRL i2s_rec_ctrl;
MV_AUDIO_FREQ_DATA dco_ctrl;
dco_ctrl.offset = chip->dco_ctrl_offst;
switch(audio_stream->rate) {
case 44100:
dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH;
break;
case 48000:
dco_ctrl.baseFreq = AUDIO_FREQ_48KH;
break;
case 96000:
dco_ctrl.baseFreq = AUDIO_FREQ_96KH;
break;
default:
snd_printk("Requested rate %d is not supported\n",
runtime->rate); return -1;
}
pcm_rec_ctrl.burst = (chip->burst == 128)?AUDIO_128BYTE_BURST:
AUDIO_32BYTE_BURST;
if (audio_stream->format == SAMPLE_16IN16) {
pcm_rec_ctrl.sampleSize = SAMPLE_16BIT;
}
else if (audio_stream->format == SAMPLE_24IN32) {
pcm_rec_ctrl.sampleSize = SAMPLE_24BIT;
}
else if (audio_stream->format == SAMPLE_32IN32) {
pcm_rec_ctrl.sampleSize = SAMPLE_32BIT;
}
else {
snd_printk("Requested format %d is not supported\n", runtime->format);
return -1;
}
pcm_rec_ctrl.mono = (audio_stream->stereo) ? MV_FALSE : MV_TRUE;
if (pcm_rec_ctrl.mono) {
switch (audio_stream->mono_mode) {
case MONO_LEFT:
pcm_rec_ctrl.monoChannel = AUDIO_REC_LEFT_MONO;
break;
default:
case MONO_RIGHT:
pcm_rec_ctrl.monoChannel = AUDIO_REC_RIGHT_MONO;
break;
}
}
else pcm_rec_ctrl.monoChannel = AUDIO_REC_LEFT_MONO;
pcm_rec_ctrl.bufferPhyBase = audio_stream->dma_addr;
pcm_rec_ctrl.bufferSize = audio_stream->dma_size;
pcm_rec_ctrl.intByteCount = audio_stream->period_size;
/* I2S record streem stuff */
i2s_rec_ctrl.sample = pcm_rec_ctrl.sampleSize;
i2s_rec_ctrl.justf = I2S_JUSTIFIED;
spin_lock_irq(&chip->reg_lock);
/* set clock only if record is not enabled*/
if (!(mv88fx_snd_readl(chip, MV_AUDIO_RECORD_CTRL_REG(0)) &
(ARCR_RECORD_SPDIF_EN_MASK | ARCR_RECORD_I2S_EN_MASK))) {
if (MV_OK != mvAudioDCOCtrlSet(0, &dco_ctrl)) {
snd_printk("Failed to initialize DCO clock control.\n");
return -1;
}
}
if (MV_OK != mvAudioRecordControlSet(0, &pcm_rec_ctrl)) {
snd_printk("Failed to initialize PCM record control.\n");
return -1;
}
if (MV_OK != mvI2SRecordCntrlSet(0, &i2s_rec_ctrl)) {
snd_printk("Failed to initialize I2S record control.\n");
return -1;
}
spin_unlock_irq(&chip->reg_lock);
#if 0
mv88fx_snd_debug("pcm_rec_ctrl.bufferSize=0x%x\n",(int)pcm_rec_ctrl.bufferSize);
mv88fx_snd_debug("pcm_rec_ctrl.intByteCount=0x%x\n",(int)pcm_rec_ctrl.intByteCount);
mv88fx_snd_debug("runtime->xfer_align=%d\n",(int)runtime->xfer_align);
mv88fx_snd_debug("runtime->format=%d\n",runtime->format);
mv88fx_snd_debug("runtime->channels=%d\n",runtime->channels);
mv88fx_snd_debug("runtime->rate=%d\n",runtime->rate);
mv88fx_snd_debug("runtime->dma_addr=0x%x\n",runtime->dma_addr);
mv88fx_snd_debug("runtime->dma_area=0x%x\n",(unsigned int)runtime->dma_area);
mv88fx_snd_debug("runtime->frame_bits=0x%x\n",runtime->frame_bits);
mv88fx_snd_debug("runtime->period_size=0x%x\n",
(unsigned int)runtime->period_size);
mv88fx_snd_debug("runtime->buffer_size=0x%x\n",
(unsigned int)runtime->buffer_size);
mv88fx_snd_debug("bufferPhyBase=0x%x\n",runtime->dma_addr);
#endif
return 0;
}