From: Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.com>
To: Mark Brown <broonie@opensource.wolfsonmicro.com>,
Takashi Iwai <tiwai@suse.de>
Cc: perex@perex.cz, linux-kernel@vger.kernel.org, "Wang,
Qi" <qi.wang@intel.com>, "Wang, Yong Y" <yong.y.wang@intel.com>,
"Clark, Joel" <joel.clark@intel.com>,
"Ewe, Kok Howg" <kok.howg.ewe@intel.com>,
Liam Girdwood <lrg@ti.com>,
alsa-devel@alsa-project.org
Subject: Re: [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH
Date: Tue, 08 Nov 2011 18:03:53 +0900 [thread overview]
Message-ID: <4EB8F079.9080703@dsn.lapis-semi.com> (raw)
In-Reply-To: <20111024122015.GA26033@opensource.wolfsonmicro.com>
[-- Attachment #1: Type: text/plain, Size: 1246 bytes --]
(2011/10/24 21:20), Mark Brown wrote:
> On Mon, Oct 24, 2011 at 09:12:42PM +0900, Tomoya MORINAGA wrote:
>
>> 1. PCI interface function.
>> Any current ASoC drivers don't have PCI interface function.
>> So I don't know where the function should be in machine driver or
>> platform driver.
>
> It depends on what the driver is for. Probably you want a driver which
> is some combination of machine driver and the various drivers that are
> normally part of the SoC - whatever roles in the system are filled by
> this hardware the driver ought to register subsystem drivers for those
> roles.
>
>> 2. Register Access
>> Can platform driver access register ?
>> According to the soc document, platform driver must not access hardware,
>> however, some drivers looks accessing their hardware.
>
> What makes you say this? A driver that can't access hardware would be
> rather useless...
>
>
Though roughly, we re-created 3 files codec driver, platform drive and
machine driver. (Not debugged/tested)
Could you check these files ?
(1)codec driver
ml26124.c ml26124.h
(2)platform driver
ml7213ioh-plat.c ml7213ioh-plat.h ioh_i2s_config.h ioh_i2s.h
(3)machine driver
ml7213ioh-machine.c
Thanks in advance.
--
tomoya
ROHM Co., Ltd.
[-- Attachment #2: ml26124.c --]
[-- Type: text/plain, Size: 19969 bytes --]
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include "ml26124.h"
struct cbdata {
struct ioh_i2s_data *priv;
int stop;
int cnt;
};
struct snd_ml7213i2s_pcm {
enum snd_soc_control_type control_type;
struct snd_ml7213i2s *ml7213i2s;
spinlock_t lock;
unsigned int irq_pos;
unsigned int buf_pos;
struct snd_pcm_substream *substream;
struct cbdata cbd; /* i2s callback info */
unsigned int channels;
unsigned int rw;
unsigned int rate;
unsigned int ch;
unsigned int setup_flag;
unsigned int format;
unsigned int bclkfs;
struct mutex i2c_mutex;
};
/*
* wm8731 register cache
* We can't read the WM8731 register space when we are
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
0x0097, 0x0097, 0x0079, 0x0079,
0x000a, 0x0008, 0x009f, 0x000a,
0x0000, 0x0000
};
#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0)
static const char *ml26124_lrmcon[] = {"Use L", "Use R", "Use (L+R)", "Use (L+R)/2"};
static const char *ml26124_dvfconcon[] = {"1/fs=20.8us", "2/fs", "4/fs", "8/fs",
"16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs", "2048/fs",
"4096/fs", "8192/fs", "16384/fs=341msec"};
static const char *ml26124_hpf2cut[] = {"FS=8n/11.025n/16n = 80/110/120",
"100/138/150", "130/179/195",
"160/221/240", "200/276/300",
"260/358/390", "320/441/480",
"400/551/600"};
static const char *ml26124_alcmode[] = {"ALC mode", "Limitter mode"};
static const char *ml26124_ngtyp[] = {"Gain hold mode", "Mute mode"};
static const char *ml26124_alcatk[] = {"ALC mode=4/fs Limiter mode=ALC mode/4",
"8/fs", "16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs",
"2048/fs", "4096/fs", "8192/fs", "16384", "32768", "65536", "131072"};
static const char *ml26124_alcdcy[] = {"Limitter mode=4/fs ALC mode=Limitter mode * 4",
"8/fs", "16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs",
"2048/fs", "4096/fs", "8192/fs", "16384", "32768", "65536", "131072"};
static const char *ml26124_alchld[] = {"128/fs", "256/fs", "512/fs", "1024/fs",
"2048/fs", "4096/fs", "8192/fs", "16384/fs", "32768/fs", "65536/fs", "131072/fs",
"262144/fs", "524288/fs", "1048576/fs", "2097152/fs"};
static const char *ml26124_alczctm[] = {"128/fs", "256/fs", "512/fs", "1024/fs"};
static const char *ml26124_platk[] = {"1/fs=20.8us", "2/fs", "4/fs", "8/fs",
"16/fs", "32/fs", "64/fs", "128/fs", "256/fs", "512/fs", "1024/fs", "2048/fs",
"4096/fs", "8192/fs", "16384/fs=341msec", "32768/fs"};
static const char *ml26124_pldcy[] = {"4/fs", "8/fs", "16/fs", "32/fs", "64/fs",
"128/fs", "256/fs", "512/fs", "1024/fs", "2048/fs", "4096/fs", "8192/fs",
"16384", "32768", "65536", "131072"};
static const char *ml26124_plzctm[] = {"128/fs", "256/fs", "512/fs", "1024/fs"};
static const char *ml26124_input_select[] = {"Analog MIC-in", "Digital MIC-in"};
static const struct soc_enum ml26124_enum[] = {
/* #define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) */
SOC_ENUM_SINGLE(ML26124_MIXER_VOL_CTL, 0, 4, ml26124_lrmcon),
SOC_ENUM_SINGLE(ML26124_MIXER_VOL_CTL, 4, 15, ml26124_dvfconcon),
SOC_ENUM_SINGLE(ML26124_HPF2_CUTOFF, 0, 8, ml26124_hpf2cut),
SOC_ENUM_SINGLE(ML26124_ALC_MODE, 0, 2, ml26124_alcmode),
SOC_ENUM_SINGLE(ML26124_ALC_MODE, 2, 2, ml26124_ngtyp),
SOC_ENUM_SINGLE(ML26124_ALC_ATTACK_TIM, 0, 16, ml26124_alcatk),
SOC_ENUM_SINGLE(ML26124_ALC_DECAY_TIM, 0, 16, ml26124_alcdcy),
SOC_ENUM_SINGLE(ML26124_ALC_HOLD_TIM, 0, 16, ml26124_alchld),
SOC_ENUM_SINGLE(ML26124_ALC_ZERO_TIMOUT, 0, 4, ml26124_alczctm),
SOC_ENUM_SINGLE(ML26124_PL_ATTACKTIME, 0, 16, ml26124_platk),
SOC_ENUM_SINGLE(ML26124_PL_DECAYTIME, 0, 16, ml26124_pldcy),
SOC_ENUM_SINGLE(ML26124_PL_0CROSS_TIMOUT, 0, 4, ml26124_plzctm),
SOC_ENUM_SINGLE(ML26124_MIC_IF_CTL, 0, 1, ml26124_input_select),
};
/* ML26124 configuration */
static const DECLARE_TLV_DB_SCALE(rec_play_digi_vol, -7150, 50, 0);
static const DECLARE_TLV_DB_SCALE(digi_boost_vol, -1200, 75, 0);
static const DECLARE_TLV_DB_SCALE(eq_band_gain, -7150, 50, 0);
static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0);
static const DECLARE_TLV_DB_SCALE(alcmingain, -1200, 600, 0);
static const DECLARE_TLV_DB_SCALE(alcmaxgain, -675, 600, 0);
static const DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
static const DECLARE_TLV_DB_SCALE(plilv, -2250, 150, 0);
static const DECLARE_TLV_DB_SCALE(plmingain, -1200, 600, 0);
static const DECLARE_TLV_DB_SCALE(plmaxgain, -675, 600, 0);
static const DECLARE_TLV_DB_SCALE(plvl, -1200, 75, 0);
static const struct snd_kcontrol_new ml26124_snd_controls[] = {
SOC_SINGLE_TLV("Record Digital Volume", ML26124_RECORD_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0, 0xff, 1, rec_play_digi_vol),
SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0, 0x3f, 0, digi_boost_vol),
SOC_SINGLE_TLV("EQ Band0 Gain Setting", ML26124_EQ_GAIN_BRAND0, 0, 0xff, 1, eq_band_gain),
SOC_SINGLE_TLV("EQ Band1 Gain Setting", ML26124_EQ_GAIN_BRAND1, 0, 0xff, 1, eq_band_gain),
SOC_SINGLE_TLV("EQ Band2 Gain Setting", ML26124_EQ_GAIN_BRAND2, 0, 0xff, 1, eq_band_gain),
SOC_SINGLE_TLV("EQ Band3 Gain Setting", ML26124_EQ_GAIN_BRAND3, 0, 0xff, 1, eq_band_gain),
SOC_SINGLE_TLV("EQ Band4 Gain Setting", ML26124_EQ_GAIN_BRAND4, 0, 0xff, 1, eq_band_gain),
SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0, 0xf, 1, alclvl),
SOC_SINGLE_TLV("ALC Min Gain Control", ML26124_ALC_MAXMIN_GAIN, 0, 7, 0, alcmingain),
SOC_SINGLE_TLV("ALC MAX Gain Control", ML26124_ALC_MAXMIN_GAIN, 4, 7, 1, alcmaxgain),
SOC_SINGLE_TLV("Noise Gate Threshold", ML26124_NOIS_GATE_THRSH, 0, 0x1f, 0, ngth),
SOC_SINGLE_TLV("Playback Limitter Target Level", ML26124_PL_TARGETTIME, 0, 0xf, 1, plilv),
SOC_SINGLE_TLV("Playback Limitter Min Gain", ML26124_PL_MAXMIN_GAIN, 0, 7, 0, plmingain),
SOC_SINGLE_TLV("Playback Limitter Max Gain", ML26124_PL_MAXMIN_GAIN, 4, 7, 1, plmaxgain),
SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0, 0x3f, 0, plvl),
};
static const struct snd_kcontrol_new ml26124_dsp_controls[] = {
SOC_SINGLE("Play Limitter ON/OFF", ML26124_FILTER_EN, 0, 1, 0),
SOC_SINGLE("Record ALC ON/OFF", ML26124_FILTER_EN, 1, 1, 0),
SOC_SINGLE("Digital Volume Fade ON/OFF", ML26124_FILTER_EN, 3, 1, 0),
SOC_SINGLE("Ditital Volume MUTE", ML26124_FILTER_EN, 4, 1, 0),
SOC_SINGLE("Set ALC position", ML26124_FILTER_EN, 5, 1, 0),
SOC_SINGLE("Noise Gate ON/OFF", ML26124_ALC_MODE, 1, 1, 0),
};
static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = {
SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_MICBIAS("MICBIAS", ML26124_PW_REF_PW_MNG, 0, 0),
SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0),
SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0),
SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_LINE("LINEOUT", NULL),
SND_SOC_DAPM_INPUT("VIDEOIN"),
SND_SOC_DAPM_INPUT("MDIN"),
SND_SOC_DAPM_INPUT("MIN"),
SND_SOC_DAPM_INPUT("LIN"),
SND_SOC_DAPM_INPUT("SDIN"),
SND_SOC_DAPM_OUTPUT("VIDEOOUT"),
SND_SOC_DAPM_OUTPUT("SPOUT"),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("SDOUT"),
};
#define CODEC_DEV_ADDR (0x1A)
static struct i2c_board_info ioh_hwmon_info[] = {
{I2C_BOARD_INFO("ioh_i2c-0", CODEC_DEV_ADDR + 1)},
{I2C_BOARD_INFO("ioh_i2c-1", CODEC_DEV_ADDR + 2)},
{I2C_BOARD_INFO("ioh_i2c-2", CODEC_DEV_ADDR + 3)},
{I2C_BOARD_INFO("ioh_i2c-3", CODEC_DEV_ADDR + 4)},
{I2C_BOARD_INFO("ioh_i2c-4", CODEC_DEV_ADDR + 5)},
{I2C_BOARD_INFO("ioh_i2c-5", CODEC_DEV_ADDR + 0)},
};
static const struct snd_soc_dapm_route intercon[] = {
};
static int snd_card_codec_reg_read(struct snd_ml7213i2s_pcm *priv,
unsigned char reg, unsigned char *val)
{
unsigned char data;
struct i2c_client *i2c;
mutex_lock(&priv->i2c_mutex);
i2c = i2c_new_device(i2c_get_adapter(1),
&ioh_hwmon_info[priv->substream->number]);
if (!i2c) {
mutex_unlock(&priv->i2c_mutex);
return -1;
}
if (i2c_master_send(i2c, ®, 1) != 1) {
mutex_unlock(&priv->i2c_mutex);
return -1;
}
if (i2c_master_recv(i2c, &data, 1) != 1) {
mutex_unlock(&priv->i2c_mutex);
return -1;
}
*val = data;
i2c_unregister_device(i2c);
mutex_unlock(&priv->i2c_mutex);
return 0;
}
static int snd_card_codec_reg_write(struct snd_ml7213i2s_pcm *priv,
unsigned char reg, unsigned char val)
{
unsigned char buf[2] = {(reg|1), val};
struct i2c_client *i2c;
mutex_lock(&priv->i2c_mutex);
i2c = i2c_new_device(i2c_get_adapter(1),
&ioh_hwmon_info[priv->substream->number]);
if (i2c_master_send(i2c, &buf[0], 2) != 2) {
mutex_unlock(&priv->i2c_mutex);
return -1;
}
i2c_unregister_device(i2c);
mutex_unlock(&priv->i2c_mutex);
return 0;
}
static int snd_card_codec_set(int number, struct snd_ml7213i2s_pcm *priv)
{
unsigned char data;
unsigned int rate = priv->rate;
unsigned int channels = priv->ch;
snd_card_codec_reg_read(priv, 0x30, &data); /* Read MICVIAS Voltage */
snd_card_codec_reg_write(priv, 0x10, 0x01); /* soft reset assert */
snd_card_codec_reg_write(priv, 0x10, 0x00); /* soft reset negate */
snd_card_codec_reg_write(priv, 0x0c, 0x00); /* Stop clock */
switch (rate) {
case 16000:
snd_card_codec_reg_write(priv, 0x00, 0x03);
snd_card_codec_reg_write(priv, 0x02, 0x0c);
snd_card_codec_reg_write(priv, 0x04, 0x00);
snd_card_codec_reg_write(priv, 0x06, 0x20);
snd_card_codec_reg_write(priv, 0x08, 0x00);
snd_card_codec_reg_write(priv, 0x0a, 0x04);
break;
case 32000:
snd_card_codec_reg_write(priv, 0x00, 0x06);
snd_card_codec_reg_write(priv, 0x02, 0x0c);
snd_card_codec_reg_write(priv, 0x04, 0x00);
snd_card_codec_reg_write(priv, 0x06, 0x20);
snd_card_codec_reg_write(priv, 0x08, 0x00);
snd_card_codec_reg_write(priv, 0x0a, 0x04);
break;
case 48000:
snd_card_codec_reg_write(priv, 0x00, 0x08);
snd_card_codec_reg_write(priv, 0x02, 0x0c);
snd_card_codec_reg_write(priv, 0x04, 0x00);
snd_card_codec_reg_write(priv, 0x06, 0x30);
snd_card_codec_reg_write(priv, 0x08, 0x00);
snd_card_codec_reg_write(priv, 0x0a, 0x04);
break;
default:
pr_err("%s:this rate is no support for ml26124\n", __func__);
break;
}
snd_card_codec_reg_write(priv, 0x0c, 0x03); /* Start MCLK and PLL */
msleep(20);
snd_card_codec_reg_write(priv, 0x0c, 0x0f); /* Clock control: MCLKI use */
snd_card_codec_reg_write(priv, 0x0e, 0x04);
if (channels == 1) {
snd_card_codec_reg_write(priv, 0x60, 0x23); /* SAI transmitter control */
/* 0x23 : FMTO=1, H=Left L=Right, no-delay*/
snd_card_codec_reg_write(priv, 0x62, 0x23); /* Receive side SAI control */
} else {
snd_card_codec_reg_write(priv, 0x60, 0x00);
snd_card_codec_reg_write(priv, 0x62, 0x00);
}
snd_card_codec_reg_write(priv, 0x64, 0x00); /* master/slave set slave */
snd_card_codec_reg_write(priv, 0x20, 0x02); /* VMID on. normal mode */
msleep(50);
snd_card_codec_reg_write(priv, 0x20, 0x06); /* Analog REference Power Managemet */
snd_card_codec_reg_write(priv, 0x22, 0x0a); /* Analog Input Power Management */
snd_card_codec_reg_write(priv, 0x24, 0x02); /* DAC power Management */
snd_card_codec_reg_write(priv, 0x26, 0x13);
snd_card_codec_reg_write(priv, 0x26, 0x1f); /* Speaker Amplified Poer Management */
snd_card_codec_reg_write(priv, 0x28, 0x02); /* LOUT Control Regsister */
snd_card_codec_reg_write(priv, 0x54, 0x02); /* Speaker Amplifier output Control */
snd_card_codec_reg_write(priv, 0x5a, 0x00); /* Mic Interface Control Register */
snd_card_codec_reg_write(priv, 0x12, 0x01); /* Record/Playback Running Control Register */
snd_card_codec_reg_write(priv, 0x12, 0x03);
msleep(20);
snd_card_codec_reg_write(priv, 0x66, 0x03); /* DSP filter function Enable */
snd_card_codec_reg_write(priv, 0x3a, 0x27); /* Speaker Amplifier Volume Control */
snd_card_codec_reg_write(priv, 0x32, 0x20); /* Mic Input Volume Control */
return 0;
}
static int ml26124_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct snd_ml7213i2s_pcm *priv = snd_soc_codec_get_drvdata(codec);
if (snd_card_codec_set(substream->number, priv))
return -1;
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int snd_card_codec_free(int number, struct snd_ml7213i2s_pcm *priv)
{
snd_card_codec_reg_write(priv, 0x10, 1); /* soft reset assert */
return 0;
}
static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_ml7213i2s_pcm *priv = substream->runtime->private_data;
if (snd_card_codec_free(substream->number, priv))
return -1;
return snd_pcm_lib_free_pages(substream);
}
static int ml26124_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return 0;
}
static void ml26124_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
}
#define ML26124_DVOL_CTL 0x68 /* Digital Volume Conotrol
Function Enable Register */
#define DVOL_CTL_DVMUTE_ON BIT(4) /* Digital volume MUTE On */
#define DVOL_CTL_DVMUTE_OFF 0 /* Digital volume MUTE Off */
static int ml26124_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
struct snd_ml7213i2s_pcm *priv = snd_soc_codec_get_drvdata(codec);
if (mute)
snd_card_codec_reg_write(priv, ML26124_DVOL_CTL,
DVOL_CTL_DVMUTE_ON);
else
snd_card_codec_reg_write(priv, ML26124_DVOL_CTL,
DVOL_CTL_DVMUTE_OFF);
return 0;
}
static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_ml7213i2s_pcm *priv = snd_soc_codec_get_drvdata(codec);
unsigned char mode;
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
mode = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
mode = 0;
break;
default:
return -EINVAL;
}
snd_card_codec_reg_write(priv, ML26124_SAI_MODE_SEL, mode);
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
break;
default:
return -EINVAL;
}
/* clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
default:
return -EINVAL;
}
return 0;
}
#define ML26124_REF_PM 0x20
#define REF_PM_MICBEN BIT(2) /* MIC BIAS Enable */
#define REF_PM_LOBIAS BIT(6) /* LOUT terminal BIAS Enable (1/2REGOUT) */
#define REF_PM_VMID_ON BIT(1) /* VMID generation circuit ON */
#define REF_PM_VMID_ON_FAST BIT(0) /* VMID generation circuit Fast mode ON */
#define REF_PM_VMID_OFF 0 /* VMID generation circuit OFF */
static int ml26124_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct snd_ml7213i2s_pcm *priv = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
snd_card_codec_reg_write(priv, ML26124_REF_PM, REF_PM_VMID_ON);
msleep(50);
snd_card_codec_reg_write(priv, ML26124_REF_PM,
REF_PM_VMID_ON | REF_PM_MICBEN);
break;
case SND_SOC_BIAS_OFF:
snd_card_codec_reg_write(priv, ML26124_REF_PM,
REF_PM_VMID_OFF | REF_PM_MICBEN);
break;
}
codec->bias_level = level;
return 0;
}
#define ML26124_RATES SNDRV_PCM_RATE_8000_96000
#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops ml26124_dai_ops = {
.prepare = ml26124_pcm_prepare,
.hw_params = ml26124_hw_params,
.hw_free = snd_card_ml7213i2s_hw_free,
.shutdown = ml26124_shutdown,
.digital_mute = ml26124_mute,
.set_fmt = ml26124_set_dai_fmt,
};
struct snd_soc_dai_driver ml26124_dai = {
.name = "ml26124-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = ML26124_RATES,
.formats = ML26124_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = ML26124_RATES,
.formats = ML26124_FORMATS,},
.ops = &ml26124_dai_ops,
.symmetric_rates = 1,
};
#ifdef CONFIG_PM
static int ml26124_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
ml26124_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
static int ml26124_resume(struct snd_soc_codec *codec)
{
ml26124_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
#else
#define ml26124_suspend NULL
#define ml26124_resume NULL
#endif
static int ml26124_probe(struct snd_soc_codec *codec)
{
snd_soc_add_controls(codec, ml26124_snd_controls,
ARRAY_SIZE(ml26124_snd_controls));
snd_soc_dapm_new_controls(codec, ml26124_dapm_widgets,
ARRAY_SIZE(ml26124_dapm_widgets));
return 0;
}
/* power down chip */
static int ml26124_remove(struct snd_soc_codec *codec)
{
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
.probe = ml26124_probe,
.remove = ml26124_remove,
.suspend = ml26124_suspend,
.resume = ml26124_resume,
.set_bias_level = ml26124_set_bias_level,
.reg_cache_size = ARRAY_SIZE(wm8731_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8731_reg,
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_ml7213i2s_pcm *priv;
int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, priv);
priv->control_type = SND_SOC_I2C;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ml26124, &ml26124_dai, 1);
if (ret < 0)
kfree(priv);
mutex_init(&priv->i2c_mutex);
return ret;
}
static __devexit int ml26124_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client));
return 0;
}
static const struct i2c_device_id ml26124_i2c_id[] = {
{ "ml26124", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
static struct i2c_driver ml26124_i2c_driver = {
.driver = {
.name = "ml26124-codec",
.owner = THIS_MODULE,
},
.probe = ml26124_i2c_probe,
.remove = __devexit_p(ml26124_i2c_remove),
.id_table = ml26124_i2c_id,
};
#endif
static int __init ml26124_modinit(void)
{
int ret = 0;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&ml26124_i2c_driver);
if (ret != 0) {
pr_err("Failed to register ML26124 I2C driver: %d\n", ret);
}
#endif
return ret;
}
module_init(ml26124_modinit);
static void __exit ml26124_exit(void)
{
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&ml26124_i2c_driver);
#endif
}
module_exit(ml26124_exit);
MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.com>");
MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver");
MODULE_LICENSE("GPL");
[-- Attachment #3: ioh_i2s.h --]
[-- Type: text/plain, Size: 3428 bytes --]
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ML7213_IOH_I2S
#define ML7213_IOH_I2S
#define I2S_AEMPTY_THRESH 64 /* Almost Empty Threshold */
#define I2S_AFULL_THRESH 64 /* Almost Full Threshold */
#define MAX_I2S_RX_CH MAX_I2S_CH
#define MAX_I2S_TX_CH MAX_I2S_CH
#define INTER_BUFF_SIZE (I2S_AEMPTY_THRESH*4 \
*I2S_DMA_SG_NUM \
*I2S_DMA_SG_MAX)
#define I2S_DMA_SG_NUM (128)
#define I2S_DMA_SG_MAX (64)
struct ioh_i2s_data;
enum ioh_direction {
IOH_PLAYBACK = 0,
IOH_CAPTURE,
};
enum ioh_i2s_fifo_type {
IOH_FIFO_32 = 4,
IOH_FIFO_16 = 2,
IOH_FIFO_8 = 1,
};
enum ioh_i2s_status {
IOH_EOK = 0,
IOH_EDONE = 1,
IOH_EUNDERRUN = 2,
IOH_EOVERRUN = 3,
IOH_EFRAMESYNC = 4,
};
struct ioh_i2s_config_common_reg {
u32 i2sclkcnt; /*clock control register(ch0~5) */
u32 i2sistatus; /*interrupt status */
u32 i2sidisp; /*active interrupts */
u32 i2simask; /*interrupt mask */
u32 i2simaskclr; /*interrupt mask clear */
};
struct ioh_i2s_config_tx_reg {
u32 i2sdrtx; /*data register */
u32 i2scnttx; /*control register */
u32 i2sfifoctx; /*FIFO control register */
u32 i2saftx; /*almost full threshold setting */
u32 i2saetx; /*almost empty threshold setting */
u32 i2smsktx; /*interrupt mask settings */
u32 i2sisttx; /*for acknowledging interrupts */
u32 i2smontx; /*monitor register */
};
struct ioh_i2s_config_rx_reg {
u32 i2sdrrx; /* data register */
u32 i2scntrx; /* control register */
u32 i2sfifocrx;/* FIFO control register */
u32 i2safrx; /* almost full threshold setting */
u32 i2saerx; /* almost empty threshold setting */
u32 i2smskrx; /* interrupt mask settings */
u32 i2sistrx; /* for acknowledging interrupts */
u32 i2smonrx; /* monitor register */
};
struct ioh_i2s_config_reg {
/* The common register settings */
struct ioh_i2s_config_common_reg cmn;
/* TX channel settings */
struct ioh_i2s_config_tx_reg tx;
/* RX channel settings */
struct ioh_i2s_config_rx_reg rx;
};
/* For power management save/retore use */
struct ioh_i2s_pm_common_reg {
u32 i2sclkcnt[6];
u32 i2simask;
};
struct ioh_i2s_pm_ch_reg {
u32 i2sdrtx; /* Tx: data register */
u32 i2scnttx; /* Tx: control register */
u32 i2sfifoctx; /* Tx: FIFO control register */
u32 i2saftx; /* Tx: almost full threshold setting */
u32 i2saetx; /* Tx: almost empty threshold setting */
u32 i2smsktx; /* Tx: interrupt mask settings */
u32 i2sisttx; /* Tx: for acknowledging interrupts */
u32 i2scntrx; /* Rx: control register */
u32 i2sfifocrx; /* Rx: FIFO control register */
u32 i2safrx; /* Rx: almost full threshold setting */
u32 i2saerx; /* Rx: almost empty threshold setting */
u32 i2smskrx; /* Rx: interrupt mask settings */
u32 i2sistrx; /* Rx: for acknowledging interrupts */
};
#endif
[-- Attachment #4: ioh_i2s_config.h --]
[-- Type: text/plain, Size: 64670 bytes --]
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ML7213_IOH_I2S_CONFIG
#define ML7213_IOH_I2S_CONFIG
#include "ioh_i2s.h"
#define IOH_I2S_USE_PARAM (1)
#define I2S_SUPPORT_FS_NUM (7)
#define PERIOD_POS_MAX (I2S_DMA_SG_NUM)
#define PERIOD_LEN_TX (I2S_AEMPTY_THRESH * PERIOD_POS_MAX)
#define PERIOD_LEN_RX (I2S_AFULL_THRESH * PERIOD_POS_MAX)
#define SUPPORT_FORMAT (SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define MAX_PERIOD_SIZE_TX (PERIOD_LEN_TX*4)
#define MAX_PERIOD_SIZE_RX (PERIOD_LEN_RX*4)
#if (PERIOD_LEN_TX < (I2S_AEMPTY_THRESH*I2S_DMA_SG_NUM))
#error IOH_I2S_CONFIG Error : PERIOD_LEN_TX is enlarged more.
#endif
#if (PERIOD_LEN_RX < (I2S_AFULL_THRESH*I2S_DMA_SG_NUM))
#error IOH_I2S_CONFIG Error : PERIOD_LEN_RX is enlarged more.
#endif
/* ######################################################################## */
/* ### Parameter setup possible value ### */
/* ######################################################################## */
/* ioh_mssel_t */
#define ioh_mssel_slave (0)
#define ioh_mssel_master (1)
enum ioh_bclkpol_t {
ioh_bclkpol_falling = 0,
ioh_bclkpol_rising,
};
enum ioh_masterclksel_t {
ioh_masterclksel_mclk = 0,
ioh_masterclksel_mlbclk,
};
enum ioh_lrckfmt_t {
ioh_lrclkfmt_i2s = 1,
ioh_lrclkfmt_longframe,
ioh_lrclkfmt_shortframe,
};
enum ioh_mclkfs_t {
ioh_mclkfs_64fs = 0,
ioh_mclkfs_128fs,
ioh_mclkfs_192fs,
ioh_mclkfs_256fs,
ioh_mclkfs_384fs,
ioh_mclkfs_512fs,
ioh_mclkfs_768fs,
ioh_mclkfs_1024fs,
};
/* ioh_dabit_t */
#define ioh_dabit_8bit (0)
#define ioh_dabit_16bit (2)
#define ioh_dabit_24bit (5)
/* ioh_bclkfs_t */
#define ioh_bclkfs_8fs (0)
#define ioh_bclkfs_16fs (1)
#define ioh_bclkfs_32fs (2)
#define ioh_bclkfs_64fs (3)
/* ioh_tel_t */
#define ioh_tel_i2s_fmt (0)
#define ioh_tel_tel_fmt (1)
enum ioh_dlyoff_t {
ioh_dlyoff_dly_on = 0, /* date delat on */
ioh_dlyoff_dly_off, /* date delat off */
};
/* ioh_lsb_t */
#define ioh_lsb_msb_first (0)
#define ioh_lsb_lsb_first (1)
enum ioh_lrpol_t {
ioh_lrpol_no_invert = 0, /* Low of LRCLK is L data.
High of LRCLK is R data. */
ioh_lrpol_invert, /* Low of LRCLK is R data.
High of LRCLK is L data. */
};
enum ioh_aft_t {
ioh_aft_front = 0,
ioh_aft_back,
};
struct i2s_config_tab_t {
unsigned int i2sclkcnt;
unsigned int i2scnttx;
unsigned int i2scntrx;
unsigned int i2s_mclk;
};
struct i2s_config_rate_sub_t {
unsigned int rate;
unsigned int mclkfs;
};
struct i2s_config_rate_t {
struct i2s_config_rate_sub_t i2s_config_rate_sub[I2S_SUPPORT_FS_NUM];
};
/* ######################################################################## */
/* ### Parameter Config PreProcessor ### */
/* ######################################################################## */
/* I2S Config Value */
#define USE_CHANNELS_MIN 1
#define USE_CHANNELS_MAX 2
#define MAX_I2S_CH 6 /*I2S0 ~ I2S5*/
/* =================== I2S CH0 config =================== */
#define I2S_CH0_MCLK (12288000) /* Master Clock Frequency[Hz] */
#if IOH_I2S_USE_PARAM
#define I2S_CH0_FS_16000 16000
#define I2S_CH0_FS_32000 32000
#define I2S_CH0_FS_48000 48000
#else
#define I2S_CH0_FS_8000 8000
#define I2S_CH0_FS_11025 11025
#define I2S_CH0_FS_22050 22050
#define I2S_CH0_FS_44100 44100
#endif
/* select master or slave. The value is ioh_mssel_t */
#define I2S_CH0_MSSEL (ioh_mssel_master)
/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
#define I2S_CH0_MASTERCLKSEL (ioh_masterclksel_mclk)
/* I2S CH0 stereo config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH0_BCLKPOL_STEREO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH0_LRCKFMT_STEREO (ioh_lrclkfmt_i2s)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH0_TX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH0_TX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
#define I2S_CH0_TX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of TX side. The value is ioh_aft_t */
#define I2S_CH0_TX_AFT_STEREO (ioh_aft_front)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH0_RX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH0_RX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
#define I2S_CH0_RX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of RX side. The value is ioh_aft_t */
#define I2S_CH0_RX_AFT_STEREO (ioh_aft_front)
/* I2S CH0 monaural config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH0_BCLKPOL_MONO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH0_LRCKFMT_MONO (ioh_lrclkfmt_longframe)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH0_TX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH0_TX_LSB_MONO (ioh_lsb_msb_first)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH0_RX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH0_RX_LSB_MONO (ioh_lsb_msb_first)
/* =================== I2S CH1 config =================== */
#define I2S_CH1_MCLK (12288000) /* Master Clock Frequency[Hz] */
#if IOH_I2S_USE_PARAM
#define I2S_CH1_FS_16000 16000
#define I2S_CH1_FS_32000 32000
#define I2S_CH1_FS_48000 48000
#else
#define I2S_CH1_FS_8000 8000
#define I2S_CH1_FS_11025 11025
#define I2S_CH1_FS_22050 22050
#define I2S_CH1_FS_44100 44100
#endif
/* select master or slave. The value is ioh_mssel_t */
#define I2S_CH1_MSSEL (ioh_mssel_master)
/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
#define I2S_CH1_MASTERCLKSEL (ioh_masterclksel_mclk)
/* I2S CH1 stereo config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH1_BCLKPOL_STEREO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH1_LRCKFMT_STEREO (ioh_lrclkfmt_i2s)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH1_TX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH1_TX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
#define I2S_CH1_TX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of TX side. The value is ioh_aft_t */
#define I2S_CH1_TX_AFT_STEREO (ioh_aft_front)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH1_RX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH1_RX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
#define I2S_CH1_RX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of RX side. The value is ioh_aft_t */
#define I2S_CH1_RX_AFT_STEREO (ioh_aft_front)
/* I2S CH1 monaural config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH1_BCLKPOL_MONO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH1_LRCKFMT_MONO (ioh_lrclkfmt_longframe)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH1_TX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH1_TX_LSB_MONO (ioh_lsb_msb_first)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH1_RX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH1_RX_LSB_MONO (ioh_lsb_msb_first)
/* =================== I2S CH2 config =================== */
#define I2S_CH2_MCLK (12288000) /* Master Clock Frequency[Hz] */
#if IOH_I2S_USE_PARAM
#define I2S_CH2_FS_16000 16000
#define I2S_CH2_FS_32000 32000
#define I2S_CH2_FS_48000 48000
#else
#define I2S_CH2_FS_8000 8000
#define I2S_CH2_FS_11025 11025
#define I2S_CH2_FS_22050 22050
#define I2S_CH2_FS_44100 44100
#endif
/* select master or slave. The value is ioh_mssel_t */
#define I2S_CH2_MSSEL (ioh_mssel_master)
/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
#define I2S_CH2_MASTERCLKSEL (ioh_masterclksel_mclk)
/* I2S CH2 stereo config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH2_BCLKPOL_STEREO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH2_LRCKFMT_STEREO (ioh_lrclkfmt_i2s)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH2_TX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH2_TX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
#define I2S_CH2_TX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of TX side. The value is ioh_aft_t */
#define I2S_CH2_TX_AFT_STEREO (ioh_aft_front)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH2_RX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH2_RX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
#define I2S_CH2_RX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of RX side. The value is ioh_aft_t */
#define I2S_CH2_RX_AFT_STEREO (ioh_aft_front)
/* I2S CH2 monaural config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH2_BCLKPOL_MONO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH2_LRCKFMT_MONO (ioh_lrclkfmt_longframe)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH2_TX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH2_TX_LSB_MONO (ioh_lsb_msb_first)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH2_RX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH2_RX_LSB_MONO (ioh_lsb_msb_first)
/* =================== I2S CH3 config =================== */
#define I2S_CH3_MCLK (12288000) /* Master Clock Frequency[Hz] */
#if IOH_I2S_USE_PARAM
#define I2S_CH3_FS_16000 16000
#define I2S_CH3_FS_32000 32000
#define I2S_CH3_FS_48000 48000
#else
#define I2S_CH3_FS_8000 8000
#define I2S_CH3_FS_11025 11025
#define I2S_CH3_FS_22050 22050
#define I2S_CH3_FS_44100 44100
#endif
/* select master or slave. The value is ioh_mssel_t */
#define I2S_CH3_MSSEL (ioh_mssel_master)
/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
#define I2S_CH3_MASTERCLKSEL (ioh_masterclksel_mclk)
/* I2S CH3 stereo config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH3_BCLKPOL_STEREO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH3_LRCKFMT_STEREO (ioh_lrclkfmt_i2s)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH3_TX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH3_TX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
#define I2S_CH3_TX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of TX side. The value is ioh_aft_t */
#define I2S_CH3_TX_AFT_STEREO (ioh_aft_front)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH3_RX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH3_RX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
#define I2S_CH3_RX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of RX side. The value is ioh_aft_t */
#define I2S_CH3_RX_AFT_STEREO (ioh_aft_front)
/* I2S CH3 monaural config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH3_BCLKPOL_MONO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH3_LRCKFMT_MONO (ioh_lrclkfmt_longframe)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH3_TX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH3_TX_LSB_MONO (ioh_lsb_msb_first)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH3_RX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH3_RX_LSB_MONO (ioh_lsb_msb_first)
/* =================== I2S CH4 config =================== */
#define I2S_CH4_MCLK (12288000) /* Master Clock Frequency[Hz] */
#if IOH_I2S_USE_PARAM
#define I2S_CH4_FS_16000 16000
#define I2S_CH4_FS_32000 32000
#define I2S_CH4_FS_48000 48000
#else
#define I2S_CH4_FS_8000 8000
#define I2S_CH4_FS_11025 11025
#define I2S_CH4_FS_22050 22050
#define I2S_CH4_FS_44100 44100
#endif
/* select master or slave. The value is ioh_mssel_t */
#define I2S_CH4_MSSEL (ioh_mssel_master)
/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
#define I2S_CH4_MASTERCLKSEL (ioh_masterclksel_mclk)
/* I2S CH4 stereo config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH4_BCLKPOL_STEREO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH4_LRCKFMT_STEREO (ioh_lrclkfmt_i2s)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH4_TX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH4_TX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
#define I2S_CH4_TX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of TX side. The value is ioh_aft_t */
#define I2S_CH4_TX_AFT_STEREO (ioh_aft_front)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH4_RX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH4_RX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
#define I2S_CH4_RX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of RX side. The value is ioh_aft_t */
#define I2S_CH4_RX_AFT_STEREO (ioh_aft_front)
/* I2S CH4 monaural config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH4_BCLKPOL_MONO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH4_LRCKFMT_MONO (ioh_lrclkfmt_longframe)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH4_TX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH4_TX_LSB_MONO (ioh_lsb_msb_first)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH4_RX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH4_RX_LSB_MONO (ioh_lsb_msb_first)
/* =================== I2S CH5 config =================== */
#define I2S_CH5_MCLK (12288000) /* Master Clock Frequency[Hz] */
#if IOH_I2S_USE_PARAM
#define I2S_CH5_FS_16000 16000
#define I2S_CH5_FS_32000 32000
#define I2S_CH5_FS_48000 48000
#else
#define I2S_CH5_FS_8000 8000
#define I2S_CH5_FS_11025 11025
#define I2S_CH5_FS_22050 22050
#define I2S_CH5_FS_44100 44100
#endif
/* select master or slave. The value is ioh_mssel_t */
#define I2S_CH5_MSSEL (ioh_mssel_master)
/* select MCLK or MLBCLK into Master Clock. The value is enum ioh_masterclk_t */
#define I2S_CH5_MASTERCLKSEL (ioh_masterclksel_mclk)
/* I2S CH5 stereo config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH5_BCLKPOL_STEREO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH5_LRCKFMT_STEREO (ioh_lrclkfmt_i2s)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH5_TX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH5_TX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of TX side. The value is ioh_lrpol_t */
#define I2S_CH5_TX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of TX side. The value is ioh_aft_t */
#define I2S_CH5_TX_AFT_STEREO (ioh_aft_front)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH5_RX_DLYOFF_STEREO (ioh_dlyoff_dly_on)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH5_RX_LSB_STEREO (ioh_lsb_msb_first)
/* select LRCLK polarity of RX side. The value is ioh_lrpol_t */
#define I2S_CH5_RX_LRPOL_STEREO (ioh_lrpol_no_invert)
/* select transmit data front or back of RX side. The value is ioh_aft_t */
#define I2S_CH5_RX_AFT_STEREO (ioh_aft_front)
/* I2S CH5 monaural config */
/* select BCLK polarity. The value is enum ioh_bclkpol_t */
#define I2S_CH5_BCLKPOL_MONO (ioh_bclkpol_falling)
/* select DAI format. The value is enum ioh_lrckfmt_t */
#define I2S_CH5_LRCKFMT_MONO (ioh_lrclkfmt_longframe)
/* select TX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH5_TX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select TX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH5_TX_LSB_MONO (ioh_lsb_msb_first)
/* select RX data delay on or off. The value is enum ioh_dlyoff_t */
#define I2S_CH5_RX_DLYOFF_MONO (ioh_dlyoff_dly_off)
/* select RX data format LSB or MSB first. The value is ioh_lsb_t */
#define I2S_CH5_RX_LSB_MONO (ioh_lsb_msb_first)
/* ######################################################################## */
/* ### Parameter Check PreProcessor ### */
/* ######################################################################## */
/* ===== CH0 Parameter Error Check ==== */
#define USE_RATE_MAX_CH0 (0)
#ifdef I2S_CH0_FS_8000
#define USE_RATE_CH0_8000 SNDRV_PCM_RATE_8000
#define USE_RATE_MIN_CH0 (8000)
#if (USE_RATE_MAX_CH0 < 8000)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (8000)
#endif
#else
#define USE_RATE_CH0_8000 0
#endif
#ifdef I2S_CH0_FS_11025
#define USE_RATE_CH0_11025 SNDRV_PCM_RATE_11025
#ifndef USE_RATE_MIN_CH0
#define USE_RATE_MIN_CH0 (11025)
#endif
#if (USE_RATE_MAX_CH0 < 11025)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (11025)
#endif
#else
#define USE_RATE_CH0_11025 0
#endif
#ifdef I2S_CH0_FS_16000
#define USE_RATE_CH0_16000 SNDRV_PCM_RATE_16000
#ifndef USE_RATE_MIN_CH0
#define USE_RATE_MIN_CH0 (16000)
#endif
#if (USE_RATE_MAX_CH0 < 16000)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (16000)
#endif
#else
#define USE_RATE_CH0_16000 0
#endif
#ifdef I2S_CH0_FS_22050
#define USE_RATE_CH0_22050 SNDRV_PCM_RATE_22050
#ifndef USE_RATE_MIN_CH0
#define USE_RATE_MIN_CH0 (22050)
#endif
#if (USE_RATE_MAX_CH0 < 22050)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (22050)
#endif
#else
#define USE_RATE_CH0_22050 0
#endif
#ifdef I2S_CH0_FS_32000
#define USE_RATE_CH0_32000 SNDRV_PCM_RATE_32000
#ifndef USE_RATE_MIN_CH0
#define USE_RATE_MIN_CH0 (32000)
#endif
#if (USE_RATE_MAX_CH0 < 32000)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (32000)
#endif
#else
#define USE_RATE_CH0_32000 0
#endif
#ifdef I2S_CH0_FS_44100
#define USE_RATE_CH0_44100 SNDRV_PCM_RATE_44100
#ifndef USE_RATE_MIN_CH0
#define USE_RATE_MIN_CH0 (44100)
#endif
#if (USE_RATE_MAX_CH0 < 44100)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (44100)
#endif
#else
#define USE_RATE_CH0_44100 0
#endif
#ifdef I2S_CH0_FS_48000
#define USE_RATE_CH0_48000 SNDRV_PCM_RATE_48000
#ifndef USE_RATE_MIN_CH0
#define USE_RATE_MIN_CH0 (48000)
#endif
#if (USE_RATE_MAX_CH0 < 48000)
#undef USE_RATE_MAX_CH0
#define USE_RATE_MAX_CH0 (48000)
#endif
#else
#define USE_RATE_CH0_48000 0
#endif
#define USE_RATE_CH0 (USE_RATE_CH0_8000|USE_RATE_CH0_11025 | \
USE_RATE_CH0_16000|USE_RATE_CH0_22050 | \
USE_RATE_CH0_32000|USE_RATE_CH0_44100 | \
USE_RATE_CH0_48000)
#define I2S_CH0_64FS (I2S_CH0_MCLK/64)
#define I2S_CH0_128FS (I2S_CH0_MCLK/128)
#define I2S_CH0_192FS (I2S_CH0_MCLK/192)
#define I2S_CH0_256FS (I2S_CH0_MCLK/256)
#define I2S_CH0_384FS (I2S_CH0_MCLK/384)
#define I2S_CH0_512FS (I2S_CH0_MCLK/512)
#define I2S_CH0_768FS (I2S_CH0_MCLK/768)
#define I2S_CH0_1024FS (I2S_CH0_MCLK/1024)
#if (I2S_CH0_MSSEL == ioh_mssel_master)
#ifdef I2S_CH0_FS_8000
#if (I2S_CH0_64FS != I2S_CH0_FS_8000) && \
(I2S_CH0_128FS != I2S_CH0_FS_8000) && \
(I2S_CH0_192FS != I2S_CH0_FS_8000) && \
(I2S_CH0_256FS != I2S_CH0_FS_8000) && \
(I2S_CH0_384FS != I2S_CH0_FS_8000) && \
(I2S_CH0_512FS != I2S_CH0_FS_8000) && \
(I2S_CH0_768FS != I2S_CH0_FS_8000) && \
(I2S_CH0_1024FS != I2S_CH0_FS_8000)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 8000Hz can not generate.
#endif
#endif
#ifdef I2S_CH0_FS_11025
#if (I2S_CH0_64FS != I2S_CH0_FS_11025) && \
(I2S_CH0_128FS != I2S_CH0_FS_11025) && \
(I2S_CH0_192FS != I2S_CH0_FS_11025) && \
(I2S_CH0_256FS != I2S_CH0_FS_11025) && \
(I2S_CH0_384FS != I2S_CH0_FS_11025) && \
(I2S_CH0_512FS != I2S_CH0_FS_11025) && \
(I2S_CH0_768FS != I2S_CH0_FS_11025) && \
(I2S_CH0_1024FS != I2S_CH0_FS_11025)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 11025Hz can not generate.
#endif
#endif
#ifdef I2S_CH0_FS_16000
#if (I2S_CH0_64FS != I2S_CH0_FS_16000) && \
(I2S_CH0_128FS != I2S_CH0_FS_16000) && \
(I2S_CH0_192FS != I2S_CH0_FS_16000) && \
(I2S_CH0_256FS != I2S_CH0_FS_16000) && \
(I2S_CH0_384FS != I2S_CH0_FS_16000) && \
(I2S_CH0_512FS != I2S_CH0_FS_16000) && \
(I2S_CH0_768FS != I2S_CH0_FS_16000) && \
(I2S_CH0_1024FS != I2S_CH0_FS_16000)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 16000Hz can not generate.
#endif
#endif
#ifdef I2S_CH0_FS_22050
#if (I2S_CH0_64FS != I2S_CH0_FS_22050) && \
(I2S_CH0_128FS != I2S_CH0_FS_22050) && \
(I2S_CH0_192FS != I2S_CH0_FS_22050) && \
(I2S_CH0_256FS != I2S_CH0_FS_22050) && \
(I2S_CH0_384FS != I2S_CH0_FS_22050) && \
(I2S_CH0_512FS != I2S_CH0_FS_22050) && \
(I2S_CH0_768FS != I2S_CH0_FS_22050) && \
(I2S_CH0_1024FS != I2S_CH0_FS_22050)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 22050Hz can not generate.
#endif
#endif
#ifdef I2S_CH0_FS_32000
#if (I2S_CH0_64FS != I2S_CH0_FS_32000) && \
(I2S_CH0_128FS != I2S_CH0_FS_32000) && \
(I2S_CH0_192FS != I2S_CH0_FS_32000) && \
(I2S_CH0_256FS != I2S_CH0_FS_32000) && \
(I2S_CH0_384FS != I2S_CH0_FS_32000) && \
(I2S_CH0_512FS != I2S_CH0_FS_32000) && \
(I2S_CH0_768FS != I2S_CH0_FS_32000) && \
(I2S_CH0_1024FS != I2S_CH0_FS_32000)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 32000Hz can not generate.
#endif
#endif
#ifdef I2S_CH0_FS_44100
#if (I2S_CH0_64FS != I2S_CH0_FS_44100) && \
(I2S_CH0_128FS != I2S_CH0_FS_44100) && \
(I2S_CH0_192FS != I2S_CH0_FS_44100) && \
(I2S_CH0_256FS != I2S_CH0_FS_44100) && \
(I2S_CH0_384FS != I2S_CH0_FS_44100) && \
(I2S_CH0_512FS != I2S_CH0_FS_44100) && \
(I2S_CH0_768FS != I2S_CH0_FS_44100) && \
(I2S_CH0_1024FS != I2S_CH0_FS_44100)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 44100Hz can not generate.
#endif
#endif
#ifdef I2S_CH0_FS_48000
#if (I2S_CH0_64FS != I2S_CH0_FS_48000) && \
(I2S_CH0_128FS != I2S_CH0_FS_48000) && \
(I2S_CH0_192FS != I2S_CH0_FS_48000) && \
(I2S_CH0_256FS != I2S_CH0_FS_48000) && \
(I2S_CH0_384FS != I2S_CH0_FS_48000) && \
(I2S_CH0_512FS != I2S_CH0_FS_48000) && \
(I2S_CH0_768FS != I2S_CH0_FS_48000) && \
(I2S_CH0_1024FS != I2S_CH0_FS_48000)
#error IOH_I2S_CH0_CONFIG Error : \
Sampling frequency 48000Hz can not generate.
#endif
#endif
#endif /* end of [#if (I2S_CH0_MSSEL == ioh_mssel_master)] */
/* ===== CH1 Parameter Error Check ==== */
#define USE_RATE_MAX_CH1 (0)
#ifdef I2S_CH1_FS_8000
#define USE_RATE_CH1_8000 SNDRV_PCM_RATE_8000
#define USE_RATE_MIN_CH1 (8000)
#if (USE_RATE_MAX_CH1 < 8000)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (8000)
#endif
#else
#define USE_RATE_CH1_8000 0
#endif
#ifdef I2S_CH1_FS_11025
#define USE_RATE_CH1_11025 SNDRV_PCM_RATE_11025
#ifndef USE_RATE_MIN_CH1
#define USE_RATE_MIN_CH1 (11025)
#endif
#if (USE_RATE_MAX_CH1 < 11025)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (11025)
#endif
#else
#define USE_RATE_CH1_11025 0
#endif
#ifdef I2S_CH1_FS_16000
#define USE_RATE_CH1_16000 SNDRV_PCM_RATE_16000
#ifndef USE_RATE_MIN_CH1
#define USE_RATE_MIN_CH1 (16000)
#endif
#if (USE_RATE_MAX_CH1 < 16000)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (16000)
#endif
#else
#define USE_RATE_CH1_16000 0
#endif
#ifdef I2S_CH1_FS_22050
#define USE_RATE_CH1_22050 SNDRV_PCM_RATE_22050
#ifndef USE_RATE_MIN_CH1
#define USE_RATE_MIN_CH1 (22050)
#endif
#if (USE_RATE_MAX_CH1 < 22050)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (22050)
#endif
#else
#define USE_RATE_CH1_22050 0
#endif
#ifdef I2S_CH1_FS_32000
#define USE_RATE_CH1_32000 SNDRV_PCM_RATE_32000
#ifndef USE_RATE_MIN_CH1
#define USE_RATE_MIN_CH1 (32000)
#endif
#if (USE_RATE_MAX_CH1 < 32000)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (32000)
#endif
#else
#define USE_RATE_CH1_32000 0
#endif
#ifdef I2S_CH1_FS_44100
#define USE_RATE_CH1_44100 SNDRV_PCM_RATE_44100
#ifndef USE_RATE_MIN_CH1
#define USE_RATE_MIN_CH1 (44100)
#endif
#if (USE_RATE_MAX_CH1 < 44100)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (44100)
#endif
#else
#define USE_RATE_CH1_44100 0
#endif
#ifdef I2S_CH1_FS_48000
#define USE_RATE_CH1_48000 SNDRV_PCM_RATE_48000
#ifndef USE_RATE_MIN_CH1
#define USE_RATE_MIN_CH1 (48000)
#endif
#if (USE_RATE_MAX_CH1 < 48000)
#undef USE_RATE_MAX_CH1
#define USE_RATE_MAX_CH1 (48000)
#endif
#else
#define USE_RATE_CH1_48000 0
#endif
#define USE_RATE_CH1 (USE_RATE_CH1_8000|USE_RATE_CH1_11025 | \
USE_RATE_CH1_16000|USE_RATE_CH1_22050 | \
USE_RATE_CH1_32000|USE_RATE_CH1_44100 | \
USE_RATE_CH1_48000)
#define I2S_CH1_64FS (I2S_CH1_MCLK/64)
#define I2S_CH1_128FS (I2S_CH1_MCLK/128)
#define I2S_CH1_192FS (I2S_CH1_MCLK/192)
#define I2S_CH1_256FS (I2S_CH1_MCLK/256)
#define I2S_CH1_384FS (I2S_CH1_MCLK/384)
#define I2S_CH1_512FS (I2S_CH1_MCLK/512)
#define I2S_CH1_768FS (I2S_CH1_MCLK/768)
#define I2S_CH1_1024FS (I2S_CH1_MCLK/1024)
#if (I2S_CH1_MSSEL == ioh_mssel_master)
#ifdef I2S_CH1_FS_8000
#if (I2S_CH1_64FS != I2S_CH1_FS_8000) && \
(I2S_CH1_128FS != I2S_CH1_FS_8000) && \
(I2S_CH1_192FS != I2S_CH1_FS_8000) && \
(I2S_CH1_256FS != I2S_CH1_FS_8000) && \
(I2S_CH1_384FS != I2S_CH1_FS_8000) && \
(I2S_CH1_512FS != I2S_CH1_FS_8000) && \
(I2S_CH1_768FS != I2S_CH1_FS_8000) && \
(I2S_CH1_1024FS != I2S_CH1_FS_8000)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 8000Hz can not generate.
#endif
#endif
#ifdef I2S_CH1_FS_11025
#if (I2S_CH1_64FS != I2S_CH1_FS_11025) && \
(I2S_CH1_128FS != I2S_CH1_FS_11025) && \
(I2S_CH1_192FS != I2S_CH1_FS_11025) && \
(I2S_CH1_256FS != I2S_CH1_FS_11025) && \
(I2S_CH1_384FS != I2S_CH1_FS_11025) && \
(I2S_CH1_512FS != I2S_CH1_FS_11025) && \
(I2S_CH1_768FS != I2S_CH1_FS_11025) && \
(I2S_CH1_1024FS != I2S_CH1_FS_11025)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 11025Hz can not generate.
#endif
#endif
#ifdef I2S_CH1_FS_16000
#if (I2S_CH1_64FS != I2S_CH1_FS_16000) && \
(I2S_CH1_128FS != I2S_CH1_FS_16000) && \
(I2S_CH1_192FS != I2S_CH1_FS_16000) && \
(I2S_CH1_256FS != I2S_CH1_FS_16000) && \
(I2S_CH1_384FS != I2S_CH1_FS_16000) && \
(I2S_CH1_512FS != I2S_CH1_FS_16000) && \
(I2S_CH1_768FS != I2S_CH1_FS_16000) && \
(I2S_CH1_1024FS != I2S_CH1_FS_16000)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 16000Hz can not generate.
#endif
#endif
#ifdef I2S_CH1_FS_22050
#if (I2S_CH1_64FS != I2S_CH1_FS_22050) && \
(I2S_CH1_128FS != I2S_CH1_FS_22050) && \
(I2S_CH1_192FS != I2S_CH1_FS_22050) && \
(I2S_CH1_256FS != I2S_CH1_FS_22050) && \
(I2S_CH1_384FS != I2S_CH1_FS_22050) && \
(I2S_CH1_512FS != I2S_CH1_FS_22050) && \
(I2S_CH1_768FS != I2S_CH1_FS_22050) && \
(I2S_CH1_1024FS != I2S_CH1_FS_22050)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 22050Hz can not generate.
#endif
#endif
#ifdef I2S_CH1_FS_32000
#if (I2S_CH1_64FS != I2S_CH1_FS_32000) && \
(I2S_CH1_128FS != I2S_CH1_FS_32000) && \
(I2S_CH1_192FS != I2S_CH1_FS_32000) && \
(I2S_CH1_256FS != I2S_CH1_FS_32000) && \
(I2S_CH1_384FS != I2S_CH1_FS_32000) && \
(I2S_CH1_512FS != I2S_CH1_FS_32000) && \
(I2S_CH1_768FS != I2S_CH1_FS_32000) && \
(I2S_CH1_1024FS != I2S_CH1_FS_32000)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 32000Hz can not generate.
#endif
#endif
#ifdef I2S_CH1_FS_44100
#if (I2S_CH1_64FS != I2S_CH1_FS_44100) && \
(I2S_CH1_128FS != I2S_CH1_FS_44100) && \
(I2S_CH1_192FS != I2S_CH1_FS_44100) && \
(I2S_CH1_256FS != I2S_CH1_FS_44100) && \
(I2S_CH1_384FS != I2S_CH1_FS_44100) && \
(I2S_CH1_512FS != I2S_CH1_FS_44100) && \
(I2S_CH1_768FS != I2S_CH1_FS_44100) && \
(I2S_CH1_1024FS != I2S_CH1_FS_44100)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 44100Hz can not generate.
#endif
#endif
#ifdef I2S_CH1_FS_48000
#if (I2S_CH1_64FS != I2S_CH1_FS_48000) && \
(I2S_CH1_128FS != I2S_CH1_FS_48000) && \
(I2S_CH1_192FS != I2S_CH1_FS_48000) && \
(I2S_CH1_256FS != I2S_CH1_FS_48000) && \
(I2S_CH1_384FS != I2S_CH1_FS_48000) && \
(I2S_CH1_512FS != I2S_CH1_FS_48000) && \
(I2S_CH1_768FS != I2S_CH1_FS_48000) && \
(I2S_CH1_1024FS != I2S_CH1_FS_48000)
#error IOH_I2S_CH1_CONFIG Error : \
Sampling frequency 48000Hz can not generate.
#endif
#endif
#endif /* end of [#if (I2S_CH1_MSSEL == ioh_mssel_master)] */
/* ===== CH2 Parameter Error Check ==== */
#define USE_RATE_MAX_CH2 (0)
#ifdef I2S_CH2_FS_8000
#define USE_RATE_CH2_8000 SNDRV_PCM_RATE_8000
#define USE_RATE_MIN_CH2 (8000)
#if (USE_RATE_MAX_CH2 < 8000)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (8000)
#endif
#else
#define USE_RATE_CH2_8000 0
#endif
#ifdef I2S_CH2_FS_11025
#define USE_RATE_CH2_11025 SNDRV_PCM_RATE_11025
#ifndef USE_RATE_MIN_CH2
#define USE_RATE_MIN_CH2 (11025)
#endif
#if (USE_RATE_MAX_CH2 < 11025)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (11025)
#endif
#else
#define USE_RATE_CH2_11025 0
#endif
#ifdef I2S_CH2_FS_16000
#define USE_RATE_CH2_16000 SNDRV_PCM_RATE_16000
#ifndef USE_RATE_MIN_CH2
#define USE_RATE_MIN_CH2 (16000)
#endif
#if (USE_RATE_MAX_CH2 < 16000)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (16000)
#endif
#else
#define USE_RATE_CH2_16000 0
#endif
#ifdef I2S_CH2_FS_22050
#define USE_RATE_CH2_22050 SNDRV_PCM_RATE_22050
#ifndef USE_RATE_MIN_CH2
#define USE_RATE_MIN_CH2 (22050)
#endif
#if (USE_RATE_MAX_CH2 < 22050)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (22050)
#endif
#else
#define USE_RATE_CH2_22050 0
#endif
#ifdef I2S_CH2_FS_32000
#define USE_RATE_CH2_32000 SNDRV_PCM_RATE_32000
#ifndef USE_RATE_MIN_CH2
#define USE_RATE_MIN_CH2 (32000)
#endif
#if (USE_RATE_MAX_CH2 < 32000)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (32000)
#endif
#else
#define USE_RATE_CH2_32000 0
#endif
#ifdef I2S_CH2_FS_44100
#define USE_RATE_CH2_44100 SNDRV_PCM_RATE_44100
#ifndef USE_RATE_MIN_CH2
#define USE_RATE_MIN_CH2 (44100)
#endif
#if (USE_RATE_MAX_CH2 < 44100)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (44100)
#endif
#else
#define USE_RATE_CH2_44100 0
#endif
#ifdef I2S_CH2_FS_48000
#define USE_RATE_CH2_48000 SNDRV_PCM_RATE_48000
#ifndef USE_RATE_MIN_CH2
#define USE_RATE_MIN_CH2 (48000)
#endif
#if (USE_RATE_MAX_CH2 < 48000)
#undef USE_RATE_MAX_CH2
#define USE_RATE_MAX_CH2 (48000)
#endif
#else
#define USE_RATE_CH2_48000 0
#endif
#define USE_RATE_CH2 (USE_RATE_CH2_8000|USE_RATE_CH2_11025 | \
USE_RATE_CH2_16000|USE_RATE_CH2_22050 | \
USE_RATE_CH2_32000|USE_RATE_CH2_44100 | \
USE_RATE_CH2_48000)
#define I2S_CH2_64FS (I2S_CH2_MCLK/64)
#define I2S_CH2_128FS (I2S_CH2_MCLK/128)
#define I2S_CH2_192FS (I2S_CH2_MCLK/192)
#define I2S_CH2_256FS (I2S_CH2_MCLK/256)
#define I2S_CH2_384FS (I2S_CH2_MCLK/384)
#define I2S_CH2_512FS (I2S_CH2_MCLK/512)
#define I2S_CH2_768FS (I2S_CH2_MCLK/768)
#define I2S_CH2_1024FS (I2S_CH2_MCLK/1024)
#if (I2S_CH2_MSSEL == ioh_mssel_master)
#ifdef I2S_CH2_FS_8000
#if (I2S_CH2_64FS != I2S_CH2_FS_8000) && \
(I2S_CH2_128FS != I2S_CH2_FS_8000) && \
(I2S_CH2_192FS != I2S_CH2_FS_8000) && \
(I2S_CH2_256FS != I2S_CH2_FS_8000) && \
(I2S_CH2_384FS != I2S_CH2_FS_8000) && \
(I2S_CH2_512FS != I2S_CH2_FS_8000) && \
(I2S_CH2_768FS != I2S_CH2_FS_8000) && \
(I2S_CH2_1024FS != I2S_CH2_FS_8000)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 8000Hz can not generate.
#endif
#endif
#ifdef I2S_CH2_FS_11025
#if (I2S_CH2_64FS != I2S_CH2_FS_11025) && \
(I2S_CH2_128FS != I2S_CH2_FS_11025) && \
(I2S_CH2_192FS != I2S_CH2_FS_11025) && \
(I2S_CH2_256FS != I2S_CH2_FS_11025) && \
(I2S_CH2_384FS != I2S_CH2_FS_11025) && \
(I2S_CH2_512FS != I2S_CH2_FS_11025) && \
(I2S_CH2_768FS != I2S_CH2_FS_11025) && \
(I2S_CH2_1024FS != I2S_CH2_FS_11025)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 11025Hz can not generate.
#endif
#endif
#ifdef I2S_CH2_FS_16000
#if (I2S_CH2_64FS != I2S_CH2_FS_16000) && \
(I2S_CH2_128FS != I2S_CH2_FS_16000) && \
(I2S_CH2_192FS != I2S_CH2_FS_16000) && \
(I2S_CH2_256FS != I2S_CH2_FS_16000) && \
(I2S_CH2_384FS != I2S_CH2_FS_16000) && \
(I2S_CH2_512FS != I2S_CH2_FS_16000) && \
(I2S_CH2_768FS != I2S_CH2_FS_16000) && \
(I2S_CH2_1024FS != I2S_CH2_FS_16000)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 16000Hz can not generate.
#endif
#endif
#ifdef I2S_CH2_FS_22050
#if (I2S_CH2_64FS != I2S_CH2_FS_22050) && \
(I2S_CH2_128FS != I2S_CH2_FS_22050) && \
(I2S_CH2_192FS != I2S_CH2_FS_22050) && \
(I2S_CH2_256FS != I2S_CH2_FS_22050) && \
(I2S_CH2_384FS != I2S_CH2_FS_22050) && \
(I2S_CH2_512FS != I2S_CH2_FS_22050) && \
(I2S_CH2_768FS != I2S_CH2_FS_22050) && \
(I2S_CH2_1024FS != I2S_CH2_FS_22050)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 22050Hz can not generate.
#endif
#endif
#ifdef I2S_CH2_FS_32000
#if (I2S_CH2_64FS != I2S_CH2_FS_32000) && \
(I2S_CH2_128FS != I2S_CH2_FS_32000) && \
(I2S_CH2_192FS != I2S_CH2_FS_32000) && \
(I2S_CH2_256FS != I2S_CH2_FS_32000) && \
(I2S_CH2_384FS != I2S_CH2_FS_32000) && \
(I2S_CH2_512FS != I2S_CH2_FS_32000) && \
(I2S_CH2_768FS != I2S_CH2_FS_32000) && \
(I2S_CH2_1024FS != I2S_CH2_FS_32000)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 32000Hz can not generate.
#endif
#endif
#ifdef I2S_CH2_FS_44100
#if (I2S_CH2_64FS != I2S_CH2_FS_44100) && \
(I2S_CH2_128FS != I2S_CH2_FS_44100) && \
(I2S_CH2_192FS != I2S_CH2_FS_44100) && \
(I2S_CH2_256FS != I2S_CH2_FS_44100) && \
(I2S_CH2_384FS != I2S_CH2_FS_44100) && \
(I2S_CH2_512FS != I2S_CH2_FS_44100) && \
(I2S_CH2_768FS != I2S_CH2_FS_44100) && \
(I2S_CH2_1024FS != I2S_CH2_FS_44100)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 44100Hz can not generate.
#endif
#endif
#ifdef I2S_CH2_FS_48000
#if (I2S_CH2_64FS != I2S_CH2_FS_48000) && \
(I2S_CH2_128FS != I2S_CH2_FS_48000) && \
(I2S_CH2_192FS != I2S_CH2_FS_48000) && \
(I2S_CH2_256FS != I2S_CH2_FS_48000) && \
(I2S_CH2_384FS != I2S_CH2_FS_48000) && \
(I2S_CH2_512FS != I2S_CH2_FS_48000) && \
(I2S_CH2_768FS != I2S_CH2_FS_48000) && \
(I2S_CH2_1024FS != I2S_CH2_FS_48000)
#error IOH_I2S_CH2_CONFIG Error : \
Sampling frequency 48000Hz can not generate.
#endif
#endif
#endif /* end of [#if (I2S_CH2_MSSEL == ioh_mssel_master)] */
/* ===== CH3 Parameter Error Check ==== */
#define USE_RATE_MAX_CH3 (0)
#ifdef I2S_CH3_FS_8000
#define USE_RATE_CH3_8000 SNDRV_PCM_RATE_8000
#define USE_RATE_MIN_CH3 (8000)
#if (USE_RATE_MAX_CH3 < 8000)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (8000)
#endif
#else
#define USE_RATE_CH3_8000 0
#endif
#ifdef I2S_CH3_FS_11025
#define USE_RATE_CH3_11025 SNDRV_PCM_RATE_11025
#ifndef USE_RATE_MIN_CH3
#define USE_RATE_MIN_CH3 (11025)
#endif
#if (USE_RATE_MAX_CH3 < 11025)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (11025)
#endif
#else
#define USE_RATE_CH3_11025 0
#endif
#ifdef I2S_CH3_FS_16000
#define USE_RATE_CH3_16000 SNDRV_PCM_RATE_16000
#ifndef USE_RATE_MIN_CH3
#define USE_RATE_MIN_CH3 (16000)
#endif
#if (USE_RATE_MAX_CH3 < 16000)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (16000)
#endif
#else
#define USE_RATE_CH3_16000 0
#endif
#ifdef I2S_CH3_FS_22050
#define USE_RATE_CH3_22050 SNDRV_PCM_RATE_22050
#ifndef USE_RATE_MIN_CH3
#define USE_RATE_MIN_CH3 (22050)
#endif
#if (USE_RATE_MAX_CH3 < 22050)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (22050)
#endif
#else
#define USE_RATE_CH3_22050 0
#endif
#ifdef I2S_CH3_FS_32000
#define USE_RATE_CH3_32000 SNDRV_PCM_RATE_32000
#ifndef USE_RATE_MIN_CH3
#define USE_RATE_MIN_CH3 (32000)
#endif
#if (USE_RATE_MAX_CH3 < 32000)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (32000)
#endif
#else
#define USE_RATE_CH3_32000 0
#endif
#ifdef I2S_CH3_FS_44100
#define USE_RATE_CH3_44100 SNDRV_PCM_RATE_44100
#ifndef USE_RATE_MIN_CH3
#define USE_RATE_MIN_CH3 (44100)
#endif
#if (USE_RATE_MAX_CH3 < 44100)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (44100)
#endif
#else
#define USE_RATE_CH3_44100 0
#endif
#ifdef I2S_CH3_FS_48000
#define USE_RATE_CH3_48000 SNDRV_PCM_RATE_48000
#ifndef USE_RATE_MIN_CH3
#define USE_RATE_MIN_CH3 (48000)
#endif
#if (USE_RATE_MAX_CH3 < 48000)
#undef USE_RATE_MAX_CH3
#define USE_RATE_MAX_CH3 (48000)
#endif
#else
#define USE_RATE_CH3_48000 0
#endif
#define USE_RATE_CH3 (USE_RATE_CH3_8000|USE_RATE_CH3_11025 | \
USE_RATE_CH3_16000|USE_RATE_CH3_22050 | \
USE_RATE_CH3_32000|USE_RATE_CH3_44100 | \
USE_RATE_CH3_48000)
#define I2S_CH3_64FS (I2S_CH3_MCLK/64)
#define I2S_CH3_128FS (I2S_CH3_MCLK/128)
#define I2S_CH3_192FS (I2S_CH3_MCLK/192)
#define I2S_CH3_256FS (I2S_CH3_MCLK/256)
#define I2S_CH3_384FS (I2S_CH3_MCLK/384)
#define I2S_CH3_512FS (I2S_CH3_MCLK/512)
#define I2S_CH3_768FS (I2S_CH3_MCLK/768)
#define I2S_CH3_1024FS (I2S_CH3_MCLK/1024)
#if (I2S_CH3_MSSEL == ioh_mssel_master)
#ifdef I2S_CH3_FS_8000
#if (I2S_CH3_64FS != I2S_CH3_FS_8000) && \
(I2S_CH3_128FS != I2S_CH3_FS_8000) && \
(I2S_CH3_192FS != I2S_CH3_FS_8000) && \
(I2S_CH3_256FS != I2S_CH3_FS_8000) && \
(I2S_CH3_384FS != I2S_CH3_FS_8000) && \
(I2S_CH3_512FS != I2S_CH3_FS_8000) && \
(I2S_CH3_768FS != I2S_CH3_FS_8000) && \
(I2S_CH3_1024FS != I2S_CH3_FS_8000)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 8000Hz can not generate.
#endif
#endif
#ifdef I2S_CH3_FS_11025
#if (I2S_CH3_64FS != I2S_CH3_FS_11025) && \
(I2S_CH3_128FS != I2S_CH3_FS_11025) && \
(I2S_CH3_192FS != I2S_CH3_FS_11025) && \
(I2S_CH3_256FS != I2S_CH3_FS_11025) && \
(I2S_CH3_384FS != I2S_CH3_FS_11025) && \
(I2S_CH3_512FS != I2S_CH3_FS_11025) && \
(I2S_CH3_768FS != I2S_CH3_FS_11025) && \
(I2S_CH3_1024FS != I2S_CH3_FS_11025)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 11025Hz can not generate.
#endif
#endif
#ifdef I2S_CH3_FS_16000
#if (I2S_CH3_64FS != I2S_CH3_FS_16000) && \
(I2S_CH3_128FS != I2S_CH3_FS_16000) && \
(I2S_CH3_192FS != I2S_CH3_FS_16000) && \
(I2S_CH3_256FS != I2S_CH3_FS_16000) && \
(I2S_CH3_384FS != I2S_CH3_FS_16000) && \
(I2S_CH3_512FS != I2S_CH3_FS_16000) && \
(I2S_CH3_768FS != I2S_CH3_FS_16000) && \
(I2S_CH3_1024FS != I2S_CH3_FS_16000)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 16000Hz can not generate.
#endif
#endif
#ifdef I2S_CH3_FS_22050
#if (I2S_CH3_64FS != I2S_CH3_FS_22050) && \
(I2S_CH3_128FS != I2S_CH3_FS_22050) && \
(I2S_CH3_192FS != I2S_CH3_FS_22050) && \
(I2S_CH3_256FS != I2S_CH3_FS_22050) && \
(I2S_CH3_384FS != I2S_CH3_FS_22050) && \
(I2S_CH3_512FS != I2S_CH3_FS_22050) && \
(I2S_CH3_768FS != I2S_CH3_FS_22050) && \
(I2S_CH3_1024FS != I2S_CH3_FS_22050)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 22050Hz can not generate.
#endif
#endif
#ifdef I2S_CH3_FS_32000
#if (I2S_CH3_64FS != I2S_CH3_FS_32000) && \
(I2S_CH3_128FS != I2S_CH3_FS_32000) && \
(I2S_CH3_192FS != I2S_CH3_FS_32000) && \
(I2S_CH3_256FS != I2S_CH3_FS_32000) && \
(I2S_CH3_384FS != I2S_CH3_FS_32000) && \
(I2S_CH3_512FS != I2S_CH3_FS_32000) && \
(I2S_CH3_768FS != I2S_CH3_FS_32000) && \
(I2S_CH3_1024FS != I2S_CH3_FS_32000)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 32000Hz can not generate.
#endif
#endif
#ifdef I2S_CH3_FS_44100
#if (I2S_CH3_64FS != I2S_CH3_FS_44100) && \
(I2S_CH3_128FS != I2S_CH3_FS_44100) && \
(I2S_CH3_192FS != I2S_CH3_FS_44100) && \
(I2S_CH3_256FS != I2S_CH3_FS_44100) && \
(I2S_CH3_384FS != I2S_CH3_FS_44100) && \
(I2S_CH3_512FS != I2S_CH3_FS_44100) && \
(I2S_CH3_768FS != I2S_CH3_FS_44100) && \
(I2S_CH3_1024FS != I2S_CH3_FS_44100)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 44100Hz can not generate.
#endif
#endif
#ifdef I2S_CH3_FS_48000
#if (I2S_CH3_64FS != I2S_CH3_FS_48000) && \
(I2S_CH3_128FS != I2S_CH3_FS_48000) && \
(I2S_CH3_192FS != I2S_CH3_FS_48000) && \
(I2S_CH3_256FS != I2S_CH3_FS_48000) && \
(I2S_CH3_384FS != I2S_CH3_FS_48000) && \
(I2S_CH3_512FS != I2S_CH3_FS_48000) && \
(I2S_CH3_768FS != I2S_CH3_FS_48000) && \
(I2S_CH3_1024FS != I2S_CH3_FS_48000)
#error IOH_I2S_CH3_CONFIG Error : \
Sampling frequency 48000Hz can not generate.
#endif
#endif
#endif /* end of [#if (I2S_CH3_MSSEL == ioh_mssel_master)] */
/* ===== CH4 Parameter Error Check ==== */
#define USE_RATE_MAX_CH4 (0)
#ifdef I2S_CH4_FS_8000
#define USE_RATE_CH4_8000 SNDRV_PCM_RATE_8000
#define USE_RATE_MIN_CH4 (8000)
#if (USE_RATE_MAX_CH4 < 8000)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (8000)
#endif
#else
#define USE_RATE_CH4_8000 0
#endif
#ifdef I2S_CH4_FS_11025
#define USE_RATE_CH4_11025 SNDRV_PCM_RATE_11025
#ifndef USE_RATE_MIN_CH4
#define USE_RATE_MIN_CH4 (11025)
#endif
#if (USE_RATE_MAX_CH4 < 11025)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (11025)
#endif
#else
#define USE_RATE_CH4_11025 0
#endif
#ifdef I2S_CH4_FS_16000
#define USE_RATE_CH4_16000 SNDRV_PCM_RATE_16000
#ifndef USE_RATE_MIN_CH4
#define USE_RATE_MIN_CH4 (16000)
#endif
#if (USE_RATE_MAX_CH4 < 16000)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (16000)
#endif
#else
#define USE_RATE_CH4_16000 0
#endif
#ifdef I2S_CH4_FS_22050
#define USE_RATE_CH4_22050 SNDRV_PCM_RATE_22050
#ifndef USE_RATE_MIN_CH4
#define USE_RATE_MIN_CH4 (22050)
#endif
#if (USE_RATE_MAX_CH4 < 22050)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (22050)
#endif
#else
#define USE_RATE_CH4_22050 0
#endif
#ifdef I2S_CH4_FS_32000
#define USE_RATE_CH4_32000 SNDRV_PCM_RATE_32000
#ifndef USE_RATE_MIN_CH4
#define USE_RATE_MIN_CH4 (32000)
#endif
#if (USE_RATE_MAX_CH4 < 32000)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (32000)
#endif
#else
#define USE_RATE_CH4_32000 0
#endif
#ifdef I2S_CH4_FS_44100
#define USE_RATE_CH4_44100 SNDRV_PCM_RATE_44100
#ifndef USE_RATE_MIN_CH4
#define USE_RATE_MIN_CH4 (44100)
#endif
#if (USE_RATE_MAX_CH4 < 44100)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (44100)
#endif
#else
#define USE_RATE_CH4_44100 0
#endif
#ifdef I2S_CH4_FS_48000
#define USE_RATE_CH4_48000 SNDRV_PCM_RATE_48000
#ifndef USE_RATE_MIN_CH4
#define USE_RATE_MIN_CH4 (48000)
#endif
#if (USE_RATE_MAX_CH4 < 48000)
#undef USE_RATE_MAX_CH4
#define USE_RATE_MAX_CH4 (48000)
#endif
#else
#define USE_RATE_CH4_48000 0
#endif
#define USE_RATE_CH4 (USE_RATE_CH4_8000|USE_RATE_CH4_11025 | \
USE_RATE_CH4_16000|USE_RATE_CH4_22050 | \
USE_RATE_CH4_32000|USE_RATE_CH4_44100 | \
USE_RATE_CH4_48000)
#define I2S_CH4_64FS (I2S_CH4_MCLK/64)
#define I2S_CH4_128FS (I2S_CH4_MCLK/128)
#define I2S_CH4_192FS (I2S_CH4_MCLK/192)
#define I2S_CH4_256FS (I2S_CH4_MCLK/256)
#define I2S_CH4_384FS (I2S_CH4_MCLK/384)
#define I2S_CH4_512FS (I2S_CH4_MCLK/512)
#define I2S_CH4_768FS (I2S_CH4_MCLK/768)
#define I2S_CH4_1024FS (I2S_CH4_MCLK/1024)
#if (I2S_CH4_MSSEL == ioh_mssel_master)
#ifdef I2S_CH4_FS_8000
#if (I2S_CH4_64FS != I2S_CH4_FS_8000) && \
(I2S_CH4_128FS != I2S_CH4_FS_8000) && \
(I2S_CH4_192FS != I2S_CH4_FS_8000) && \
(I2S_CH4_256FS != I2S_CH4_FS_8000) && \
(I2S_CH4_384FS != I2S_CH4_FS_8000) && \
(I2S_CH4_512FS != I2S_CH4_FS_8000) && \
(I2S_CH4_768FS != I2S_CH4_FS_8000) && \
(I2S_CH4_1024FS != I2S_CH4_FS_8000)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 8000Hz can not generate.
#endif
#endif
#ifdef I2S_CH4_FS_11025
#if (I2S_CH4_64FS != I2S_CH4_FS_11025) && \
(I2S_CH4_128FS != I2S_CH4_FS_11025) && \
(I2S_CH4_192FS != I2S_CH4_FS_11025) && \
(I2S_CH4_256FS != I2S_CH4_FS_11025) && \
(I2S_CH4_384FS != I2S_CH4_FS_11025) && \
(I2S_CH4_512FS != I2S_CH4_FS_11025) && \
(I2S_CH4_768FS != I2S_CH4_FS_11025) && \
(I2S_CH4_1024FS != I2S_CH4_FS_11025)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 11025Hz can not generate.
#endif
#endif
#ifdef I2S_CH4_FS_16000
#if (I2S_CH4_64FS != I2S_CH4_FS_16000) && \
(I2S_CH4_128FS != I2S_CH4_FS_16000) && \
(I2S_CH4_192FS != I2S_CH4_FS_16000) && \
(I2S_CH4_256FS != I2S_CH4_FS_16000) && \
(I2S_CH4_384FS != I2S_CH4_FS_16000) && \
(I2S_CH4_512FS != I2S_CH4_FS_16000) && \
(I2S_CH4_768FS != I2S_CH4_FS_16000) && \
(I2S_CH4_1024FS != I2S_CH4_FS_16000)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 16000Hz can not generate.
#endif
#endif
#ifdef I2S_CH4_FS_22050
#if (I2S_CH4_64FS != I2S_CH4_FS_22050) && \
(I2S_CH4_128FS != I2S_CH4_FS_22050) && \
(I2S_CH4_192FS != I2S_CH4_FS_22050) && \
(I2S_CH4_256FS != I2S_CH4_FS_22050) && \
(I2S_CH4_384FS != I2S_CH4_FS_22050) && \
(I2S_CH4_512FS != I2S_CH4_FS_22050) && \
(I2S_CH4_768FS != I2S_CH4_FS_22050) && \
(I2S_CH4_1024FS != I2S_CH4_FS_22050)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 22050Hz can not generate.
#endif
#endif
#ifdef I2S_CH4_FS_32000
#if (I2S_CH4_64FS != I2S_CH4_FS_32000) && \
(I2S_CH4_128FS != I2S_CH4_FS_32000) && \
(I2S_CH4_192FS != I2S_CH4_FS_32000) && \
(I2S_CH4_256FS != I2S_CH4_FS_32000) && \
(I2S_CH4_384FS != I2S_CH4_FS_32000) && \
(I2S_CH4_512FS != I2S_CH4_FS_32000) && \
(I2S_CH4_768FS != I2S_CH4_FS_32000) && \
(I2S_CH4_1024FS != I2S_CH4_FS_32000)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 32000Hz can not generate.
#endif
#endif
#ifdef I2S_CH4_FS_44100
#if (I2S_CH4_64FS != I2S_CH4_FS_44100) && \
(I2S_CH4_128FS != I2S_CH4_FS_44100) && \
(I2S_CH4_192FS != I2S_CH4_FS_44100) && \
(I2S_CH4_256FS != I2S_CH4_FS_44100) && \
(I2S_CH4_384FS != I2S_CH4_FS_44100) && \
(I2S_CH4_512FS != I2S_CH4_FS_44100) && \
(I2S_CH4_768FS != I2S_CH4_FS_44100) && \
(I2S_CH4_1024FS != I2S_CH4_FS_44100)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 44100Hz can not generate.
#endif
#endif
#ifdef I2S_CH4_FS_48000
#if (I2S_CH4_64FS != I2S_CH4_FS_48000) && \
(I2S_CH4_128FS != I2S_CH4_FS_48000) && \
(I2S_CH4_192FS != I2S_CH4_FS_48000) && \
(I2S_CH4_256FS != I2S_CH4_FS_48000) && \
(I2S_CH4_384FS != I2S_CH4_FS_48000) && \
(I2S_CH4_512FS != I2S_CH4_FS_48000) && \
(I2S_CH4_768FS != I2S_CH4_FS_48000) && \
(I2S_CH4_1024FS != I2S_CH4_FS_48000)
#error IOH_I2S_CH4_CONFIG Error : \
Sampling frequency 48000Hz can not generate.
#endif
#endif
#endif /* end of [#if (I2S_CH4_MSSEL == ioh_mssel_master)] */
/* ===== CH5 Parameter Error Check ==== */
#define USE_RATE_MAX_CH5 (0)
#ifdef I2S_CH5_FS_8000
#define USE_RATE_CH5_8000 SNDRV_PCM_RATE_8000
#define USE_RATE_MIN_CH5 (8000)
#if (USE_RATE_MAX_CH5 < 8000)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (8000)
#endif
#else
#define USE_RATE_CH5_8000 0
#endif
#ifdef I2S_CH5_FS_11025
#define USE_RATE_CH5_11025 SNDRV_PCM_RATE_11025
#ifndef USE_RATE_MIN_CH5
#define USE_RATE_MIN_CH5 (11025)
#endif
#if (USE_RATE_MAX_CH5 < 11025)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (11025)
#endif
#else
#define USE_RATE_CH5_11025 0
#endif
#ifdef I2S_CH5_FS_16000
#define USE_RATE_CH5_16000 SNDRV_PCM_RATE_16000
#ifndef USE_RATE_MIN_CH5
#define USE_RATE_MIN_CH5 (16000)
#endif
#if (USE_RATE_MAX_CH5 < 16000)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (16000)
#endif
#else
#define USE_RATE_CH5_16000 0
#endif
#ifdef I2S_CH5_FS_22050
#define USE_RATE_CH5_22050 SNDRV_PCM_RATE_22050
#ifndef USE_RATE_MIN_CH5
#define USE_RATE_MIN_CH5 (22050)
#endif
#if (USE_RATE_MAX_CH5 < 22050)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (22050)
#endif
#else
#define USE_RATE_CH5_22050 0
#endif
#ifdef I2S_CH5_FS_32000
#define USE_RATE_CH5_32000 SNDRV_PCM_RATE_32000
#ifndef USE_RATE_MIN_CH5
#define USE_RATE_MIN_CH5 (32000)
#endif
#if (USE_RATE_MAX_CH5 < 32000)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (32000)
#endif
#else
#define USE_RATE_CH5_32000 0
#endif
#ifdef I2S_CH5_FS_44100
#define USE_RATE_CH5_44100 SNDRV_PCM_RATE_44100
#ifndef USE_RATE_MIN_CH5
#define USE_RATE_MIN_CH5 (44100)
#endif
#if (USE_RATE_MAX_CH5 < 44100)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (44100)
#endif
#else
#define USE_RATE_CH5_44100 0
#endif
#ifdef I2S_CH5_FS_48000
#define USE_RATE_CH5_48000 SNDRV_PCM_RATE_48000
#ifndef USE_RATE_MIN_CH5
#define USE_RATE_MIN_CH5 (48000)
#endif
#if (USE_RATE_MAX_CH5 < 48000)
#undef USE_RATE_MAX_CH5
#define USE_RATE_MAX_CH5 (48000)
#endif
#else
#define USE_RATE_CH5_48000 0
#endif
#define USE_RATE_CH5 (USE_RATE_CH5_8000|USE_RATE_CH5_11025 | \
USE_RATE_CH5_16000|USE_RATE_CH5_22050 | \
USE_RATE_CH5_32000|USE_RATE_CH5_44100 | \
USE_RATE_CH5_48000)
#define I2S_CH5_64FS (I2S_CH5_MCLK/64)
#define I2S_CH5_128FS (I2S_CH5_MCLK/128)
#define I2S_CH5_192FS (I2S_CH5_MCLK/192)
#define I2S_CH5_256FS (I2S_CH5_MCLK/256)
#define I2S_CH5_384FS (I2S_CH5_MCLK/384)
#define I2S_CH5_512FS (I2S_CH5_MCLK/512)
#define I2S_CH5_768FS (I2S_CH5_MCLK/768)
#define I2S_CH5_1024FS (I2S_CH5_MCLK/1024)
#if (I2S_CH5_MSSEL == ioh_mssel_master)
#ifdef I2S_CH5_FS_8000
#if (I2S_CH5_64FS != I2S_CH5_FS_8000) && \
(I2S_CH5_128FS != I2S_CH5_FS_8000) && \
(I2S_CH5_192FS != I2S_CH5_FS_8000) && \
(I2S_CH5_256FS != I2S_CH5_FS_8000) && \
(I2S_CH5_384FS != I2S_CH5_FS_8000) && \
(I2S_CH5_512FS != I2S_CH5_FS_8000) && \
(I2S_CH5_768FS != I2S_CH5_FS_8000) && \
(I2S_CH5_1024FS != I2S_CH5_FS_8000)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 8000Hz can not generate.
#endif
#endif
#ifdef I2S_CH5_FS_11025
#if (I2S_CH5_64FS != I2S_CH5_FS_11025) && \
(I2S_CH5_128FS != I2S_CH5_FS_11025) && \
(I2S_CH5_192FS != I2S_CH5_FS_11025) && \
(I2S_CH5_256FS != I2S_CH5_FS_11025) && \
(I2S_CH5_384FS != I2S_CH5_FS_11025) && \
(I2S_CH5_512FS != I2S_CH5_FS_11025) && \
(I2S_CH5_768FS != I2S_CH5_FS_11025) && \
(I2S_CH5_1024FS != I2S_CH5_FS_11025)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 11025Hz can not generate.
#endif
#endif
#ifdef I2S_CH5_FS_16000
#if (I2S_CH5_64FS != I2S_CH5_FS_16000) && \
(I2S_CH5_128FS != I2S_CH5_FS_16000) && \
(I2S_CH5_192FS != I2S_CH5_FS_16000) && \
(I2S_CH5_256FS != I2S_CH5_FS_16000) && \
(I2S_CH5_384FS != I2S_CH5_FS_16000) && \
(I2S_CH5_512FS != I2S_CH5_FS_16000) && \
(I2S_CH5_768FS != I2S_CH5_FS_16000) && \
(I2S_CH5_1024FS != I2S_CH5_FS_16000)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 16000Hz can not generate.
#endif
#endif
#ifdef I2S_CH5_FS_22050
#if (I2S_CH5_64FS != I2S_CH5_FS_22050) && \
(I2S_CH5_128FS != I2S_CH5_FS_22050) && \
(I2S_CH5_192FS != I2S_CH5_FS_22050) && \
(I2S_CH5_256FS != I2S_CH5_FS_22050) && \
(I2S_CH5_384FS != I2S_CH5_FS_22050) && \
(I2S_CH5_512FS != I2S_CH5_FS_22050) && \
(I2S_CH5_768FS != I2S_CH5_FS_22050) && \
(I2S_CH5_1024FS != I2S_CH5_FS_22050)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 22050Hz can not generate.
#endif
#endif
#ifdef I2S_CH5_FS_32000
#if (I2S_CH5_64FS != I2S_CH5_FS_32000) && \
(I2S_CH5_128FS != I2S_CH5_FS_32000) && \
(I2S_CH5_192FS != I2S_CH5_FS_32000) && \
(I2S_CH5_256FS != I2S_CH5_FS_32000) && \
(I2S_CH5_384FS != I2S_CH5_FS_32000) && \
(I2S_CH5_512FS != I2S_CH5_FS_32000) && \
(I2S_CH5_768FS != I2S_CH5_FS_32000) && \
(I2S_CH5_1024FS != I2S_CH5_FS_32000)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 32000Hz can not generate.
#endif
#endif
#ifdef I2S_CH5_FS_44100
#if (I2S_CH5_64FS != I2S_CH5_FS_44100) && \
(I2S_CH5_128FS != I2S_CH5_FS_44100) && \
(I2S_CH5_192FS != I2S_CH5_FS_44100) && \
(I2S_CH5_256FS != I2S_CH5_FS_44100) && \
(I2S_CH5_384FS != I2S_CH5_FS_44100) && \
(I2S_CH5_512FS != I2S_CH5_FS_44100) && \
(I2S_CH5_768FS != I2S_CH5_FS_44100) && \
(I2S_CH5_1024FS != I2S_CH5_FS_44100)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 44100Hz can not generate.
#endif
#endif
#ifdef I2S_CH5_FS_48000
#if (I2S_CH5_64FS != I2S_CH5_FS_48000) && \
(I2S_CH5_128FS != I2S_CH5_FS_48000) && \
(I2S_CH5_192FS != I2S_CH5_FS_48000) && \
(I2S_CH5_256FS != I2S_CH5_FS_48000) && \
(I2S_CH5_384FS != I2S_CH5_FS_48000) && \
(I2S_CH5_512FS != I2S_CH5_FS_48000) && \
(I2S_CH5_768FS != I2S_CH5_FS_48000) && \
(I2S_CH5_1024FS != I2S_CH5_FS_48000)
#error IOH_I2S_CH5_CONFIG Error : \
Sampling frequency 48000Hz can not generate.
#endif
#endif
#endif /* end of [#if (I2S_CH5_MSSEL == ioh_mssel_master)] */
/* ######################################################################## */
/* ### Parameter Table Data ### */
/* ######################################################################## */
#define I2SCLKCNT_MSSEL_OFFSET (0)
#define I2SCLKCNT_BCLKPOL_OFFSET (1)
#define I2SCLKCNT_MASTERCLKSEL_OFFSET (2)
#define I2SCLKCNT_LRCKFMT_OFFSET (4)
#define I2SCLKCNT_MCLKFS_OFFSET (8)
#define I2SCLKCNT_BCLKFS_OFFSET (12)
#define I2SCNT_DABIT_OFFSET (8)
#define I2SCNTTX_TX_TEL_OFFSET (0)
#define I2SCNTTX_TX_DLYOFF_OFFSET (12)
#define I2SCNTTX_TX_LSB_OFFSET (13)
#define I2SCNTTX_TX_LRPOL_OFFSET (14)
#define I2SCNTTX_TX_AFT_OFFSET (15)
#define I2SCNTRX_RX_TEL_OFFSET (0)
#define I2SCNTRX_RX_DLYOFF_OFFSET (12)
#define I2SCNTRX_RX_LSB_OFFSET (13)
#define I2SCNTRX_RX_LRPOL_OFFSET (14)
#define I2SCNTRX_RX_AFT_OFFSET (15)
#define STEREO_OFFSET 0
#define MONAURAL_OFFSET MAX_I2S_CH
static const struct i2s_config_tab_t i2s_config_table[MAX_I2S_CH*2] = {
/* CH0 Stereo */
{
(I2S_CH0_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH0_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH0_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH0_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH0_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH0_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
(I2S_CH0_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
(I2S_CH0_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH0_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH0_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
(I2S_CH0_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
(I2S_CH0_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH0_MCLK,
},
/* CH1 Stereo */
{
(I2S_CH1_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH1_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH1_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH1_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH1_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH1_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
(I2S_CH1_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
(I2S_CH1_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH1_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH1_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
(I2S_CH1_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
(I2S_CH1_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH1_MCLK,
},
/* CH2 Stereo */
{
(I2S_CH2_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH2_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH2_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH2_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH2_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH2_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
(I2S_CH2_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
(I2S_CH2_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH2_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH2_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
(I2S_CH2_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
(I2S_CH2_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH2_MCLK,
},
/* CH3 Stereo */
{
(I2S_CH3_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH3_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH3_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH3_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH3_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH3_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
(I2S_CH3_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
(I2S_CH3_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH3_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH3_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
(I2S_CH3_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
(I2S_CH3_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH3_MCLK,
},
/* CH4 Stereo */
{
(I2S_CH4_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH4_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH4_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH4_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH4_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH4_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
(I2S_CH4_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
(I2S_CH4_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH4_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH4_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
(I2S_CH4_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
(I2S_CH4_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH4_MCLK,
},
/* CH5 Stereo */
{
(I2S_CH5_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH5_BCLKPOL_STEREO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH5_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH5_LRCKFMT_STEREO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH5_TX_DLYOFF_STEREO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH5_TX_LSB_STEREO << I2SCNTTX_TX_LSB_OFFSET) | \
(I2S_CH5_TX_LRPOL_STEREO << I2SCNTTX_TX_LRPOL_OFFSET) | \
(I2S_CH5_TX_AFT_STEREO << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH5_RX_DLYOFF_STEREO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH5_RX_LSB_STEREO << I2SCNTRX_RX_LSB_OFFSET) | \
(I2S_CH5_RX_LRPOL_STEREO << I2SCNTRX_RX_LRPOL_OFFSET) | \
(I2S_CH5_RX_AFT_STEREO << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH5_MCLK,
},
/* CH0 Mono */
{
(I2S_CH0_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH0_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH0_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH0_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH0_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH0_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH0_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH0_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH0_MCLK,
},
/* CH1 Mono */
{
(I2S_CH1_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH1_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH1_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH1_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH1_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH1_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH1_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH1_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH1_MCLK,
},
/* CH2 Mono */
{
(I2S_CH2_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH2_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH2_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH2_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH2_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH2_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH2_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH2_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH2_MCLK,
},
/* CH3 Mono */
{
(I2S_CH3_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH3_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH3_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH3_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH3_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH3_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH3_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH3_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH3_MCLK,
},
/* CH4 Mono */
{
(I2S_CH4_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH4_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH4_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH4_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH4_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH4_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH4_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH4_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH4_MCLK,
},
/* CH5 Mono */
{
(I2S_CH5_MSSEL << I2SCLKCNT_MSSEL_OFFSET) | \
(I2S_CH5_BCLKPOL_MONO << I2SCLKCNT_BCLKPOL_OFFSET) | \
(I2S_CH5_MASTERCLKSEL << I2SCLKCNT_MASTERCLKSEL_OFFSET) | \
(I2S_CH5_LRCKFMT_MONO << I2SCLKCNT_LRCKFMT_OFFSET),
(I2S_CH5_TX_DLYOFF_MONO << I2SCNTTX_TX_DLYOFF_OFFSET) | \
(I2S_CH5_TX_LSB_MONO << I2SCNTTX_TX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTTX_TX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTTX_TX_AFT_OFFSET),
(I2S_CH5_RX_DLYOFF_MONO << I2SCNTRX_RX_DLYOFF_OFFSET) | \
(I2S_CH5_RX_LSB_MONO << I2SCNTRX_RX_LSB_OFFSET) | \
(ioh_lrpol_no_invert << I2SCNTRX_RX_LRPOL_OFFSET) | \
(ioh_aft_front << I2SCNTRX_RX_AFT_OFFSET),
I2S_CH5_MCLK,
},
};
#endif
[-- Attachment #5: ml7213ioh-machine.c --]
[-- Type: text/plain, Size: 2280 bytes --]
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
static struct snd_soc_dai_link ioh_i2s_dai = {
.name = "I2S",
.stream_name = "I2S HiFi",
.cpu_dai_name = "ml7213ioh-i2s",
.codec_dai_name = "ml7213ioh-hifi",
.platform_name = "ml7213ioh-pcm-audio",
.codec_name = "ml26124-codec",
};
static struct snd_soc_card ioh_i2s_card = {
.name = "ml7213ioh i2s",
.dai_link = &ioh_i2s_dai,
.num_links = 1,
};
static struct platform_device *soc_pdev;
static int __init ioh_i2s_probe(struct platform_device *pdev)
{
int ret;
soc_pdev = platform_device_alloc("soc-audio", -1);
if (!soc_pdev)
return -ENOMEM;
platform_set_drvdata(soc_pdev, &ioh_i2s_card);
ret = platform_device_add(soc_pdev);
if (ret) {
platform_device_put(soc_pdev);
return ret;
}
return 0;
}
static int __exit ioh_i2s_remove(struct platform_device *pdev)
{
platform_device_unregister(soc_pdev);
return 0;
}
static struct platform_driver ioh_i2s_driver = {
.remove = ioh_i2s_remove,
.driver = {
.name = "ml7213ioh-machine",
.owner = THIS_MODULE,
},
};
static int __init ioh_i2s_init(void)
{
return platform_driver_probe(&ioh_i2s_driver,
ioh_i2s_probe);
}
static void __exit ioh_i2s_exit(void)
{
platform_driver_unregister(&ioh_i2s_driver);
}
module_init(ioh_i2s_init);
module_exit(ioh_i2s_exit);
MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.com>");
MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC machine driver");
MODULE_LICENSE("GPL");
[-- Attachment #6: ml7213ioh-plat.c --]
[-- Type: text/plain, Size: 66728 bytes --]
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include "ioh_i2s.h"
#include "ioh_i2s_config.h"
#include "ml7213ioh-plat.h"
#define USE_PERIODS_MIN (I2S_DMA_SG_MAX)
#define USE_PERIODS_MAX (I2S_DMA_SG_MAX)
#ifndef add_capture_constraints
#define add_capture_constraints(x) 0
#endif
#define I2S_WRITE_MASTER_BIT (0)
#define I2S_READ_MASTER_BIT (8)
#define I2S_WRITE_ENABLE_BIT (0)
#define I2S_READ_ENABLE_BIT (8)
/* RW flag */
#define SND_CAPTURE_SUBSTREAM 0
#define SND_PLAYBACK_SUBSTREAM 1
/* Codec Device Address */
#define I2SCNTRX_RXTEL BIT(0)
#define I2SCNTRX_RXDABIT_8BIT 0
#define I2SCNTRX_RXDABIT_14BIT BIT(8)
#define I2SCNTRX_RXDABIT_16BIT BIT(9)
#define I2SCNTRX_RXDABIT_18BIT (BIT(8) | BIT(9))
#define I2SCNTRX_RXDABIT_20BIT BIT(10)
#define I2SCNTRX_RXDABIT_24BIT (BIT(8) | BIT(10))
#define I2SCNTRX_RXDLYOFF BIT(12)
#define I2SCNTRX_RX_LSB BIT(13)
#define I2SCNTTX_TXTEL I2SCNTRX_RXTEL
#define I2SCNTTX_TXDABIT_8BIT I2SCNTRX_RXDABIT_8BIT
#define I2SCNTTX_TXDABIT_14BIT I2SCNTRX_RXDABIT_14BIT
#define I2SCNTTX_TXDABIT_16BIT I2SCNTRX_RXDABIT_16BIT
#define I2SCNTTX_TXDABIT_18BIT I2SCNTRX_RXDABIT_18BIT
#define I2SCNTTX_TXDABIT_20BIT I2SCNTRX_RXDABIT_20BIT
#define I2SCNTTX_TXDABIT_24BIT I2SCNTRX_RXDABIT_24BIT
#define I2SCNTTX_TXDLYOFF I2SCNTRX_RXDLYOFF
#define I2SCNTTX_TX_LSB I2SCNTRX_RX_LSB
#define I2SCLKCNT_MCLKFS_64FS 0
#define I2SCLKCNT_MCLKFS_128FS BIT(8)
#define I2SCLKCNT_MCLKFS_192FS BIT(9)
#define I2SCLKCNT_MCLKFS_256FS (BIT(8) | BIT(9))
#define I2SCLKCNT_MCLKFS_384FS BIT(10)
#define I2SCLKCNT_MCLKFS_512FS (BIT(8) | BIT(10))
#define I2SCLKCNT_MCLKFS_768FS (BIT(9) | BIT(10))
#define I2SCLKCNT_MCLKFS_1024FS (BIT(8) | BIT(9) | BIT(10))
#define I2SCLKCNT_BCLKFS_8FS 0
#define I2SCLKCNT_BCLKFS_16FS BIT(12)
#define I2SCLKCNT_BCLKFS_32FS BIT(13)
#define I2SCLKCNT_BCLKFS_64FS (BIT(12) | BIT(13))
#define I2SCLKCNT_MSSEL BIT(0)
#define I2SCLKCNT_BCLKPOL BIT(1)
#define I2SCLKCNT_LRCKFMT_I2S 0
#define I2SCLKCNT_LRCKFMT_LONG BIT(5)
#define I2SCLKCNT_LRCKFMT_SHORT (BIT(4)|BIT(5))
#define DRV_NAME "ml7213_ioh_i2s"
#define PCI_VENDOR_ID_ROHM 0X10DB
#define PCI_DEVICE_ID_ML7213_I2S 0X8033
struct snd_ml7213i2s {
struct snd_card *card;
struct snd_pcm *pcm;
struct ioh_i2s_data_pci *pci_dat;
};
struct cbdata {
struct ioh_i2s_data *priv;
int stop;
int cnt;
};
struct snd_ml7213i2s_pcm {
struct snd_ml7213i2s *ml7213i2s;
spinlock_t lock;
unsigned int irq_pos;
unsigned int buf_pos;
struct snd_pcm_substream *substream;
struct cbdata cbd; /* i2s callback info */
unsigned int channels;
unsigned int rw;
unsigned int rate;
unsigned int ch;
unsigned int setup_flag;
unsigned int format;
unsigned int bclkfs;
unsigned int master_mode;
unsigned int enable_mode;
};
static struct snd_pcm_hardware snd_card_ml7213i2s_capture[MAX_I2S_CH] = {
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH0,
.rate_min = USE_RATE_MIN_CH0,
.rate_max = USE_RATE_MAX_CH0,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_RX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_RX,
.period_bytes_max = MAX_PERIOD_SIZE_RX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH1,
.rate_min = USE_RATE_MIN_CH1,
.rate_max = USE_RATE_MAX_CH1,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_RX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_RX,
.period_bytes_max = MAX_PERIOD_SIZE_RX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH2,
.rate_min = USE_RATE_MIN_CH2,
.rate_max = USE_RATE_MAX_CH2,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_RX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_RX,
.period_bytes_max = MAX_PERIOD_SIZE_RX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH3,
.rate_min = USE_RATE_MIN_CH3,
.rate_max = USE_RATE_MAX_CH3,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_RX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_RX,
.period_bytes_max = MAX_PERIOD_SIZE_RX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH4,
.rate_min = USE_RATE_MIN_CH4,
.rate_max = USE_RATE_MAX_CH4,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_RX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_RX,
.period_bytes_max = MAX_PERIOD_SIZE_RX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH5,
.rate_min = USE_RATE_MIN_CH5,
.rate_max = USE_RATE_MAX_CH5,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_RX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_RX,
.period_bytes_max = MAX_PERIOD_SIZE_RX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
};
static struct snd_pcm_hardware snd_card_ml7213i2s_playback[MAX_I2S_CH] = {
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH0,
.rate_min = USE_RATE_MIN_CH0,
.rate_max = USE_RATE_MAX_CH0,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_TX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_TX,
.period_bytes_max = MAX_PERIOD_SIZE_TX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH1,
.rate_min = USE_RATE_MIN_CH1,
.rate_max = USE_RATE_MAX_CH1,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_TX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_TX,
.period_bytes_max = MAX_PERIOD_SIZE_TX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH2,
.rate_min = USE_RATE_MIN_CH2,
.rate_max = USE_RATE_MAX_CH2,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_TX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_TX,
.period_bytes_max = MAX_PERIOD_SIZE_TX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH3,
.rate_min = USE_RATE_MIN_CH3,
.rate_max = USE_RATE_MAX_CH3,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_TX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_TX,
.period_bytes_max = MAX_PERIOD_SIZE_TX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH4,
.rate_min = USE_RATE_MIN_CH4,
.rate_max = USE_RATE_MAX_CH4,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_TX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_TX,
.period_bytes_max = MAX_PERIOD_SIZE_TX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SUPPORT_FORMAT,
.rates = USE_RATE_CH5,
.rate_min = USE_RATE_MIN_CH5,
.rate_max = USE_RATE_MAX_CH5,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = (MAX_PERIOD_SIZE_TX *
USE_PERIODS_MAX),
.period_bytes_min = MAX_PERIOD_SIZE_TX,
.period_bytes_max = MAX_PERIOD_SIZE_TX,
.periods_min = USE_PERIODS_MIN,
.periods_max = USE_PERIODS_MAX,
.fifo_size = 0,
},
};
static int ignore_overrun = 1;
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
module_param(ignore_overrun, int, 0444);
module_param(index, int, 0444);
MODULE_PARM_DESC(ignore_overrun, "ignore RX overruns (default=0)");
MODULE_PARM_DESC(index, "Index value for ML7213 IOH I2S Controller Reference");
/*****************************************************************************
* I2S HAL (Hardware Abstruction Layer)
*****************************************************************************/
static void ioh_i2s_reset(struct ioh_i2s_data *priv)
{
int channel = priv->ch;
iowrite32(1 << channel, priv->iobase + I2SSRST_OFFSET);
iowrite32(0, priv->iobase + I2SSRST_OFFSET);
}
static void ioh_i2s_enable_interrupts(struct ioh_i2s_data *priv,
enum dma_data_direction dir)
{
int channel = priv->ch;
unsigned int intr_lines;
if (dir)
intr_lines = 1 << (I2S_IMASK_RX_BIT_START + channel);
else
intr_lines = 1 << (I2S_IMASK_TX_BIT_START + channel);
/*enable interrupts for specified channel */
iowrite32(intr_lines, priv->iobase + I2SIMASKCLR_OFFSET);
}
static void ioh_i2s_disable_interrupts(struct ioh_i2s_data *priv,
enum dma_data_direction dir)
{
int channel = priv->ch;
unsigned int intr_lines;
/*intr_lines&=I2S_ALL_INTERRUPT_BITS; */
intr_lines = ioread32(priv->iobase + I2SIMASK_OFFSET);
/*disable interrupts for specified channel */
if (dir)
intr_lines |= 1 << (I2S_IMASK_RX_BIT_START + channel);
else
intr_lines |= 1 << (I2S_IMASK_TX_BIT_START + channel);
/*Mask the specific interrupt bits */
iowrite32(intr_lines, priv->iobase + I2SIMASK_OFFSET);
}
/* Run FIFO */
static void ioh_i2s_run_tx_fifo(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SFIFOCTX_OFFSET + offset);
val |= I2S_FIFO_TX_RUN;
iowrite32(val, priv->iobase + I2SFIFOCTX_OFFSET + offset);
}
/* Clear TX FIFO */
static void ioh_i2s_clear_tx_fifo(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SFIFOCTX_OFFSET + offset);
val |= I2S_FIFO_TX_FCLR;
iowrite32(val, priv->iobase + I2SFIFOCTX_OFFSET + offset);
}
/* Clear interrupt status */
static void ioh_i2s_clear_tx_sts_ir(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
iowrite32(I2S_TX_FINT | I2S_TX_AFINT | I2S_TX_EINT | I2S_TX_AEINT,
priv->iobase + I2SISTTX_OFFSET + offset);
}
/* Run FIFO */
static void ioh_i2s_run_rx_fifo(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SFIFOCRX_OFFSET + offset);
val |= I2S_FIFO_RX_RUN;
iowrite32(val, priv->iobase + I2SFIFOCRX_OFFSET + offset);
}
/* Clear RX FIFO */
static void ioh_i2s_clear_rx_fifo(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SFIFOCRX_OFFSET + offset);
val |= I2S_FIFO_RX_FCLR;
iowrite32(val, priv->iobase + I2SFIFOCRX_OFFSET + offset);
}
/* Clear interrupt status */
static void ioh_i2s_clear_rx_sts_ir(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
iowrite32(I2S_RX_FINT | I2S_RX_AFINT | I2S_RX_EINT | I2S_RX_AEINT,
priv->iobase + I2SISTRX_OFFSET + offset);
}
/* Clear DMA mask setting */
static void ioh_i2s_tx_clear_dma_mask(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SMSKTX_OFFSET + offset);
val &= ~TX_BIT_DMAMSK; /* Enable Tx DMA Request */
iowrite32(val, priv->iobase + I2SMSKTX_OFFSET + offset);
}
/* Clear DMA mask setting */
static void ioh_i2s_rx_clear_dma_mask(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SMSKRX_OFFSET + offset);
val &= ~RX_BIT_DMAMSK; /* Enable Rx DMA Request */
iowrite32(val, priv->iobase + I2SMSKRX_OFFSET + offset);
}
/* Clear the mask setting of the corresponding interrupt source bit */
static void ioh_i2s_enable_tx_empty_ir(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SMSKTX_OFFSET + offset);
val &= ~TX_BIT_AEIMSK; /* Enable Almost empty interrupt */
val &= ~TX_BIT_EIMSK; /* Enable Empty interrupt */
iowrite32(val, priv->iobase + I2SMSKTX_OFFSET + offset);
}
/* Clear the mask setting of the corresponding interrupt source bit */
static void ioh_i2s_enable_rx_full_ir(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SMSKRX_OFFSET + offset);
val &= ~RX_BIT_AFIMSK; /* Enable Almost empty interrupt */
val &= ~RX_BIT_FIMSK; /* Enable Empty interrupt */
iowrite32(val, priv->iobase + I2SMSKRX_OFFSET + offset);
}
static void ioh_i2s_disable_tx_empty_ir(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SMSKTX_OFFSET + offset);
val |= TX_BIT_AEIMSK; /* Disble Almost empty interrupt */
val |= TX_BIT_EIMSK; /* Disble Empty interrupt */
iowrite32(val, priv->iobase + I2SMSKTX_OFFSET + offset);
}
static void ioh_i2s_disable_rx_full_ir(struct ioh_i2s_data *priv)
{
int ch = priv->ch;
int offset = ch * 0x800;
u32 val;
val = ioread32(priv->iobase + I2SMSKRX_OFFSET + offset);
val |= RX_BIT_AFIMSK; /* Disble Almost full interrupt */
val |= RX_BIT_FIMSK; /* Disble full interrupt */
iowrite32(val, priv->iobase + I2SMSKRX_OFFSET + offset);
}
/*****************************************************************************
* I2S Middle ware
*****************************************************************************/
static bool filter(struct dma_chan *chan, void *slave)
{
struct pch_dma_slave *param = slave;
if ((chan->chan_id == param->chan_id) && (param->dma_dev ==
chan->device->dev)) {
chan->private = param;
return true;
} else {
return false;
}
}
static struct dma_chan *ioh_request_dma_channel_common(
struct ioh_i2s_data *priv,
char *chan_name,
enum dma_data_direction dir)
{
dma_cap_mask_t mask;
struct dma_chan *chan;
struct pci_dev *dma_dev;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0, 1)); /* Get DMA's dev
information */
if (dir == DMA_FROM_DEVICE) { /* Rx */
priv->param_rx.width = priv->dma_rx_width;
priv->param_rx.dma_dev = &dma_dev->dev;
priv->param_rx.chan_id = priv->ch * 2 + 1; /* ch Rx=1,3,...11 */
priv->param_rx.rx_reg = (dma_addr_t)(priv->mapbase +\
priv->ch * 0x800 +\
I2SDRRXMIRROR_OFFSET);
chan = dma_request_channel(mask, filter, &priv->param_rx);
if (chan == NULL) {
dev_err(priv->dev, "Failed dma_request_channel %s for"
" I2S %s\n", chan_name, priv->rx_name);
return NULL;
}
priv->chan_rx = chan;
} else if (dir == DMA_TO_DEVICE) { /* Tx */
priv->param_tx.width = priv->dma_tx_width;
priv->param_tx.dma_dev = &dma_dev->dev;
priv->param_tx.chan_id = priv->ch * 2; /* DMA ch Tx=0,2,...10 */
priv->param_tx.tx_reg = (dma_addr_t)(priv->mapbase +\
priv->ch * 0x800 +\
I2SDRTXMIRROR_OFFSET);
priv->param_tx.tx_reg_list = NULL;
chan = dma_request_channel(mask, filter, &priv->param_tx);
if (chan == NULL) {
dev_err(priv->dev, "Failed dma_request_channel %s for"
" I2S %s\n", chan_name, priv->tx_name);
return NULL;
}
priv->chan_tx = chan;
} else {
dev_err(priv->dev, "%s:Invalid direction (%d)\n",
chan_name, dir);
return NULL;
}
return chan;
}
static void ioh_setup_rx_dma(struct ioh_i2s_data *priv)
{
ioh_request_dma_channel_common(
priv,
priv->dma_config->rx_chan,
DMA_FROM_DEVICE);
}
static void ioh_setup_tx_dma(struct ioh_i2s_data *priv)
{
ioh_request_dma_channel_common(
priv,
priv->dma_config->tx_chan,
DMA_TO_DEVICE);
}
static void ioh_i2s_ignore_rx_overrun(struct ioh_i2s_data *priv)
{
priv->ignore_rx_overrun = 1;
}
static void ioh_i2s_write(struct ioh_i2s_data *priv,
const void *data,
int len)
{
int rem1;
int rem2;
int tx_index;
struct scatterlist *sg = priv->sg_tx_p;
int t_num = 0;
int l;
int *ptr_fmt;
int *ptr32;
short *ptr16;
char *ptr8;
if (priv->tx_avail >= INTER_BUFF_SIZE) {
dev_err(priv->dev, "%s[%d]: internal buffer full\n",
__func__, priv->ch);
return;
}
dev_dbg(priv->dev, "%s: [ch%d] len=%d data_head=%p data_complete=%p",
__func__, priv->ch, len, priv->tx_data_head, priv->tx_complete);
if ((priv->tx_data_head + ((len/priv->dma_tx_unit)*4)) <=
priv->tx_tail) {
tx_index = (int)(priv->tx_data_head - priv->tx_head) /
(I2S_AEMPTY_THRESH * 4);
sg = sg + tx_index;
t_num = len/(I2S_AEMPTY_THRESH * priv->dma_tx_unit);
dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);
ptr_fmt = (int *)priv->tx_data_head;
switch (priv->dma_tx_unit) {
case 1:
ptr8 = (char *)data;
for (l = 0; l < (len/priv->dma_tx_unit); l++)
*ptr_fmt++ = (int)*ptr8++;
break;
case 2:
ptr16 = (short *)data;
for (l = 0; l < (len/priv->dma_tx_unit); l++)
*ptr_fmt++ = (int)*ptr16++;
break;
case 4:
ptr32 = (int *)data;
for (l = 0; l < (len/priv->dma_tx_unit); l++)
*ptr_fmt++ = *ptr32++;
break;
}
dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
priv->tx_data_head += (len/priv->dma_tx_unit)*4;
} else {
rem1 = (priv->tx_tail - priv->tx_data_head)/4;
rem2 = (len/priv->dma_tx_unit) - rem1;
tx_index = (int)(priv->tx_data_head-priv->tx_head) /
(I2S_AEMPTY_THRESH * 4);
sg = sg + tx_index;
t_num = rem1/I2S_AEMPTY_THRESH;
dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);
ptr_fmt = (int *)priv->tx_data_head;
switch (priv->dma_tx_unit) {
case 1:
ptr8 = (char *)data;
for (l = 0; l < rem1; l++)
*ptr_fmt++ = (int)*ptr8++;
break;
case 2:
ptr16 = (short *)data;
for (l = 0; l < rem1; l++)
*ptr_fmt++ = (int)*ptr16++;
break;
case 4:
ptr32 = (int *)data;
for (l = 0; l < rem1; l++)
*ptr_fmt++ = *ptr32++;
break;
}
dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
priv->tx_data_head = priv->tx_head;
sg = priv->sg_tx_p;
t_num = rem2/I2S_AEMPTY_THRESH;
dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_TO_DEVICE);
ptr_fmt = (int *)priv->tx_data_head;
switch (priv->dma_tx_unit) {
case 1:
ptr8 = (char *)(data+rem1*priv->dma_tx_unit);
for (l = 0; l < rem2; l++)
*ptr_fmt++ = (int)*ptr8++;
break;
case 2:
ptr16 = (short *)(data+rem1*priv->dma_tx_unit);
for (l = 0; l < rem2; l++)
*ptr_fmt++ = (int)*ptr16++;
break;
case 4:
ptr32 = (int *)(data+rem1*priv->dma_tx_unit);
for (l = 0; l < rem2; l++)
*ptr_fmt++ = *ptr32++;
break;
}
dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_TO_DEVICE);
priv->tx_data_head += rem2*4;
}
if (priv->tx_data_head >= priv->tx_tail)
priv->tx_data_head = priv->tx_head;
dev_dbg(priv->dev, "-->data_head=%p\n", priv->tx_data_head);
priv->tx_avail += (len/priv->dma_tx_unit)*4;
}
static void ioh_i2s_stop_i2s_regs(struct ioh_i2s_data *priv,
unsigned long bitrate,
struct ioh_i2s_config_reg *config,
enum ioh_direction dir,
unsigned int flag)
{
int ch = priv->ch;
if (dir) {
/* Interrupt stop */
ioh_i2s_disable_rx_full_ir(priv);
/* FIFO setting */
ioh_i2s_clear_rx_fifo(priv);
ioh_i2s_clear_rx_sts_ir(priv);
} else {
/* Interrupt stop */
ioh_i2s_disable_tx_empty_ir(priv);
/* FIFO setting */
ioh_i2s_clear_tx_fifo(priv);
ioh_i2s_clear_tx_sts_ir(priv);
}
/* Common register */
if (!flag) {
iowrite32(config->cmn.i2sclkcnt,
priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
}
}
static void ioh_i2s_configure_i2s_regs(struct ioh_i2s_data *priv,
unsigned long bitrate,
struct ioh_i2s_config_reg *config,
enum ioh_direction dir,
unsigned int unit)
{
int ch = priv->ch;
int offset = ch * 0x800;
/* Common register */
iowrite32(config->cmn.i2sclkcnt,
priv->iobase + I2SCLKCNT0_OFFSET + 0x10*ch);
if (dir) {
priv->dma_rx_unit = unit;
/* Rx register */
iowrite32(config->rx.i2scntrx,
priv->iobase + I2SCNTRX_OFFSET + offset);
iowrite32(config->rx.i2sfifocrx,
priv->iobase + I2SFIFOCRX_OFFSET + offset);
iowrite32(config->rx.i2safrx,
priv->iobase + I2SAFRX_OFFSET + offset);
iowrite32(config->rx.i2saerx,
priv->iobase + I2SAERX_OFFSET + offset);
iowrite32(config->rx.i2smskrx,
priv->iobase + I2SMSKRX_OFFSET + offset);
iowrite32(config->rx.i2sistrx,
priv->iobase + I2SISTRX_OFFSET + offset);
/* FIFO setting */
ioh_i2s_clear_rx_fifo(priv);
ioh_i2s_run_rx_fifo(priv);
/* Interrupt setting */
ioh_i2s_clear_rx_sts_ir(priv);
ioh_i2s_enable_rx_full_ir(priv);
} else {
priv->dma_tx_unit = unit;
/* Tx register */
iowrite32(config->tx.i2scnttx,
priv->iobase + I2SCNTTX_OFFSET + offset);
iowrite32(config->tx.i2sfifoctx,
priv->iobase + I2SFIFOCTX_OFFSET + offset);
iowrite32(config->tx.i2saftx,
priv->iobase + I2SAFTX_OFFSET + offset);
iowrite32(config->tx.i2saetx,
priv->iobase + I2SAETX_OFFSET + offset);
iowrite32(config->tx.i2smsktx,
priv->iobase + I2SMSKTX_OFFSET + offset);
iowrite32(config->tx.i2sisttx,
priv->iobase + I2SISTTX_OFFSET + offset);
/* FIFO setting */
ioh_i2s_clear_tx_fifo(priv);
ioh_i2s_run_tx_fifo(priv);
/* Interrupt setting */
ioh_i2s_clear_tx_sts_ir(priv);
ioh_i2s_enable_tx_empty_ir(priv);
}
}
static void i2s_rx_tasklet(unsigned long data)
{
struct ioh_i2s_data *priv = (struct ioh_i2s_data *)data;
int num = 0;
if (priv->rxexe_flag) {
if (priv->rx_done) {
switch (priv->dma_rx_unit) {
case 1:
num = priv->rx_avail/4;
break;
case 2:
num = priv->rx_avail/2;
break;
case 4:
num = priv->rx_avail;
break;
}
priv->rx_done(priv->rx_callback_data,
IOH_EOK, num, num);
}
}
}
static void i2s_tx_tasklet(unsigned long data)
{
struct ioh_i2s_data *priv = (struct ioh_i2s_data *)data;
int num = 0;
int avail = 0;
if (priv->txexe_flag) {
if (priv->tx_done) {
switch (priv->dma_tx_unit) {
case 1:
num = (INTER_BUFF_SIZE - priv->tx_avail)/4;
avail = priv->tx_avail/4;
break;
case 2:
num = (INTER_BUFF_SIZE - priv->tx_avail)/2;
avail = priv->tx_avail/2;
break;
case 4:
num = (INTER_BUFF_SIZE - priv->tx_avail);
avail = priv->tx_avail;
break;
}
priv->tx_done(priv->tx_callback_data,
IOH_EOK, num, avail);
}
}
}
static void ioh_i2s_release(struct ioh_i2s_data *priv, enum ioh_direction dir)
{
if (!priv) {
dev_err(priv->dev, "%s: i2s is NULL\n", __func__);
return;
}
if (dir) {
dma_sync_sg_for_cpu(priv->dev, priv->sg_rx_p, priv->rx_nent,
DMA_FROM_DEVICE);
ioh_i2s_disable_interrupts(priv, IOH_CAPTURE);
ioh_i2s_disable_rx_full_ir(priv);
if (priv->chan_rx) {
priv->chan_rx->device->device_control(priv->chan_rx,
DMA_TERMINATE_ALL,
0);
dma_release_channel(priv->chan_rx);
priv->chan_rx = NULL;
}
kfree(priv->sg_rx_p);
if (priv->rxbuf_virt)
dma_free_coherent(priv->dev, INTER_BUFF_SIZE,
priv->rxbuf_virt,
priv->rx_buf_dma);
priv->rxbuf_virt = NULL;
priv->rx_buf_dma = 0;
atomic_dec(&priv->rx_busy);
tasklet_disable(&priv->rx_tasklet);
tasklet_kill(&priv->rx_tasklet);
} else {
dma_sync_sg_for_cpu(priv->dev, priv->sg_tx_p, priv->tx_nent,
DMA_TO_DEVICE);
ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
ioh_i2s_disable_tx_empty_ir(priv);
if (priv->chan_tx) {
priv->chan_tx->device->device_control(priv->chan_tx,
DMA_TERMINATE_ALL,
0);
dma_release_channel(priv->chan_tx);
priv->chan_tx = NULL;
}
kfree(priv->sg_tx_p);
if (priv->txbuf_virt)
dma_free_coherent(priv->dev, INTER_BUFF_SIZE,
priv->txbuf_virt,
priv->tx_buf_dma);
priv->txbuf_virt = NULL;
priv->tx_buf_dma = 0;
atomic_dec(&priv->tx_busy);
tasklet_disable(&priv->tx_tasklet);
tasklet_kill(&priv->tx_tasklet);
}
}
static struct ioh_i2s_data *ioh_i2s_open(int ch, enum ioh_direction dir,
const char *name, void *cbd,
void (*cb)(void *cbd, int status,
int num, int avail))
{
struct ioh_i2s_data *obj = NULL;
struct scatterlist *sg;
int rx_size;
int rx_num;
int tx_size;
int tx_num;
int i;
if (ch >= MAX_I2S_IF) {
dev_err(obj->dev,
"Tried to open i2s with number %d which is more then"
" the available number\n", ch);
return 0;
}
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
obj->ignore_rx_overrun = 0;
obj->dma_tx_width = PCH_DMA_WIDTH_4_BYTES;
obj->dma_rx_width = PCH_DMA_WIDTH_4_BYTES;
obj->ch = ch;
obj->dev = obj->dev;
atomic_set(&obj->rx_busy, 0);
atomic_set(&obj->tx_busy, 0);
strcpy(obj->rx_name, "FREE");
strcpy(obj->tx_name, "FREE");
if (dir) {
/* Rx configuration */
if (atomic_read(&obj->rx_busy)) {
dev_err(obj->dev, "rx i2s%d have already opened\n", ch);
atomic_dec(&obj->rx_busy);
return 0;
}
atomic_inc(&obj->rx_busy);
strcpy(obj->rx_name, name);
ioh_setup_rx_dma(obj);
if (!obj->chan_rx) {
dev_err(obj->dev, "%s:ioh_setup_rx_dma failed\n",
__func__);
return NULL;
}
obj->rxbuf_virt = dma_alloc_coherent(obj->dev, INTER_BUFF_SIZE,
&obj->rx_buf_dma, GFP_KERNEL);
if (!obj->rxbuf_virt) {
dev_err(obj->dev, "dma_alloc_coherent Failed\n");
return NULL;
}
rx_size = I2S_AFULL_THRESH * 4;
/* The number of scatter list (Franction area is not used) */
rx_num = INTER_BUFF_SIZE / rx_size;
dev_dbg(obj->dev, "%s: rx: scatter_num=%d scatter_size=%d\n",
__func__, rx_num, rx_size);
obj->sg_rx_p =\
kzalloc(sizeof(struct scatterlist) *rx_num, GFP_ATOMIC);
sg = obj->sg_rx_p;
sg_init_table(sg, rx_num); /* Initialize SG table */
for (i = 0; i < rx_num; i++, sg++) {
sg_set_page(sg, virt_to_page(obj->rxbuf_virt), rx_size,
rx_size * i);
sg_dma_len(sg) = rx_size / 4;
sg_dma_address(sg) = obj->rx_buf_dma + sg->offset;
}
obj->rx_head = (unsigned char *)obj->rxbuf_virt;
obj->rx_tail = (unsigned char *)obj->rxbuf_virt +
rx_num * rx_size;
obj->rx_data_head = (unsigned char *)obj->rxbuf_virt;
obj->rx_complete = (unsigned char *)obj->rxbuf_virt;
obj->rx_avail = 0;
obj->rx_nent = rx_num;
dma_sync_sg_for_device(obj->dev, obj->sg_rx_p, obj->rx_nent,
DMA_FROM_DEVICE);
tasklet_init(&obj->rx_tasklet, i2s_rx_tasklet,
(unsigned long)obj);
} else {
/* Tx configuration */
if (atomic_read(&obj->tx_busy)) {
dev_err(obj->dev, "tx i2s%d have already opened\n", ch);
atomic_dec(&obj->tx_busy);
return 0;
}
atomic_inc(&obj->tx_busy);
strcpy(obj->tx_name, name);
ioh_setup_tx_dma(obj);
if (!obj->chan_tx) {
dev_err(obj->dev, "%s:ioh_setup_tx_dma failed\n",
__func__);
return NULL;
}
tx_size = I2S_AEMPTY_THRESH * 4;
if (INTER_BUFF_SIZE % tx_size)
/* tx_num = The number of scatter list */
tx_num = INTER_BUFF_SIZE / tx_size + 1;
else
tx_num = INTER_BUFF_SIZE / tx_size;
obj->txbuf_virt = dma_alloc_coherent(obj->dev, INTER_BUFF_SIZE,
&obj->tx_buf_dma, GFP_KERNEL);
if (!obj->txbuf_virt) {
dev_err(obj->dev, "dma_alloc_coherent Failed\n");
return NULL;
}
obj->tx_head = (unsigned char *)obj->txbuf_virt;
obj->tx_tail = (unsigned char *)obj->txbuf_virt +
INTER_BUFF_SIZE;
obj->tx_data_head = (unsigned char *)obj->txbuf_virt;
obj->tx_complete = (unsigned char *)obj->txbuf_virt;
obj->tx_avail = 0;
dev_dbg(obj->dev, "%s: tx: scatter_num=%d scatter_size=%d\n",
__func__, tx_num, tx_size);
obj->sg_tx_p =\
kzalloc(sizeof(struct scatterlist) *tx_num, GFP_ATOMIC);
sg_init_table(obj->sg_tx_p, tx_num); /* Initialize SG table */
sg = obj->sg_tx_p;
for (i = 0; i < tx_num; i++, sg++) {
if (i == (tx_num - 1)) {
if (INTER_BUFF_SIZE % tx_size) {
sg_set_page(sg,
virt_to_page(obj->txbuf_virt),
INTER_BUFF_SIZE % tx_size,
tx_size * i);
sg_dma_len(sg) =
(INTER_BUFF_SIZE % tx_size)
/ 4;
} else {
sg_set_page(sg,
virt_to_page(obj->txbuf_virt),
tx_size, tx_size * i);
sg_dma_len(sg) = tx_size\
/ 4;
}
} else {
sg_set_page(sg, virt_to_page(obj->txbuf_virt),
tx_size, tx_size * i);
sg_dma_len(sg) = tx_size / 4;
}
sg_dma_address(sg) = obj->tx_buf_dma + sg->offset;
}
obj->tx_nent = tx_num;
dma_sync_sg_for_device(obj->dev, obj->sg_tx_p, obj->tx_nent,
DMA_TO_DEVICE);
tasklet_init(&obj->tx_tasklet, i2s_tx_tasklet,
(unsigned long)obj);
}
return obj;
}
static void ioh_i2s_read(struct ioh_i2s_data *priv,
void *data,
int len)
{
unsigned int rem1 = 0, rem2 = 0;
struct scatterlist *sg = priv->sg_rx_p;
int rx_index;
int t_num = 0;
int *ptr_fmt;
int *ptr32;
short *ptr16;
char *ptr8;
int l;
switch (priv->dma_rx_unit) {
case 1:
t_num = priv->rx_avail/4;
break;
case 2:
t_num = priv->rx_avail/2;
break;
case 4:
t_num = priv->rx_avail;
break;
}
if (t_num < len) {
dev_err(priv->dev, "%s[%d]: internal buffer empty\n",
__func__, priv->ch);
return;
}
if ((priv->rx_complete + ((len/priv->dma_rx_unit)*4)) <=
priv->rx_tail) {
rx_index = (int)(priv->rx_complete - priv->rx_head) /
(I2S_AFULL_THRESH * 4);
sg = sg + rx_index;
t_num = len/(I2S_AFULL_THRESH * priv->dma_rx_unit);
dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);
ptr_fmt = (int *)priv->rx_complete;
switch (priv->dma_rx_unit) {
case 1:
ptr8 = (char *)data;
for (l = 0; l < (len/priv->dma_rx_unit); l++)
*ptr8++ = (char)*ptr_fmt++;
break;
case 2:
ptr16 = (short *)data;
for (l = 0; l < (len/priv->dma_rx_unit); l++)
*ptr16++ = (short)*ptr_fmt++;
break;
case 4:
ptr32 = (int *)data;
for (l = 0; l < (len/priv->dma_rx_unit); l++)
*ptr32++ = *ptr_fmt++;
break;
}
dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
priv->rx_complete += (len/priv->dma_rx_unit)*4;
} else {
rem1 = (priv->rx_tail - priv->rx_complete)/4;
rem2 = (len/priv->dma_rx_unit) - rem1;
rx_index = (int)(priv->rx_complete-priv->rx_head) /
(I2S_AFULL_THRESH * 4);
sg = sg + rx_index;
t_num = rem1/I2S_AFULL_THRESH;
dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);
ptr_fmt = (int *)priv->rx_complete;
switch (priv->dma_rx_unit) {
case 1:
ptr8 = (char *)data;
for (l = 0; l < rem1; l++)
*ptr8++ = (char)*ptr_fmt++;
break;
case 2:
ptr16 = (short *)data;
for (l = 0; l < rem1; l++)
*ptr16++ = (short)*ptr_fmt++;
break;
case 4:
ptr32 = (int *)data;
for (l = 0; l < rem1; l++)
*ptr32++ = *ptr_fmt++;
break;
}
dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
priv->rx_complete = priv->rx_head;
sg = priv->sg_rx_p;
t_num = rem2/I2S_AFULL_THRESH;
dma_sync_sg_for_cpu(priv->dev, sg, t_num, DMA_FROM_DEVICE);
ptr_fmt = (int *)priv->rx_complete;
switch (priv->dma_rx_unit) {
case 1:
ptr8 = (char *)(data+rem1*priv->dma_rx_unit);
for (l = 0; l < rem2; l++)
*ptr8++ = (char)*ptr_fmt++;
break;
case 2:
ptr16 = (short *)(data+rem1*priv->dma_rx_unit);
for (l = 0; l < rem2; l++)
*ptr16++ = (short)*ptr_fmt++;
break;
case 4:
ptr32 = (int *)(data+rem1*priv->dma_rx_unit);
for (l = 0; l < rem2; l++)
*ptr32++ = *ptr_fmt++;
break;
}
dma_sync_sg_for_device(priv->dev, sg, t_num, DMA_FROM_DEVICE);
priv->rx_complete += rem2*4;
}
if (priv->rx_complete >= priv->rx_tail)
priv->rx_complete = priv->rx_head;
priv->rx_avail -= (len/priv->dma_rx_unit)*4;
}
static void i2s_dma_rx_complete(void *arg)
{
struct ioh_i2s_data *priv = arg;
struct scatterlist *sg = priv->sg_rx_cur;
int num = priv->rx_num;
int i;
async_tx_ack(priv->desc_rx);
for (i = 0; i < num; i++, sg++) {
priv->rx_data_head += sg_dma_len(sg) * 4;
priv->rx_avail += sg_dma_len(sg) * 4;
}
if (priv->rx_data_head >= priv->rx_tail)
priv->rx_data_head = priv->rx_head;
ioh_i2s_clear_rx_sts_ir(priv);
ioh_i2s_enable_rx_full_ir(priv);
}
static void i2s_dma_tx_complete(void *arg)
{
struct ioh_i2s_data *priv = arg;
struct scatterlist *sg = priv->sg_tx_cur;
int num = priv->tx_num;
int i;
async_tx_ack(priv->desc_tx);
for (i = 0; i < num; i++, sg++) {
priv->tx_complete += sg_dma_len(sg) * 4;
priv->tx_avail -= sg_dma_len(sg) * 4;
}
if (priv->tx_complete >= priv->tx_tail)
priv->tx_complete = priv->tx_head;
ioh_i2s_clear_tx_sts_ir(priv);
ioh_i2s_enable_tx_empty_ir(priv);
}
/*****************************************************************************
* Interrupt control
*****************************************************************************/
static void i2s_tx_almost_empty_ir(struct ioh_i2s_data *priv)
{
struct dma_async_tx_descriptor *desc;
int num;
int tx_comp_index;
struct scatterlist *sg = priv->sg_tx_p;
dev_dbg(priv->dev, "%s: data_head=%p data_complete=%p\n", __func__,
priv->tx_data_head, priv->tx_complete);
num = ((int)priv->tx_avail) / (I2S_AEMPTY_THRESH * 4);
tx_comp_index = (((int)(priv->tx_complete - priv->tx_head))) /\
(I2S_AEMPTY_THRESH * 4);
if ((tx_comp_index + num) >= priv->tx_nent)
num = priv->tx_nent - tx_comp_index;
if (num > I2S_DMA_SG_NUM)
num = I2S_DMA_SG_NUM;
if (!num) {
dev_err(priv->dev, "%s:Internal buffer empty\n",
__func__);
tasklet_schedule(&priv->tx_tasklet);
return; /* No data to transmit */
}
sg = sg + tx_comp_index; /* Point head of sg must be sent */
priv->sg_tx_cur = sg; /* Save tx condition */
priv->tx_num = num; /* Save tx condition */
desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx,
sg, num, DMA_TO_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
__func__);
return;
}
/* To prevent this function from calling again before DMA completion */
ioh_i2s_disable_tx_empty_ir(priv);
priv->desc_tx = desc;
desc->callback = i2s_dma_tx_complete;
desc->callback_param = priv;
atomic_inc(&priv->pending_tx);
desc->tx_submit(desc);
tasklet_schedule(&priv->tx_tasklet);
}
void i2s_tx_empty_ir(struct ioh_i2s_data *priv, int ch)
{
dev_warn(priv->dev, "%s:I2S under flow occurs(ch=%d)\n", __func__, ch);
}
void i2s_rx_full_ir(struct ioh_i2s_data *priv, int ch)
{
dev_warn(priv->dev, "%s:I2S overrun occurs(ch=%d)\n", __func__, ch);
}
static inline void ioh_i2s_interrupt_sub_tx(struct ioh_i2s_data *priv)
{
unsigned int status;
int channel = priv->ch;
int offset = channel * 0x800;
status = ioread32(priv->iobase + I2SISTTX_OFFSET + offset);
if (status & I2S_TX_EINT)
i2s_tx_empty_ir(priv, channel);
if (status & I2S_TX_AEINT)
i2s_tx_almost_empty_ir(priv);
/*Clear the interrupt status */
iowrite32(status, priv->iobase + I2SISTTX_OFFSET + offset);
}
static void i2s_rx_almost_full_ir(struct ioh_i2s_data *priv)
{
struct dma_async_tx_descriptor *desc;
struct scatterlist *sg;
int rx_data_index;
int num;
num = (int)(INTER_BUFF_SIZE - priv->rx_avail) / (I2S_AFULL_THRESH * 4);
if (num < 1) {
dev_err(priv->dev, "%s:Internal buffer full\n",
__func__);
tasklet_schedule(&priv->rx_tasklet);
return;
}
sg = priv->sg_rx_p;
rx_data_index = ((int)(priv->rx_data_head - priv->rx_head)) /\
(I2S_AFULL_THRESH * 4);
if ((rx_data_index + num) >= priv->rx_nent)
num = priv->rx_nent - rx_data_index;
if (num > I2S_DMA_SG_NUM)
num = I2S_DMA_SG_NUM;
sg += rx_data_index;
desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx,
sg, num, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_err(priv->dev, "%s:device_prep_slave_sg Failed\n",
__func__);
return;
}
priv->sg_rx_cur = sg; /* Save rx condition */
ioh_i2s_disable_rx_full_ir(priv);
priv->rx_num = num;
priv->desc_rx = desc;
desc->callback = i2s_dma_rx_complete;
desc->callback_param = priv;
desc->tx_submit(desc);
tasklet_schedule(&priv->rx_tasklet);
}
static inline void ioh_i2s_interrupt_sub_rx(struct ioh_i2s_data *priv)
{
unsigned int status;
int channel = priv->ch;
int offset = channel * 0x800;
status = ioread32(priv->iobase + I2SISTRX_OFFSET + offset);
if (status & I2S_RX_FINT)
i2s_rx_full_ir(priv, channel);
if (status & I2S_RX_AFINT)
i2s_rx_almost_full_ir(priv);
/*Clear the interrupt status */
iowrite32(status, priv->iobase + I2SISTRX_OFFSET + offset);
}
void ioh_i2s_event(struct ioh_i2s_data *priv, u32 idisp)
{
unsigned long flags;
spin_lock_irqsave(&priv->tx_lock, flags);
if (idisp & BIT(priv->ch + 16)) {
dev_dbg(priv->dev, "Rx%d interrupt occures\n", priv->ch);
ioh_i2s_interrupt_sub_rx(priv);
}
if (idisp & BIT(priv->ch)) {
dev_dbg(priv->dev, "Tx%d interrupt occures\n", priv->ch);
ioh_i2s_interrupt_sub_tx(priv);
}
spin_unlock_irqrestore(&priv->tx_lock, flags);
return;
}
static irqreturn_t ioh_i2s_irq(int irq, void *data)
{
struct pci_dev *pdev = (struct pci_dev *)data;
struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
int i;
u32 idisp;
struct ioh_i2s_data *priv;
priv = &drvdata->devs[0];
idisp = ioread32(priv->iobase + I2SIDISP_OFFSET);
for (i = 0; i < MAX_I2S_IF; i++)
ioh_i2s_event(&drvdata->devs[i], idisp);
return IRQ_HANDLED;
}
/*****************************************************************************
* Sound Card
*****************************************************************************/
static void i2s_read_period(struct snd_pcm_substream *substream)
{
struct snd_ml7213i2s_pcm *dpcm;
struct cbdata *cbd;
int period;
void *read_ptr;
int read_size;
dpcm = substream->runtime->private_data;
cbd = &dpcm->cbd;
if (!cbd->priv)
return;
period = substream->runtime->period_size;
read_ptr = substream->runtime->dma_area
+(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
read_size = period * (substream->runtime->sample_bits/8) *
(substream->runtime->channels);
ioh_i2s_read(cbd->priv,
read_ptr,
read_size);
dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
}
static void read_done(void *callback_data, int status, int num, int avail)
{
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)callback_data;
struct snd_ml7213i2s_pcm *dpcm;
struct cbdata *cbd;
dpcm = substream->runtime->private_data;
if (num < snd_card_ml7213i2s_capture[dpcm->ch].period_bytes_max)
return;
cbd = &dpcm->cbd;
switch (status) {
case IOH_EOK:
if (!cbd->stop) {
i2s_read_period(substream);
dpcm->buf_pos = (dpcm->buf_pos + 1) %
substream->runtime->periods;
snd_pcm_period_elapsed(dpcm->substream);
}
cbd->cnt++;
break;
case IOH_EDONE:
pr_debug("Done stopping channel %d\n", cbd->stop);
cbd->stop = 2;
break;
case IOH_EOVERRUN:
if (ignore_overrun)
pr_debug("overrun ignore\n");
else {
pr_err("RX overrun\n");
cbd->stop = 2;
}
break;
case IOH_EFRAMESYNC:
pr_err("Frame sync error\n");
cbd->stop = 2;
break;
}
if (cbd->stop)
pr_debug("stopping... %d\n", cbd->stop);
}
static int i2s_config_rate_reg(struct ioh_i2s_config_reg *config,
unsigned int rate,
struct snd_ml7213i2s_pcm *dpcm)
{
unsigned int i2s_mclk;
unsigned int ret;
i2s_mclk = i2s_config_table[dpcm->ch+STEREO_OFFSET].i2s_mclk;
if ((i2s_mclk/64) == rate)
ret = ioh_mclkfs_64fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/128) == rate)
ret = ioh_mclkfs_128fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/192) == rate)
ret = ioh_mclkfs_192fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/256) == rate)
ret = ioh_mclkfs_256fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/384) == rate)
ret = ioh_mclkfs_384fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/512) == rate)
ret = ioh_mclkfs_512fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/768) == rate)
ret = ioh_mclkfs_768fs << I2SCLKCNT_MCLKFS_OFFSET;
else if ((i2s_mclk/1024) == rate)
ret = ioh_mclkfs_1024fs << I2SCLKCNT_MCLKFS_OFFSET;
else
ret = 0;
return ret;
}
static int i2s_rx_configure_reg(struct ioh_i2s_config_reg *config,
unsigned int rate,
struct snd_ml7213i2s_pcm *dpcm)
{
int ret = 0;
memset(config, 0, sizeof(*config));
/* Set ML7213 IOH register default value */
config->cmn.i2simask = 0x003f003f;
config->rx.i2saerx = 0x1F;
config->rx.i2smskrx = 0x1F;
config->rx.i2sistrx = 0xC;
/* Configuration */
if (dpcm->channels == 1) {
config->rx.i2scntrx =
i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2scntrx |
dpcm->format |
(ioh_tel_tel_fmt << I2SCNTRX_RX_TEL_OFFSET);
config->cmn.i2sclkcnt = dpcm->bclkfs |
i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2sclkcnt;
} else {
config->rx.i2scntrx =
i2s_config_table[dpcm->ch+STEREO_OFFSET].i2scntrx |
dpcm->format |
(ioh_tel_i2s_fmt << I2SCNTRX_RX_TEL_OFFSET);
config->cmn.i2sclkcnt = dpcm->bclkfs |
i2s_config_table[dpcm->ch+STEREO_OFFSET].i2sclkcnt;
}
config->cmn.i2sclkcnt |= i2s_config_rate_reg(config, rate, dpcm);
config->rx.i2safrx = I2S_AFULL_THRESH / 2; /* Almost full threshold */
if (((config->cmn.i2sclkcnt & 0x3000) == 0x3000) &&
((config->cmn.i2sclkcnt & 0x7) == 0x2)) {
pr_err("%s: Failed not support setting\n", __func__);
ret = -1;
}
return ret;
}
static int setup_i2s_read(struct snd_pcm_substream *substream, int ch)
{
struct snd_ml7213i2s_pcm *dpcm;
struct ioh_i2s_config_reg config;
unsigned int master;
int ret = 0;
unsigned int byte;
master = (i2s_config_table[ch].i2sclkcnt >> I2SCLKCNT_MSSEL_OFFSET) & 1;
dpcm = substream->runtime->private_data;
dpcm->cbd.priv = ioh_i2s_open(ch, IOH_CAPTURE, "radio-i2s-in",
substream,
read_done);
dpcm->setup_flag = 1;
if (!dpcm->cbd.priv) {
pr_err("%s: Cannot open the device\n", __func__);
return -1;
}
if (ignore_overrun)
ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
ret = i2s_rx_configure_reg(&config, dpcm->rate, dpcm);
if (master)
dpcm->master_mode |= 1 << (ch + I2S_READ_MASTER_BIT);
else
dpcm->master_mode |= 0 << (ch + I2S_READ_MASTER_BIT);
switch (dpcm->format) {
case (ioh_dabit_8bit << I2SCNT_DABIT_OFFSET):
byte = 1;
break;
case (ioh_dabit_16bit << I2SCNT_DABIT_OFFSET):
byte = 2;
break;
case (ioh_dabit_24bit << I2SCNT_DABIT_OFFSET):
byte = 4;
break;
default:
pr_err("%s: format error\n", __func__);
return -1;
break;
}
ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_CAPTURE,
byte);
return ret;
}
static void i2s_write_period(struct snd_pcm_substream *substream)
{
struct snd_ml7213i2s_pcm *dpcm;
struct cbdata *cbd;
int period;
void *write_ptr;
int write_size;
dpcm = substream->runtime->private_data;
cbd = &dpcm->cbd;
if (!cbd->priv)
return;
period = substream->runtime->period_size;
write_ptr = substream->runtime->dma_area
+(snd_pcm_lib_period_bytes(substream) * dpcm->irq_pos);
write_size = period * (substream->runtime->sample_bits/8) *
(substream->runtime->channels);
ioh_i2s_write(cbd->priv,
write_ptr,
write_size);
dpcm->irq_pos = (dpcm->irq_pos + 1) % substream->runtime->periods;
}
static void write_done(void *callback_data, int status, int num, int avail)
{
struct snd_pcm_substream *substream =
(struct snd_pcm_substream *)callback_data;
struct snd_ml7213i2s_pcm *dpcm;
struct cbdata *cbd;
dpcm = substream->runtime->private_data;
if (num < snd_card_ml7213i2s_playback[dpcm->ch].period_bytes_max)
return;
if (avail >= snd_card_ml7213i2s_playback[dpcm->ch].period_bytes_max*2)
return;
if (!substream) {
pr_debug("%s:!substream NULL\n", __func__);
return;
}
if (!substream->runtime) {
pr_debug("%s:!substream->runtime NULL\n", __func__);
return;
}
if (!substream->runtime->private_data) {
pr_debug("%s:!substream->runtime->private_data NULL\n",
__func__);
return;
}
cbd = &dpcm->cbd;
switch (status) {
case IOH_EOK:
if (!cbd->stop) {
i2s_write_period(substream);
dpcm->buf_pos = (dpcm->buf_pos + 1) %
substream->runtime->periods;
snd_pcm_period_elapsed(dpcm->substream);
}
cbd->cnt++;
break;
case IOH_EDONE:
pr_debug("Done stopping channel %d\n", cbd->stop);
cbd->stop = 2;
break;
default:
pr_debug("%s:default(%d)\n", __func__, status);
break;
}
}
static int i2s_tx_configure_reg(struct ioh_i2s_config_reg *config,
unsigned int rate,
struct snd_ml7213i2s_pcm *dpcm)
{
int ret = 0;
memset(config, 0, sizeof(*config));
/* Set ML7213 IOH register default value */
config->cmn.i2simask = 0x003f003f;
config->tx.i2saftx = 0x0;
config->tx.i2smsktx = 0x1F;
config->tx.i2sisttx = 0xC;
/* Configuration */
if (dpcm->channels == 1) {
config->tx.i2scnttx =
i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2scnttx |
dpcm->format |
(ioh_tel_tel_fmt << I2SCNTTX_TX_TEL_OFFSET);
config->cmn.i2sclkcnt = dpcm->bclkfs |
i2s_config_table[dpcm->ch+MONAURAL_OFFSET].i2sclkcnt;
} else {
config->tx.i2scnttx =
i2s_config_table[dpcm->ch+STEREO_OFFSET].i2scnttx |
dpcm->format |
(ioh_tel_i2s_fmt << I2SCNTTX_TX_TEL_OFFSET);
config->cmn.i2sclkcnt = dpcm->bclkfs |
i2s_config_table[dpcm->ch+STEREO_OFFSET].i2sclkcnt;
}
config->cmn.i2sclkcnt |= i2s_config_rate_reg(config, rate, dpcm);
config->tx.i2saetx = I2S_AEMPTY_THRESH / 2; /* Almost empty threshold */
if (((config->cmn.i2sclkcnt & 0x3000) == 0x3000) &&
((config->cmn.i2sclkcnt & 0x7) == 0x2)) {
pr_err("%s: Failed not support setting\n", __func__);
ret = -1;
}
return ret;
}
static int setup_i2s_write(struct snd_pcm_substream *substream, int ch)
{
struct snd_ml7213i2s_pcm *dpcm;
struct ioh_i2s_config_reg config;
unsigned int master;
int ret = 0;
unsigned int byte;
master = (i2s_config_table[ch].i2sclkcnt >> I2SCLKCNT_MSSEL_OFFSET) & 1;
dpcm = substream->runtime->private_data;
dpcm->cbd.priv = ioh_i2s_open(ch, IOH_PLAYBACK, "radio-i2s-out",
substream,
write_done);
dpcm->setup_flag = 1;
if (!dpcm->cbd.priv) {
pr_err("%s: Cannot open the device\n", __func__);
return -1;
}
if (ignore_overrun)
ioh_i2s_ignore_rx_overrun(dpcm->cbd.priv);
ret = i2s_tx_configure_reg(&config, dpcm->rate, dpcm);
if (master)
dpcm->master_mode |= 1 << (ch + I2S_WRITE_MASTER_BIT);
else
dpcm->master_mode |= 0 << (ch + I2S_WRITE_MASTER_BIT);
switch (dpcm->format) {
case (ioh_dabit_8bit << I2SCNT_DABIT_OFFSET):
byte = 1;
break;
case (ioh_dabit_16bit << I2SCNT_DABIT_OFFSET):
byte = 2;
break;
case (ioh_dabit_24bit << I2SCNT_DABIT_OFFSET):
byte = 4;
break;
default:
pr_err("%s: format error\n", __func__);
return -1;
break;
}
ioh_i2s_configure_i2s_regs(dpcm->cbd.priv, 0, &config, IOH_PLAYBACK,
byte);
return ret;
}
static void __snd_card_ml7213i2s_runtime_free(struct snd_pcm_runtime *runtime)
{
struct snd_ml7213i2s_pcm *dpcm;
static int cnt;
dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
/* FIXME: This is just a big ball of race right now...
*/
if (!dpcm->cbd.stop)
dpcm->cbd.stop = 1;
else {
while (dpcm->cbd.stop != 2) {
if (cnt++ > 100) {
pr_debug("oops, failed to close ml7213i2s..\n");
pr_debug("it's ok if i2s isn't running\n");
break;
}
msleep(20);
}
}
}
static void snd_card_ml7213i2s_runtime_capture_free
(struct snd_pcm_runtime *runtime)
{
struct snd_ml7213i2s_pcm *dpcm;
dpcm = (struct snd_ml7213i2s_pcm *)runtime->private_data;
__snd_card_ml7213i2s_runtime_free(runtime);
if (dpcm->setup_flag)
ioh_i2s_release(dpcm->cbd.priv, IOH_CAPTURE);
kfree(runtime->private_data);
}
static struct snd_ml7213i2s_pcm *
new_pcm_stream(struct snd_pcm_substream *substream)
{
struct snd_ml7213i2s_pcm *dpcm;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (!dpcm)
return dpcm;
spin_lock_init(&dpcm->lock);
dpcm->substream = substream;
return dpcm;
}
static int snd_card_ml7213i2s_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ml7213i2s_pcm *dpcm;
int err;
dpcm = new_pcm_stream(substream);
if (dpcm == NULL)
return -ENOMEM;
runtime->private_data = dpcm;
/* makes the infrastructure responsible for freeing dpcm */
runtime->private_free = snd_card_ml7213i2s_runtime_capture_free;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
dpcm->rw = SND_CAPTURE_SUBSTREAM;
runtime->hw = snd_card_ml7213i2s_capture[substream->number];
} else {
dpcm->rw = SND_PLAYBACK_SUBSTREAM;
runtime->hw = snd_card_ml7213i2s_playback[substream->number];
}
dpcm->setup_flag = 0;
err = add_capture_constraints(runtime);
if (err < 0)
return err;
return 0;
}
static int snd_card_ml7213i2s_close(struct snd_pcm_substream *substream)
{
return 0;
}
static int snd_card_ml7213i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
unsigned int enable_flag = 0;
unsigned int ch = substream->number;
dpcm->channels = params_channels(hw_params);
dpcm->ch = substream->number;
dpcm->rate = params_rate(hw_params);
switch (params_format(hw_params)) {
case SNDRV_PCM_FORMAT_U8:
dpcm->format = ioh_dabit_8bit << I2SCNT_DABIT_OFFSET;
if (dpcm->channels == 1)
dpcm->bclkfs = ioh_bclkfs_32fs <<
I2SCLKCNT_BCLKFS_OFFSET;
else
dpcm->bclkfs = ioh_bclkfs_32fs <<
I2SCLKCNT_BCLKFS_OFFSET;
break;
case SNDRV_PCM_FORMAT_S16_LE:
dpcm->format = ioh_dabit_16bit << I2SCNT_DABIT_OFFSET;
if (dpcm->channels == 1)
dpcm->bclkfs = ioh_bclkfs_32fs <<
I2SCLKCNT_BCLKFS_OFFSET;
else
dpcm->bclkfs = ioh_bclkfs_32fs <<
I2SCLKCNT_BCLKFS_OFFSET;
break;
case SNDRV_PCM_FORMAT_S32_LE:
dpcm->format = ioh_dabit_24bit << I2SCNT_DABIT_OFFSET;
if (dpcm->channels == 1)
dpcm->bclkfs = ioh_bclkfs_64fs <<
I2SCLKCNT_BCLKFS_OFFSET;
else
dpcm->bclkfs = ioh_bclkfs_64fs <<
I2SCLKCNT_BCLKFS_OFFSET;
break;
default:
pr_err("%s: Failed not support format\n", __func__);
return -1;
break;
}
switch (dpcm->rw) {
case SND_CAPTURE_SUBSTREAM:
if (setup_i2s_read(substream, substream->number))
return -1;
dpcm->enable_mode |= 1 << (ch + I2S_READ_ENABLE_BIT);
enable_flag = (dpcm->enable_mode >> (ch+I2S_WRITE_ENABLE_BIT)) & 1;
break;
case SND_PLAYBACK_SUBSTREAM:
if (setup_i2s_write(substream, substream->number))
return -1;
dpcm->enable_mode |= 1 << (ch + I2S_WRITE_ENABLE_BIT);
enable_flag = (dpcm->enable_mode >> (ch+I2S_READ_ENABLE_BIT)) & 1;
break;
default:
return -1;
}
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int snd_card_ml7213i2s_hw_free(struct snd_pcm_substream *substream)
{
struct snd_ml7213i2s_pcm *dpcm = substream->runtime->private_data;
struct ioh_i2s_config_reg config;
unsigned int flag;
unsigned int enable_flag = 0;
unsigned int ch = substream->number;
switch (dpcm->rw) {
case SND_CAPTURE_SUBSTREAM:
i2s_rx_configure_reg(&config, dpcm->rate, dpcm);
config.cmn.i2sclkcnt &= ~I2SCLKCNT_MSSEL;
dpcm->master_mode &= ~(1<<(ch+I2S_READ_MASTER_BIT));
flag = (dpcm->master_mode >> (ch+I2S_WRITE_MASTER_BIT)) & 1;
dpcm->enable_mode &= ~(1<<(ch+I2S_READ_ENABLE_BIT));
enable_flag = (dpcm->enable_mode >> (ch+I2S_WRITE_ENABLE_BIT)) & 1;
if (dpcm->setup_flag) {
ioh_i2s_stop_i2s_regs(dpcm->cbd.priv, 0, &config,
IOH_CAPTURE, flag);
kfree(dpcm->cbd.priv);
}
break;
case SND_PLAYBACK_SUBSTREAM:
i2s_tx_configure_reg(&config, dpcm->rate, dpcm);
config.cmn.i2sclkcnt &= ~I2SCLKCNT_MSSEL;
dpcm->master_mode &= ~(1<<(ch+I2S_WRITE_MASTER_BIT));
flag = (dpcm->master_mode >> (ch+I2S_READ_MASTER_BIT)) & 1;
dpcm->enable_mode &= ~(1<<(ch+I2S_WRITE_ENABLE_BIT));
enable_flag = (dpcm->enable_mode >> (ch+I2S_READ_ENABLE_BIT)) & 1;
if (dpcm->setup_flag) {
ioh_i2s_stop_i2s_regs(dpcm->cbd.priv, 0, &config,
IOH_PLAYBACK, flag);
kfree(dpcm->cbd.priv);
}
break;
default:
return -1;
}
if (!enable_flag) {
pr_err("%s: enable_flag is NULL\n", __func__);
return -1;
}
return snd_pcm_lib_free_pages(substream);
}
void ioh_i2s_irq_stop(struct ioh_i2s_data *priv, enum ioh_direction dir)
{
if (!priv) {
dev_err(priv->dev, "%s: i2s is NULL\n", __func__);
return;
}
if (dir) {
ioh_i2s_disable_interrupts(priv, IOH_CAPTURE);
ioh_i2s_disable_rx_full_ir(priv);
ioh_i2s_clear_rx_sts_ir(priv);
priv->rxexe_flag = 0;
} else {
ioh_i2s_disable_interrupts(priv, IOH_PLAYBACK);
ioh_i2s_disable_tx_empty_ir(priv);
ioh_i2s_clear_tx_sts_ir(priv);
priv->txexe_flag = 0;
}
}
void ioh_i2s_write_start(struct ioh_i2s_data *priv, void *callback_data,
void (*done) (void *callback_data, int status,
int num, int avail))
{
priv->txexe_flag = 1;
priv->tx_data_head = priv->tx_head;
priv->tx_complete = priv->tx_head;
priv->tx_avail = 0;
priv->tx_callback_data = callback_data;
priv->tx_done = done;
ioh_i2s_clear_tx_sts_ir(priv);
ioh_i2s_tx_clear_dma_mask(priv);
ioh_i2s_enable_interrupts(priv, IOH_PLAYBACK);
}
void ioh_i2s_read_start(struct ioh_i2s_data *priv, void *callback_data,
void (*done) (void *callback_data, int status,
int num, int avail))
{
priv->rxexe_flag = 1;
priv->rx_data_head = priv->rx_head;
priv->rx_complete = priv->rx_head;
priv->rx_avail = 0;
priv->rx_callback_data = callback_data;
priv->rx_done = done;
ioh_i2s_clear_rx_sts_ir(priv);
ioh_i2s_rx_clear_dma_mask(priv);
ioh_i2s_enable_interrupts(priv, IOH_CAPTURE);
}
static inline void
snd_card_ml7213i2s_pcm_i2s_start(struct snd_pcm_substream *substream)
{
struct snd_ml7213i2s_pcm *dpcm;
struct cbdata *cbd;
dpcm = substream->runtime->private_data;
cbd = &dpcm->cbd;
if (!cbd->priv)
return;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
ioh_i2s_read_start(cbd->priv, substream, read_done);
} else {
ioh_i2s_write_start(cbd->priv, substream, write_done);
i2s_write_period(substream);
}
}
static int snd_card_ml7213i2s_pcm_trigger
(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
int err = 0;
spin_lock(&dpcm->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
dpcm->cbd.stop = 0;
snd_card_ml7213i2s_pcm_i2s_start(substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
pr_debug("stop..\n");
if (!dpcm->cbd.stop)
dpcm->cbd.stop = 1;
else
pr_debug("already stopped %d\n", dpcm->cbd.stop);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
ioh_i2s_irq_stop(dpcm->cbd.priv, IOH_CAPTURE);
else
ioh_i2s_irq_stop(dpcm->cbd.priv, IOH_PLAYBACK);
break;
default:
err = -EINVAL;
break;
}
spin_unlock(&dpcm->lock);
return 0;
}
static int snd_card_ml7213i2s_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
dpcm->irq_pos = 0;
dpcm->buf_pos = 0;
snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
bytes_to_samples(runtime, runtime->dma_bytes));
return 0;
}
static snd_pcm_uframes_t
snd_card_ml7213i2s_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ml7213i2s_pcm *dpcm = runtime->private_data;
return substream->runtime->period_size*dpcm->buf_pos;
}
static struct snd_pcm_ops snd_card_ml7213i2s_capture_ops = {
.open = snd_card_ml7213i2s_open,
.close = snd_card_ml7213i2s_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_card_ml7213i2s_hw_params,
.hw_free = snd_card_ml7213i2s_hw_free,
.prepare = snd_card_ml7213i2s_pcm_prepare,
.trigger = snd_card_ml7213i2s_pcm_trigger,
.pointer = snd_card_ml7213i2s_pcm_pointer,
};
static int ml7213ioh_pcm_probe(struct snd_soc_platform *platform)
{
return 0;
}
static int ml7213ioh_pcm_remove(struct snd_soc_platform *platform)
{
return 0;
}
static struct snd_soc_platform_driver ml7213ioh_soc_platform = {
.probe = ml7213ioh_pcm_probe,
.remove = ml7213ioh_pcm_remove,
.ops = &snd_card_ml7213i2s_capture_ops,
};
static int __devinit snd_card_ml7213i2s_pcm(struct snd_ml7213i2s *ml7213i2s,
int device)
{
struct snd_pcm *pcm;
int err;
/* The number of I2S interface is (Rx + Tx) x 6CH */
err = snd_pcm_new(ml7213i2s->card, "ml7213i2s PCM", device,
MAX_I2S_TX_CH, MAX_I2S_RX_CH, &pcm);
if (err < 0)
return err;
ml7213i2s->pcm = pcm;
pcm->private_data = ml7213i2s;
pcm->info_flags = 0;
strcpy(pcm->name, "ml7213i2s PCM");
snd_pcm_lib_preallocate_pages_for_all(
pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
0, 64*1024);
return 0;
}
static struct snd_device_ops ops = {NULL};
/*****************************************************************************
* PCI functions
*****************************************************************************/
DEFINE_PCI_DEVICE_TABLE(ioh_pci_tbl) = {
{
.vendor = PCI_VENDOR_ID_ROHM,
.device = PCI_DEVICE_ID_ML7213_I2S,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{0,}
};
static int ioh_i2s_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int rv = 0;
int i;
int err;
struct ioh_i2s_data_pci *drvdata;
void __iomem *tbl;
unsigned int mapbase;
struct snd_card *card;
struct snd_ml7213i2s *ml7213i2s;
int dev = 0;
drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
pci_set_drvdata(pdev, drvdata);
rv = pci_enable_device(pdev);
if (rv)
goto out_free;
tbl = pci_iomap(pdev, 1, 0);
if (!tbl) {
rv = -ENOMEM;
printk(KERN_ERR "pci_iomap failed\n");
goto out_ipmap;
}
mapbase = pci_resource_start(pdev, 1);
if (!mapbase) {
rv = -ENOMEM;
printk(KERN_ERR "pci_resource_start failed\n");
goto out_pci_resource_start;
}
drvdata->membase = tbl;
drvdata->mapbase = mapbase;
drvdata->dev = &pdev->dev;
for (i = 0; i < MAX_I2S_IF; i++) {
drvdata->devs[i].iobase = tbl;
drvdata->devs[i].mapbase = mapbase;
spin_lock_init(&drvdata->devs[i].tx_lock);
drvdata->devs[i].dma_config = &ioh_dma_config[i];
}
rv = request_irq(pdev->irq, ioh_i2s_irq, IRQF_SHARED, "ml7213_ioh",
pdev);
if (rv != 0) {
printk(KERN_ERR "Failed to allocate irq\n");
goto out_irq;
}
err = snd_card_create(index, "ml7213i2s", THIS_MODULE,
sizeof(struct snd_ml7213i2s), &card);
if (err < 0)
goto out_snd_card_create;
ml7213i2s = card->private_data;
ml7213i2s->card = card;
ml7213i2s->pci_dat = drvdata;
err = snd_card_ml7213i2s_pcm(ml7213i2s, 0);
if (err < 0)
goto snd_card_ml7213i2s_pc;
strcpy(card->driver, "ml7213i2s");
strcpy(card->shortname, "ml7213i2s");
sprintf(card->longname, "ml7213i2s %i", dev + 1);
snd_card_set_dev(card, &pdev->dev);
snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml7213i2s, &ops);
err = snd_card_register(card);
if (err)
goto out_snd_card_register;
drvdata->card = card;
snd_soc_register_platform(&pdev->dev, &ml7213ioh_soc_platform);
return 0;
out_snd_card_register:
snd_card_ml7213i2s_pc:
snd_card_free(card);
out_snd_card_create:
free_irq(pdev->irq, pdev);
out_irq:
out_pci_resource_start:
pci_iounmap(pdev, tbl);
out_ipmap:
pci_disable_device(pdev);
out_free:
kfree(drvdata);
return rv;
}
static void ioh_i2s_pci_remove(struct pci_dev *pdev)
{
struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
int i;
for (i = 0; i < MAX_I2S_IF; i++)
ioh_i2s_reset(&drvdata->devs[i]);
snd_soc_unregister_platform(&pdev->dev);
kfree(drvdata);
pci_disable_device(pdev);
pci_iounmap(pdev, drvdata->membase);
free_irq(pdev->irq, pdev);
snd_card_free(drvdata->card);
pci_set_drvdata(pdev, NULL);
}
static void ioh_i2s_save_reg_conf(struct pci_dev *pdev)
{
int i;
struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
void *iobase;
struct ioh_i2s_pm_ch_reg *save;
int offset;
for (i = 0, offset = 0; i < MAX_I2S_IF; i++, offset = i * 0x800) {
iobase = drvdata->devs[i].iobase;
save = &drvdata->devs[i].ch_reg_save;
save->i2sdrtx = ioread32(iobase + offset + I2SDRTX_OFFSET);
save->i2scnttx = ioread32(iobase + offset + I2SCNTTX_OFFSET);
save->i2sfifoctx =
ioread32(iobase + offset + I2SFIFOCTX_OFFSET);
save->i2saftx = ioread32(iobase + offset + I2SAFTX_OFFSET);
save->i2saetx = ioread32(iobase + offset + I2SAETX_OFFSET);
save->i2smsktx = ioread32(iobase + offset + I2SMSKTX_OFFSET);
save->i2sisttx = ioread32(iobase + offset + I2SISTTX_OFFSET);
save->i2scntrx = ioread32(iobase + offset + I2SCNTRX_OFFSET);
save->i2sfifocrx =
ioread32(iobase + offset + I2SFIFOCRX_OFFSET);
save->i2safrx = ioread32(iobase + offset + I2SAFRX_OFFSET);
save->i2saerx = ioread32(iobase + offset + I2SAERX_OFFSET);
save->i2smskrx = ioread32(iobase + offset + I2SMSKRX_OFFSET);
save->i2sistrx = ioread32(iobase + offset + I2SISTRX_OFFSET);
}
for (i = 0; i < MAX_I2S_IF; i++) {
drvdata->cmn_reg_save.i2sclkcnt[i] =
ioread32(drvdata->devs[i].iobase + I2SCLKCNT0_OFFSET + 0x10*i);
}
drvdata->cmn_reg_save.i2simask =
ioread32(drvdata->devs[0].iobase + I2SIMASK_OFFSET);
}
static int ioh_i2s_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
int ret;
ioh_i2s_save_reg_conf(pdev);
ret = pci_save_state(pdev);
if (ret) {
dev_err(&pdev->dev,
" %s -pci_save_state returns %d\n", __func__, ret);
return ret;
}
pci_enable_wake(pdev, PCI_D3hot, 0);
pci_disable_device(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
static void ioh_i2s_restore_reg_conf(struct pci_dev *pdev)
{
int i;
struct ioh_i2s_data_pci *drvdata = pci_get_drvdata(pdev);
void *iobase;
struct ioh_i2s_pm_ch_reg *save;
int offset;
for (i = 0, offset = 0; i < MAX_I2S_IF; i++, offset = i * 0x800) {
iobase = drvdata->devs[i].iobase;
save = &drvdata->devs[i].ch_reg_save;
iowrite32(save->i2sdrtx, iobase + offset + I2SDRTX_OFFSET);
iowrite32(save->i2scnttx, iobase + offset + I2SCNTTX_OFFSET);
iowrite32(save->i2sfifoctx,
iobase + offset + I2SFIFOCTX_OFFSET);
iowrite32(save->i2saftx, iobase + offset + I2SAFTX_OFFSET);
iowrite32(save->i2saetx, iobase + offset + I2SAETX_OFFSET);
iowrite32(save->i2smsktx, iobase + offset + I2SMSKTX_OFFSET);
iowrite32(save->i2sisttx, iobase + offset + I2SISTTX_OFFSET);
iowrite32(save->i2scntrx, iobase + offset + I2SCNTRX_OFFSET);
iowrite32(save->i2sfifocrx,
iobase + offset + I2SFIFOCRX_OFFSET);
iowrite32(save->i2safrx, iobase + offset + I2SAFRX_OFFSET);
iowrite32(save->i2saerx, iobase + offset + I2SAERX_OFFSET);
iowrite32(save->i2smskrx, iobase + offset + I2SMSKRX_OFFSET);
iowrite32(save->i2sistrx, iobase + offset + I2SISTRX_OFFSET);
}
for (i = 0; i < MAX_I2S_IF; i++) {
iowrite32(drvdata->cmn_reg_save.i2sclkcnt[i],
drvdata->devs[i].iobase + I2SCLKCNT0_OFFSET + 0x10*i);
}
iowrite32(drvdata->cmn_reg_save.i2simask,
drvdata->devs[0].iobase + I2SIMASK_OFFSET);
}
static int ioh_i2s_pci_resume(struct pci_dev *pdev)
{
int ret;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
ret = pci_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev,
"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
return ret;
}
pci_enable_wake(pdev, PCI_D3hot, 0);
ioh_i2s_restore_reg_conf(pdev);
return 0;
}
static struct pci_driver ioh_i2s_driver = {
.name = DRV_NAME,
.probe = ioh_i2s_pci_probe,
.remove = __devexit_p(ioh_i2s_pci_remove),
.id_table = ioh_pci_tbl,
#ifdef CONFIG_PM
.suspend = ioh_i2s_pci_suspend,
.resume = ioh_i2s_pci_resume,
#endif
};
static int __init ioh_i2s_init(void)
{
return pci_register_driver(&ioh_i2s_driver);
}
static void __exit ioh_i2s_cleanup(void)
{
pci_unregister_driver(&ioh_i2s_driver);
}
module_init(ioh_i2s_init);
module_exit(ioh_i2s_cleanup);
MODULE_AUTHOR("Tomoya MORINAGA <tomoya-linux@dsn.lapis-semi.com>");
MODULE_DESCRIPTION("LAPIS Semiconductor ML7213 IOH ALSA SoC platform driver");
MODULE_LICENSE("GPL");
[-- Attachment #7: ml7213ioh-plat.h --]
[-- Type: text/plain, Size: 6184 bytes --]
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ML7213IOH_PLAT_H
#define ML7213IOH_PLAT_H
#include <linux/interrupt.h>
#include <linux/pch_dma.h>
#define I2SCLKCNT0_OFFSET 0x3000
#define I2SCLKCNT1_OFFSET 0x3010
#define I2SCLKCNT2_OFFSET 0x3020
#define I2SCLKCNT3_OFFSET 0x3030
#define I2SCLKCNT4_OFFSET 0x3040
#define I2SCLKCNT5_OFFSET 0x3050
#define I2SISTATUS_OFFSET 0x3080
#define I2SIDISP_OFFSET 0x3084
#define I2SIMASK_OFFSET 0x3088
#define I2SIMASKCLR_OFFSET 0x308C
#define I2SSRST_OFFSET 0x3FFC
#define I2SDRTX_OFFSET 0x0
#define I2SCNTTX_OFFSET 0x4
#define I2SFIFOCTX_OFFSET 0x8
#define I2SAFTX_OFFSET 0xC
#define I2SAETX_OFFSET 0x10
#define I2SMSKTX_OFFSET 0x14
#define I2SISTTX_OFFSET 0x18
#define I2SMONTX_OFFSET 0x1C
#define I2SDRRX_OFFSET 0x20
#define I2SCNTRX_OFFSET 0x24
#define I2SFIFOCRX_OFFSET 0x28
#define I2SAFRX_OFFSET 0x2C
#define I2SAERX_OFFSET 0x30
#define I2SMSKRX_OFFSET 0x34
#define I2SISTRX_OFFSET 0x38
#define I2SMONRX_OFFSET 0x3C
#define FIRST_TX_OFFSET 0x0
#define FIRST_RX_OFFSET 0x0
#define I2SDRTXMIRROR_OFFSET 0x100
#define I2SDRRXMIRROR_OFFSET 0x400
#define TX_OFFSET_INCREMENT 0x800
#define I2S_ALL_INTERRUPT_BITS 0x3F003F
#define I2S_IDISP_BITS 0x3F003F
#define I2S_IDISP_TX_BITS 0x00003F
#define I2S_IDISP_RX_BITS 0x3F0000
#define TX_BIT_FIMSK 0x1 /*Fifo full interrupt mask bit*/
#define TX_BIT_AFIMSK 0x2 /*Fifo Almost full interrupt mask bit*/
#define TX_BIT_EIMSK 0x4 /*Fifo empty interrupt mask bit*/
#define TX_BIT_AEIMSK 0x8 /*Fifo Almost empty interrupt mask bit*/
#define TX_BIT_DMAMSK 0x10 /*Masks DMA*/
#define TX_BIT_DMATC 0x100
#define I2S_TX_ALL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK | TX_BIT_EIMSK \
| TX_BIT_AEIMSK)
#define I2S_TX_NORMAL_INTR_MASK_BITS (TX_BIT_FIMSK | TX_BIT_AFIMSK)
#define RX_BIT_FIMSK 0x1 /*Fifo full interrupt mask bit*/
#define RX_BIT_AFIMSK 0x2 /*Fifo Almost full interrupt mask bit*/
#define RX_BIT_EIMSK 0x4 /*Fifo empty interrupt mask bit*/
#define RX_BIT_AEIMSK 0x8 /*Fifo Almost empty interrupt mask bit*/
#define RX_BIT_DMAMSK 0x10 /*Masks DMA*/
#define RX_BIT_DMATC 0x100
#define I2S_RX_ALL_INTR_MASK_BITS (RX_BIT_FIMSK | RX_BIT_AFIMSK | RX_BIT_EIMSK \
| RX_BIT_AEIMSK)
#define I2S_RX_NORMAL_INTR_MASK_BITS (RX_BIT_EIMSK | RX_BIT_AEIMSK)
#define I2S_TX_FINT 0x1 /*Full Interrupt*/
#define I2S_TX_AFINT 0x2 /*Almost full interrupt*/
#define I2S_TX_EINT 0x4 /*Empty interrupt*/
#define I2S_TX_AEINT 0x8 /*Almost empty interrupt*/
#define I2S_RX_FINT 0x1 /*Full Interrupt*/
#define I2S_RX_AFINT 0x2 /*Almost full interrupt*/
#define I2S_RX_EINT 0x4 /*Empty interrupt*/
#define I2S_RX_AEINT 0x8 /*Almost empty interrupt*/
#define I2S_FIFO_TX_FCLR BIT(0)
#define I2S_FIFO_TX_RUN BIT(4)
#define I2S_FIFO_RX_FCLR BIT(0)
#define I2S_FIFO_RX_RUN BIT(4)
#define FIFO_CTRL_BIT_TX_RUN 0x10
#define FIFO_CTRL_BIT_RX_RUN 0x10
#define I2S_CNT_BIT_TEL 0x1
#define I2S_IMASK_TX_BIT_START 0
#define I2S_IMASK_RX_BIT_START 16
#define MAX_I2S_IF MAX_I2S_CH
/* DMA channel name configuration */
static struct ioh_dma_config {
char rx_chan[8];
char tx_chan[8];
} ioh_dma_config[] = {
{ /* I2S0 */
.tx_chan = "i2s_tx0",
.rx_chan = "i2s_rx0",
},
{ /* I2S1 */
.tx_chan = "i2s_tx1",
.rx_chan = "i2s_rx1",
},
{ /* I2S2 */
.tx_chan = "i2s_tx2",
.rx_chan = "i2s_rx2",
},
{ /* I2S3 */
.tx_chan = "i2s_tx3",
.rx_chan = "i2s_rx3",
},
{ /* I2S4 */
.tx_chan = "i2s_tx4",
.rx_chan = "i2s_rx4",
},
{ /* I2S5 */
.tx_chan = "i2s_tx5",
.rx_chan = "i2s_rx5",
},
};
struct ioh_i2s_data {
struct device *dev;
void *iobase;
int ch;
atomic_t rx_busy;
atomic_t tx_busy;
int ignore_rx_overrun;
/* Transmit side DMA */
atomic_t pending_tx;
struct ioh_dma_config *dma_config;
char rx_name[16];
char tx_name[16];
struct scatterlist *sg_tx_p;
struct scatterlist *sg_rx_p;
struct scatterlist *sg_tx_cur; /* current head of tx sg */
struct scatterlist *sg_rx_cur; /* current head of tx sg */
int tx_num; /* The number of sent sg */
int rx_num; /* The number of sent sg */
void *rxbuf_virt;
void *txbuf_virt;
unsigned char *tx_tail;
unsigned char *tx_head;
unsigned char *tx_data_head;
unsigned char *tx_complete;
unsigned int tx_avail;
unsigned char *rx_tail;
unsigned char *rx_head;
unsigned char *rx_data_head;
unsigned char *rx_complete;
unsigned int rx_avail;
struct dma_chan *chan_tx;
struct dma_chan *chan_rx;
int rx_nent; /* The number of rx scatter list */
int tx_nent; /* The number of tx scatter list */
struct dma_async_tx_descriptor *desc_tx;
struct dma_async_tx_descriptor *desc_rx;
dma_addr_t tx_buf_dma;
dma_addr_t rx_buf_dma;
spinlock_t tx_lock;
struct pch_dma_slave param_tx;
struct pch_dma_slave param_rx;
unsigned int mapbase;
dma_addr_t tx_reg_addr[2];
void *rx_callback_data;
void (*rx_done) (void *callback_data, int status, int num, int avail);
void *tx_callback_data;
void (*tx_done) (void *callback_data, int status, int num, int avail);
unsigned int tx_lower_data_flag;
int dma_tx_unit; /* 1Byte of 2Byte or 4Byte */
int dma_rx_unit; /* 1Byte of 2Byte or 4Byte */
int dma_tx_width;
int dma_rx_width;
int txexe_flag;
int rxexe_flag;
struct tasklet_struct tx_tasklet;
struct tasklet_struct rx_tasklet;
struct ioh_i2s_pm_ch_reg ch_reg_save;
};
struct ioh_i2s_data_pci {
struct ioh_i2s_data devs[MAX_I2S_IF];
void __iomem *membase;
unsigned int mapbase;
struct ioh_i2s_pm_common_reg cmn_reg_save;
struct snd_card *card;
struct device *dev;
};
#endif
[-- Attachment #8: ml26124.h --]
[-- Type: text/plain, Size: 4578 bytes --]
/*
* Copyright (c) 2005 Openedhand Ltd.
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
*
* This code was derived from the WM8731 Soc Audio driver
* by Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _WM8731_H
#define _WM8731_H
/* WM8731 register space */
#define WM8731_LINVOL 0x00
#define WM8731_RINVOL 0x01
#define WM8731_LOUT1V 0x02
#define WM8731_ROUT1V 0x03
#define WM8731_APANA 0x04
#define WM8731_APDIGI 0x05
#define WM8731_PWR 0x06
#define WM8731_IFACE 0x07
#define WM8731_SRATE 0x08
#define WM8731_ACTIVE 0x09
#define WM8731_RESET 0x0f
#define WM8731_CACHEREGNUM 10
#define WM8731_SYSCLK_XTAL 1
#define WM8731_SYSCLK_MCLK 2
#define WM8731_DAI 0
/* Clock Control Register */
#define ML26124_SMPLING_RATE 0x00
#define ML26124_PLLNL 0x02
#define ML26124_PLLNH 0x04
#define ML26124_PLLML 0x06
#define ML26124_PLLMH 0x08
#define ML26124_PLLDIV 0x0a
#define ML26124_CLK_EN 0x0c
#define ML26124_CLK_CTL 0x0e
/* System Control Register */
#define ML26124_SW_RST 0x10
#define ML26124_REC_PLYBAK_RUN 0x12
#define ML26124_MIC_TIM 0x14
/* Power Mnagement Register */
#define ML26124_PW_REF_PW_MNG 0x20
#define ML26124_PW_IN_PW_MNG 0x22
#define ML26124_PW_DAC_PW_MNG 0x24
#define ML26124_PW_SPAMP_PW_MNG 0x26
#define ML26124_PW_LOUT_PW_MNG 0x28
#define ML26124_PW_VOUT_PW_MNG 0x2a
#define ML26124_PW_ZCCMP_PW_MNG 0x2e
/* Analog Reference Control Register */
#define ML26124_PW_MICBIAS_VOL 0x30
/* Input/Output Amplifier Control Register */
#define ML26124_PW_MIC_IN_VOL 0x32
#define ML26124_PW_MIC_BOST_VOL 0x38
#define ML26124_PW_SPK_AMP_VOL 0x3a
#define ML26124_PW_AMP_VOL_FUNC 0x48
#define ML26124_PW_AMP_VOL_FADE 0x4a
/* Analog Path Control Register */
#define ML26124_SPK_AMP_OUT 0x54
#define ML26124_MIC_IF_CTL 0x5a
#define ML26124_MIC_SELECT 0xe8
//#define ML26124_CTL 0x
/* Audio Interface Control Register */
#define ML26124_SAI_TRANS_CTL 0x60
#define ML26124_SAI_RCV_CTL 0x62
#define ML26124_SAI_MODE_SEL 0x64
/* DSP Control Register */
#define ML26124_FILTER_EN 0x66
#define ML26124_VOL_CTL_EN 0x68
#define ML26124_MIXER_VOL_CTL 0x6a
#define ML26124_RECORD_DIG_VOL 0x6c
#define ML26124_PLBAK_DIG_VOL 0x70
#define ML26124_DIGI_BOOST_VOL 0x72
#define ML26124_EQ_GAIN_BRAND0 0x74
#define ML26124_EQ_GAIN_BRAND1 0x76
#define ML26124_EQ_GAIN_BRAND2 0x78
#define ML26124_EQ_GAIN_BRAND3 0x7a
#define ML26124_EQ_GAIN_BRAND4 0x7c
#define ML26124_HPF2_CUTOFF 0x7e
#define ML26124_EQBRAND0_F0L 0x80
#define ML26124_EQBRAND0_F0H 0x82
#define ML26124_EQBRAND0_F1L 0x84
#define ML26124_EQBRAND0_F1H 0x86
#define ML26124_EQBRAND1_F0L 0x88
#define ML26124_EQBRAND1_F0H 0x8a
#define ML26124_EQBRAND1_F1L 0x8c
#define ML26124_EQBRAND1_F1H 0x8e
#define ML26124_EQBRAND2_F0L 0x90
#define ML26124_EQBRAND2_F0H 0x92
#define ML26124_EQBRAND2_F1L 0x94
#define ML26124_EQBRAND2_F1H 0x96
#define ML26124_EQBRAND3_F0L 0x98
#define ML26124_EQBRAND3_F0H 0x9a
#define ML26124_EQBRAND3_F1L 0x9c
#define ML26124_EQBRAND3_F1H 0x9e
#define ML26124_EQBRAND4_F0L 0xa0
#define ML26124_EQBRAND4_F0H 0xa2
#define ML26124_EQBRAND4_F1L 0xa4
#define ML26124_EQBRAND4_F1H 0xa6
/* ALC Control Register */
#define ML26124_ALC_MODE 0xb0
#define ML26124_ALC_ATTACK_TIM 0xb2
#define ML26124_ALC_DECAY_TIM 0xb4
#define ML26124_ALC_HOLD_TIM 0xb6
#define ML26124_ALC_TARGET_LEV 0xb8
#define ML26124_ALC_MAXMIN_GAIN 0xba
#define ML26124_NOIS_GATE_THRSH 0xbc
#define ML26124_ALC_ZERO_TIMOUT 0xbe
/* Playback Limiter Control Register */
#define ML26124_PL_ATTACKTIME 0xc0
#define ML26124_PL_DECAYTIME 0xc2
#define ML26124_PL_TARGETTIME 0xc4
#define ML26124_PL_MAXMIN_GAIN 0xc6
#define ML26124_PLYBAK_BOST_VOL 0xc8
#define ML26124_PL_0CROSS_TIMOUT 0xca
/* Video Amplifer Control Register */
#define ML26124_VIDEO_AMP_GAIN_CTL 0xd0
#define ML26124_VIDEO_AMP_SETUP1 0xd2
#define ML26124_VIDEO_AMP_CTL2 0xd4
#endif
next prev parent reply other threads:[~2011-11-08 9:03 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <70D251FDDC55405882A8447CC455D56E@hacdom.okisemi.com>
[not found] ` <8486F61FC3B94B908BFE654234DD6C97@hacdom.okisemi.com>
2011-10-17 4:28 ` [PATCH] Add SoundCard driver for OKI SEMICONDUCTOR ML7213 IOH Tomoya MORINAGA
2011-10-21 14:16 ` Takashi Iwai
2011-10-24 12:12 ` Tomoya MORINAGA
2011-10-24 12:20 ` Mark Brown
2011-10-24 12:20 ` Mark Brown
2011-11-08 9:03 ` Tomoya MORINAGA [this message]
2011-11-08 10:39 ` Mark Brown
2011-11-08 10:39 ` Mark Brown
2011-11-09 2:56 ` Tomoya MORINAGA
2011-11-08 14:38 ` Mark Brown
2011-11-08 14:38 ` Mark Brown
2011-11-10 5:00 ` Tomoya MORINAGA
2011-11-10 11:22 ` Mark Brown
2011-11-10 11:22 ` Mark Brown
2011-07-06 10:27 Toshiharu Okada
2011-07-06 11:06 ` Takashi Iwai
2011-07-07 7:54 ` Toshiharu Okada
2011-07-07 7:54 ` Toshiharu Okada
2011-07-07 9:00 ` Takashi Iwai
2011-07-07 9:00 ` Takashi Iwai
2011-07-07 12:35 ` Toshiharu Okada
2011-07-07 12:35 ` Toshiharu Okada
2011-07-07 16:38 ` Takashi Iwai
2011-07-07 16:38 ` Takashi Iwai
2011-07-08 10:52 ` Toshiharu Okada
2011-07-08 10:52 ` Toshiharu Okada
2011-07-09 1:34 ` Mark Brown
2011-07-09 7:40 ` Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4EB8F079.9080703@dsn.lapis-semi.com \
--to=tomoya-linux@dsn.lapis-semi.com \
--cc=alsa-devel@alsa-project.org \
--cc=broonie@opensource.wolfsonmicro.com \
--cc=joel.clark@intel.com \
--cc=kok.howg.ewe@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lrg@ti.com \
--cc=perex@perex.cz \
--cc=qi.wang@intel.com \
--cc=tiwai@suse.de \
--cc=yong.y.wang@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.