210 lines
5.8 KiB
C
210 lines
5.8 KiB
C
/*
|
|
* arch/sh/boards/mach-st/mb705-audio.c
|
|
*
|
|
* Copyright (C) 2008 STMicroelectronics Limited
|
|
* Author: Pawel Moll <pawel.moll@st.com>
|
|
*
|
|
* May be copied or modified under the terms of the GNU General Public
|
|
* License. See linux/COPYING for more information.
|
|
*
|
|
* WARNING! This driver (so far) supports only mb705+mb680 duet.
|
|
* (SW2[1..4] should be OFF)
|
|
* In that case (mb705+mb680) audio outputs layout presents as follows:
|
|
*
|
|
* +--------------------------------------+
|
|
* | |
|
|
* | (S.I) (---) (---) (0.5) (0.6) | TOP
|
|
* | |
|
|
* | (---) (---) (---) (0.3) (0.4) |
|
|
* | |
|
|
* | (S.O) (---) (---) (0.1) (0.2) | BOTTOM
|
|
* | |
|
|
* +--------------------------------------+
|
|
* CN5 CN4 CN3 CN2 CN1
|
|
*
|
|
* where:
|
|
* - S.I - SPDIF input - PCM Reader #0
|
|
* - S.O - SPDIF output - SPDIF Player (HDMI)
|
|
* - 0.1-6 - audio outputs - PCM Player #0, channels 1 to 6
|
|
* (PCM Player #0 has 8-channels output, however only 3 pairs
|
|
* are available on pads)
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/stm/platform.h>
|
|
#include <linux/stm/stx7105.h>
|
|
#include <sound/stm.h>
|
|
#include <mach/common.h>
|
|
#include "mb705-epld.h"
|
|
|
|
struct mb705_audio_conv {
|
|
struct snd_stm_conv_converter *conv;
|
|
|
|
unsigned int reset_mask;
|
|
|
|
unsigned int mute_offset;
|
|
unsigned int mute_mask;
|
|
};
|
|
|
|
static struct mb705_audio_conv mb705_audio_spdif_input = {
|
|
.reset_mask = EPLD_AUDIO_RESET_SPDIFIN,
|
|
};
|
|
|
|
static struct mb705_audio_conv mb705_audio_8ch_dac = {
|
|
.reset_mask = EPLD_AUDIO_RESET_AUDDAC2,
|
|
.mute_offset = EPLD_AUDIO_DAC2,
|
|
.mute_mask = EPLD_AUDIO_DAC2_SMUTE,
|
|
};
|
|
|
|
|
|
|
|
static unsigned int mb705_audio_get_format(void *priv)
|
|
{
|
|
return SND_STM_FORMAT__I2S | SND_STM_FORMAT__SUBFRAME_32_BITS;
|
|
}
|
|
|
|
static int mb705_audio_get_oversampling(void *priv)
|
|
{
|
|
return 256;
|
|
}
|
|
|
|
static int mb705_audio_set_enabled(int enabled, void *priv)
|
|
{
|
|
struct mb705_audio_conv *data = priv;
|
|
unsigned int value = epld_read(EPLD_AUDIO_RESET);
|
|
|
|
if (enabled)
|
|
value &= ~data->reset_mask;
|
|
else
|
|
value |= data->reset_mask;
|
|
|
|
epld_write(value, EPLD_AUDIO_RESET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mb705_audio_set_muted(int muted, void *priv)
|
|
{
|
|
struct mb705_audio_conv *data = priv;
|
|
unsigned int value = epld_read(data->mute_offset);
|
|
|
|
if (muted)
|
|
value |= data->mute_mask;
|
|
else
|
|
value &= ~data->mute_mask;
|
|
|
|
epld_write(value, data->mute_offset);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct snd_stm_conv_ops mb705_audio_enable_ops = {
|
|
.get_format = mb705_audio_get_format,
|
|
.get_oversampling = mb705_audio_get_oversampling,
|
|
.set_enabled = mb705_audio_set_enabled,
|
|
};
|
|
|
|
static struct snd_stm_conv_ops mb705_audio_enable_mute_ops = {
|
|
.get_format = mb705_audio_get_format,
|
|
.get_oversampling = mb705_audio_get_oversampling,
|
|
.set_enabled = mb705_audio_set_enabled,
|
|
.set_muted = mb705_audio_set_muted,
|
|
};
|
|
|
|
|
|
|
|
static int __init mb705_audio_init(void)
|
|
{
|
|
int pcm_reader, pcm_player;
|
|
static char *pcm_reader_bus_id = "snd_pcm_reader.x";
|
|
static char *pcm_player_bus_id = "snd_pcm_player.x";
|
|
|
|
/* To be defined in processor board, which knows what SOC is there... */
|
|
mbxxx_configure_audio_pins(&pcm_reader, &pcm_player);
|
|
|
|
if (pcm_reader < 0)
|
|
sprintf(pcm_reader_bus_id, "snd_pcm_reader");
|
|
else if (pcm_reader < 10)
|
|
sprintf(pcm_reader_bus_id, "snd_pcm_reader.%d", pcm_reader);
|
|
else
|
|
BUG();
|
|
|
|
if (pcm_player < 0)
|
|
sprintf(pcm_player_bus_id, "snd_pcm_player");
|
|
else if (pcm_player < 10)
|
|
sprintf(pcm_player_bus_id, "snd_pcm_player.%d", pcm_player);
|
|
else
|
|
BUG();
|
|
|
|
/* Check the SPDIF test mode */
|
|
if ((epld_read(EPLD_AUDIO_SWITCH2) & (EPLD_AUDIO_SWITCH1_SW21 |
|
|
EPLD_AUDIO_SWITCH1_SW22 | EPLD_AUDIO_SWITCH1_SW23 |
|
|
EPLD_AUDIO_SWITCH1_SW24)) == 0) {
|
|
printk(KERN_WARNING "WARNING! MB705 is in audio test mode!\n");
|
|
printk(KERN_WARNING "You won't hear any generated sound!\n");
|
|
}
|
|
|
|
/* Disable (enable reset) all converters */
|
|
|
|
epld_write(EPLD_AUDIO_RESET_AUDDAC0 | EPLD_AUDIO_RESET_AUDDAC1 |
|
|
EPLD_AUDIO_RESET_AUDDAC2 | EPLD_AUDIO_RESET_SPDIFIN |
|
|
EPLD_AUDIO_RESET_SPDIFOUT, EPLD_AUDIO_RESET);
|
|
|
|
/* Configure and register SPDIF-I2S converter (CS8416, IC5) */
|
|
|
|
epld_write(EPLD_AUDIO_SPDIFIN_C | EPLD_AUDIO_SPDIFIN_RCBL,
|
|
EPLD_AUDIO_SPDIFIN);
|
|
mb705_audio_spdif_input.conv = snd_stm_conv_register_converter("SPDIF"
|
|
" Input", &mb705_audio_enable_ops,
|
|
&mb705_audio_spdif_input, &platform_bus_type,
|
|
pcm_reader_bus_id, 0, 1, NULL);
|
|
if (!mb705_audio_spdif_input.conv) {
|
|
printk(KERN_ERR "%s:%u: Can't register SPDIF Input converter!"
|
|
"\n", __FILE__, __LINE__);
|
|
goto error;
|
|
}
|
|
|
|
/* Configure and register 8-channels external DAC (AK4359, IC1) */
|
|
|
|
epld_write(EPLD_AUDIO_DAC2_DIF0 | EPLD_AUDIO_DAC2_DIF1 |
|
|
EPLD_AUDIO_DAC2_SMUTE | EPLD_AUDIO_DAC2_ACKS |
|
|
EPLD_AUDIO_DAC2_DEM0 | EPLD_AUDIO_DAC2_PNOTS,
|
|
EPLD_AUDIO_DAC2);
|
|
mb705_audio_8ch_dac.conv = snd_stm_conv_register_converter("External "
|
|
"8-channels DAC", &mb705_audio_enable_mute_ops,
|
|
&mb705_audio_8ch_dac, &platform_bus_type,
|
|
pcm_player_bus_id, 0, 7, NULL);
|
|
if (!mb705_audio_8ch_dac.conv) {
|
|
printk(KERN_ERR "%s:%u: Can't register external 8-channels "
|
|
"DAC!\n", __FILE__, __LINE__);
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (mb705_audio_8ch_dac.conv)
|
|
snd_stm_conv_unregister_converter(mb705_audio_8ch_dac.conv);
|
|
if (mb705_audio_spdif_input.conv)
|
|
snd_stm_conv_unregister_converter(mb705_audio_spdif_input.conv);
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void __exit mb705_audio_exit(void)
|
|
{
|
|
/* Disable all converters, just to be sure ;-) */
|
|
|
|
epld_write(EPLD_AUDIO_RESET_AUDDAC0 | EPLD_AUDIO_RESET_AUDDAC1 |
|
|
EPLD_AUDIO_RESET_AUDDAC2 | EPLD_AUDIO_RESET_SPDIFIN |
|
|
EPLD_AUDIO_RESET_SPDIFOUT, EPLD_AUDIO_RESET);
|
|
|
|
/* Unregister converters */
|
|
|
|
snd_stm_conv_unregister_converter(mb705_audio_8ch_dac.conv);
|
|
snd_stm_conv_unregister_converter(mb705_audio_spdif_input.conv);
|
|
}
|
|
|
|
module_init(mb705_audio_init);
|
|
module_exit(mb705_audio_exit);
|