Commit 81b6863c authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/pxa', 'asoc/topic/qcom',...

Merge remote-tracking branches 'asoc/topic/pxa', 'asoc/topic/qcom', 'asoc/topic/rcar', 'asoc/topic/rk3036' and 'asoc/topic/rockchip' into asoc-next
Inno audio codec for RK3036
Inno audio codec is integrated inside RK3036 SoC.
Required properties:
- compatible : Should be "rockchip,rk3036-codec".
- reg : The registers of codec.
- clock-names : Should be "acodec_pclk".
- clocks : The clock of codec.
- rockchip,grf : The phandle of grf device node.
Example:
acodec: acodec-ana@20030000 {
compatible = "rk3036-codec";
reg = <0x20030000 0x4000>;
rockchip,grf = <&grf>;
clock-names = "acodec_pclk";
clocks = <&cru ACLK_VCODEC>;
};
...@@ -7,8 +7,11 @@ Required properties: ...@@ -7,8 +7,11 @@ Required properties:
"renesas,rcar_sound-gen3" if generation3 "renesas,rcar_sound-gen3" if generation3
Examples with soctypes are: Examples with soctypes are:
- "renesas,rcar_sound-r8a7778" (R-Car M1A) - "renesas,rcar_sound-r8a7778" (R-Car M1A)
- "renesas,rcar_sound-r8a7779" (R-Car H1)
- "renesas,rcar_sound-r8a7790" (R-Car H2) - "renesas,rcar_sound-r8a7790" (R-Car H2)
- "renesas,rcar_sound-r8a7791" (R-Car M2-W) - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
- "renesas,rcar_sound-r8a7793" (R-Car M2-N)
- "renesas,rcar_sound-r8a7794" (R-Car E2)
- "renesas,rcar_sound-r8a7795" (R-Car H3) - "renesas,rcar_sound-r8a7795" (R-Car H3)
- reg : Should contain the register physical address. - reg : Should contain the register physical address.
required register is required register is
...@@ -34,6 +37,8 @@ Required properties: ...@@ -34,6 +37,8 @@ Required properties:
see below for detail. see below for detail.
- #sound-dai-cells : it must be 0 if your system is using single DAI - #sound-dai-cells : it must be 0 if your system is using single DAI
it must be 1 if your system is using multi DAI it must be 1 if your system is using multi DAI
Optional properties:
- #clock-cells : it must be 0 if your system has audio_clkout - #clock-cells : it must be 0 if your system has audio_clkout
it must be 1 if your system has audio_clkout0/1/2/3 it must be 1 if your system has audio_clkout0/1/2/3
- clock-frequency : for all audio_clkout0/1/2/3 - clock-frequency : for all audio_clkout0/1/2/3
...@@ -244,3 +249,80 @@ rcar_sound: sound@ec500000 { ...@@ -244,3 +249,80 @@ rcar_sound: sound@ec500000 {
}; };
}; };
}; };
Example: simple sound card
rsnd_ak4643: sound {
compatible = "simple-audio-card";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>;
sndcpu: simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
};
sndcodec: simple-audio-card,codec {
sound-dai = <&ak4643>;
clocks = <&audio_clock>;
};
};
&rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;
pinctrl-names = "default";
/* Single DAI */
#sound-dai-cells = <0>;
status = "okay";
rcar_sound,dai {
dai0 {
playback = <&ssi0 &src2 &dvc0>;
capture = <&ssi1 &src3 &dvc1>;
};
};
};
&ssi1 {
shared-pin;
};
Example: simple sound card for TDM
rsnd_tdm: sound {
compatible = "simple-audio-card";
simple-audio-card,format = "left_j";
simple-audio-card,bitclock-master = <&sndcodec>;
simple-audio-card,frame-master = <&sndcodec>;
sndcpu: simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
dai-tdm-slot-num = <6>;
};
sndcodec: simple-audio-card,codec {
sound-dai = <&xxx>;
};
};
Example: simple sound card for Multi channel
&rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;
pinctrl-names = "default";
/* Single DAI */
#sound-dai-cells = <0>;
status = "okay";
rcar_sound,dai {
dai0 {
playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
};
};
};
...@@ -4,8 +4,8 @@ Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC ...@@ -4,8 +4,8 @@ Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC
Required properties: Required properties:
- compatible : "renesas,rsrc-card,<board>" - compatible : "renesas,rsrc-card{,<board>}"
Examples with soctypes are: Examples with boards are:
- "renesas,rsrc-card" - "renesas,rsrc-card"
- "renesas,rsrc-card,lager" - "renesas,rsrc-card,lager"
- "renesas,rsrc-card,koelsch" - "renesas,rsrc-card,koelsch"
......
...@@ -19,6 +19,7 @@ Required properties: ...@@ -19,6 +19,7 @@ Required properties:
- clock-names: should contain followings: - clock-names: should contain followings:
- "i2s_hclk": clock for I2S BUS - "i2s_hclk": clock for I2S BUS
- "i2s_clk" : clock for I2S controller - "i2s_clk" : clock for I2S controller
- rockchip,playback-channels: max playback channels, if not set, 8 channels default.
- rockchip,capture-channels: max capture channels, if not set, 2 channels default. - rockchip,capture-channels: max capture channels, if not set, 2 channels default.
Example for rk3288 I2S controller: Example for rk3288 I2S controller:
...@@ -31,5 +32,6 @@ i2s@ff890000 { ...@@ -31,5 +32,6 @@ i2s@ff890000 {
dma-names = "tx", "rx"; dma-names = "tx", "rx";
clock-names = "i2s_hclk", "i2s_clk"; clock-names = "i2s_hclk", "i2s_clk";
clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>; clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
rockchip,playback-channels = <8>;
rockchip,capture-channels = <2>; rockchip,capture-channels = <2>;
}; };
...@@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS ...@@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_GTM601 select SND_SOC_GTM601
select SND_SOC_HDAC_HDMI select SND_SOC_HDAC_HDMI
select SND_SOC_ICS43432 select SND_SOC_ICS43432
select SND_SOC_INNO_RK3036
select SND_SOC_ISABELLE if I2C select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C select SND_SOC_LM4857 if I2C
...@@ -492,6 +493,9 @@ config SND_SOC_HDAC_HDMI ...@@ -492,6 +493,9 @@ config SND_SOC_HDAC_HDMI
config SND_SOC_ICS43432 config SND_SOC_ICS43432
tristate tristate
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
config SND_SOC_ISABELLE config SND_SOC_ISABELLE
tristate tristate
......
...@@ -63,6 +63,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o ...@@ -63,6 +63,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
snd-soc-gtm601-objs := gtm601.o snd-soc-gtm601-objs := gtm601.o
snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o
snd-soc-ics43432-objs := ics43432.o snd-soc-ics43432-objs := ics43432.o
snd-soc-inno-rk3036-objs := inno_rk3036.o
snd-soc-isabelle-objs := isabelle.o snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-l3-objs := l3.o snd-soc-l3-objs := l3.o
...@@ -264,6 +265,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o ...@@ -264,6 +265,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
......
/*
* Driver of Inno codec for rk3036 by Rockchip Inc.
*
* Author: Rockchip Inc.
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
*/
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/soc-dapm.h>
#include <sound/soc-dai.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/device.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/io.h>
#include "inno_rk3036.h"
struct rk3036_codec_priv {
void __iomem *base;
struct clk *pclk;
struct regmap *regmap;
struct device *dev;
};
static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int val, ret, regval;
ret = snd_soc_component_read(component, INNO_R09, &regval);
if (ret)
return ret;
val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
ucontrol->value.integer.value[0] = val;
val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
ucontrol->value.integer.value[1] = val;
return 0;
}
static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
int val, ret, regmsk;
val = (ucontrol->value.integer.value[0] ?
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
INNO_R09_HPL_ANITPOP_SHIFT;
val |= (ucontrol->value.integer.value[1] ?
INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
INNO_R09_HPR_ANITPOP_SHIFT;
regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
ret = snd_soc_component_update_bits(component, INNO_R09,
regmsk, val);
if (ret < 0)
return ret;
return 0;
}
#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
.put = rk3036_codec_antipop_put, }
static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
INNO_R09_HPR_MUTE_SHIFT, 1, 0),
SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
};
static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
INNO_R05_HPL_WORK_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
INNO_R05_HPR_WORK_SHIFT, 1, 0),
};
static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
INNO_R04_DACL_SW_SHIFT, 0),
SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
INNO_R04_DACR_SW_SHIFT, 0),
SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpl_mixer_controls,
ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpr_mixer_controls,
ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpl_switch_controls,
ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0,
rk3036_codec_hpr_switch_controls,
ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
};
static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
{"DACL VREF", NULL, "DAC PWR"},
{"DACR VREF", NULL, "DAC PWR"},
{"DACL HiLo VREF", NULL, "DAC PWR"},
{"DACR HiLo VREF", NULL, "DAC PWR"},
{"DACL CLK", NULL, "DAC PWR"},
{"DACR CLK", NULL, "DAC PWR"},
{"DACL", NULL, "DACL VREF"},
{"DACL", NULL, "DACL HiLo VREF"},
{"DACL", NULL, "DACL CLK"},
{"DACR", NULL, "DACR VREF"},
{"DACR", NULL, "DACR HiLo VREF"},
{"DACR", NULL, "DACR CLK"},
{"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
{"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
{"HP Left Out", NULL, "Left Headphone Mixer"},
{"HP Right Out", NULL, "Right Headphone Mixer"},
{"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
{"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
{"HPL", NULL, "HP Left Switch"},
{"HPR", NULL, "HP Right Switch"},
};
static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0;
dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
INNO_R01_I2SMODE_SLAVE;
break;
case SND_SOC_DAIFMT_CBM_CFM:
reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
INNO_R01_I2SMODE_MASTER;
break;
default:
dev_err(codec->dev, "invalid fmt\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
reg02_val |= INNO_R02_DACM_PCM;
break;
case SND_SOC_DAIFMT_I2S:
reg02_val |= INNO_R02_DACM_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
reg02_val |= INNO_R02_DACM_RJM;
break;
case SND_SOC_DAIFMT_LEFT_J:
reg02_val |= INNO_R02_DACM_LJM;
break;
default:
dev_err(codec->dev, "set dai format failed\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_BCP_NORMAL;
break;
case SND_SOC_DAIFMT_IB_IF:
reg02_val |= INNO_R02_LRCP_REVERSAL;
reg03_val |= INNO_R03_BCP_REVERSAL;
break;
case SND_SOC_DAIFMT_IB_NF:
reg02_val |= INNO_R02_LRCP_REVERSAL;
reg03_val |= INNO_R03_BCP_NORMAL;
break;
case SND_SOC_DAIFMT_NB_IF:
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_BCP_REVERSAL;
break;
default:
dev_err(codec->dev, "set dai format failed\n");
return -EINVAL;
}
snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
INNO_R01_PINDIR_MSK, reg01_val);
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_DACM_MSK, reg02_val);
snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
return 0;
}
static int rk3036_codec_dai_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;
unsigned int reg02_val = 0, reg03_val = 0;
switch (params_format(hw_params)) {
case SNDRV_PCM_FORMAT_S16_LE:
reg02_val |= INNO_R02_VWL_16BIT;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
reg02_val |= INNO_R02_VWL_20BIT;
break;
case SNDRV_PCM_FORMAT_S24_LE:
reg02_val |= INNO_R02_VWL_24BIT;
break;
case SNDRV_PCM_FORMAT_S32_LE:
reg02_val |= INNO_R02_VWL_32BIT;
break;
default:
return -EINVAL;
}
reg02_val |= INNO_R02_LRCP_NORMAL;
reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
INNO_R02_VWL_MSK, reg02_val);
snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
INNO_R03_FWL_MSK, reg03_val);
return 0;
}
#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000)
#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops rk3036_codec_dai_ops = {
.set_fmt = rk3036_codec_dai_set_fmt,
.hw_params = rk3036_codec_dai_hw_params,
};
static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
{
.name = "rk3036-codec-dai",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RK3036_CODEC_RATES,
.formats = RK3036_CODEC_FMTS,
},
.ops = &rk3036_codec_dai_ops,
.symmetric_rates = 1,
},
};
static void rk3036_codec_reset(struct snd_soc_codec *codec)
{
snd_soc_write(codec, INNO_R00,
INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
snd_soc_write(codec, INNO_R00,
INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
}
static int rk3036_codec_probe(struct snd_soc_codec *codec)
{
rk3036_codec_reset(codec);
return 0;
}
static int rk3036_codec_remove(struct snd_soc_codec *codec)
{
rk3036_codec_reset(codec);
return 0;
}
static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_STANDBY:
/* set a big current for capacitor charging. */
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
/* start precharge */
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_PRECHARGE);
break;
case SND_SOC_BIAS_OFF:
/* set a big current for capacitor discharging. */
snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
/* start discharge. */
snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
break;
default:
break;
}
return 0;
}
static struct snd_soc_codec_driver rk3036_codec_driver = {
.probe = rk3036_codec_probe,
.remove = rk3036_codec_remove,
.set_bias_level = rk3036_codec_set_bias_level,
.controls = rk3036_codec_dapm_controls,
.num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
.dapm_routes = rk3036_codec_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
.dapm_widgets = rk3036_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
};
static const struct regmap_config rk3036_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
};
#define GRF_SOC_CON0 0x00140
#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10))
static int rk3036_codec_platform_probe(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv;
struct device_node *of_node = pdev->dev.of_node;
struct resource *res;
void __iomem *base;
struct regmap *grf;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
priv->base = base;
priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
&rk3036_codec_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(&pdev->dev, "init regmap failed\n");
return PTR_ERR(priv->regmap);
}
grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
if (IS_ERR(grf)) {
dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
return PTR_ERR(grf);
}
ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
if (ret) {
dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
return ret;
}
priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
if (IS_ERR(priv->pclk))
return PTR_ERR(priv->pclk);
ret = clk_prepare_enable(priv->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable clk\n");
return ret;
}
priv->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, priv);
ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
rk3036_codec_dai_driver,
ARRAY_SIZE(rk3036_codec_dai_driver));
if (ret) {
clk_disable_unprepare(priv->pclk);
dev_set_drvdata(&pdev->dev, NULL);
}
return ret;
}
static int rk3036_codec_platform_remove(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
clk_disable_unprepare(priv->pclk);
return 0;
}
static const struct of_device_id rk3036_codec_of_match[] = {
{ .compatible = "rockchip,rk3036-codec", },
{}
};
MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
static struct platform_driver rk3036_codec_platform_driver = {
.driver = {
.name = "rk3036-codec-platform",
.of_match_table = of_match_ptr(rk3036_codec_of_match),
},
.probe = rk3036_codec_platform_probe,
.remove = rk3036_codec_platform_remove,
};
module_platform_driver(rk3036_codec_platform_driver);
MODULE_AUTHOR("Rockchip Inc.");
MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
MODULE_LICENSE("GPL");
/*
* Driver of Inno Codec for rk3036 by Rockchip Inc.
*
* Author: Zheng ShunQian<zhengsq@rock-chips.com>
*/
#ifndef _INNO_RK3036_CODEC_H
#define _INNO_RK3036_CODEC_H
/* codec registers */
#define INNO_R00 0x00
#define INNO_R01 0x0c
#define INNO_R02 0x10
#define INNO_R03 0x14
#define INNO_R04 0x88
#define INNO_R05 0x8c
#define INNO_R06 0x90
#define INNO_R07 0x94
#define INNO_R08 0x98
#define INNO_R09 0x9c
#define INNO_R10 0xa0
/* register bit filed */
#define INNO_R00_CSR_RESET (0x0 << 0) /*codec system reset*/
#define INNO_R00_CSR_WORK (0x1 << 0)
#define INNO_R00_CDCR_RESET (0x0 << 1) /*codec digital core reset*/
#define INNO_R00_CDCR_WORK (0x1 << 1)
#define INNO_R00_PRB_DISABLE (0x0 << 6) /*power reset bypass*/
#define INNO_R00_PRB_ENABLE (0x1 << 6)
#define INNO_R01_I2SMODE_MSK (0x1 << 4)
#define INNO_R01_I2SMODE_SLAVE (0x0 << 4)
#define INNO_R01_I2SMODE_MASTER (0x1 << 4)
#define INNO_R01_PINDIR_MSK (0x1 << 5)
#define INNO_R01_PINDIR_IN_SLAVE (0x0 << 5) /*direction of pin*/
#define INNO_R01_PINDIR_OUT_MASTER (0x1 << 5)
#define INNO_R02_LRS_MSK (0x1 << 2)
#define INNO_R02_LRS_NORMAL (0x0 << 2) /*DAC Left Right Swap*/
#define INNO_R02_LRS_SWAP (0x1 << 2)
#define INNO_R02_DACM_MSK (0x3 << 3)
#define INNO_R02_DACM_PCM (0x3 << 3) /*DAC Mode*/
#define INNO_R02_DACM_I2S (0x2 << 3)
#define INNO_R02_DACM_LJM (0x1 << 3)
#define INNO_R02_DACM_RJM (0x0 << 3)
#define INNO_R02_VWL_MSK (0x3 << 5)
#define INNO_R02_VWL_32BIT (0x3 << 5) /*1/2Frame Valid Word Len*/
#define INNO_R02_VWL_24BIT (0x2 << 5)
#define INNO_R02_VWL_20BIT (0x1 << 5)
#define INNO_R02_VWL_16BIT (0x0 << 5)
#define INNO_R02_LRCP_MSK (0x1 << 7)
#define INNO_R02_LRCP_NORMAL (0x0 << 7) /*Left Right Polarity*/
#define INNO_R02_LRCP_REVERSAL (0x1 << 7)
#define INNO_R03_BCP_MSK (0x1 << 0)
#define INNO_R03_BCP_NORMAL (0x0 << 0) /*DAC bit clock polarity*/
#define INNO_R03_BCP_REVERSAL (0x1 << 0)
#define INNO_R03_DACR_MSK (0x1 << 1)
#define INNO_R03_DACR_RESET (0x0 << 1) /*DAC Reset*/
#define INNO_R03_DACR_WORK (0x1 << 1)
#define INNO_R03_FWL_MSK (0x3 << 2)
#define INNO_R03_FWL_32BIT (0x3 << 2) /*1/2Frame Word Length*/
#define INNO_R03_FWL_24BIT (0x2 << 2)
#define INNO_R03_FWL_20BIT (0x1 << 2)
#define INNO_R03_FWL_16BIT (0x0 << 2)
#define INNO_R04_DACR_SW_SHIFT 0
#define INNO_R04_DACL_SW_SHIFT 1
#define INNO_R04_DACR_CLK_SHIFT 2
#define INNO_R04_DACL_CLK_SHIFT 3
#define INNO_R04_DACR_VREF_SHIFT 4
#define INNO_R04_DACL_VREF_SHIFT 5
#define INNO_R05_HPR_EN_SHIFT 0
#define INNO_R05_HPL_EN_SHIFT 1
#define INNO_R05_HPR_WORK_SHIFT 2
#define INNO_R05_HPL_WORK_SHIFT 3
#define INNO_R06_VOUTR_CZ_SHIFT 0
#define INNO_R06_VOUTL_CZ_SHIFT 1
#define INNO_R06_DACR_HILO_VREF_SHIFT 2
#define INNO_R06_DACL_HILO_VREF_SHIFT 3
#define INNO_R06_DAC_EN_SHIFT 5
#define INNO_R06_DAC_PRECHARGE (0x0 << 4) /*PreCharge control for DAC*/
#define INNO_R06_DAC_DISCHARGE (0x1 << 4)
#define INNO_HP_GAIN_SHIFT 0
/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */
#define INNO_HP_GAIN_0DB 0x1a
#define INNO_HP_GAIN_N39DB 0x0
#define INNO_R09_HP_ANTIPOP_MSK 0x3
#define INNO_R09_HP_ANTIPOP_OFF 0x1
#define INNO_R09_HP_ANTIPOP_ON 0x2
#define INNO_R09_HPR_ANITPOP_SHIFT 0
#define INNO_R09_HPL_ANITPOP_SHIFT 2
#define INNO_R09_HPR_MUTE_SHIFT 4
#define INNO_R09_HPL_MUTE_SHIFT 5
#define INNO_R09_DACR_SWITCH_SHIFT 6
#define INNO_R09_DACL_SWITCH_SHIFT 7
#define INNO_R10_CHARGE_SEL_CUR_400I_YES (0x0 << 0)
#define INNO_R10_CHARGE_SEL_CUR_400I_NO (0x1 << 0)
#define INNO_R10_CHARGE_SEL_CUR_260I_YES (0x0 << 1)
#define INNO_R10_CHARGE_SEL_CUR_260I_NO (0x1 << 1)
#define INNO_R10_CHARGE_SEL_CUR_130I_YES (0x0 << 2)
#define INNO_R10_CHARGE_SEL_CUR_130I_NO (0x1 << 2)
#define INNO_R10_CHARGE_SEL_CUR_100I_YES (0x0 << 3)
#define INNO_R10_CHARGE_SEL_CUR_100I_NO (0x1 << 3)
#define INNO_R10_CHARGE_SEL_CUR_050I_YES (0x0 << 4)
#define INNO_R10_CHARGE_SEL_CUR_050I_NO (0x1 << 4)
#define INNO_R10_CHARGE_SEL_CUR_027I_YES (0x0 << 5)
#define INNO_R10_CHARGE_SEL_CUR_027I_NO (0x1 << 5)
#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \
INNO_R10_CHARGE_SEL_CUR_260I_YES | \
INNO_R10_CHARGE_SEL_CUR_130I_YES | \
INNO_R10_CHARGE_SEL_CUR_100I_YES | \
INNO_R10_CHARGE_SEL_CUR_050I_YES | \
INNO_R10_CHARGE_SEL_CUR_027I_YES)
#endif
...@@ -63,8 +63,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, ...@@ -63,8 +63,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
sysclk = params_rate(params) * 512; sysclk = params_rate(params) * 512;
sspa_mclk = params_rate(params) * 64; sspa_mclk = params_rate(params) * 64;
} }
sspa_div = freq_out; sspa_div = freq_out / sspa_mclk;
do_div(sspa_div, sspa_mclk);
snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0); snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk); snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
......
...@@ -355,6 +355,7 @@ static struct regmap_config lpass_cpu_regmap_config = { ...@@ -355,6 +355,7 @@ static struct regmap_config lpass_cpu_regmap_config = {
.readable_reg = lpass_cpu_regmap_readable, .readable_reg = lpass_cpu_regmap_readable,
.volatile_reg = lpass_cpu_regmap_volatile, .volatile_reg = lpass_cpu_regmap_volatile,
.cache_type = REGCACHE_FLAT, .cache_type = REGCACHE_FLAT,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
}; };
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
......
...@@ -34,13 +34,7 @@ struct rk_i2s_dev { ...@@ -34,13 +34,7 @@ struct rk_i2s_dev {
struct regmap *regmap; struct regmap *regmap;
/* bool is_master_mode;
* Used to indicate the tx/rx status.
* I2S controller hopes to start the tx and rx together,
* also to stop them when they are both try to stop.
*/
bool tx_start;
bool rx_start;
}; };
static int i2s_runtime_suspend(struct device *dev) static int i2s_runtime_suspend(struct device *dev)
...@@ -81,37 +75,29 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) ...@@ -81,37 +75,29 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
regmap_update_bits(i2s->regmap, I2S_XFER, regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START, I2S_XFER_TXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_START); I2S_XFER_TXS_START);
i2s->tx_start = true;
} else { } else {
i2s->tx_start = false;
regmap_update_bits(i2s->regmap, I2S_DMACR, regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
if (!i2s->rx_start) { regmap_update_bits(i2s->regmap, I2S_XFER,
regmap_update_bits(i2s->regmap, I2S_XFER, I2S_XFER_TXS_START,
I2S_XFER_TXS_START | I2S_XFER_TXS_STOP);
I2S_XFER_RXS_START,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP);
regmap_update_bits(i2s->regmap, I2S_CLR, regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC | I2S_CLR_RXC, I2S_CLR_TXC,
I2S_CLR_TXC | I2S_CLR_RXC); I2S_CLR_TXC);
regmap_read(i2s->regmap, I2S_CLR, &val); regmap_read(i2s->regmap, I2S_CLR, &val);
/* Should wait for clear operation to finish */ /* Should wait for clear operation to finish */
while (val) { while (val & I2S_CLR_TXC) {
regmap_read(i2s->regmap, I2S_CLR, &val); regmap_read(i2s->regmap, I2S_CLR, &val);
retry--; retry--;
if (!retry) { if (!retry) {
dev_warn(i2s->dev, "fail to clear\n"); dev_warn(i2s->dev, "fail to clear\n");
break; break;
}
} }
} }
} }
...@@ -127,37 +113,29 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) ...@@ -127,37 +113,29 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
regmap_update_bits(i2s->regmap, I2S_XFER, regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START, I2S_XFER_RXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_START); I2S_XFER_RXS_START);
i2s->rx_start = true;
} else { } else {
i2s->rx_start = false;
regmap_update_bits(i2s->regmap, I2S_DMACR, regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE); I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
if (!i2s->tx_start) { regmap_update_bits(i2s->regmap, I2S_XFER,
regmap_update_bits(i2s->regmap, I2S_XFER, I2S_XFER_RXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_STOP);
I2S_XFER_RXS_START,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP);
regmap_update_bits(i2s->regmap, I2S_CLR, regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC | I2S_CLR_RXC, I2S_CLR_RXC,
I2S_CLR_TXC | I2S_CLR_RXC); I2S_CLR_RXC);
regmap_read(i2s->regmap, I2S_CLR, &val); regmap_read(i2s->regmap, I2S_CLR, &val);
/* Should wait for clear operation to finish */ /* Should wait for clear operation to finish */
while (val) { while (val & I2S_CLR_RXC) {
regmap_read(i2s->regmap, I2S_CLR, &val); regmap_read(i2s->regmap, I2S_CLR, &val);
retry--; retry--;
if (!retry) { if (!retry) {
dev_warn(i2s->dev, "fail to clear\n"); dev_warn(i2s->dev, "fail to clear\n");
break; break;
}
} }
} }
} }
...@@ -174,9 +152,11 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, ...@@ -174,9 +152,11 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
/* Set source clock in Master mode */ /* Set source clock in Master mode */
val = I2S_CKR_MSS_MASTER; val = I2S_CKR_MSS_MASTER;
i2s->is_master_mode = true;
break; break;
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
val = I2S_CKR_MSS_SLAVE; val = I2S_CKR_MSS_SLAVE;
i2s->is_master_mode = false;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -228,6 +208,26 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -228,6 +208,26 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct rk_i2s_dev *i2s = to_info(dai); struct rk_i2s_dev *i2s = to_info(dai);
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int val = 0; unsigned int val = 0;
unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck;
if (i2s->is_master_mode) {
mclk_rate = clk_get_rate(i2s->mclk);
bclk_rate = 2 * 32 * params_rate(params);
if (bclk_rate && mclk_rate % bclk_rate)
return -EINVAL;
div_bclk = mclk_rate / bclk_rate;
div_lrck = bclk_rate / params_rate(params);
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_MDIV_MASK,
I2S_CKR_MDIV(div_bclk));
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_TSD_MASK |
I2S_CKR_RSD_MASK,
I2S_CKR_TSD(div_lrck) |
I2S_CKR_RSD(div_lrck));
}
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8: case SNDRV_PCM_FORMAT_S8:
...@@ -451,6 +451,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) ...@@ -451,6 +451,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
{ {
struct device_node *node = pdev->dev.of_node; struct device_node *node = pdev->dev.of_node;
struct rk_i2s_dev *i2s; struct rk_i2s_dev *i2s;
struct snd_soc_dai_driver *soc_dai;
struct resource *res; struct resource *res;
void __iomem *regs; void __iomem *regs;
int ret; int ret;
...@@ -511,17 +512,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev) ...@@ -511,17 +512,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
goto err_pm_disable; goto err_pm_disable;
} }
/* refine capture channels */ soc_dai = devm_kzalloc(&pdev->dev,
sizeof(*soc_dai), GFP_KERNEL);
if (!soc_dai)
return -ENOMEM;
memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai));
if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
if (val >= 2 && val <= 8)
soc_dai->playback.channels_max = val;
}
if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
if (val >= 2 && val <= 8) if (val >= 2 && val <= 8)
rockchip_i2s_dai.capture.channels_max = val; soc_dai->capture.channels_max = val;
else
rockchip_i2s_dai.capture.channels_max = 2;
} }
ret = devm_snd_soc_register_component(&pdev->dev, ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component, &rockchip_i2s_component,
&rockchip_i2s_dai, 1); soc_dai, 1);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Could not register DAI\n"); dev_err(&pdev->dev, "Could not register DAI\n");
goto err_suspend; goto err_suspend;
......
...@@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, ...@@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) { switch (params_rate(params)) {
case 8000: case 8000:
case 16000: case 16000:
case 24000:
case 32000:
case 48000: case 48000:
case 64000:
case 96000: case 96000:
mclk = 12288000; mclk = 12288000;
break; break;
case 11025:
case 22050:
case 44100: case 44100:
case 88200:
mclk = 11289600; mclk = 11289600;
break; break;
default: default:
......
...@@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, ...@@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) { switch (params_rate(params)) {
case 8000: case 8000:
case 16000: case 16000:
case 24000:
case 32000:
case 48000: case 48000:
case 64000:
case 96000: case 96000:
mclk = 12288000; mclk = 12288000;
break; break;
case 11025:
case 22050:
case 44100: case 44100:
case 88200:
mclk = 11289600; mclk = 11289600;
break; break;
default: default:
......
...@@ -36,7 +36,6 @@ config SND_SOC_SH4_SIU ...@@ -36,7 +36,6 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support" tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on DMA_OF
depends on COMMON_CLK depends on COMMON_CLK
select SND_SIMPLE_CARD select SND_SIMPLE_CARD
select REGMAP_MMIO select REGMAP_MMIO
......
snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o snd-soc-rcar-objs := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
snd-soc-rsrc-card-objs := rsrc-card.o snd-soc-rsrc-card-objs := rsrc-card.o
......
...@@ -68,8 +68,8 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div) ...@@ -68,8 +68,8 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div)
static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
{ {
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
int id = rsnd_mod_id(mod); int id = rsnd_mod_id(ssi_mod);
int ws = id; int ws = id;
if (rsnd_ssi_is_pin_sharing(io)) { if (rsnd_ssi_is_pin_sharing(io)) {
...@@ -90,13 +90,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) ...@@ -90,13 +90,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
return (0x6 + ws) << 8; return (0x6 + ws) << 8;
} }
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io)
{ {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
int id = rsnd_mod_id(mod); int id = rsnd_mod_id(cmd_mod);
int shift = (id % 2) ? 16 : 0; int shift = (id % 2) ? 16 : 0;
u32 mask, val; u32 mask, val;
...@@ -242,68 +242,6 @@ int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, ...@@ -242,68 +242,6 @@ int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
} }
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
struct rsnd_mod *mod,
unsigned int src_rate,
unsigned int dst_rate)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
struct device *dev = rsnd_priv_to_dev(priv);
int idx, sel, div, shift;
u32 mask, val;
int id = rsnd_mod_id(mod);
unsigned int sel_rate [] = {
clk_get_rate(adg->clk[CLKA]), /* 000: CLKA */
clk_get_rate(adg->clk[CLKB]), /* 001: CLKB */
clk_get_rate(adg->clk[CLKC]), /* 010: CLKC */
0, /* 011: MLBCLK (not used) */
adg->rbga_rate_for_441khz, /* 100: RBGA */
adg->rbgb_rate_for_48khz, /* 101: RBGB */
};
/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
for (div = 128, idx = 0;
div <= 2048;
div *= 2, idx++) {
if (src_rate == sel_rate[sel] / div) {
val = (idx << 4) | sel;
goto find_rate;
}
}
}
dev_err(dev, "can't find convert src clk\n");
return -EINVAL;
find_rate:
shift = (id % 4) * 8;
mask = 0xFF << shift;
val = val << shift;
dev_dbg(dev, "adg convert src clk = %02x\n", val);
switch (id / 4) {
case 0:
rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
break;
case 1:
rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
break;
case 2:
rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
break;
}
/*
* Gen1 doesn't need dst_rate settings,
* since it uses SSI WS pin.
* see also rsnd_src_set_route_if_gen1()
*/
return 0;
}
static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{ {
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
...@@ -337,20 +275,16 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) ...@@ -337,20 +275,16 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
} }
} }
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
{ {
/* rsnd_adg_set_ssi_clk(ssi_mod, 0);
* "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, 0);
return 0; return 0;
} }
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
{ {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct clk *clk; struct clk *clk;
...@@ -394,14 +328,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) ...@@ -394,14 +328,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
found_clock: found_clock:
/* rsnd_adg_set_ssi_clk(ssi_mod, data);
* This "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, data);
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
data, rate); data, rate);
return 0; return 0;
...@@ -418,15 +348,20 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, ...@@ -418,15 +348,20 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
[CLKC] = "clk_c", [CLKC] = "clk_c",
[CLKI] = "clk_i", [CLKI] = "clk_i",
}; };
int i; int i, ret;
for (i = 0; i < CLKMAX; i++) { for (i = 0; i < CLKMAX; i++) {
clk = devm_clk_get(dev, clk_name[i]); clk = devm_clk_get(dev, clk_name[i]);
adg->clk[i] = IS_ERR(clk) ? NULL : clk; adg->clk[i] = IS_ERR(clk) ? NULL : clk;
} }
for_each_rsnd_clk(clk, adg, i) for_each_rsnd_clk(clk, adg, i) {
ret = clk_prepare_enable(clk);
if (ret < 0)
dev_warn(dev, "can't use clk %d\n", i);
dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
}
} }
static void rsnd_adg_get_clkout(struct rsnd_priv *priv, static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
...@@ -437,7 +372,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, ...@@ -437,7 +372,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
u32 ckr, rbgx, rbga, rbgb; u32 ckr, rbgx, rbga, rbgb;
u32 rate, req_rate, div; u32 rate, req_rate = 0, div;
uint32_t count = 0; uint32_t count = 0;
unsigned long req_48kHz_rate, req_441kHz_rate; unsigned long req_48kHz_rate, req_441kHz_rate;
int i; int i;
...@@ -572,9 +507,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, ...@@ -572,9 +507,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
ckr, rbga, rbgb); ckr, rbga, rbgb);
} }
int rsnd_adg_probe(struct platform_device *pdev, int rsnd_adg_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct rsnd_adg *adg; struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
...@@ -600,3 +533,14 @@ int rsnd_adg_probe(struct platform_device *pdev, ...@@ -600,3 +533,14 @@ int rsnd_adg_probe(struct platform_device *pdev,
return 0; return 0;
} }
void rsnd_adg_remove(struct rsnd_priv *priv)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct clk *clk;
int i;
for_each_rsnd_clk(clk, adg, i) {
clk_disable_unprepare(clk);
}
}
/*
* Renesas R-Car CMD support
*
* Copyright (C) 2015 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "rsnd.h"
struct rsnd_cmd {
struct rsnd_mod mod;
};
#define CMD_NAME "cmd"
#define rsnd_cmd_nr(priv) ((priv)->cmd_nr)
#define for_each_rsnd_cmd(pos, priv, i) \
for ((i) = 0; \
((i) < rsnd_cmd_nr(priv)) && \
((pos) = (struct rsnd_cmd *)(priv)->cmd + i); \
i++)
static int rsnd_cmd_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
if (!mix && !dvc)
return 0;
if (mix) {
struct rsnd_dai *rdai;
int i;
u32 path[] = {
[0] = 0,
[1] = 1 << 0,
[2] = 0,
[3] = 0,
[4] = 0,
[5] = 1 << 8
};
/*
* it is assuming that integrater is well understanding about
* data path. Here doesn't check impossible connection,
* like src2 + src5
*/
data = 0;
for_each_rsnd_dai(rdai, priv, i) {
io = &rdai->playback;
if (mix == rsnd_io_to_mod_mix(io))
data |= path[rsnd_mod_id(src)];
io = &rdai->capture;
if (mix == rsnd_io_to_mod_mix(io))
data |= path[rsnd_mod_id(src)];
}
} else {
u32 path[] = {
[0] = 0x30000,
[1] = 0x30001,
[2] = 0x40000,
[3] = 0x10000,
[4] = 0x20000,
[5] = 0x40100
};
data = path[rsnd_mod_id(src)];
}
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
rsnd_adg_set_cmd_timsel_gen2(mod, io);
return 0;
}
static int rsnd_cmd_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0x10);
return 0;
}
static int rsnd_cmd_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0);
return 0;
}
static struct rsnd_mod_ops rsnd_cmd_ops = {
.name = CMD_NAME,
.init = rsnd_cmd_init,
.start = rsnd_cmd_start,
.stop = rsnd_cmd_stop,
};
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id);
return rsnd_dai_connect(mod, io, mod->type);
}
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
{
if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
id = 0;
return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
}
int rsnd_cmd_probe(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_cmd *cmd;
int i, nr, ret;
/* This driver doesn't support Gen1 at this point */
if (rsnd_is_gen1(priv))
return 0;
/* same number as DVC */
nr = priv->dvc_nr;
if (!nr)
return 0;
cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
priv->cmd_nr = nr;
priv->cmd = cmd;
for_each_rsnd_cmd(cmd, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
&rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
if (ret)
return ret;
}
return 0;
}
void rsnd_cmd_remove(struct rsnd_priv *priv)
{
struct rsnd_cmd *cmd;
int i;
for_each_rsnd_cmd(cmd, priv, i) {
rsnd_mod_quit(rsnd_mod_get(cmd));
}
}
...@@ -99,34 +99,17 @@ ...@@ -99,34 +99,17 @@
#define RSND_RATES SNDRV_PCM_RATE_8000_96000 #define RSND_RATES SNDRV_PCM_RATE_8000_96000
#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
static const struct rsnd_of_data rsnd_of_data_gen1 = {
.flags = RSND_GEN1,
};
static const struct rsnd_of_data rsnd_of_data_gen2 = {
.flags = RSND_GEN2,
};
static const struct of_device_id rsnd_of_match[] = { static const struct of_device_id rsnd_of_match[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
{ .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */ { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */
{}, {},
}; };
MODULE_DEVICE_TABLE(of, rsnd_of_match); MODULE_DEVICE_TABLE(of, rsnd_of_match);
/* /*
* rsnd_platform functions * rsnd_mod functions
*/ */
#define rsnd_platform_call(priv, dai, func, param...) \
(!(priv->info->func) ? 0 : \
priv->info->func(param))
#define rsnd_is_enable_path(io, name) \
((io)->info ? (io)->info->name : NULL)
#define rsnd_info_id(priv, io, name) \
((io)->info->name - priv->info->name##_info)
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
{ {
if (mod->type != type) { if (mod->type != type) {
...@@ -138,9 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) ...@@ -138,9 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
} }
} }
/*
* rsnd_mod functions
*/
char *rsnd_mod_name(struct rsnd_mod *mod) char *rsnd_mod_name(struct rsnd_mod *mod)
{ {
if (!mod || !mod->ops) if (!mod || !mod->ops)
...@@ -192,19 +172,16 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod, ...@@ -192,19 +172,16 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io; struct rsnd_dai_stream *io;
struct rsnd_dai *rdai; struct rsnd_dai *rdai;
int i, j; int i;
for_each_rsnd_dai(rdai, priv, j) {
for (i = 0; i < RSND_MOD_MAX; i++) { for_each_rsnd_dai(rdai, priv, i) {
io = &rdai->playback; io = &rdai->playback;
if (mod == io->mod[i]) if (mod == io->mod[mod->type])
callback(mod, io); callback(mod, io);
io = &rdai->capture; io = &rdai->capture;
if (mod == io->mod[i]) if (mod == io->mod[mod->type])
callback(mod, io); callback(mod, io);
}
} }
} }
...@@ -214,6 +191,43 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io) ...@@ -214,6 +191,43 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
return !!io->substream; return !!io->substream;
} }
void rsnd_set_slot(struct rsnd_dai *rdai,
int slots, int num)
{
rdai->slots = slots;
rdai->slots_num = num;
}
int rsnd_get_slot(struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
return rdai->slots;
}
int rsnd_get_slot_num(struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
return rdai->slots_num;
}
int rsnd_get_slot_width(struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int chan = runtime->channels;
/* Multi channel Mode */
if (rsnd_ssi_multi_slaves(io))
chan /= rsnd_get_slot_num(io);
/* TDM Extend Mode needs 8ch */
if (chan == 6)
chan = 8;
return chan;
}
/* /*
* ADINR function * ADINR function
*/ */
...@@ -222,21 +236,17 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ...@@ -222,21 +236,17 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
u32 adinr = runtime->channels;
switch (runtime->sample_bits) { switch (runtime->sample_bits) {
case 16: case 16:
adinr |= (8 << 16); return 8 << 16;
break;
case 32: case 32:
adinr |= (0 << 16); return 0 << 16;
break;
default:
dev_warn(dev, "not supported sample bits\n");
return 0;
} }
return adinr; dev_warn(dev, "not supported sample bits\n");
return 0;
} }
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
...@@ -267,13 +277,22 @@ u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ...@@ -267,13 +277,22 @@ u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
*/ */
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
{ {
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *target = src ? src : ssi; struct rsnd_mod *target;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 val = 0x76543210; u32 val = 0x76543210;
u32 mask = ~0; u32 mask = ~0;
if (rsnd_io_is_play(io)) {
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
target = src ? src : ssi;
} else {
struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
target = cmd ? cmd : ssi;
}
mask <<= runtime->channels * 4; mask <<= runtime->channels * 4;
val = val & mask; val = val & mask;
...@@ -300,20 +319,22 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ...@@ -300,20 +319,22 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
/* /*
* rsnd_dai functions * rsnd_dai functions
*/ */
#define rsnd_mod_call(mod, io, func, param...) \ #define rsnd_mod_call(idx, io, func, param...) \
({ \ ({ \
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct rsnd_mod *mod = (io)->mod[idx]; \
struct device *dev = rsnd_priv_to_dev(priv); \ struct device *dev = rsnd_priv_to_dev(priv); \
u32 *status = (io)->mod_status + idx; \
u32 mask = 0xF << __rsnd_mod_shift_##func; \ u32 mask = 0xF << __rsnd_mod_shift_##func; \
u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \ int ret = 0; \
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
mod->status = (mod->status & ~mask) + \ *status = (*status & ~mask) + \
(add << __rsnd_mod_shift_##func); \ (add << __rsnd_mod_shift_##func); \
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), \ rsnd_mod_name(mod), rsnd_mod_id(mod), \
mod->status, call ? #func : ""); \ *status, call ? #func : ""); \
if (call) \ if (call) \
ret = (mod)->ops->func(mod, io, param); \ ret = (mod)->ops->func(mod, io, param); \
ret; \ ret; \
...@@ -327,13 +348,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) ...@@ -327,13 +348,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
mod = (io)->mod[i]; \ mod = (io)->mod[i]; \
if (!mod) \ if (!mod) \
continue; \ continue; \
ret |= rsnd_mod_call(mod, io, fn, param); \ ret |= rsnd_mod_call(i, io, fn, param); \
} \ } \
ret; \ ret; \
}) })
static int rsnd_dai_connect(struct rsnd_mod *mod, int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io,
enum rsnd_mod_type type)
{ {
struct rsnd_priv *priv; struct rsnd_priv *priv;
struct device *dev; struct device *dev;
...@@ -341,10 +363,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, ...@@ -341,10 +363,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
if (!mod) if (!mod)
return -EIO; return -EIO;
if (io->mod[type])
return -EINVAL;
priv = rsnd_mod_to_priv(mod); priv = rsnd_mod_to_priv(mod);
dev = rsnd_priv_to_dev(priv); dev = rsnd_priv_to_dev(priv);
io->mod[mod->type] = mod; io->mod[type] = mod;
dev_dbg(dev, "%s[%d] is connected to io (%s)\n", dev_dbg(dev, "%s[%d] is connected to io (%s)\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), rsnd_mod_name(mod), rsnd_mod_id(mod),
...@@ -354,9 +379,10 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, ...@@ -354,9 +379,10 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
} }
static void rsnd_dai_disconnect(struct rsnd_mod *mod, static void rsnd_dai_disconnect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io,
enum rsnd_mod_type type)
{ {
io->mod[mod->type] = NULL; io->mod[type] = NULL;
} }
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
...@@ -469,7 +495,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -469,7 +495,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct rsnd_priv *priv = rsnd_dai_to_priv(dai); struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
int ret; int ret;
unsigned long flags; unsigned long flags;
...@@ -479,10 +504,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -479,10 +504,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
rsnd_dai_stream_init(io, substream); rsnd_dai_stream_init(io, substream);
ret = rsnd_platform_call(priv, dai, start, ssi_id);
if (ret < 0)
goto dai_trigger_end;
ret = rsnd_dai_call(init, io, priv); ret = rsnd_dai_call(init, io, priv);
if (ret < 0) if (ret < 0)
goto dai_trigger_end; goto dai_trigger_end;
...@@ -496,8 +517,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -496,8 +517,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret |= rsnd_dai_call(quit, io, priv); ret |= rsnd_dai_call(quit, io, priv);
ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
rsnd_dai_stream_quit(io); rsnd_dai_stream_quit(io);
break; break;
default: default:
...@@ -567,332 +586,157 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -567,332 +586,157 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0; return 0;
} }
static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
.trigger = rsnd_soc_dai_trigger, u32 tx_mask, u32 rx_mask,
.set_fmt = rsnd_soc_dai_set_fmt, int slots, int slot_width)
};
#define rsnd_path_add(priv, io, type) \
({ \
struct rsnd_mod *mod; \
int ret = 0; \
int id = -1; \
\
if (rsnd_is_enable_path(io, type)) { \
id = rsnd_info_id(priv, io, type); \
if (id >= 0) { \
mod = rsnd_##type##_mod_get(priv, id); \
ret = rsnd_dai_connect(mod, io); \
} \
} \
ret; \
})
#define rsnd_path_remove(priv, io, type) \
{ \
struct rsnd_mod *mod; \
int id = -1; \
\
if (rsnd_is_enable_path(io, type)) { \
id = rsnd_info_id(priv, io, type); \
if (id >= 0) { \
mod = rsnd_##type##_mod_get(priv, id); \
rsnd_dai_disconnect(mod, io); \
} \
} \
}
void rsnd_path_parse(struct rsnd_priv *priv,
struct rsnd_dai_stream *io)
{ {
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *cmd;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
/* Gen1 is not supported */ switch (slots) {
if (rsnd_is_gen1(priv)) case 6:
return; /* TDM Extend Mode */
rsnd_set_slot(rdai, slots, 1);
if (!mix && !dvc) break;
return; default:
dev_err(dev, "unsupported TDM slots (%d)\n", slots);
if (mix) { return -EINVAL;
struct rsnd_dai *rdai;
int i;
u32 path[] = {
[0] = 0,
[1] = 1 << 0,
[2] = 0,
[3] = 0,
[4] = 0,
[5] = 1 << 8
};
/*
* it is assuming that integrater is well understanding about
* data path. Here doesn't check impossible connection,
* like src2 + src5
*/
data = 0;
for_each_rsnd_dai(rdai, priv, i) {
io = &rdai->playback;
if (mix == rsnd_io_to_mod_mix(io))
data |= path[rsnd_mod_id(src)];
io = &rdai->capture;
if (mix == rsnd_io_to_mod_mix(io))
data |= path[rsnd_mod_id(src)];
}
/*
* We can't use ctu = rsnd_io_ctu() here.
* Since, ID of dvc/mix are 0 or 1 (= same as CMD number)
* but ctu IDs are 0 - 7 (= CTU00 - CTU13)
*/
cmd = mix;
} else {
u32 path[] = {
[0] = 0x30000,
[1] = 0x30001,
[2] = 0x40000,
[3] = 0x10000,
[4] = 0x20000,
[5] = 0x40100
};
data = path[rsnd_mod_id(src)];
cmd = dvc;
} }
dev_dbg(dev, "ctu/mix path = 0x%08x", data); return 0;
rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
rsnd_mod_write(cmd, CMD_CTRL, 0x10);
} }
static int rsnd_path_init(struct rsnd_priv *priv, static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
struct rsnd_dai *rdai, .trigger = rsnd_soc_dai_trigger,
struct rsnd_dai_stream *io) .set_fmt = rsnd_soc_dai_set_fmt,
{ .set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
int ret; };
/*
* Gen1 is created by SRU/SSI, and this SRU is base module of
* Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
*
* Easy image is..
* Gen1 SRU = Gen2 SCU + SSIU + etc
*
* Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
* using fixed path.
*/
/* SSI */
ret = rsnd_path_add(priv, io, ssi);
if (ret < 0)
return ret;
/* SRC */
ret = rsnd_path_add(priv, io, src);
if (ret < 0)
return ret;
/* CTU */ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
ret = rsnd_path_add(priv, io, ctu); struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
if (ret < 0) struct device_node *node,
return ret; struct device_node *playback,
struct device_node *capture)
{
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
struct device_node *np;
struct rsnd_mod *mod;
int i;
/* MIX */ if (!node)
ret = rsnd_path_add(priv, io, mix); return;
if (ret < 0)
return ret;
/* DVC */ i = 0;
ret = rsnd_path_add(priv, io, dvc); for_each_child_of_node(node, np) {
if (ret < 0) mod = mod_get(priv, i);
return ret; if (np == playback)
rsnd_dai_connect(mod, &rdai->playback, mod->type);
if (np == capture)
rsnd_dai_connect(mod, &rdai->capture, mod->type);
i++;
}
return ret; of_node_put(node);
} }
static void rsnd_of_parse_dai(struct platform_device *pdev, static int rsnd_dai_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct device_node *dai_node, *dai_np; struct device_node *dai_node;
struct device_node *ssi_node, *ssi_np; struct device_node *dai_np;
struct device_node *src_node, *src_np;
struct device_node *ctu_node, *ctu_np;
struct device_node *mix_node, *mix_np;
struct device_node *dvc_node, *dvc_np;
struct device_node *playback, *capture; struct device_node *playback, *capture;
struct rsnd_dai_platform_info *dai_info; struct rsnd_dai_stream *io_playback;
struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct rsnd_dai_stream *io_capture;
struct device *dev = &pdev->dev; struct snd_soc_dai_driver *rdrv, *drv;
int nr, i; struct rsnd_dai *rdai;
int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i; struct device *dev = rsnd_priv_to_dev(priv);
int nr, dai_i, io_i;
if (!of_data) int ret;
return;
dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai");
if (!dai_node)
return;
dai_node = rsnd_dai_of_node(priv);
nr = of_get_child_count(dai_node); nr = of_get_child_count(dai_node);
if (!nr) if (!nr) {
return; ret = -EINVAL;
goto rsnd_dai_probe_done;
}
dai_info = devm_kzalloc(dev, rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
sizeof(struct rsnd_dai_platform_info) * nr, rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
GFP_KERNEL); if (!rdrv || !rdai) {
if (!dai_info) { ret = -ENOMEM;
dev_err(dev, "dai info allocation error\n"); goto rsnd_dai_probe_done;
return;
} }
info->dai_info_nr = nr; priv->rdai_nr = nr;
info->dai_info = dai_info; priv->daidrv = rdrv;
priv->rdai = rdai;
ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
#define mod_parse(name) \
if (name##_node) { \
struct rsnd_##name##_platform_info *name##_info; \
\
name##_i = 0; \
for_each_child_of_node(name##_node, name##_np) { \
name##_info = info->name##_info + name##_i; \
\
if (name##_np == playback) \
dai_info->playback.name = name##_info; \
if (name##_np == capture) \
dai_info->capture.name = name##_info; \
\
name##_i++; \
} \
}
/* /*
* parse all dai * parse all dai
*/ */
dai_i = 0; dai_i = 0;
for_each_child_of_node(dai_node, dai_np) { for_each_child_of_node(dai_node, dai_np) {
dai_info = info->dai_info + dai_i; rdai = rsnd_rdai_get(priv, dai_i);
drv = rdrv + dai_i;
for (i = 0;; i++) { io_playback = &rdai->playback;
io_capture = &rdai->capture;
playback = of_parse_phandle(dai_np, "playback", i);
capture = of_parse_phandle(dai_np, "capture", i); snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
rdai->priv = priv;
drv->name = rdai->name;
drv->ops = &rsnd_soc_dai_ops;
snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
"DAI%d Playback", dai_i);
drv->playback.rates = RSND_RATES;
drv->playback.formats = RSND_FMTS;
drv->playback.channels_min = 2;
drv->playback.channels_max = 6;
drv->playback.stream_name = rdai->playback.name;
snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
"DAI%d Capture", dai_i);
drv->capture.rates = RSND_RATES;
drv->capture.formats = RSND_FMTS;
drv->capture.channels_min = 2;
drv->capture.channels_max = 6;
drv->capture.stream_name = rdai->capture.name;
rdai->playback.rdai = rdai;
rdai->capture.rdai = rdai;
rsnd_set_slot(rdai, 2, 1); /* default */
for (io_i = 0;; io_i++) {
playback = of_parse_phandle(dai_np, "playback", io_i);
capture = of_parse_phandle(dai_np, "capture", io_i);
if (!playback && !capture) if (!playback && !capture)
break; break;
mod_parse(ssi); rsnd_parse_connect_ssi(rdai, playback, capture);
mod_parse(src); rsnd_parse_connect_src(rdai, playback, capture);
mod_parse(ctu); rsnd_parse_connect_ctu(rdai, playback, capture);
mod_parse(mix); rsnd_parse_connect_mix(rdai, playback, capture);
mod_parse(dvc); rsnd_parse_connect_dvc(rdai, playback, capture);
of_node_put(playback); of_node_put(playback);
of_node_put(capture); of_node_put(capture);
} }
dai_i++; dai_i++;
}
}
static int rsnd_dai_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct snd_soc_dai_driver *drv;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct rsnd_dai *rdai;
struct rsnd_ssi_platform_info *pmod, *cmod;
struct device *dev = rsnd_priv_to_dev(priv);
int dai_nr;
int i;
rsnd_of_parse_dai(pdev, of_data, priv);
dai_nr = info->dai_info_nr; dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
if (!dai_nr) { rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
dev_err(dev, "no dai\n"); rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
return -EIO;
} }
drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); ret = 0;
rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL);
if (!drv || !rdai) {
dev_err(dev, "dai allocate failed\n");
return -ENOMEM;
}
priv->rdai_nr = dai_nr;
priv->daidrv = drv;
priv->rdai = rdai;
for (i = 0; i < dai_nr; i++) { rsnd_dai_probe_done:
of_node_put(dai_node);
pmod = info->dai_info[i].playback.ssi; return ret;
cmod = info->dai_info[i].capture.ssi;
/*
* init rsnd_dai
*/
snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
rdai[i].priv = priv;
/*
* init snd_soc_dai_driver
*/
drv[i].name = rdai[i].name;
drv[i].ops = &rsnd_soc_dai_ops;
if (pmod) {
snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE,
"DAI%d Playback", i);
drv[i].playback.rates = RSND_RATES;
drv[i].playback.formats = RSND_FMTS;
drv[i].playback.channels_min = 2;
drv[i].playback.channels_max = 2;
drv[i].playback.stream_name = rdai[i].playback.name;
rdai[i].playback.info = &info->dai_info[i].playback;
rdai[i].playback.rdai = rdai + i;
rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
}
if (cmod) {
snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE,
"DAI%d Capture", i);
drv[i].capture.rates = RSND_RATES;
drv[i].capture.formats = RSND_FMTS;
drv[i].capture.channels_min = 2;
drv[i].capture.channels_max = 2;
drv[i].capture.stream_name = rdai[i].capture.name;
rdai[i].capture.info = &info->dai_info[i].capture;
rdai[i].capture.rdai = rdai + i;
rsnd_path_init(priv, &rdai[i], &rdai[i].capture);
}
dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name,
pmod ? "play" : " -- ",
cmod ? "capture" : " -- ");
}
return 0;
} }
/* /*
...@@ -1076,10 +920,14 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, ...@@ -1076,10 +920,14 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
void (*update)(struct rsnd_dai_stream *io, void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod), struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg, struct rsnd_kctrl_cfg_m *_cfg,
int ch_size,
u32 max) u32 max)
{ {
if (ch_size > RSND_DVC_CHANNELS)
return -EINVAL;
_cfg->cfg.max = max; _cfg->cfg.max = max;
_cfg->cfg.size = RSND_DVC_CHANNELS; _cfg->cfg.size = ch_size;
_cfg->cfg.val = _cfg->val; _cfg->cfg.val = _cfg->val;
return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
} }
...@@ -1160,6 +1008,9 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, ...@@ -1160,6 +1008,9 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
ret = rsnd_dai_call(probe, io, priv); ret = rsnd_dai_call(probe, io, priv);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
int i;
/* /*
* Fallback to PIO mode * Fallback to PIO mode
*/ */
...@@ -1174,10 +1025,12 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, ...@@ -1174,10 +1025,12 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
rsnd_dai_call(remove, io, priv); rsnd_dai_call(remove, io, priv);
/* /*
* remove SRC/DVC from DAI, * remove all mod from io
* and, re connect ssi
*/ */
rsnd_path_remove(priv, io, src); for (i = 0; i < RSND_MOD_MAX; i++)
rsnd_path_remove(priv, io, dvc); rsnd_dai_disconnect((io)->mod[i], io, i);
rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
/* /*
* fallback * fallback
...@@ -1199,33 +1052,25 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv, ...@@ -1199,33 +1052,25 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
*/ */
static int rsnd_probe(struct platform_device *pdev) static int rsnd_probe(struct platform_device *pdev)
{ {
struct rcar_snd_info *info;
struct rsnd_priv *priv; struct rsnd_priv *priv;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct rsnd_dai *rdai; struct rsnd_dai *rdai;
const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
const struct rsnd_of_data *of_data; int (*probe_func[])(struct rsnd_priv *priv) = {
int (*probe_func[])(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv) = {
rsnd_gen_probe, rsnd_gen_probe,
rsnd_dma_probe, rsnd_dma_probe,
rsnd_ssi_probe, rsnd_ssi_probe,
rsnd_ssiu_probe,
rsnd_src_probe, rsnd_src_probe,
rsnd_ctu_probe, rsnd_ctu_probe,
rsnd_mix_probe, rsnd_mix_probe,
rsnd_dvc_probe, rsnd_dvc_probe,
rsnd_cmd_probe,
rsnd_adg_probe, rsnd_adg_probe,
rsnd_dai_probe, rsnd_dai_probe,
}; };
int ret, i; int ret, i;
info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
of_data = of_id->data;
/* /*
* init priv data * init priv data
*/ */
...@@ -1236,14 +1081,14 @@ static int rsnd_probe(struct platform_device *pdev) ...@@ -1236,14 +1081,14 @@ static int rsnd_probe(struct platform_device *pdev)
} }
priv->pdev = pdev; priv->pdev = pdev;
priv->info = info; priv->flags = (unsigned long)of_id->data;
spin_lock_init(&priv->lock); spin_lock_init(&priv->lock);
/* /*
* init each module * init each module
*/ */
for (i = 0; i < ARRAY_SIZE(probe_func); i++) { for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
ret = probe_func[i](pdev, of_data, priv); ret = probe_func[i](priv);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1296,13 +1141,15 @@ static int rsnd_remove(struct platform_device *pdev) ...@@ -1296,13 +1141,15 @@ static int rsnd_remove(struct platform_device *pdev)
{ {
struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
struct rsnd_dai *rdai; struct rsnd_dai *rdai;
void (*remove_func[])(struct platform_device *pdev, void (*remove_func[])(struct rsnd_priv *priv) = {
struct rsnd_priv *priv) = {
rsnd_ssi_remove, rsnd_ssi_remove,
rsnd_ssiu_remove,
rsnd_src_remove, rsnd_src_remove,
rsnd_ctu_remove, rsnd_ctu_remove,
rsnd_mix_remove, rsnd_mix_remove,
rsnd_dvc_remove, rsnd_dvc_remove,
rsnd_cmd_remove,
rsnd_adg_remove,
}; };
int ret = 0, i; int ret = 0, i;
...@@ -1314,7 +1161,7 @@ static int rsnd_remove(struct platform_device *pdev) ...@@ -1314,7 +1161,7 @@ static int rsnd_remove(struct platform_device *pdev)
} }
for (i = 0; i < ARRAY_SIZE(remove_func); i++) for (i = 0; i < ARRAY_SIZE(remove_func); i++)
remove_func[i](pdev, priv); remove_func[i](priv);
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_platform(&pdev->dev);
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#define CTU_NAME "ctu" #define CTU_NAME "ctu"
struct rsnd_ctu { struct rsnd_ctu {
struct rsnd_ctu_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod; struct rsnd_mod mod;
}; };
...@@ -24,6 +23,7 @@ struct rsnd_ctu { ...@@ -24,6 +23,7 @@ struct rsnd_ctu {
((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
i++) i++)
#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) #define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) #define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
...@@ -31,6 +31,13 @@ static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) ...@@ -31,6 +31,13 @@ static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
rsnd_mod_write(mod, CTU_CTUIR, enable); rsnd_mod_write(mod, CTU_CTUIR, enable);
} }
static int rsnd_ctu_probe_(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
}
static int rsnd_ctu_init(struct rsnd_mod *mod, static int rsnd_ctu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
...@@ -57,6 +64,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, ...@@ -57,6 +64,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
static struct rsnd_mod_ops rsnd_ctu_ops = { static struct rsnd_mod_ops rsnd_ctu_ops = {
.name = CTU_NAME, .name = CTU_NAME,
.probe = rsnd_ctu_probe_,
.init = rsnd_ctu_init, .init = rsnd_ctu_init,
.quit = rsnd_ctu_quit, .quit = rsnd_ctu_quit,
}; };
...@@ -66,51 +74,13 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) ...@@ -66,51 +74,13 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv))) if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
id = 0; id = 0;
return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id); return rsnd_mod_get(rsnd_ctu_get(priv, id));
} }
static void rsnd_of_parse_ctu(struct platform_device *pdev, int rsnd_ctu_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct device_node *node; struct device_node *node;
struct rsnd_ctu_platform_info *ctu_info; struct device_node *np;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr;
if (!of_data)
return;
node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
if (!node)
return;
nr = of_get_child_count(node);
if (!nr)
goto rsnd_of_parse_ctu_end;
ctu_info = devm_kzalloc(dev,
sizeof(struct rsnd_ctu_platform_info) * nr,
GFP_KERNEL);
if (!ctu_info) {
dev_err(dev, "ctu info allocation error\n");
goto rsnd_of_parse_ctu_end;
}
info->ctu_info = ctu_info;
info->ctu_info_nr = nr;
rsnd_of_parse_ctu_end:
of_node_put(node);
}
int rsnd_ctu_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ctu *ctu; struct rsnd_ctu *ctu;
struct clk *clk; struct clk *clk;
...@@ -121,20 +91,30 @@ int rsnd_ctu_probe(struct platform_device *pdev, ...@@ -121,20 +91,30 @@ int rsnd_ctu_probe(struct platform_device *pdev,
if (rsnd_is_gen1(priv)) if (rsnd_is_gen1(priv))
return 0; return 0;
rsnd_of_parse_ctu(pdev, of_data, priv); node = rsnd_ctu_of_node(priv);
if (!node)
return 0; /* not used is not error */
nr = info->ctu_info_nr; nr = of_get_child_count(node);
if (!nr) if (!nr) {
return 0; ret = -EINVAL;
goto rsnd_ctu_probe_done;
}
ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL); ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
if (!ctu) if (!ctu) {
return -ENOMEM; ret = -ENOMEM;
goto rsnd_ctu_probe_done;
}
priv->ctu_nr = nr; priv->ctu_nr = nr;
priv->ctu = ctu; priv->ctu = ctu;
for_each_rsnd_ctu(ctu, priv, i) { i = 0;
ret = 0;
for_each_child_of_node(node, np) {
ctu = rsnd_ctu_get(priv, i);
/* /*
* CTU00, CTU01, CTU02, CTU03 => CTU0 * CTU00, CTU01, CTU02, CTU03 => CTU0
* CTU10, CTU11, CTU12, CTU13 => CTU1 * CTU10, CTU11, CTU12, CTU13 => CTU1
...@@ -143,22 +123,27 @@ int rsnd_ctu_probe(struct platform_device *pdev, ...@@ -143,22 +123,27 @@ int rsnd_ctu_probe(struct platform_device *pdev,
CTU_NAME, i / 4); CTU_NAME, i / 4);
clk = devm_clk_get(dev, name); clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) if (IS_ERR(clk)) {
return PTR_ERR(clk); ret = PTR_ERR(clk);
goto rsnd_ctu_probe_done;
ctu->info = &info->ctu_info[i]; }
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
clk, RSND_MOD_CTU, i); clk, RSND_MOD_CTU, i);
if (ret) if (ret)
return ret; goto rsnd_ctu_probe_done;
i++;
} }
return 0;
rsnd_ctu_probe_done:
of_node_put(node);
return ret;
} }
void rsnd_ctu_remove(struct platform_device *pdev, void rsnd_ctu_remove(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_ctu *ctu; struct rsnd_ctu *ctu;
int i; int i;
......
...@@ -22,21 +22,36 @@ ...@@ -22,21 +22,36 @@
/* PDMACHCR */ /* PDMACHCR */
#define PDMACHCR_DE (1 << 0) #define PDMACHCR_DE (1 << 0)
struct rsnd_dmaen {
struct dma_chan *chan;
};
struct rsnd_dmapp {
int dmapp_id;
u32 chcr;
};
struct rsnd_dma {
struct rsnd_mod mod;
dma_addr_t src_addr;
dma_addr_t dst_addr;
union {
struct rsnd_dmaen en;
struct rsnd_dmapp pp;
} dma;
};
struct rsnd_dma_ctrl { struct rsnd_dma_ctrl {
void __iomem *base; void __iomem *base;
int dmaen_num;
int dmapp_num; int dmapp_num;
}; };
struct rsnd_dma_ops {
char *name;
void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
};
#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma)
#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod)
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
/* /*
* Audio DMAC * Audio DMAC
...@@ -77,18 +92,24 @@ static void rsnd_dmaen_complete(void *data) ...@@ -77,18 +92,24 @@ static void rsnd_dmaen_complete(void *data)
rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
} }
static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) static int rsnd_dmaen_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
dmaengine_terminate_all(dmaen->chan); dmaengine_terminate_all(dmaen->chan);
return 0;
} }
static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) static int rsnd_dmaen_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct snd_pcm_substream *substream = io->substream; struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
...@@ -103,18 +124,20 @@ static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) ...@@ -103,18 +124,20 @@ static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
if (!desc) { if (!desc) {
dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
return; return -EIO;
} }
desc->callback = rsnd_dmaen_complete; desc->callback = rsnd_dmaen_complete;
desc->callback_param = mod; desc->callback_param = rsnd_mod_get(dma);
if (dmaengine_submit(desc) < 0) { if (dmaengine_submit(desc) < 0) {
dev_err(dev, "dmaengine_submit() fail\n"); dev_err(dev, "dmaengine_submit() fail\n");
return; return -EIO;
} }
dma_async_issue_pending(dmaen->chan); dma_async_issue_pending(dmaen->chan);
return 0;
} }
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
...@@ -152,12 +175,29 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, ...@@ -152,12 +175,29 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
return rsnd_mod_dma_req(io, mod_to); return rsnd_mod_dma_req(io, mod_to);
} }
static int rsnd_dmaen_init(struct rsnd_dai_stream *io, static int rsnd_dmaen_remove(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
if (dmaen->chan)
dma_release_channel(dmaen->chan);
dmaen->chan = NULL;
return 0;
}
static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
struct rsnd_dma *dma, int id, struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{ {
struct rsnd_mod *mod = rsnd_mod_get(dma);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct dma_slave_config cfg = {}; struct dma_slave_config cfg = {};
int is_play = rsnd_io_is_play(io); int is_play = rsnd_io_is_play(io);
...@@ -191,18 +231,20 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, ...@@ -191,18 +231,20 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dev_dbg(dev, "%s %pad -> %pad\n", dev_dbg(dev, "%s[%d] %pad -> %pad\n",
dma->ops->name, rsnd_mod_name(mod), rsnd_mod_id(mod),
&cfg.src_addr, &cfg.dst_addr); &cfg.src_addr, &cfg.dst_addr);
ret = dmaengine_slave_config(dmaen->chan, &cfg); ret = dmaengine_slave_config(dmaen->chan, &cfg);
if (ret < 0) if (ret < 0)
goto rsnd_dma_init_err; goto rsnd_dma_attach_err;
dmac->dmaen_num++;
return 0; return 0;
rsnd_dma_init_err: rsnd_dma_attach_err:
rsnd_dma_quit(io, dma); rsnd_dmaen_remove(mod, io, priv);
rsnd_dma_channel_err: rsnd_dma_channel_err:
/* /*
...@@ -214,22 +256,11 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io, ...@@ -214,22 +256,11 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
return -EAGAIN; return -EAGAIN;
} }
static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) static struct rsnd_mod_ops rsnd_dmaen_ops = {
{
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
if (dmaen->chan)
dma_release_channel(dmaen->chan);
dmaen->chan = NULL;
}
static struct rsnd_dma_ops rsnd_dmaen_ops = {
.name = "audmac", .name = "audmac",
.start = rsnd_dmaen_start, .start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop, .stop = rsnd_dmaen_stop,
.init = rsnd_dmaen_init, .remove = rsnd_dmaen_remove,
.quit = rsnd_dmaen_quit,
}; };
/* /*
...@@ -307,7 +338,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, ...@@ -307,7 +338,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
(0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
{ {
struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_mod *mod = rsnd_mod_get(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
...@@ -319,38 +350,48 @@ static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) ...@@ -319,38 +350,48 @@ static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
{ {
struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_mod *mod = rsnd_mod_get(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
} }
static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) static int rsnd_dmapp_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
int i; int i;
rsnd_dmapp_write(dma, 0, PDMACHCR); rsnd_dmapp_write(dma, 0, PDMACHCR);
for (i = 0; i < 1024; i++) { for (i = 0; i < 1024; i++) {
if (0 == rsnd_dmapp_read(dma, PDMACHCR)) if (0 == rsnd_dmapp_read(dma, PDMACHCR))
return; return 0;
udelay(1); udelay(1);
} }
return -EIO;
} }
static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) static int rsnd_dmapp_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); rsnd_dmapp_write(dma, dma->src_addr, PDMASAR);
rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR);
rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR);
return 0;
} }
static int rsnd_dmapp_init(struct rsnd_dai_stream *io, static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
struct rsnd_dma *dma, int id, struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{ {
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_priv *priv = rsnd_io_to_priv(io);
...@@ -362,19 +403,16 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io, ...@@ -362,19 +403,16 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
dmac->dmapp_num++; dmac->dmapp_num++;
rsnd_dmapp_stop(io, dma);
dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
return 0; return 0;
} }
static struct rsnd_dma_ops rsnd_dmapp_ops = { static struct rsnd_mod_ops rsnd_dmapp_ops = {
.name = "audmac-pp", .name = "audmac-pp",
.start = rsnd_dmapp_start, .start = rsnd_dmapp_start,
.stop = rsnd_dmapp_stop, .stop = rsnd_dmapp_stop,
.init = rsnd_dmapp_init,
.quit = rsnd_dmapp_stop, .quit = rsnd_dmapp_stop,
}; };
...@@ -497,13 +535,12 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, ...@@ -497,13 +535,12 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
} }
#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ #define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
static void rsnd_dma_of_path(struct rsnd_dma *dma, static void rsnd_dma_of_path(struct rsnd_mod *this,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
int is_play, int is_play,
struct rsnd_mod **mod_from, struct rsnd_mod **mod_from,
struct rsnd_mod **mod_to) struct rsnd_mod **mod_to)
{ {
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
...@@ -513,7 +550,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, ...@@ -513,7 +550,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
struct rsnd_mod *mod_start, *mod_end; struct rsnd_mod *mod_start, *mod_end;
struct rsnd_priv *priv = rsnd_mod_to_priv(this); struct rsnd_priv *priv = rsnd_mod_to_priv(this);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
int nr, i; int nr, i, idx;
if (!ssi) if (!ssi)
return; return;
...@@ -542,23 +579,24 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, ...@@ -542,23 +579,24 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
mod_start = (is_play) ? NULL : ssi; mod_start = (is_play) ? NULL : ssi;
mod_end = (is_play) ? ssi : NULL; mod_end = (is_play) ? ssi : NULL;
mod[0] = mod_start; idx = 0;
mod[idx++] = mod_start;
for (i = 1; i < nr; i++) { for (i = 1; i < nr; i++) {
if (src) { if (src) {
mod[i] = src; mod[idx++] = src;
src = NULL; src = NULL;
} else if (ctu) { } else if (ctu) {
mod[i] = ctu; mod[idx++] = ctu;
ctu = NULL; ctu = NULL;
} else if (mix) { } else if (mix) {
mod[i] = mix; mod[idx++] = mix;
mix = NULL; mix = NULL;
} else if (dvc) { } else if (dvc) {
mod[i] = dvc; mod[idx++] = dvc;
dvc = NULL; dvc = NULL;
} }
} }
mod[i] = mod_end; mod[idx] = mod_end;
/* /*
* | SSI | SRC | * | SSI | SRC |
...@@ -567,8 +605,8 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, ...@@ -567,8 +605,8 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
* !is_play | * | o | * !is_play | * | o |
*/ */
if ((this == ssi) == (is_play)) { if ((this == ssi) == (is_play)) {
*mod_from = mod[nr - 1]; *mod_from = mod[idx - 1];
*mod_to = mod[nr]; *mod_to = mod[idx];
} else { } else {
*mod_from = mod[0]; *mod_from = mod[0];
*mod_to = mod[1]; *mod_to = mod[1];
...@@ -576,7 +614,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, ...@@ -576,7 +614,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
dev_dbg(dev, "module connection (this is %s[%d])\n", dev_dbg(dev, "module connection (this is %s[%d])\n",
rsnd_mod_name(this), rsnd_mod_id(this)); rsnd_mod_name(this), rsnd_mod_id(this));
for (i = 0; i <= nr; i++) { for (i = 0; i <= idx; i++) {
dev_dbg(dev, " %s[%d]%s\n", dev_dbg(dev, " %s[%d]%s\n",
rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]),
(mod[i] == *mod_from) ? " from" : (mod[i] == *mod_from) ? " from" :
...@@ -584,36 +622,22 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, ...@@ -584,36 +622,22 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
} }
} }
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
{ struct rsnd_mod *mod, int id)
dma->ops->stop(io, dma);
}
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
dma->ops->start(io, dma);
}
void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
if (!dmac)
return;
dma->ops->quit(io, dma);
}
int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
{ {
struct rsnd_mod *dma_mod;
struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL; struct rsnd_mod *mod_to = NULL;
struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct rsnd_dma *dma;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops;
enum rsnd_mod_type type;
int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
int is_play = rsnd_io_is_play(io); int is_play = rsnd_io_is_play(io);
int ret, dma_id;
/* /*
* DMA failed. try to PIO mode * DMA failed. try to PIO mode
...@@ -622,35 +646,64 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) ...@@ -622,35 +646,64 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
* rsnd_rdai_continuance_probe() * rsnd_rdai_continuance_probe()
*/ */
if (!dmac) if (!dmac)
return -EAGAIN; return ERR_PTR(-EAGAIN);
rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to); dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
if (!dma)
return ERR_PTR(-ENOMEM);
rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
/* for Gen2 */ /* for Gen2 */
if (mod_from && mod_to) if (mod_from && mod_to) {
dma->ops = &rsnd_dmapp_ops; ops = &rsnd_dmapp_ops;
else attach = rsnd_dmapp_attach;
dma->ops = &rsnd_dmaen_ops; dma_id = dmac->dmapp_num;
type = RSND_MOD_AUDMAPP;
} else {
ops = &rsnd_dmaen_ops;
attach = rsnd_dmaen_attach;
dma_id = dmac->dmaen_num;
type = RSND_MOD_AUDMA;
}
/* for Gen1, overwrite */ /* for Gen1, overwrite */
if (rsnd_is_gen1(priv)) if (rsnd_is_gen1(priv)) {
dma->ops = &rsnd_dmaen_ops; ops = &rsnd_dmaen_ops;
attach = rsnd_dmaen_attach;
dma_id = dmac->dmaen_num;
type = RSND_MOD_AUDMA;
}
dma_mod = rsnd_mod_get(dma);
ret = rsnd_mod_init(priv, dma_mod,
ops, NULL, type, dma_id);
if (ret < 0)
return ERR_PTR(ret);
dev_dbg(dev, "%s %s[%d] -> %s[%d]\n", dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
dma->ops->name, rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
return dma->ops->init(io, dma, id, mod_from, mod_to); ret = attach(io, dma, id, mod_from, mod_to);
if (ret < 0)
return ERR_PTR(ret);
ret = rsnd_dai_connect(dma_mod, io, type);
if (ret < 0)
return ERR_PTR(ret);
return rsnd_mod_get(dma);
} }
int rsnd_dma_probe(struct platform_device *pdev, int rsnd_dma_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct platform_device *pdev = rsnd_priv_to_pdev(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dma_ctrl *dmac; struct rsnd_dma_ctrl *dmac;
struct resource *res; struct resource *res;
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#define DVC_NAME "dvc" #define DVC_NAME "dvc"
struct rsnd_dvc { struct rsnd_dvc {
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod; struct rsnd_mod mod;
struct rsnd_kctrl_cfg_m volume; struct rsnd_kctrl_cfg_m volume;
struct rsnd_kctrl_cfg_m mute; struct rsnd_kctrl_cfg_m mute;
...@@ -24,6 +23,7 @@ struct rsnd_dvc { ...@@ -24,6 +23,7 @@ struct rsnd_dvc {
struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */
}; };
#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) #define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
#define rsnd_dvc_of_node(priv) \ #define rsnd_dvc_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
...@@ -64,79 +64,142 @@ static const char * const dvc_ramp_rate[] = { ...@@ -64,79 +64,142 @@ static const char * const dvc_ramp_rate[] = {
"0.125 dB/8192 steps", /* 10111 */ "0.125 dB/8192 steps", /* 10111 */
}; };
static void rsnd_dvc_soft_reset(struct rsnd_mod *mod) static void rsnd_dvc_activation(struct rsnd_mod *mod)
{ {
rsnd_mod_write(mod, DVC_SWRSR, 0); rsnd_mod_write(mod, DVC_SWRSR, 0);
rsnd_mod_write(mod, DVC_SWRSR, 1); rsnd_mod_write(mod, DVC_SWRSR, 1);
} }
#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1) static void rsnd_dvc_halt(struct rsnd_mod *mod)
#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0)
static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable)
{ {
rsnd_mod_write(mod, DVC_DVUIR, enable); rsnd_mod_write(mod, DVC_DVUIR, 1);
rsnd_mod_write(mod, DVC_SWRSR, 0);
} }
static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, #define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val)
struct rsnd_mod *mod) #define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13))
static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{ {
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 val[RSND_DVC_CHANNELS]; u32 val[RSND_DVC_CHANNELS];
u32 dvucr = 0;
u32 mute = 0;
int i; int i;
for (i = 0; i < dvc->mute.cfg.size; i++) /* Enable Ramp */
mute |= (!!dvc->mute.cfg.val[i]) << i; if (dvc->ren.val)
for (i = 0; i < RSND_DVC_CHANNELS; i++)
val[i] = dvc->volume.cfg.max;
else
for (i = 0; i < RSND_DVC_CHANNELS; i++)
val[i] = dvc->volume.val[i];
/* Disable DVC Register access */ /* Enable Digital Volume */
rsnd_mod_write(mod, DVC_DVUER, 0); rsnd_mod_write(mod, DVC_VOL0R, val[0]);
rsnd_mod_write(mod, DVC_VOL1R, val[1]);
rsnd_mod_write(mod, DVC_VOL2R, val[2]);
rsnd_mod_write(mod, DVC_VOL3R, val[3]);
rsnd_mod_write(mod, DVC_VOL4R, val[4]);
rsnd_mod_write(mod, DVC_VOL5R, val[5]);
rsnd_mod_write(mod, DVC_VOL6R, val[6]);
rsnd_mod_write(mod, DVC_VOL7R, val[7]);
}
static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 adinr = 0;
u32 dvucr = 0;
u32 vrctr = 0;
u32 vrpdr = 0;
u32 vrdbr = 0;
adinr = rsnd_get_adinr_bit(mod, io) |
rsnd_get_adinr_chan(mod, io);
/* Enable Digital Volume, Zero Cross Mute Mode */
dvucr |= 0x101;
/* Enable Ramp */ /* Enable Ramp */
if (dvc->ren.val) { if (dvc->ren.val) {
dvucr |= 0x10; dvucr |= 0x10;
/* Digital Volume Max */
for (i = 0; i < RSND_DVC_CHANNELS; i++)
val[i] = dvc->volume.cfg.max;
rsnd_mod_write(mod, DVC_VRCTR, 0xff);
rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
dvc->rdown.val);
/* /*
* FIXME !! * FIXME !!
* use scale-downed Digital Volume * use scale-downed Digital Volume
* as Volume Ramp * as Volume Ramp
* 7F FFFF -> 3FF * 7F FFFF -> 3FF
*/ */
rsnd_mod_write(mod, DVC_VRDBR, vrctr = 0xff;
0x3ff - (dvc->volume.val[0] >> 13)); vrpdr = rsnd_dvc_get_vrpdr(dvc);
vrdbr = rsnd_dvc_get_vrdbr(dvc);
} else {
for (i = 0; i < RSND_DVC_CHANNELS; i++)
val[i] = dvc->volume.val[i];
} }
/* Enable Digital Volume */ /* Initialize operation */
dvucr |= 0x100; rsnd_mod_write(mod, DVC_DVUIR, 1);
rsnd_mod_write(mod, DVC_VOL0R, val[0]);
rsnd_mod_write(mod, DVC_VOL1R, val[1]); /* General Information */
rsnd_mod_write(mod, DVC_ADINR, adinr);
rsnd_mod_write(mod, DVC_DVUCR, dvucr);
/* Volume Ramp Parameter */
rsnd_mod_write(mod, DVC_VRCTR, vrctr);
rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
/* Digital Volume Function Parameter */
rsnd_dvc_volume_parameter(io, mod);
/* cancel operation */
rsnd_mod_write(mod, DVC_DVUIR, 0);
}
static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 zcmcr = 0;
u32 vrpdr = 0;
u32 vrdbr = 0;
int i;
for (i = 0; i < dvc->mute.cfg.size; i++)
zcmcr |= (!!dvc->mute.cfg.val[i]) << i;
/* Enable Mute */ if (dvc->ren.val) {
if (mute) { vrpdr = rsnd_dvc_get_vrpdr(dvc);
dvucr |= 0x1; vrdbr = rsnd_dvc_get_vrdbr(dvc);
rsnd_mod_write(mod, DVC_ZCMCR, mute);
} }
rsnd_mod_write(mod, DVC_DVUCR, dvucr); /* Disable DVC Register access */
rsnd_mod_write(mod, DVC_DVUER, 0);
/* Zero Cross Mute Function */
rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
/* Volume Ramp Function */
rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
/* add DVC_VRWTR here */
/* Digital Volume Function Parameter */
rsnd_dvc_volume_parameter(io, mod);
/* Enable DVC Register access */ /* Enable DVC Register access */
rsnd_mod_write(mod, DVC_DVUER, 1); rsnd_mod_write(mod, DVC_DVUER, 1);
} }
static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, static int rsnd_dvc_probe_(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
}
static int rsnd_dvc_remove_(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
...@@ -155,19 +218,12 @@ static int rsnd_dvc_init(struct rsnd_mod *mod, ...@@ -155,19 +218,12 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
{ {
rsnd_mod_power_on(mod); rsnd_mod_power_on(mod);
rsnd_dvc_soft_reset(mod); rsnd_dvc_activation(mod);
rsnd_dvc_initialize_lock(mod);
rsnd_path_parse(priv, io);
rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io)); rsnd_dvc_volume_init(io, mod);
/* ch0/ch1 Volume */
rsnd_dvc_volume_update(io, mod); rsnd_dvc_volume_update(io, mod);
rsnd_adg_set_cmd_timsel_gen2(mod, io);
return 0; return 0;
} }
...@@ -175,27 +231,9 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, ...@@ -175,27 +231,9 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
rsnd_mod_power_off(mod); rsnd_dvc_halt(mod);
return 0; rsnd_mod_power_off(mod);
}
static int rsnd_dvc_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_dvc_initialize_unlock(mod);
rsnd_mod_write(mod, CMD_CTRL, 0x10);
return 0;
}
static int rsnd_dvc_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_write(mod, CMD_CTRL, 0);
return 0; return 0;
} }
...@@ -206,6 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -206,6 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
{ {
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
int is_play = rsnd_io_is_play(io); int is_play = rsnd_io_is_play(io);
int slots = rsnd_get_slot(io);
int ret; int ret;
/* Volume */ /* Volume */
...@@ -213,7 +252,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -213,7 +252,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
is_play ? is_play ?
"DVC Out Playback Volume" : "DVC In Capture Volume", "DVC Out Playback Volume" : "DVC In Capture Volume",
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->volume, 0x00800000 - 1); &dvc->volume, slots,
0x00800000 - 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -222,7 +262,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, ...@@ -222,7 +262,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
is_play ? is_play ?
"DVC Out Mute Switch" : "DVC In Mute Switch", "DVC Out Mute Switch" : "DVC In Mute Switch",
rsnd_dvc_volume_update, rsnd_dvc_volume_update,
&dvc->mute, 1); &dvc->mute, slots,
1);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -269,11 +310,10 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, ...@@ -269,11 +310,10 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
static struct rsnd_mod_ops rsnd_dvc_ops = { static struct rsnd_mod_ops rsnd_dvc_ops = {
.name = DVC_NAME, .name = DVC_NAME,
.dma_req = rsnd_dvc_dma_req, .dma_req = rsnd_dvc_dma_req,
.remove = rsnd_dvc_remove_gen2, .probe = rsnd_dvc_probe_,
.remove = rsnd_dvc_remove_,
.init = rsnd_dvc_init, .init = rsnd_dvc_init,
.quit = rsnd_dvc_quit, .quit = rsnd_dvc_quit,
.start = rsnd_dvc_start,
.stop = rsnd_dvc_stop,
.pcm_new = rsnd_dvc_pcm_new, .pcm_new = rsnd_dvc_pcm_new,
}; };
...@@ -282,50 +322,13 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) ...@@ -282,50 +322,13 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
id = 0; id = 0;
return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id); return rsnd_mod_get(rsnd_dvc_get(priv, id));
} }
static void rsnd_of_parse_dvc(struct platform_device *pdev, int rsnd_dvc_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct device_node *node; struct device_node *node;
struct rsnd_dvc_platform_info *dvc_info; struct device_node *np;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr;
if (!of_data)
return;
node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
if (!node)
return;
nr = of_get_child_count(node);
if (!nr)
goto rsnd_of_parse_dvc_end;
dvc_info = devm_kzalloc(dev,
sizeof(struct rsnd_dvc_platform_info) * nr,
GFP_KERNEL);
if (!dvc_info) {
dev_err(dev, "dvc info allocation error\n");
goto rsnd_of_parse_dvc_end;
}
info->dvc_info = dvc_info;
info->dvc_info_nr = nr;
rsnd_of_parse_dvc_end:
of_node_put(node);
}
int rsnd_dvc_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dvc *dvc; struct rsnd_dvc *dvc;
struct clk *clk; struct clk *clk;
...@@ -336,40 +339,54 @@ int rsnd_dvc_probe(struct platform_device *pdev, ...@@ -336,40 +339,54 @@ int rsnd_dvc_probe(struct platform_device *pdev,
if (rsnd_is_gen1(priv)) if (rsnd_is_gen1(priv))
return 0; return 0;
rsnd_of_parse_dvc(pdev, of_data, priv); node = rsnd_dvc_of_node(priv);
if (!node)
return 0; /* not used is not error */
nr = info->dvc_info_nr; nr = of_get_child_count(node);
if (!nr) if (!nr) {
return 0; ret = -EINVAL;
goto rsnd_dvc_probe_done;
}
dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
if (!dvc) if (!dvc) {
return -ENOMEM; ret = -ENOMEM;
goto rsnd_dvc_probe_done;
}
priv->dvc_nr = nr; priv->dvc_nr = nr;
priv->dvc = dvc; priv->dvc = dvc;
for_each_rsnd_dvc(dvc, priv, i) { i = 0;
ret = 0;
for_each_child_of_node(node, np) {
dvc = rsnd_dvc_get(priv, i);
snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
DVC_NAME, i); DVC_NAME, i);
clk = devm_clk_get(dev, name); clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) if (IS_ERR(clk)) {
return PTR_ERR(clk); ret = PTR_ERR(clk);
goto rsnd_dvc_probe_done;
dvc->info = &info->dvc_info[i]; }
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i); clk, RSND_MOD_DVC, i);
if (ret) if (ret)
return ret; goto rsnd_dvc_probe_done;
i++;
} }
return 0; rsnd_dvc_probe_done:
of_node_put(node);
return ret;
} }
void rsnd_dvc_remove(struct platform_device *pdev, void rsnd_dvc_remove(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_dvc *dvc; struct rsnd_dvc *dvc;
int i; int i;
......
...@@ -31,29 +31,33 @@ struct rsnd_gen { ...@@ -31,29 +31,33 @@ struct rsnd_gen {
/* RSND_REG_MAX base */ /* RSND_REG_MAX base */
struct regmap_field *regs[RSND_REG_MAX]; struct regmap_field *regs[RSND_REG_MAX];
const char *reg_name[RSND_REG_MAX];
}; };
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
#define rsnd_reg_name(gen, id) ((gen)->reg_name[id])
struct rsnd_regmap_field_conf { struct rsnd_regmap_field_conf {
int idx; int idx;
unsigned int reg_offset; unsigned int reg_offset;
unsigned int id_offset; unsigned int id_offset;
const char *reg_name;
}; };
#define RSND_REG_SET(id, offset, _id_offset) \ #define RSND_REG_SET(id, offset, _id_offset, n) \
{ \ { \
.idx = id, \ .idx = id, \
.reg_offset = offset, \ .reg_offset = offset, \
.id_offset = _id_offset, \ .id_offset = _id_offset, \
.reg_name = n, \
} }
/* single address mapping */ /* single address mapping */
#define RSND_GEN_S_REG(id, offset) \ #define RSND_GEN_S_REG(id, offset) \
RSND_REG_SET(RSND_REG_##id, offset, 0) RSND_REG_SET(RSND_REG_##id, offset, 0, #id)
/* multi address mapping */ /* multi address mapping */
#define RSND_GEN_M_REG(id, offset, _id_offset) \ #define RSND_GEN_M_REG(id, offset, _id_offset) \
RSND_REG_SET(RSND_REG_##id, offset, _id_offset) RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id)
/* /*
* basic function * basic function
...@@ -83,8 +87,9 @@ u32 rsnd_read(struct rsnd_priv *priv, ...@@ -83,8 +87,9 @@ u32 rsnd_read(struct rsnd_priv *priv,
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
dev_dbg(dev, "r %s[%d] - %4d : %08x\n", dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val); rsnd_mod_name(mod), rsnd_mod_id(mod),
rsnd_reg_name(gen, reg), reg, val);
return val; return val;
} }
...@@ -99,10 +104,11 @@ void rsnd_write(struct rsnd_priv *priv, ...@@ -99,10 +104,11 @@ void rsnd_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg)) if (!rsnd_is_accessible_reg(priv, gen, reg))
return; return;
dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
rsnd_reg_name(gen, reg), reg, data);
} }
void rsnd_force_write(struct rsnd_priv *priv, void rsnd_force_write(struct rsnd_priv *priv,
...@@ -115,10 +121,11 @@ void rsnd_force_write(struct rsnd_priv *priv, ...@@ -115,10 +121,11 @@ void rsnd_force_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg)) if (!rsnd_is_accessible_reg(priv, gen, reg))
return; return;
dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
rsnd_reg_name(gen, reg), reg, data);
} }
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
...@@ -130,11 +137,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, ...@@ -130,11 +137,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
if (!rsnd_is_accessible_reg(priv, gen, reg)) if (!rsnd_is_accessible_reg(priv, gen, reg))
return; return;
dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
mask, data); mask, data);
dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
rsnd_reg_name(gen, reg), reg, data, mask);
} }
phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
...@@ -150,7 +159,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, ...@@ -150,7 +159,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
int id_size, int id_size,
int reg_id, int reg_id,
const char *name, const char *name,
struct rsnd_regmap_field_conf *conf, const struct rsnd_regmap_field_conf *conf,
int conf_size) int conf_size)
{ {
struct platform_device *pdev = rsnd_priv_to_pdev(priv); struct platform_device *pdev = rsnd_priv_to_pdev(priv);
...@@ -203,6 +212,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, ...@@ -203,6 +212,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
/* RSND_REG_MAX base */ /* RSND_REG_MAX base */
gen->regs[conf[i].idx] = regs; gen->regs[conf[i].idx] = regs;
gen->reg_name[conf[i].idx] = conf[i].reg_name;
} }
return 0; return 0;
...@@ -211,25 +221,31 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, ...@@ -211,25 +221,31 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
/* /*
* Gen2 * Gen2
*/ */
static int rsnd_gen2_probe(struct platform_device *pdev, static int rsnd_gen2_probe(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_regmap_field_conf conf_ssiu[] = { const static struct rsnd_regmap_field_conf conf_ssiu[] = {
RSND_GEN_S_REG(SSI_MODE0, 0x800), RSND_GEN_S_REG(SSI_MODE0, 0x800),
RSND_GEN_S_REG(SSI_MODE1, 0x804), RSND_GEN_S_REG(SSI_MODE1, 0x804),
RSND_GEN_S_REG(SSI_MODE2, 0x808),
RSND_GEN_S_REG(SSI_CONTROL, 0x810),
/* FIXME: it needs SSI_MODE2/3 in the future */ /* FIXME: it needs SSI_MODE2/3 in the future */
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80), RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80), RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8, 0x80),
RSND_GEN_M_REG(SSI_MODE, 0xc, 0x80),
RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80), RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80),
RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80), RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
}; };
struct rsnd_regmap_field_conf conf_scu[] = {
RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20), const static struct rsnd_regmap_field_conf conf_scu[] = {
RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0, 0x20),
RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4, 0x20),
RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20), RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8, 0x20),
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20), RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20), RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20), RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20),
RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188, 0x20),
RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20), RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20),
RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20), RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20),
RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
...@@ -266,9 +282,15 @@ static int rsnd_gen2_probe(struct platform_device *pdev, ...@@ -266,9 +282,15 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100), RSND_GEN_M_REG(DVC_VRDBR, 0xe20, 0x100),
RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100), RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100), RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
RSND_GEN_M_REG(DVC_VOL2R, 0xe30, 0x100),
RSND_GEN_M_REG(DVC_VOL3R, 0xe34, 0x100),
RSND_GEN_M_REG(DVC_VOL4R, 0xe38, 0x100),
RSND_GEN_M_REG(DVC_VOL5R, 0xe3c, 0x100),
RSND_GEN_M_REG(DVC_VOL6R, 0xe40, 0x100),
RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100),
RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100), RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
}; };
struct rsnd_regmap_field_conf conf_adg[] = { const static struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRA, 0x00),
RSND_GEN_S_REG(BRRB, 0x04), RSND_GEN_S_REG(BRRB, 0x04),
RSND_GEN_S_REG(SSICKR, 0x08), RSND_GEN_S_REG(SSICKR, 0x08),
...@@ -288,7 +310,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev, ...@@ -288,7 +310,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58), RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c), RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
}; };
struct rsnd_regmap_field_conf conf_ssi[] = { const static struct rsnd_regmap_field_conf conf_ssi[] = {
RSND_GEN_M_REG(SSICR, 0x00, 0x40), RSND_GEN_M_REG(SSICR, 0x00, 0x40),
RSND_GEN_M_REG(SSISR, 0x04, 0x40), RSND_GEN_M_REG(SSISR, 0x04, 0x40),
RSND_GEN_M_REG(SSITDR, 0x08, 0x40), RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
...@@ -317,65 +339,30 @@ static int rsnd_gen2_probe(struct platform_device *pdev, ...@@ -317,65 +339,30 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
* Gen1 * Gen1
*/ */
static int rsnd_gen1_probe(struct platform_device *pdev, static int rsnd_gen1_probe(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_regmap_field_conf conf_sru[] = { const static struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00),
RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08),
RSND_GEN_S_REG(SRC_TMG_SEL1, 0x0c),
RSND_GEN_S_REG(SRC_TMG_SEL2, 0x10),
RSND_GEN_S_REG(SRC_ROUTE_CTRL, 0xc0),
RSND_GEN_S_REG(SSI_MODE0, 0xD0),
RSND_GEN_S_REG(SSI_MODE1, 0xD4),
RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x20, 0x4),
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0x50, 0x8),
RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40),
RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40),
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40),
/*
* ADD US
*
* SRC_STATUS
* SRC_INT_EN
* SCU_SYS_STATUS0
* SCU_SYS_STATUS1
* SCU_SYS_INT_EN0
* SCU_SYS_INT_EN1
*/
};
struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(BRRA, 0x00), RSND_GEN_S_REG(BRRA, 0x00),
RSND_GEN_S_REG(BRRB, 0x04), RSND_GEN_S_REG(BRRB, 0x04),
RSND_GEN_S_REG(SSICKR, 0x08), RSND_GEN_S_REG(SSICKR, 0x08),
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c), RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10), RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
RSND_GEN_S_REG(AUDIO_CLK_SEL3, 0x18),
RSND_GEN_S_REG(AUDIO_CLK_SEL4, 0x1c),
RSND_GEN_S_REG(AUDIO_CLK_SEL5, 0x20),
}; };
struct rsnd_regmap_field_conf conf_ssi[] = { const static struct rsnd_regmap_field_conf conf_ssi[] = {
RSND_GEN_M_REG(SSICR, 0x00, 0x40), RSND_GEN_M_REG(SSICR, 0x00, 0x40),
RSND_GEN_M_REG(SSISR, 0x04, 0x40), RSND_GEN_M_REG(SSISR, 0x04, 0x40),
RSND_GEN_M_REG(SSITDR, 0x08, 0x40), RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40), RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
RSND_GEN_M_REG(SSIWSR, 0x20, 0x40), RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
}; };
int ret_sru;
int ret_adg; int ret_adg;
int ret_ssi; int ret_ssi;
ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru);
ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg); ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg);
ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi); ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
if (ret_sru < 0 || if (ret_adg < 0 ||
ret_adg < 0 ||
ret_ssi < 0) ret_ssi < 0)
return ret_sru | ret_adg | ret_ssi; return ret_adg | ret_ssi;
return 0; return 0;
} }
...@@ -383,28 +370,12 @@ static int rsnd_gen1_probe(struct platform_device *pdev, ...@@ -383,28 +370,12 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
/* /*
* Gen * Gen
*/ */
static void rsnd_of_parse_gen(struct platform_device *pdev, int rsnd_gen_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct rcar_snd_info *info = priv->info;
if (!of_data)
return;
info->flags = of_data->flags;
}
int rsnd_gen_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen; struct rsnd_gen *gen;
int ret; int ret;
rsnd_of_parse_gen(pdev, of_data, priv);
gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
if (!gen) { if (!gen) {
dev_err(dev, "GEN allocate failed\n"); dev_err(dev, "GEN allocate failed\n");
...@@ -415,9 +386,9 @@ int rsnd_gen_probe(struct platform_device *pdev, ...@@ -415,9 +386,9 @@ int rsnd_gen_probe(struct platform_device *pdev,
ret = -ENODEV; ret = -ENODEV;
if (rsnd_is_gen1(priv)) if (rsnd_is_gen1(priv))
ret = rsnd_gen1_probe(pdev, priv); ret = rsnd_gen1_probe(priv);
else if (rsnd_is_gen2(priv)) else if (rsnd_is_gen2(priv))
ret = rsnd_gen2_probe(pdev, priv); ret = rsnd_gen2_probe(priv);
if (ret < 0) if (ret < 0)
dev_err(dev, "unknown generation R-Car sound device\n"); dev_err(dev, "unknown generation R-Car sound device\n");
......
...@@ -13,10 +13,10 @@ ...@@ -13,10 +13,10 @@
#define MIX_NAME "mix" #define MIX_NAME "mix"
struct rsnd_mix { struct rsnd_mix {
struct rsnd_mix_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod; struct rsnd_mod mod;
}; };
#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
#define rsnd_mix_nr(priv) ((priv)->mix_nr) #define rsnd_mix_nr(priv) ((priv)->mix_nr)
#define for_each_rsnd_mix(pos, priv, i) \ #define for_each_rsnd_mix(pos, priv, i) \
for ((i) = 0; \ for ((i) = 0; \
...@@ -24,58 +24,77 @@ struct rsnd_mix { ...@@ -24,58 +24,77 @@ struct rsnd_mix {
((pos) = (struct rsnd_mix *)(priv)->mix + i); \ ((pos) = (struct rsnd_mix *)(priv)->mix + i); \
i++) i++)
static void rsnd_mix_activation(struct rsnd_mod *mod)
static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
{ {
rsnd_mod_write(mod, MIX_SWRSR, 0); rsnd_mod_write(mod, MIX_SWRSR, 0);
rsnd_mod_write(mod, MIX_SWRSR, 1); rsnd_mod_write(mod, MIX_SWRSR, 1);
} }
#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1) static void rsnd_mix_halt(struct rsnd_mod *mod)
#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0) {
static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable) rsnd_mod_write(mod, MIX_MIXIR, 1);
rsnd_mod_write(mod, MIX_SWRSR, 0);
}
static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{ {
rsnd_mod_write(mod, MIX_MIXIR, enable); rsnd_mod_write(mod, MIX_MDBAR, 0);
rsnd_mod_write(mod, MIX_MDBBR, 0);
rsnd_mod_write(mod, MIX_MDBCR, 0);
rsnd_mod_write(mod, MIX_MDBDR, 0);
}
static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
rsnd_mod_write(mod, MIX_MIXIR, 1);
/* General Information */
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
/* volume step */
rsnd_mod_write(mod, MIX_MIXMR, 0);
rsnd_mod_write(mod, MIX_MVPDR, 0);
/* common volume parameter */
rsnd_mix_volume_parameter(io, mod);
rsnd_mod_write(mod, MIX_MIXIR, 0);
} }
static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
struct rsnd_mod *mod) struct rsnd_mod *mod)
{ {
/* Disable MIX dB setting */ /* Disable MIX dB setting */
rsnd_mod_write(mod, MIX_MDBER, 0); rsnd_mod_write(mod, MIX_MDBER, 0);
rsnd_mod_write(mod, MIX_MDBAR, 0); /* common volume parameter */
rsnd_mod_write(mod, MIX_MDBBR, 0); rsnd_mix_volume_parameter(io, mod);
rsnd_mod_write(mod, MIX_MDBCR, 0);
rsnd_mod_write(mod, MIX_MDBDR, 0);
/* Enable MIX dB setting */ /* Enable MIX dB setting */
rsnd_mod_write(mod, MIX_MDBER, 1); rsnd_mod_write(mod, MIX_MDBER, 1);
} }
static int rsnd_mix_probe_(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
}
static int rsnd_mix_init(struct rsnd_mod *mod, static int rsnd_mix_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
rsnd_mod_power_on(mod); rsnd_mod_power_on(mod);
rsnd_mix_soft_reset(mod); rsnd_mix_activation(mod);
rsnd_mix_initialize_lock(mod);
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
rsnd_path_parse(priv, io);
/* volume step */ rsnd_mix_volume_init(io, mod);
rsnd_mod_write(mod, MIX_MIXMR, 0);
rsnd_mod_write(mod, MIX_MVPDR, 0);
rsnd_mix_volume_update(io, mod); rsnd_mix_volume_update(io, mod);
rsnd_mix_initialize_unlock(mod);
return 0; return 0;
} }
...@@ -83,6 +102,8 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, ...@@ -83,6 +102,8 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
rsnd_mix_halt(mod);
rsnd_mod_power_off(mod); rsnd_mod_power_off(mod);
return 0; return 0;
...@@ -90,6 +111,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, ...@@ -90,6 +111,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
static struct rsnd_mod_ops rsnd_mix_ops = { static struct rsnd_mod_ops rsnd_mix_ops = {
.name = MIX_NAME, .name = MIX_NAME,
.probe = rsnd_mix_probe_,
.init = rsnd_mix_init, .init = rsnd_mix_init,
.quit = rsnd_mix_quit, .quit = rsnd_mix_quit,
}; };
...@@ -99,51 +121,13 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) ...@@ -99,51 +121,13 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
id = 0; id = 0;
return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id); return rsnd_mod_get(rsnd_mix_get(priv, id));
} }
static void rsnd_of_parse_mix(struct platform_device *pdev, int rsnd_mix_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct device_node *node; struct device_node *node;
struct rsnd_mix_platform_info *mix_info; struct device_node *np;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr;
if (!of_data)
return;
node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
if (!node)
return;
nr = of_get_child_count(node);
if (!nr)
goto rsnd_of_parse_mix_end;
mix_info = devm_kzalloc(dev,
sizeof(struct rsnd_mix_platform_info) * nr,
GFP_KERNEL);
if (!mix_info) {
dev_err(dev, "mix info allocation error\n");
goto rsnd_of_parse_mix_end;
}
info->mix_info = mix_info;
info->mix_info_nr = nr;
rsnd_of_parse_mix_end:
of_node_put(node);
}
int rsnd_mix_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mix *mix; struct rsnd_mix *mix;
struct clk *clk; struct clk *clk;
...@@ -154,40 +138,54 @@ int rsnd_mix_probe(struct platform_device *pdev, ...@@ -154,40 +138,54 @@ int rsnd_mix_probe(struct platform_device *pdev,
if (rsnd_is_gen1(priv)) if (rsnd_is_gen1(priv))
return 0; return 0;
rsnd_of_parse_mix(pdev, of_data, priv); node = rsnd_mix_of_node(priv);
if (!node)
return 0; /* not used is not error */
nr = info->mix_info_nr; nr = of_get_child_count(node);
if (!nr) if (!nr) {
return 0; ret = -EINVAL;
goto rsnd_mix_probe_done;
}
mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
if (!mix) if (!mix) {
return -ENOMEM; ret = -ENOMEM;
goto rsnd_mix_probe_done;
}
priv->mix_nr = nr; priv->mix_nr = nr;
priv->mix = mix; priv->mix = mix;
for_each_rsnd_mix(mix, priv, i) { i = 0;
ret = 0;
for_each_child_of_node(node, np) {
mix = rsnd_mix_get(priv, i);
snprintf(name, MIX_NAME_SIZE, "%s.%d", snprintf(name, MIX_NAME_SIZE, "%s.%d",
MIX_NAME, i); MIX_NAME, i);
clk = devm_clk_get(dev, name); clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) if (IS_ERR(clk)) {
return PTR_ERR(clk); ret = PTR_ERR(clk);
goto rsnd_mix_probe_done;
mix->info = &info->mix_info[i]; }
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
clk, RSND_MOD_MIX, i); clk, RSND_MOD_MIX, i);
if (ret) if (ret)
return ret; goto rsnd_mix_probe_done;
i++;
} }
return 0; rsnd_mix_probe_done:
of_node_put(node);
return ret;
} }
void rsnd_mix_remove(struct platform_device *pdev, void rsnd_mix_remove(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_mix *mix; struct rsnd_mix *mix;
int i; int i;
......
/*
* Renesas R-Car SRU/SCU/SSIU/SSI support
*
* Copyright (C) 2013 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef RCAR_SND_H
#define RCAR_SND_H
#define RSND_GEN1_SRU 0
#define RSND_GEN1_ADG 1
#define RSND_GEN1_SSI 2
#define RSND_GEN2_SCU 0
#define RSND_GEN2_ADG 1
#define RSND_GEN2_SSIU 2
#define RSND_GEN2_SSI 3
#define RSND_BASE_MAX 4
/*
* flags
*
* 0xAB000000
*
* A : clock sharing settings
* B : SSI direction
*/
#define RSND_SSI_CLK_PIN_SHARE (1 << 31)
#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
#define RSND_SSI(_dma_id, _irq, _flags) \
{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
#define RSND_SSI_UNUSED \
{ .dma_id = -1, .irq = -1, .flags = 0 }
struct rsnd_ssi_platform_info {
int dma_id;
int irq;
u32 flags;
};
#define RSND_SRC(rate, _dma_id) \
{ .convert_rate = rate, .dma_id = _dma_id, }
#define RSND_SRC_UNUSED \
{ .convert_rate = 0, .dma_id = -1, }
struct rsnd_src_platform_info {
u32 convert_rate; /* sampling rate convert */
int dma_id; /* for Gen2 SCU */
int irq;
};
/*
* flags
*/
struct rsnd_ctu_platform_info {
u32 flags;
};
struct rsnd_mix_platform_info {
u32 flags;
};
struct rsnd_dvc_platform_info {
u32 flags;
};
struct rsnd_dai_path_info {
struct rsnd_ssi_platform_info *ssi;
struct rsnd_src_platform_info *src;
struct rsnd_ctu_platform_info *ctu;
struct rsnd_mix_platform_info *mix;
struct rsnd_dvc_platform_info *dvc;
};
struct rsnd_dai_platform_info {
struct rsnd_dai_path_info playback;
struct rsnd_dai_path_info capture;
};
/*
* flags
*
* 0x0000000A
*
* A : generation
*/
#define RSND_GEN_MASK (0xF << 0)
#define RSND_GEN1 (1 << 0) /* fixme */
#define RSND_GEN2 (2 << 0) /* fixme */
struct rcar_snd_info {
u32 flags;
struct rsnd_ssi_platform_info *ssi_info;
int ssi_info_nr;
struct rsnd_src_platform_info *src_info;
int src_info_nr;
struct rsnd_ctu_platform_info *ctu_info;
int ctu_info_nr;
struct rsnd_mix_platform_info *mix_info;
int mix_info_nr;
struct rsnd_dvc_platform_info *dvc_info;
int dvc_info_nr;
struct rsnd_dai_platform_info *dai_info;
int dai_info_nr;
int (*start)(int id);
int (*stop)(int id);
};
#endif
...@@ -24,7 +24,16 @@ ...@@ -24,7 +24,16 @@
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include "rcar_snd.h" #define RSND_GEN1_SRU 0
#define RSND_GEN1_ADG 1
#define RSND_GEN1_SSI 2
#define RSND_GEN2_SCU 0
#define RSND_GEN2_ADG 1
#define RSND_GEN2_SSIU 2
#define RSND_GEN2_SSI 3
#define RSND_BASE_MAX 4
/* /*
* pseudo register * pseudo register
...@@ -34,10 +43,19 @@ ...@@ -34,10 +43,19 @@
* see gen1/gen2 for detail * see gen1/gen2 for detail
*/ */
enum rsnd_reg { enum rsnd_reg {
/* SRU/SCU/SSIU */ /* SCU (SRC/SSIU/MIX/CTU/DVC) */
RSND_REG_SSI_MODE, /* Gen2 only */
RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE0,
RSND_REG_SSI_MODE1, RSND_REG_SSI_MODE1,
RSND_REG_SRC_BUSIF_MODE, RSND_REG_SSI_MODE2,
RSND_REG_SSI_CONTROL,
RSND_REG_SSI_CTRL, /* Gen2 only */
RSND_REG_SSI_BUSIF_MODE, /* Gen2 only */
RSND_REG_SSI_BUSIF_ADINR, /* Gen2 only */
RSND_REG_SSI_BUSIF_DALIGN, /* Gen2 only */
RSND_REG_SSI_INT_ENABLE, /* Gen2 only */
RSND_REG_SRC_I_BUSIF_MODE,
RSND_REG_SRC_O_BUSIF_MODE,
RSND_REG_SRC_ROUTE_MODE0, RSND_REG_SRC_ROUTE_MODE0,
RSND_REG_SRC_SWRSR, RSND_REG_SRC_SWRSR,
RSND_REG_SRC_SRCIR, RSND_REG_SRC_SRCIR,
...@@ -45,9 +63,29 @@ enum rsnd_reg { ...@@ -45,9 +63,29 @@ enum rsnd_reg {
RSND_REG_SRC_IFSCR, RSND_REG_SRC_IFSCR,
RSND_REG_SRC_IFSVR, RSND_REG_SRC_IFSVR,
RSND_REG_SRC_SRCCR, RSND_REG_SRC_SRCCR,
RSND_REG_SRC_CTRL, /* Gen2 only */
RSND_REG_SRC_BSDSR, /* Gen2 only */
RSND_REG_SRC_BSISR, /* Gen2 only */
RSND_REG_SRC_INT_ENABLE0, /* Gen2 only */
RSND_REG_SRC_BUSIF_DALIGN, /* Gen2 only */
RSND_REG_SRCIN_TIMSEL0, /* Gen2 only */
RSND_REG_SRCIN_TIMSEL1, /* Gen2 only */
RSND_REG_SRCIN_TIMSEL2, /* Gen2 only */
RSND_REG_SRCIN_TIMSEL3, /* Gen2 only */
RSND_REG_SRCIN_TIMSEL4, /* Gen2 only */
RSND_REG_SRCOUT_TIMSEL0, /* Gen2 only */
RSND_REG_SRCOUT_TIMSEL1, /* Gen2 only */
RSND_REG_SRCOUT_TIMSEL2, /* Gen2 only */
RSND_REG_SRCOUT_TIMSEL3, /* Gen2 only */
RSND_REG_SRCOUT_TIMSEL4, /* Gen2 only */
RSND_REG_SCU_SYS_STATUS0, RSND_REG_SCU_SYS_STATUS0,
RSND_REG_SCU_SYS_STATUS1, /* Gen2 only */
RSND_REG_SCU_SYS_INT_EN0, RSND_REG_SCU_SYS_INT_EN0,
RSND_REG_SCU_SYS_INT_EN1, /* Gen2 only */
RSND_REG_CMD_CTRL, /* Gen2 only */
RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
RSND_REG_CMD_ROUTE_SLCT, RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
RSND_REG_CTU_CTUIR, RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR, RSND_REG_CTU_ADINR,
RSND_REG_MIX_SWRSR, RSND_REG_MIX_SWRSR,
...@@ -67,14 +105,25 @@ enum rsnd_reg { ...@@ -67,14 +105,25 @@ enum rsnd_reg {
RSND_REG_DVC_ZCMCR, RSND_REG_DVC_ZCMCR,
RSND_REG_DVC_VOL0R, RSND_REG_DVC_VOL0R,
RSND_REG_DVC_VOL1R, RSND_REG_DVC_VOL1R,
RSND_REG_DVC_VOL2R,
RSND_REG_DVC_VOL3R,
RSND_REG_DVC_VOL4R,
RSND_REG_DVC_VOL5R,
RSND_REG_DVC_VOL6R,
RSND_REG_DVC_VOL7R,
RSND_REG_DVC_DVUER, RSND_REG_DVC_DVUER,
RSND_REG_DVC_VRCTR, /* Gen2 only */
RSND_REG_DVC_VRPDR, /* Gen2 only */
RSND_REG_DVC_VRDBR, /* Gen2 only */
/* ADG */ /* ADG */
RSND_REG_BRRA, RSND_REG_BRRA,
RSND_REG_BRRB, RSND_REG_BRRB,
RSND_REG_SSICKR, RSND_REG_SSICKR,
RSND_REG_DIV_EN, /* Gen2 only */
RSND_REG_AUDIO_CLK_SEL0, RSND_REG_AUDIO_CLK_SEL0,
RSND_REG_AUDIO_CLK_SEL1, RSND_REG_AUDIO_CLK_SEL1,
RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */
/* SSI */ /* SSI */
RSND_REG_SSICR, RSND_REG_SSICR,
...@@ -83,83 +132,9 @@ enum rsnd_reg { ...@@ -83,83 +132,9 @@ enum rsnd_reg {
RSND_REG_SSIRDR, RSND_REG_SSIRDR,
RSND_REG_SSIWSR, RSND_REG_SSIWSR,
/* SHARE see below */
RSND_REG_SHARE01,
RSND_REG_SHARE02,
RSND_REG_SHARE03,
RSND_REG_SHARE04,
RSND_REG_SHARE05,
RSND_REG_SHARE06,
RSND_REG_SHARE07,
RSND_REG_SHARE08,
RSND_REG_SHARE09,
RSND_REG_SHARE10,
RSND_REG_SHARE11,
RSND_REG_SHARE12,
RSND_REG_SHARE13,
RSND_REG_SHARE14,
RSND_REG_SHARE15,
RSND_REG_SHARE16,
RSND_REG_SHARE17,
RSND_REG_SHARE18,
RSND_REG_SHARE19,
RSND_REG_SHARE20,
RSND_REG_SHARE21,
RSND_REG_SHARE22,
RSND_REG_SHARE23,
RSND_REG_SHARE24,
RSND_REG_SHARE25,
RSND_REG_SHARE26,
RSND_REG_SHARE27,
RSND_REG_SHARE28,
RSND_REG_SHARE29,
RSND_REG_MAX, RSND_REG_MAX,
}; };
/* Gen1 only */
#define RSND_REG_SRC_ROUTE_SEL RSND_REG_SHARE01
#define RSND_REG_SRC_TMG_SEL0 RSND_REG_SHARE02
#define RSND_REG_SRC_TMG_SEL1 RSND_REG_SHARE03
#define RSND_REG_SRC_TMG_SEL2 RSND_REG_SHARE04
#define RSND_REG_SRC_ROUTE_CTRL RSND_REG_SHARE05
#define RSND_REG_SRC_MNFSR RSND_REG_SHARE06
#define RSND_REG_AUDIO_CLK_SEL3 RSND_REG_SHARE07
#define RSND_REG_AUDIO_CLK_SEL4 RSND_REG_SHARE08
#define RSND_REG_AUDIO_CLK_SEL5 RSND_REG_SHARE09
/* Gen2 only */
#define RSND_REG_SRC_CTRL RSND_REG_SHARE01
#define RSND_REG_SSI_CTRL RSND_REG_SHARE02
#define RSND_REG_SSI_BUSIF_MODE RSND_REG_SHARE03
#define RSND_REG_SSI_BUSIF_ADINR RSND_REG_SHARE04
#define RSND_REG_SSI_INT_ENABLE RSND_REG_SHARE05
#define RSND_REG_SRC_BSDSR RSND_REG_SHARE06
#define RSND_REG_SRC_BSISR RSND_REG_SHARE07
#define RSND_REG_DIV_EN RSND_REG_SHARE08
#define RSND_REG_SRCIN_TIMSEL0 RSND_REG_SHARE09
#define RSND_REG_SRCIN_TIMSEL1 RSND_REG_SHARE10
#define RSND_REG_SRCIN_TIMSEL2 RSND_REG_SHARE11
#define RSND_REG_SRCIN_TIMSEL3 RSND_REG_SHARE12
#define RSND_REG_SRCIN_TIMSEL4 RSND_REG_SHARE13
#define RSND_REG_SRCOUT_TIMSEL0 RSND_REG_SHARE14
#define RSND_REG_SRCOUT_TIMSEL1 RSND_REG_SHARE15
#define RSND_REG_SRCOUT_TIMSEL2 RSND_REG_SHARE16
#define RSND_REG_SRCOUT_TIMSEL3 RSND_REG_SHARE17
#define RSND_REG_SRCOUT_TIMSEL4 RSND_REG_SHARE18
#define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19
#define RSND_REG_CMD_CTRL RSND_REG_SHARE20
#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21
#define RSND_REG_SSI_BUSIF_DALIGN RSND_REG_SHARE22
#define RSND_REG_DVC_VRCTR RSND_REG_SHARE23
#define RSND_REG_DVC_VRPDR RSND_REG_SHARE24
#define RSND_REG_DVC_VRDBR RSND_REG_SHARE25
#define RSND_REG_SCU_SYS_STATUS1 RSND_REG_SHARE26
#define RSND_REG_SCU_SYS_INT_EN1 RSND_REG_SHARE27
#define RSND_REG_SRC_INT_ENABLE0 RSND_REG_SHARE28
#define RSND_REG_SRC_BUSIF_DALIGN RSND_REG_SHARE29
struct rsnd_of_data;
struct rsnd_priv; struct rsnd_priv;
struct rsnd_mod; struct rsnd_mod;
struct rsnd_dai; struct rsnd_dai;
...@@ -187,43 +162,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, ...@@ -187,43 +162,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
void rsnd_path_parse(struct rsnd_priv *priv,
struct rsnd_dai_stream *io);
/* /*
* R-Car DMA * R-Car DMA
*/ */
struct rsnd_dma; struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, int id);
struct rsnd_dmaen { int rsnd_dma_probe(struct rsnd_priv *priv);
struct dma_chan *chan;
};
struct rsnd_dmapp {
int dmapp_id;
u32 chcr;
};
struct rsnd_dma {
struct rsnd_dma_ops *ops;
dma_addr_t src_addr;
dma_addr_t dst_addr;
union {
struct rsnd_dmaen en;
struct rsnd_dmapp pp;
} dma;
};
#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id);
void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
int rsnd_dma_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name); struct rsnd_mod *mod, char *name);
...@@ -231,11 +176,19 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, ...@@ -231,11 +176,19 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
* R-Car sound mod * R-Car sound mod
*/ */
enum rsnd_mod_type { enum rsnd_mod_type {
RSND_MOD_DVC = 0, RSND_MOD_AUDMAPP,
RSND_MOD_AUDMA,
RSND_MOD_DVC,
RSND_MOD_MIX, RSND_MOD_MIX,
RSND_MOD_CTU, RSND_MOD_CTU,
RSND_MOD_CMD,
RSND_MOD_SRC, RSND_MOD_SRC,
RSND_MOD_SSIM3, /* SSI multi 3 */
RSND_MOD_SSIM2, /* SSI multi 2 */
RSND_MOD_SSIM1, /* SSI multi 1 */
RSND_MOD_SSIP, /* SSI parent */
RSND_MOD_SSI, RSND_MOD_SSI,
RSND_MOD_SSIU,
RSND_MOD_MAX, RSND_MOD_MAX,
}; };
...@@ -278,10 +231,8 @@ struct rsnd_mod { ...@@ -278,10 +231,8 @@ struct rsnd_mod {
int id; int id;
enum rsnd_mod_type type; enum rsnd_mod_type type;
struct rsnd_mod_ops *ops; struct rsnd_mod_ops *ops;
struct rsnd_dma dma;
struct rsnd_priv *priv; struct rsnd_priv *priv;
struct clk *clk; struct clk *clk;
u32 status;
}; };
/* /*
* status * status
...@@ -328,7 +279,6 @@ struct rsnd_mod { ...@@ -328,7 +279,6 @@ struct rsnd_mod {
#define __rsnd_mod_call_hw_params 0 #define __rsnd_mod_call_hw_params 0
#define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_priv(mod) ((mod)->priv)
#define rsnd_mod_to_dma(mod) (&(mod)->dma)
#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
#define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
#define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk)
...@@ -347,6 +297,17 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, ...@@ -347,6 +297,17 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
void rsnd_mod_interrupt(struct rsnd_mod *mod, void rsnd_mod_interrupt(struct rsnd_mod *mod,
void (*callback)(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)); struct rsnd_dai_stream *io));
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct device_node *node,
struct device_node *playback,
struct device_node *capture);
void rsnd_set_slot(struct rsnd_dai *rdai,
int slots, int slots_total);
int rsnd_get_slot(struct rsnd_dai_stream *io);
int rsnd_get_slot_width(struct rsnd_dai_stream *io);
int rsnd_get_slot_num(struct rsnd_dai_stream *io);
/* /*
* R-Car sound DAI * R-Car sound DAI
...@@ -358,6 +319,7 @@ struct rsnd_dai_stream { ...@@ -358,6 +319,7 @@ struct rsnd_dai_stream {
struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai; struct rsnd_dai *rdai;
u32 mod_status[RSND_MOD_MAX];
int byte_pos; int byte_pos;
int period_pos; int period_pos;
int byte_per_period; int byte_per_period;
...@@ -365,10 +327,12 @@ struct rsnd_dai_stream { ...@@ -365,10 +327,12 @@ struct rsnd_dai_stream {
}; };
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC) #define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU) #define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX) #define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
#define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC) #define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD)
#define rsnd_io_to_rdai(io) ((io)->rdai) #define rsnd_io_to_rdai(io) ((io)->rdai)
#define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io))) #define rsnd_io_to_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
#define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io)
...@@ -382,6 +346,9 @@ struct rsnd_dai { ...@@ -382,6 +346,9 @@ struct rsnd_dai {
struct rsnd_dai_stream capture; struct rsnd_dai_stream capture;
struct rsnd_priv *priv; struct rsnd_priv *priv;
int slots;
int slots_num;
unsigned int clk_master:1; unsigned int clk_master:1;
unsigned int bit_clk_inv:1; unsigned int bit_clk_inv:1;
unsigned int frm_clk_inv:1; unsigned int frm_clk_inv:1;
...@@ -403,33 +370,28 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); ...@@ -403,33 +370,28 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type);
#define rsnd_dai_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai")
/* /*
* R-Car Gen1/Gen2 * R-Car Gen1/Gen2
*/ */
int rsnd_gen_probe(struct platform_device *pdev, int rsnd_gen_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
struct rsnd_mod *mod, struct rsnd_mod *mod,
enum rsnd_reg reg); enum rsnd_reg reg);
phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
#define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
/* /*
* R-Car ADG * R-Car ADG
*/ */
int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
int rsnd_adg_probe(struct platform_device *pdev, int rsnd_adg_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data, void rsnd_adg_remove(struct rsnd_priv *priv);
struct rsnd_priv *priv);
int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
struct rsnd_mod *mod,
unsigned int src_rate,
unsigned int dst_rate);
int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
unsigned int src_rate, unsigned int src_rate,
...@@ -442,15 +404,14 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, ...@@ -442,15 +404,14 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
/* /*
* R-Car sound priv * R-Car sound priv
*/ */
struct rsnd_of_data {
u32 flags;
};
struct rsnd_priv { struct rsnd_priv {
struct platform_device *pdev; struct platform_device *pdev;
struct rcar_snd_info *info;
spinlock_t lock; spinlock_t lock;
unsigned long flags;
#define RSND_GEN_MASK (0xF << 0)
#define RSND_GEN1 (1 << 0)
#define RSND_GEN2 (2 << 0)
/* /*
* below value will be filled on rsnd_gen_probe() * below value will be filled on rsnd_gen_probe()
...@@ -473,6 +434,12 @@ struct rsnd_priv { ...@@ -473,6 +434,12 @@ struct rsnd_priv {
void *ssi; void *ssi;
int ssi_nr; int ssi_nr;
/*
* below value will be filled on rsnd_ssiu_probe()
*/
void *ssiu;
int ssiu_nr;
/* /*
* below value will be filled on rsnd_src_probe() * below value will be filled on rsnd_src_probe()
*/ */
...@@ -497,6 +464,12 @@ struct rsnd_priv { ...@@ -497,6 +464,12 @@ struct rsnd_priv {
void *dvc; void *dvc;
int dvc_nr; int dvc_nr;
/*
* below value will be filled on rsnd_cmd_probe()
*/
void *cmd;
int cmd_nr;
/* /*
* below value will be filled on rsnd_dai_probe() * below value will be filled on rsnd_dai_probe()
*/ */
...@@ -507,7 +480,9 @@ struct rsnd_priv { ...@@ -507,7 +480,9 @@ struct rsnd_priv {
#define rsnd_priv_to_pdev(priv) ((priv)->pdev) #define rsnd_priv_to_pdev(priv) ((priv)->pdev)
#define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) #define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
#define rsnd_priv_to_info(priv) ((priv)->info)
#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
/* /*
* rsnd_kctrl * rsnd_kctrl
...@@ -523,7 +498,7 @@ struct rsnd_kctrl_cfg { ...@@ -523,7 +498,7 @@ struct rsnd_kctrl_cfg {
struct snd_kcontrol *kctrl; struct snd_kcontrol *kctrl;
}; };
#define RSND_DVC_CHANNELS 2 #define RSND_DVC_CHANNELS 8
struct rsnd_kctrl_cfg_m { struct rsnd_kctrl_cfg_m {
struct rsnd_kctrl_cfg cfg; struct rsnd_kctrl_cfg cfg;
u32 val[RSND_DVC_CHANNELS]; u32 val[RSND_DVC_CHANNELS];
...@@ -544,6 +519,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, ...@@ -544,6 +519,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
void (*update)(struct rsnd_dai_stream *io, void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod), struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg, struct rsnd_kctrl_cfg_m *_cfg,
int ch_size,
u32 max); u32 max);
int rsnd_kctrl_new_s(struct rsnd_mod *mod, int rsnd_kctrl_new_s(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
...@@ -566,70 +542,93 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, ...@@ -566,70 +542,93 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod,
/* /*
* R-Car SSI * R-Car SSI
*/ */
int rsnd_ssi_probe(struct platform_device *pdev, int rsnd_ssi_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data, void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_priv *priv);
void rsnd_ssi_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
#define rsnd_ssi_is_pin_sharing(io) \ #define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
#define rsnd_ssi_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
struct device_node *playback,
struct device_node *capture);
/*
* R-Car SSIU
*/
int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod);
int rsnd_ssiu_probe(struct rsnd_priv *priv);
void rsnd_ssiu_remove(struct rsnd_priv *priv);
/* /*
* R-Car SRC * R-Car SRC
*/ */
int rsnd_src_probe(struct platform_device *pdev, int rsnd_src_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data, void rsnd_src_remove(struct rsnd_priv *priv);
struct rsnd_priv *priv);
void rsnd_src_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime); struct snd_pcm_runtime *runtime);
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, #define rsnd_src_of_node(priv) \
struct rsnd_dai_stream *io, of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
int use_busif); #define rsnd_parse_connect_src(rdai, playback, capture) \
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
struct rsnd_dai_stream *io); rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod); playback, capture)
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
/* /*
* R-Car CTU * R-Car CTU
*/ */
int rsnd_ctu_probe(struct platform_device *pdev, int rsnd_ctu_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data, void rsnd_ctu_remove(struct rsnd_priv *priv);
struct rsnd_priv *priv);
void rsnd_ctu_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_ctu_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
#define rsnd_parse_connect_ctu(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get, \
rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
/* /*
* R-Car MIX * R-Car MIX
*/ */
int rsnd_mix_probe(struct platform_device *pdev, int rsnd_mix_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data, void rsnd_mix_remove(struct rsnd_priv *priv);
struct rsnd_priv *priv);
void rsnd_mix_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id); struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_mix_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix")
#define rsnd_parse_connect_mix(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_mix_mod_get, \
rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
/* /*
* R-Car DVC * R-Car DVC
*/ */
int rsnd_dvc_probe(struct platform_device *pdev, int rsnd_dvc_probe(struct rsnd_priv *priv);
const struct rsnd_of_data *of_data, void rsnd_dvc_remove(struct rsnd_priv *priv);
struct rsnd_priv *priv);
void rsnd_dvc_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_dvc_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
#define rsnd_parse_connect_dvc(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get, \
rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
/*
* R-Car CMD
*/
int rsnd_cmd_probe(struct rsnd_priv *priv);
void rsnd_cmd_remove(struct rsnd_priv *priv);
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
#ifdef DEBUG #ifdef DEBUG
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
......
...@@ -48,8 +48,11 @@ MODULE_DEVICE_TABLE(of, rsrc_card_of_match); ...@@ -48,8 +48,11 @@ MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
#define DAI_NAME_NUM 32 #define DAI_NAME_NUM 32
struct rsrc_card_dai { struct rsrc_card_dai {
unsigned int fmt;
unsigned int sysclk; unsigned int sysclk;
unsigned int tx_slot_mask;
unsigned int rx_slot_mask;
int slots;
int slot_width;
struct clk *clk; struct clk *clk;
char dai_name[DAI_NAME_NUM]; char dai_name[DAI_NAME_NUM];
}; };
...@@ -110,18 +113,22 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) ...@@ -110,18 +113,22 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
rtd->cpu_dai : rtd->cpu_dai :
rtd->codec_dai; rtd->codec_dai;
if (dai_props->fmt) { if (dai_props->sysclk) {
ret = snd_soc_dai_set_fmt(dai, dai_props->fmt); ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
if (ret && ret != -ENOTSUPP) { if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_fmt error\n"); dev_err(dai->dev, "set_sysclk error\n");
goto err; goto err;
} }
} }
if (dai_props->sysclk) { if (dai_props->slots) {
ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); ret = snd_soc_dai_set_tdm_slot(dai,
dai_props->tx_slot_mask,
dai_props->rx_slot_mask,
dai_props->slots,
dai_props->slot_width);
if (ret && ret != -ENOTSUPP) { if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_sysclk error\n"); dev_err(dai->dev, "set_tdm_slot error\n");
goto err; goto err;
} }
} }
...@@ -148,14 +155,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, ...@@ -148,14 +155,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
} }
static int rsrc_card_parse_daifmt(struct device_node *node, static int rsrc_card_parse_daifmt(struct device_node *node,
struct device_node *np, struct device_node *codec,
struct rsrc_card_priv *priv, struct rsrc_card_priv *priv,
int idx, bool is_fe) struct snd_soc_dai_link *dai_link,
unsigned int *retfmt)
{ {
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
struct device_node *bitclkmaster = NULL; struct device_node *bitclkmaster = NULL;
struct device_node *framemaster = NULL; struct device_node *framemaster = NULL;
struct device_node *codec = is_fe ? NULL : np;
unsigned int daifmt; unsigned int daifmt;
daifmt = snd_soc_of_parse_daifmt(node, NULL, daifmt = snd_soc_of_parse_daifmt(node, NULL,
...@@ -172,11 +178,11 @@ static int rsrc_card_parse_daifmt(struct device_node *node, ...@@ -172,11 +178,11 @@ static int rsrc_card_parse_daifmt(struct device_node *node,
daifmt |= (codec == framemaster) ? daifmt |= (codec == framemaster) ?
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
dai_props->fmt = daifmt;
of_node_put(bitclkmaster); of_node_put(bitclkmaster);
of_node_put(framemaster); of_node_put(framemaster);
*retfmt = daifmt;
return 0; return 0;
} }
...@@ -198,6 +204,15 @@ static int rsrc_card_parse_links(struct device_node *np, ...@@ -198,6 +204,15 @@ static int rsrc_card_parse_links(struct device_node *np,
if (ret) if (ret)
return ret; return ret;
/* Parse TDM slot */
ret = snd_soc_of_parse_tdm_slot(np,
&dai_props->tx_slot_mask,
&dai_props->rx_slot_mask,
&dai_props->slots,
&dai_props->slot_width);
if (ret)
return ret;
if (is_fe) { if (is_fe) {
/* BE is dummy */ /* BE is dummy */
dai_link->codec_of_node = NULL; dai_link->codec_of_node = NULL;
...@@ -208,7 +223,9 @@ static int rsrc_card_parse_links(struct device_node *np, ...@@ -208,7 +223,9 @@ static int rsrc_card_parse_links(struct device_node *np,
dai_link->dynamic = 1; dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1; dai_link->dpcm_merged_format = 1;
dai_link->cpu_of_node = args.np; dai_link->cpu_of_node = args.np;
snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
if (ret < 0)
return ret;
/* set dai_name */ /* set dai_name */
snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s", snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
...@@ -240,7 +257,9 @@ static int rsrc_card_parse_links(struct device_node *np, ...@@ -240,7 +257,9 @@ static int rsrc_card_parse_links(struct device_node *np,
dai_link->no_pcm = 1; dai_link->no_pcm = 1;
dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
dai_link->codec_of_node = args.np; dai_link->codec_of_node = args.np;
snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); ret = snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
if (ret < 0)
return ret;
/* additional name prefix */ /* additional name prefix */
if (of_data) { if (of_data) {
...@@ -305,23 +324,16 @@ static int rsrc_card_parse_clk(struct device_node *np, ...@@ -305,23 +324,16 @@ static int rsrc_card_parse_clk(struct device_node *np,
return 0; return 0;
} }
static int rsrc_card_dai_link_of(struct device_node *node, static int rsrc_card_dai_sub_link_of(struct device_node *node,
struct device_node *np, struct device_node *np,
struct rsrc_card_priv *priv, struct rsrc_card_priv *priv,
int idx) int idx, bool is_fe)
{ {
struct device *dev = rsrc_priv_to_dev(priv); struct device *dev = rsrc_priv_to_dev(priv);
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
bool is_fe = false;
int ret; int ret;
if (0 == strcmp(np->name, "cpu"))
is_fe = true;
ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe);
if (ret < 0)
return ret;
ret = rsrc_card_parse_links(np, priv, idx, is_fe); ret = rsrc_card_parse_links(np, priv, idx, is_fe);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -332,12 +344,54 @@ static int rsrc_card_dai_link_of(struct device_node *node, ...@@ -332,12 +344,54 @@ static int rsrc_card_dai_link_of(struct device_node *node,
dev_dbg(dev, "\t%s / %04x / %d\n", dev_dbg(dev, "\t%s / %04x / %d\n",
dai_props->dai_name, dai_props->dai_name,
dai_props->fmt, dai_link->dai_fmt,
dai_props->sysclk); dai_props->sysclk);
return ret; return ret;
} }
static int rsrc_card_dai_link_of(struct device_node *node,
struct rsrc_card_priv *priv)
{
struct snd_soc_dai_link *dai_link;
struct device_node *np;
unsigned int daifmt = 0;
int ret, i;
bool is_fe;
/* find 1st codec */
i = 0;
for_each_child_of_node(node, np) {
dai_link = rsrc_priv_to_link(priv, i);
if (strcmp(np->name, "codec") == 0) {
ret = rsrc_card_parse_daifmt(node, np, priv,
dai_link, &daifmt);
if (ret < 0)
return ret;
break;
}
i++;
}
i = 0;
for_each_child_of_node(node, np) {
dai_link = rsrc_priv_to_link(priv, i);
dai_link->dai_fmt = daifmt;
is_fe = false;
if (strcmp(np->name, "cpu") == 0)
is_fe = true;
ret = rsrc_card_dai_sub_link_of(node, np, priv, i, is_fe);
if (ret < 0)
return ret;
i++;
}
return 0;
}
static int rsrc_card_parse_of(struct device_node *node, static int rsrc_card_parse_of(struct device_node *node,
struct rsrc_card_priv *priv, struct rsrc_card_priv *priv,
struct device *dev) struct device *dev)
...@@ -345,9 +399,8 @@ static int rsrc_card_parse_of(struct device_node *node, ...@@ -345,9 +399,8 @@ static int rsrc_card_parse_of(struct device_node *node,
const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
struct rsrc_card_dai *props; struct rsrc_card_dai *props;
struct snd_soc_dai_link *links; struct snd_soc_dai_link *links;
struct device_node *np;
int ret; int ret;
int i, num; int num;
if (!node) if (!node)
return -EINVAL; return -EINVAL;
...@@ -388,13 +441,9 @@ static int rsrc_card_parse_of(struct device_node *node, ...@@ -388,13 +441,9 @@ static int rsrc_card_parse_of(struct device_node *node,
priv->snd_card.name ? priv->snd_card.name : "", priv->snd_card.name ? priv->snd_card.name : "",
priv->convert_rate); priv->convert_rate);
i = 0; ret = rsrc_card_dai_link_of(node, priv);
for_each_child_of_node(node, np) { if (ret < 0)
ret = rsrc_card_dai_link_of(node, np, priv, i); return ret;
if (ret < 0)
return ret;
i++;
}
if (!priv->snd_card.name) if (!priv->snd_card.name)
priv->snd_card.name = priv->snd_card.dai_link->name; priv->snd_card.name = priv->snd_card.dai_link->name;
......
...@@ -20,20 +20,21 @@ ...@@ -20,20 +20,21 @@
#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id))
struct rsnd_src { struct rsnd_src {
struct rsnd_src_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod; struct rsnd_mod mod;
struct rsnd_mod *dma;
struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
struct rsnd_kctrl_cfg_s sync; /* sync convert */ struct rsnd_kctrl_cfg_s sync; /* sync convert */
u32 convert_rate; /* sampling rate convert */ u32 convert_rate; /* sampling rate convert */
int err; int err;
int irq;
}; };
#define RSND_SRC_NAME_SIZE 16 #define RSND_SRC_NAME_SIZE 16
#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
#define rsnd_src_to_dma(src) ((src)->dma)
#define rsnd_src_nr(priv) ((priv)->src_nr) #define rsnd_src_nr(priv) ((priv)->src_nr)
#define rsnd_enable_sync_convert(src) ((src)->sen.val) #define rsnd_enable_sync_convert(src) ((src)->sen.val)
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
#define rsnd_mod_to_src(_mod) \ #define rsnd_mod_to_src(_mod) \
container_of((_mod), struct rsnd_src, mod) container_of((_mod), struct rsnd_src, mod)
...@@ -69,67 +70,16 @@ struct rsnd_src { ...@@ -69,67 +70,16 @@ struct rsnd_src {
* |-----------------| * |-----------------|
*/ */
/* static void rsnd_src_activation(struct rsnd_mod *mod)
* How to use SRC bypass mode for debugging
*
* SRC has bypass mode, and it is useful for debugging.
* In Gen2 case,
* SRCm_MODE controls whether SRC is used or not
* SSI_MODE0 controls whether SSIU which receives SRC data
* is used or not.
* Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC,
* but SRC bypass mode needs SSI_MODE0 only.
*
* This driver request
* struct rsnd_src_platform_info {
* u32 convert_rate;
* int dma_id;
* }
*
* rsnd_src_convert_rate() indicates
* above convert_rate, and it controls
* whether SRC is used or not.
*
* ex) doesn't use SRC
* static struct rsnd_dai_platform_info rsnd_dai = {
* .playback = { .ssi = &rsnd_ssi[0], },
* };
*
* ex) uses SRC
* static struct rsnd_src_platform_info rsnd_src[] = {
* RSND_SCU(48000, 0),
* ...
* };
* static struct rsnd_dai_platform_info rsnd_dai = {
* .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
* };
*
* ex) uses SRC bypass mode
* static struct rsnd_src_platform_info rsnd_src[] = {
* RSND_SCU(0, 0),
* ...
* };
* static struct rsnd_dai_platform_info rsnd_dai = {
* .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
* };
*
*/
/*
* Gen1/Gen2 common functions
*/
static void rsnd_src_soft_reset(struct rsnd_mod *mod)
{ {
rsnd_mod_write(mod, SRC_SWRSR, 0); rsnd_mod_write(mod, SRC_SWRSR, 0);
rsnd_mod_write(mod, SRC_SWRSR, 1); rsnd_mod_write(mod, SRC_SWRSR, 1);
} }
static void rsnd_src_halt(struct rsnd_mod *mod)
#define rsnd_src_initialize_lock(mod) __rsnd_src_initialize_lock(mod, 1)
#define rsnd_src_initialize_unlock(mod) __rsnd_src_initialize_lock(mod, 0)
static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable)
{ {
rsnd_mod_write(mod, SRC_SRCIR, enable); rsnd_mod_write(mod, SRC_SRCIR, 1);
rsnd_mod_write(mod, SRC_SWRSR, 0);
} }
static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
...@@ -143,99 +93,6 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, ...@@ -143,99 +93,6 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
is_play ? "rx" : "tx"); is_play ? "rx" : "tx");
} }
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io,
int use_busif)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
int ssi_id = rsnd_mod_id(ssi_mod);
/*
* SSI_MODE0
*/
rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id),
!use_busif << ssi_id);
/*
* SSI_MODE1
*/
if (rsnd_ssi_is_pin_sharing(io)) {
int shift = -1;
switch (ssi_id) {
case 1:
shift = 0;
break;
case 2:
shift = 2;
break;
case 4:
shift = 16;
break;
}
if (shift >= 0)
rsnd_mod_bset(ssi_mod, SSI_MODE1,
0x3 << shift,
rsnd_rdai_is_clk_master(rdai) ?
0x2 << shift : 0x1 << shift);
}
/*
* DMA settings for SSIU
*/
if (use_busif) {
u32 val = rsnd_get_dalign(ssi_mod, io);
rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
rsnd_get_adinr_bit(ssi_mod, io));
rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val);
}
return 0;
}
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io)
{
/*
* DMA settings for SSIU
*/
rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
return 0;
}
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
if (rsnd_is_gen1(priv))
return 0;
/* enable SSI interrupt if Gen2 */
rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
rsnd_ssi_is_dma_mode(ssi_mod) ?
0x0e000000 : 0x0f000000);
return 0;
}
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
if (rsnd_is_gen1(priv))
return 0;
/* disable SSI interrupt if Gen2 */
rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
return 0;
}
static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_src *src) struct rsnd_src *src)
{ {
...@@ -283,34 +140,6 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, ...@@ -283,34 +140,6 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
return rate; return rate;
} }
static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fsrate = 0;
if (convert_rate)
fsrate = 0x0400000 / convert_rate * runtime->rate;
/* Set channel number and output bit length */
rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io));
/* Enable the initial value of IFS */
if (fsrate) {
rsnd_mod_write(mod, SRC_IFSCR, 1);
/* Set initial value of IFS */
rsnd_mod_write(mod, SRC_IFSVR, fsrate);
}
/* use DMA transfer */
rsnd_mod_write(mod, SRC_BUSIF_MODE, 1);
return 0;
}
static int rsnd_src_hw_params(struct rsnd_mod *mod, static int rsnd_src_hw_params(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream, struct snd_pcm_substream *substream,
...@@ -319,9 +148,6 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod, ...@@ -319,9 +148,6 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod,
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
struct snd_soc_pcm_runtime *fe = substream->private_data; struct snd_soc_pcm_runtime *fe = substream->private_data;
/* default value (mainly for non-DT) */
src->convert_rate = src->info->convert_rate;
/* /*
* SRC assumes that it is used under DPCM if user want to use * SRC assumes that it is used under DPCM if user want to use
* sampling rate convert. Then, SRC should be FE. * sampling rate convert. Then, SRC should be FE.
...@@ -347,250 +173,112 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod, ...@@ -347,250 +173,112 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod,
return 0; return 0;
} }
static int rsnd_src_init(struct rsnd_mod *mod, static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_mod *mod)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
rsnd_mod_power_on(mod);
rsnd_src_soft_reset(mod);
rsnd_src_initialize_lock(mod);
src->err = 0;
/* reset sync convert_rate */
src->sync.val = 0;
return 0;
}
static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 ifscr, fsrate, adinr;
u32 cr, route;
u32 bsdsr, bsisr;
uint ratio;
rsnd_mod_power_off(mod); if (!runtime)
return;
if (src->err)
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
src->convert_rate = 0;
/* reset sync convert_rate */
src->sync.val = 0;
return 0;
}
static int rsnd_src_start(struct rsnd_mod *mod)
{
rsnd_src_initialize_unlock(mod);
return 0;
}
static int rsnd_src_stop(struct rsnd_mod *mod)
{
/* nothing to do */
return 0;
}
/* /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
* Gen1 functions if (!convert_rate)
*/ ratio = 0;
static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io, else if (convert_rate > runtime->rate)
struct rsnd_mod *mod) ratio = 100 * convert_rate / runtime->rate;
{ else
struct src_route_config { ratio = 100 * runtime->rate / convert_rate;
u32 mask;
int shift;
} routes[] = {
{ 0xF, 0, }, /* 0 */
{ 0xF, 4, }, /* 1 */
{ 0xF, 8, }, /* 2 */
{ 0x7, 12, }, /* 3 */
{ 0x7, 16, }, /* 4 */
{ 0x7, 20, }, /* 5 */
{ 0x7, 24, }, /* 6 */
{ 0x3, 28, }, /* 7 */
{ 0x3, 30, }, /* 8 */
};
u32 mask;
u32 val;
int id;
id = rsnd_mod_id(mod); if (ratio > 600) {
if (id < 0 || id >= ARRAY_SIZE(routes)) dev_err(dev, "FSO/FSI ratio error\n");
return -EIO; return;
}
/* /*
* SRC_ROUTE_SELECT * SRC_ADINR
*/ */
val = rsnd_io_is_play(io) ? 0x1 : 0x2; adinr = rsnd_get_adinr_bit(mod, io) |
val = val << routes[id].shift; rsnd_get_adinr_chan(mod, io);
mask = routes[id].mask << routes[id].shift;
rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
return 0;
}
static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 mask;
u32 val;
int shift;
int id = rsnd_mod_id(mod);
int ret;
/* /*
* SRC_TIMING_SELECT * SRC_IFSCR / SRC_IFSVR
*/ */
shift = (id % 4) * 8; ifscr = 0;
mask = 0x1F << shift; fsrate = 0;
if (convert_rate) {
ifscr = 1;
fsrate = 0x0400000 / convert_rate * runtime->rate;
}
/* /*
* ADG is used as source clock if SRC was used, * SRC_SRCCR / SRC_ROUTE_MODE0
* then, SSI WS is used as destination clock.
* SSI WS is used as source clock if SRC is not used
* (when playback, source/destination become reverse when capture)
*/ */
ret = 0; cr = 0x00011110;
route = 0x0;
if (convert_rate) { if (convert_rate) {
/* use ADG */ route = 0x1;
val = 0;
ret = rsnd_adg_set_convert_clk_gen1(priv, mod,
runtime->rate,
convert_rate);
} else if (8 == id) {
/* use SSI WS, but SRU8 is special */
val = id << shift;
} else {
/* use SSI WS */
val = (id + 1) << shift;
}
if (ret < 0) if (rsnd_enable_sync_convert(src)) {
return ret; cr |= 0x1;
route |= rsnd_io_is_play(io) ?
(0x1 << 24) : (0x1 << 25);
}
}
switch (id / 4) { /*
case 0: * SRC_BSDSR / SRC_BSISR
rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); */
break; switch (rsnd_mod_id(mod)) {
case 1: case 5:
rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); case 6:
case 7:
case 8:
bsdsr = 0x02400000; /* 6 - 1/6 */
bsisr = 0x00100060; /* 6 - 1/6 */
break; break;
case 2: default:
rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); bsdsr = 0x01800000; /* 6 - 1/6 */
bsisr = 0x00100060 ;/* 6 - 1/6 */
break; break;
} }
return 0; rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
} rsnd_mod_write(mod, SRC_ADINR, adinr);
rsnd_mod_write(mod, SRC_IFSCR, ifscr);
static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, rsnd_mod_write(mod, SRC_IFSVR, fsrate);
struct rsnd_dai_stream *io) rsnd_mod_write(mod, SRC_SRCCR, cr);
{ rsnd_mod_write(mod, SRC_BSDSR, bsdsr);
struct rsnd_src *src = rsnd_mod_to_src(mod); rsnd_mod_write(mod, SRC_BSISR, bsisr);
int ret; rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
ret = rsnd_src_set_convert_rate(mod, io);
if (ret < 0)
return ret;
/* Select SRC mode (fixed value) */
rsnd_mod_write(mod, SRC_SRCCR, 0x00010110);
/* Set the restriction value of the FS ratio (98%) */
rsnd_mod_write(mod, SRC_MNFSR,
rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
/* Gen1/Gen2 are not compatible */
if (rsnd_src_convert_rate(io, src))
rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
return 0;
}
static int rsnd_src_init_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
ret = rsnd_src_init(mod, priv);
if (ret < 0)
return ret;
ret = rsnd_src_set_route_gen1(io, mod);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_rate_gen1(mod, io);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_timing_gen1(io, mod);
if (ret < 0)
return ret;
return 0;
}
static int rsnd_src_start_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int id = rsnd_mod_id(mod);
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
return rsnd_src_start(mod);
}
static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int id = rsnd_mod_id(mod);
rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
return rsnd_src_stop(mod); if (convert_rate)
rsnd_adg_set_convert_clk_gen2(mod, io,
runtime->rate,
convert_rate);
else
rsnd_adg_set_convert_timing_gen2(mod, io);
} }
static struct rsnd_mod_ops rsnd_src_gen1_ops = { #define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1)
.name = SRC_NAME, #define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
.dma_req = rsnd_src_dma_req, static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
.init = rsnd_src_init_gen1,
.quit = rsnd_src_quit,
.start = rsnd_src_start_gen1,
.stop = rsnd_src_stop_gen1,
.hw_params = rsnd_src_hw_params,
};
/*
* Gen2 functions
*/
#define rsnd_src_irq_enable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 1)
#define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0)
static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
{ {
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 sys_int_val, int_val, sys_int_mask; u32 sys_int_val, int_val, sys_int_mask;
int irq = src->info->irq; int irq = src->irq;
int id = rsnd_mod_id(mod); int id = rsnd_mod_id(mod);
sys_int_val = sys_int_val =
...@@ -600,7 +288,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) ...@@ -600,7 +288,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
/* /*
* IRQ is not supported on non-DT * IRQ is not supported on non-DT
* see * see
* rsnd_src_probe_gen2() * rsnd_src_probe_()
*/ */
if ((irq <= 0) || !enable) { if ((irq <= 0) || !enable) {
sys_int_val = 0; sys_int_val = 0;
...@@ -620,7 +308,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable) ...@@ -620,7 +308,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
} }
static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) static void rsnd_src_status_clear(struct rsnd_mod *mod)
{ {
u32 val = OUF_SRC(rsnd_mod_id(mod)); u32 val = OUF_SRC(rsnd_mod_id(mod));
...@@ -628,7 +316,7 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod) ...@@ -628,7 +316,7 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod)
rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
} }
static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) static bool rsnd_src_record_error(struct rsnd_mod *mod)
{ {
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val0, val1; u32 val0, val1;
...@@ -652,22 +340,16 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) ...@@ -652,22 +340,16 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod)
ret = true; ret = true;
} }
/* clear error static */
rsnd_src_error_clear_gen2(mod);
return ret; return ret;
} }
static int _rsnd_src_start_gen2(struct rsnd_mod *mod, static int rsnd_src_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val; u32 val;
val = rsnd_get_dalign(mod, io);
rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val);
/* /*
* WORKAROUND * WORKAROUND
* *
...@@ -678,247 +360,149 @@ static int _rsnd_src_start_gen2(struct rsnd_mod *mod, ...@@ -678,247 +360,149 @@ static int _rsnd_src_start_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SRC_CTRL, val); rsnd_mod_write(mod, SRC_CTRL, val);
rsnd_src_error_clear_gen2(mod); return 0;
}
rsnd_src_start(mod);
rsnd_src_irq_enable_gen2(mod); static int rsnd_src_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
/*
* stop SRC output only
* see rsnd_src_quit
*/
rsnd_mod_write(mod, SRC_CTRL, 0x01);
return 0; return 0;
} }
static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) static int rsnd_src_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
rsnd_src_irq_disable_gen2(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
rsnd_mod_write(mod, SRC_CTRL, 0); rsnd_mod_power_on(mod);
rsnd_src_activation(mod);
rsnd_src_set_convert_rate(io, mod);
rsnd_src_error_record_gen2(mod); rsnd_src_status_clear(mod);
rsnd_src_irq_enable(mod);
src->err = 0;
return rsnd_src_stop(mod); /* reset sync convert_rate */
src->sync.val = 0;
return 0;
} }
static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod, static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv);
spin_lock(&priv->lock);
/* ignore all cases if not working */ rsnd_src_irq_disable(mod);
if (!rsnd_io_is_working(io))
goto rsnd_src_interrupt_gen2_out;
if (rsnd_src_error_record_gen2(mod)) { /* stop both out/in */
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); rsnd_mod_write(mod, SRC_CTRL, 0);
struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv);
dev_dbg(dev, "%s[%d] restart\n", rsnd_src_halt(mod);
rsnd_mod_name(mod), rsnd_mod_id(mod));
_rsnd_src_stop_gen2(mod); rsnd_mod_power_off(mod);
if (src->err < 1024)
_rsnd_src_start_gen2(mod, io);
else
dev_warn(dev, "no more SRC restart\n");
}
rsnd_src_interrupt_gen2_out: if (src->err)
spin_unlock(&priv->lock); dev_warn(dev, "%s[%d] under/over flow err = %d\n",
} rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) src->convert_rate = 0;
{
struct rsnd_mod *mod = data;
rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2); /* reset sync convert_rate */
src->sync.val = 0;
return IRQ_HANDLED; return 0;
} }
static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, static void __rsnd_src_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io)
{ {
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(io, src); struct device *dev = rsnd_priv_to_dev(priv);
u32 cr, route;
uint ratio;
int ret;
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */ spin_lock(&priv->lock);
if (!convert_rate)
ratio = 0;
else if (convert_rate > runtime->rate)
ratio = 100 * convert_rate / runtime->rate;
else
ratio = 100 * runtime->rate / convert_rate;
if (ratio > 600) { /* ignore all cases if not working */
dev_err(dev, "FSO/FSI ratio error\n"); if (!rsnd_io_is_working(io))
return -EINVAL; goto rsnd_src_interrupt_out;
}
ret = rsnd_src_set_convert_rate(mod, io); if (rsnd_src_record_error(mod)) {
if (ret < 0)
return ret;
cr = 0x00011110; dev_dbg(dev, "%s[%d] restart\n",
route = 0x0; rsnd_mod_name(mod), rsnd_mod_id(mod));
if (convert_rate) {
route = 0x1;
if (rsnd_enable_sync_convert(src)) { rsnd_src_stop(mod, io, priv);
cr |= 0x1; rsnd_src_start(mod, io, priv);
route |= rsnd_io_is_play(io) ?
(0x1 << 24) : (0x1 << 25);
}
} }
rsnd_mod_write(mod, SRC_SRCCR, cr); if (src->err > 1024) {
rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); rsnd_src_irq_disable(mod);
switch (rsnd_mod_id(mod)) { dev_warn(dev, "no more %s[%d] restart\n",
case 5: rsnd_mod_name(mod), rsnd_mod_id(mod));
case 6:
case 7:
case 8:
rsnd_mod_write(mod, SRC_BSDSR, 0x02400000);
break;
default:
rsnd_mod_write(mod, SRC_BSDSR, 0x01800000);
break;
} }
rsnd_mod_write(mod, SRC_BSISR, 0x00100060); rsnd_src_status_clear(mod);
rsnd_src_interrupt_out:
return 0; spin_unlock(&priv->lock);
} }
static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io, static irqreturn_t rsnd_src_interrupt(int irq, void *data)
struct rsnd_mod *mod)
{ {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_mod *mod = data;
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(io, src);
int ret;
if (convert_rate) rsnd_mod_interrupt(mod, __rsnd_src_interrupt);
ret = rsnd_adg_set_convert_clk_gen2(mod, io,
runtime->rate,
convert_rate);
else
ret = rsnd_adg_set_convert_timing_gen2(mod, io);
return ret; return IRQ_HANDLED;
} }
static int rsnd_src_probe_gen2(struct rsnd_mod *mod, static int rsnd_src_probe_(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
struct rsnd_src *src = rsnd_mod_to_src(mod); struct rsnd_src *src = rsnd_mod_to_src(mod);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
int irq = src->info->irq; int irq = src->irq;
int ret; int ret;
if (irq > 0) { if (irq > 0) {
/* /*
* IRQ is not supported on non-DT * IRQ is not supported on non-DT
* see * see
* rsnd_src_irq_enable_gen2() * rsnd_src_irq_enable()
*/ */
ret = devm_request_irq(dev, irq, ret = devm_request_irq(dev, irq,
rsnd_src_interrupt_gen2, rsnd_src_interrupt,
IRQF_SHARED, IRQF_SHARED,
dev_name(dev), mod); dev_name(dev), mod);
if (ret) if (ret)
return ret; return ret;
} }
ret = rsnd_dma_init(io, src->dma = rsnd_dma_attach(io, mod, 0);
rsnd_mod_to_dma(mod), if (IS_ERR(src->dma))
src->info->dma_id); return PTR_ERR(src->dma);
return ret; return ret;
} }
static int rsnd_src_remove_gen2(struct rsnd_mod *mod, static int rsnd_src_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
return 0;
}
static int rsnd_src_init_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
ret = rsnd_src_init(mod, priv);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_rate_gen2(mod, io);
if (ret < 0)
return ret;
ret = rsnd_src_set_convert_timing_gen2(io, mod);
if (ret < 0)
return ret;
return 0;
}
static int rsnd_src_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_dma_start(io, rsnd_mod_to_dma(mod));
return _rsnd_src_start_gen2(mod, io);
}
static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
ret = _rsnd_src_stop_gen2(mod);
rsnd_dma_stop(io, rsnd_mod_to_dma(mod));
return ret;
}
static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate = rsnd_src_convert_rate(io, src);
u32 fsrate;
if (!runtime)
return;
if (!convert_rate)
convert_rate = runtime->rate;
fsrate = 0x0400000 / convert_rate * runtime->rate;
/* update IFS */
rsnd_mod_write(mod, SRC_IFSVR, fsrate);
}
static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd) struct snd_soc_pcm_runtime *rtd)
{ {
...@@ -950,7 +534,7 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, ...@@ -950,7 +534,7 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
rsnd_io_is_play(io) ? rsnd_io_is_play(io) ?
"SRC Out Rate Switch" : "SRC Out Rate Switch" :
"SRC In Rate Switch", "SRC In Rate Switch",
rsnd_src_reconvert_update, rsnd_src_set_convert_rate,
&src->sen, 1); &src->sen, 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -959,23 +543,22 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, ...@@ -959,23 +543,22 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
rsnd_io_is_play(io) ? rsnd_io_is_play(io) ?
"SRC Out Rate" : "SRC Out Rate" :
"SRC In Rate", "SRC In Rate",
rsnd_src_reconvert_update, rsnd_src_set_convert_rate,
&src->sync, 192000); &src->sync, 192000);
return ret; return ret;
} }
static struct rsnd_mod_ops rsnd_src_gen2_ops = { static struct rsnd_mod_ops rsnd_src_ops = {
.name = SRC_NAME, .name = SRC_NAME,
.dma_req = rsnd_src_dma_req, .dma_req = rsnd_src_dma_req,
.probe = rsnd_src_probe_gen2, .probe = rsnd_src_probe_,
.remove = rsnd_src_remove_gen2, .init = rsnd_src_init,
.init = rsnd_src_init_gen2,
.quit = rsnd_src_quit, .quit = rsnd_src_quit,
.start = rsnd_src_start_gen2, .start = rsnd_src_start,
.stop = rsnd_src_stop_gen2, .stop = rsnd_src_stop,
.hw_params = rsnd_src_hw_params, .hw_params = rsnd_src_hw_params,
.pcm_new = rsnd_src_pcm_new_gen2, .pcm_new = rsnd_src_pcm_new,
}; };
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
...@@ -983,113 +566,78 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) ...@@ -983,113 +566,78 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
id = 0; id = 0;
return rsnd_mod_get((struct rsnd_src *)(priv->src) + id); return rsnd_mod_get(rsnd_src_get(priv, id));
} }
static void rsnd_of_parse_src(struct platform_device *pdev, int rsnd_src_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct device_node *src_node; struct device_node *node;
struct device_node *np; struct device_node *np;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct rsnd_src_platform_info *src_info;
struct device *dev = &pdev->dev;
int nr, i;
if (!of_data)
return;
src_node = rsnd_src_of_node(priv);
if (!src_node)
return;
nr = of_get_child_count(src_node);
if (!nr)
goto rsnd_of_parse_src_end;
src_info = devm_kzalloc(dev,
sizeof(struct rsnd_src_platform_info) * nr,
GFP_KERNEL);
if (!src_info) {
dev_err(dev, "src info allocation error\n");
goto rsnd_of_parse_src_end;
}
info->src_info = src_info;
info->src_info_nr = nr;
i = 0;
for_each_child_of_node(src_node, np) {
src_info[i].irq = irq_of_parse_and_map(np, 0);
i++;
}
rsnd_of_parse_src_end:
of_node_put(src_node);
}
int rsnd_src_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_src *src; struct rsnd_src *src;
struct rsnd_mod_ops *ops;
struct clk *clk; struct clk *clk;
char name[RSND_SRC_NAME_SIZE]; char name[RSND_SRC_NAME_SIZE];
int i, nr, ret; int i, nr, ret;
ops = NULL; /* This driver doesn't support Gen1 at this point */
if (rsnd_is_gen1(priv)) { if (rsnd_is_gen1(priv))
ops = &rsnd_src_gen1_ops; return 0;
dev_warn(dev, "Gen1 support will be removed soon\n");
}
if (rsnd_is_gen2(priv))
ops = &rsnd_src_gen2_ops;
if (!ops) {
dev_err(dev, "unknown Generation\n");
return -EIO;
}
rsnd_of_parse_src(pdev, of_data, priv); node = rsnd_src_of_node(priv);
if (!node)
return 0; /* not used is not error */
/* nr = of_get_child_count(node);
* init SRC if (!nr) {
*/ ret = -EINVAL;
nr = info->src_info_nr; goto rsnd_src_probe_done;
if (!nr) }
return 0;
src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); src = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL);
if (!src) if (!src) {
return -ENOMEM; ret = -ENOMEM;
goto rsnd_src_probe_done;
}
priv->src_nr = nr; priv->src_nr = nr;
priv->src = src; priv->src = src;
for_each_rsnd_src(src, priv, i) { i = 0;
for_each_child_of_node(node, np) {
src = rsnd_src_get(priv, i);
snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
SRC_NAME, i); SRC_NAME, i);
clk = devm_clk_get(dev, name); src->irq = irq_of_parse_and_map(np, 0);
if (IS_ERR(clk)) if (!src->irq) {
return PTR_ERR(clk); ret = -EINVAL;
goto rsnd_src_probe_done;
}
src->info = &info->src_info[i]; clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto rsnd_src_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i); ret = rsnd_mod_init(priv, rsnd_mod_get(src),
&rsnd_src_ops, clk, RSND_MOD_SRC, i);
if (ret) if (ret)
return ret; goto rsnd_src_probe_done;
i++;
} }
return 0; ret = 0;
rsnd_src_probe_done:
of_node_put(node);
return ret;
} }
void rsnd_src_remove(struct platform_device *pdev, void rsnd_src_remove(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_src *src; struct rsnd_src *src;
int i; int i;
......
...@@ -24,7 +24,9 @@ ...@@ -24,7 +24,9 @@
#define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define OIEN (1 << 26) /* Overflow Interrupt Enable */
#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
#define DIEN (1 << 24) /* Data Interrupt Enable */ #define DIEN (1 << 24) /* Data Interrupt Enable */
#define CHNL_4 (1 << 22) /* Channels */
#define CHNL_6 (2 << 22) /* Channels */
#define CHNL_8 (3 << 22) /* Channels */
#define DWL_8 (0 << 19) /* Data Word Length */ #define DWL_8 (0 << 19) /* Data Word Length */
#define DWL_16 (1 << 19) /* Data Word Length */ #define DWL_16 (1 << 19) /* Data Word Length */
#define DWL_18 (2 << 19) /* Data Word Length */ #define DWL_18 (2 << 19) /* Data Word Length */
...@@ -39,6 +41,7 @@ ...@@ -39,6 +41,7 @@
#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ #define SCKP (1 << 13) /* Serial Bit Clock Polarity */
#define SWSP (1 << 12) /* Serial WS Polarity */ #define SWSP (1 << 12) /* Serial WS Polarity */
#define SDTA (1 << 10) /* Serial Data Alignment */ #define SDTA (1 << 10) /* Serial Data Alignment */
#define PDTA (1 << 9) /* Parallel Data Alignment */
#define DEL (1 << 8) /* Serial Data Delay */ #define DEL (1 << 8) /* Serial Data Delay */
#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ #define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ #define TRMD (1 << 1) /* Transmit/Receive Mode Select */
...@@ -56,35 +59,44 @@ ...@@ -56,35 +59,44 @@
* SSIWSR * SSIWSR
*/ */
#define CONT (1 << 8) /* WS Continue Function */ #define CONT (1 << 8) /* WS Continue Function */
#define WS_MODE (1 << 0) /* WS Mode */
#define SSI_NAME "ssi" #define SSI_NAME "ssi"
struct rsnd_ssi { struct rsnd_ssi {
struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
struct rsnd_ssi *parent; struct rsnd_ssi *parent;
struct rsnd_mod mod; struct rsnd_mod mod;
struct rsnd_mod *dma;
u32 flags;
u32 cr_own; u32 cr_own;
u32 cr_clk; u32 cr_clk;
u32 cr_mode;
u32 wsr;
int chan; int chan;
int rate;
int err; int err;
int irq;
unsigned int usrcnt; unsigned int usrcnt;
}; };
/* flags */
#define RSND_SSI_CLK_PIN_SHARE (1 << 0)
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
#define for_each_rsnd_ssi(pos, priv, i) \ #define for_each_rsnd_ssi(pos, priv, i) \
for (i = 0; \ for (i = 0; \
(i < rsnd_ssi_nr(priv)) && \ (i < rsnd_ssi_nr(priv)) && \
((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \
i++) i++)
#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
#define rsnd_ssi_to_dma(mod) ((ssi)->dma)
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) #define rsnd_ssi_mode_flags(p) ((p)->flags)
#define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
#define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
#define rsnd_ssi_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{ {
...@@ -103,6 +115,16 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) ...@@ -103,6 +115,16 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
return use_busif; return use_busif;
} }
static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, SSISR, 0);
}
static u32 rsnd_ssi_status_get(struct rsnd_mod *mod)
{
return rsnd_mod_read(mod, SSISR);
}
static void rsnd_ssi_status_check(struct rsnd_mod *mod, static void rsnd_ssi_status_check(struct rsnd_mod *mod,
u32 bit) u32 bit)
{ {
...@@ -112,7 +134,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, ...@@ -112,7 +134,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
int i; int i;
for (i = 0; i < 1024; i++) { for (i = 0; i < 1024; i++) {
status = rsnd_mod_read(mod, SSISR); status = rsnd_ssi_status_get(mod);
if (status & bit) if (status & bit)
return; return;
...@@ -122,13 +144,79 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, ...@@ -122,13 +144,79 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
dev_warn(dev, "status check failed\n"); dev_warn(dev, "status check failed\n");
} }
static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
if (rsnd_is_gen1(priv))
return 0;
/* enable SSI interrupt if Gen2 */
rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
rsnd_ssi_is_dma_mode(ssi_mod) ?
0x0e000000 : 0x0f000000);
return 0;
}
static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
if (rsnd_is_gen1(priv))
return 0;
/* disable SSI interrupt if Gen2 */
rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
return 0;
}
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct device *dev = rsnd_priv_to_dev(priv);
enum rsnd_mod_type types[] = {
RSND_MOD_SSIM1,
RSND_MOD_SSIM2,
RSND_MOD_SSIM3,
};
int i, mask;
switch (runtime->channels) {
case 2: /* Multi channel is not needed for Stereo */
return 0;
case 6:
break;
default:
dev_err(dev, "unsupported channel\n");
return 0;
}
mask = 0;
for (i = 0; i < ARRAY_SIZE(types); i++) {
mod = rsnd_io_to_mod(io, types[i]);
if (!mod)
continue;
mask |= 1 << rsnd_mod_id(mod);
}
return mask;
}
static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io)
{ {
struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_mod *mod = rsnd_mod_get(ssi); struct rsnd_mod *mod = rsnd_mod_get(ssi);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
int slots = rsnd_get_slot_width(io);
int j, ret; int j, ret;
int ssi_clk_mul_table[] = { int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12, 1, 2, 4, 8, 16, 6, 12,
...@@ -136,6 +224,24 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, ...@@ -136,6 +224,24 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
unsigned int main_rate; unsigned int main_rate;
unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
if (!rsnd_rdai_is_clk_master(rdai))
return 0;
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
return 0;
if (rsnd_ssi_is_multi_slave(mod, io))
return 0;
if (ssi->usrcnt > 1) {
if (ssi->rate != rate) {
dev_err(dev, "SSI parent/child should use same rate\n");
return -EINVAL;
}
return 0;
}
/* /*
* Find best clock, and try to start ADG * Find best clock, and try to start ADG
*/ */
...@@ -143,15 +249,18 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, ...@@ -143,15 +249,18 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/* /*
* this driver is assuming that * this driver is assuming that
* system word is 64fs (= 2 x 32bit) * system word is 32bit x slots
* see rsnd_ssi_init() * see rsnd_ssi_init()
*/ */
main_rate = rate * 32 * 2 * ssi_clk_mul_table[j]; main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
if (0 == ret) { if (0 == ret) {
ssi->cr_clk = FORCE | SWL_32 | ssi->cr_clk = FORCE | SWL_32 |
SCKD | SWSD | CKDV(j); SCKD | SWSD | CKDV(j);
ssi->wsr = CONT;
ssi->rate = rate;
dev_dbg(dev, "%s[%d] outputs %u Hz\n", dev_dbg(dev, "%s[%d] outputs %u Hz\n",
rsnd_mod_name(mod), rsnd_mod_name(mod),
...@@ -165,113 +274,91 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, ...@@ -165,113 +274,91 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
return -EIO; return -EIO;
} }
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
struct rsnd_dai_stream *io)
{ {
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct rsnd_mod *mod = rsnd_mod_get(ssi); struct rsnd_mod *mod = rsnd_mod_get(ssi);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
if (!rsnd_rdai_is_clk_master(rdai))
return;
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
return;
if (ssi->usrcnt > 1)
return;
ssi->cr_clk = 0;
ssi->rate = 0;
ssi->cr_clk = 0;
rsnd_adg_ssi_clk_stop(mod); rsnd_adg_ssi_clk_stop(mod);
} }
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
struct rsnd_dai_stream *io) struct rsnd_dai_stream *io)
{ {
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_mod *mod = rsnd_mod_get(ssi); u32 cr_own;
u32 cr_mode; u32 cr_mode;
u32 cr; u32 wsr;
int is_tdm;
if (0 == ssi->usrcnt) { is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
rsnd_mod_power_on(mod);
if (rsnd_rdai_is_clk_master(rdai)) { /*
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); * always use 32bit system word.
* see also rsnd_ssi_master_clk_enable()
*/
cr_own = FORCE | SWL_32 | PDTA;
if (ssi_parent) if (rdai->bit_clk_inv)
rsnd_ssi_hw_start(ssi_parent, io); cr_own |= SCKP;
else if (rdai->frm_clk_inv ^ is_tdm)
rsnd_ssi_master_clk_start(ssi, io); cr_own |= SWSP;
} if (rdai->data_alignment)
cr_own |= SDTA;
if (rdai->sys_delay)
cr_own |= DEL;
if (rsnd_io_is_play(io))
cr_own |= TRMD;
switch (runtime->sample_bits) {
case 16:
cr_own |= DWL_16;
break;
case 32:
cr_own |= DWL_24;
break;
default:
return -EINVAL;
} }
if (rsnd_ssi_is_dma_mode(mod)) { if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
cr_mode = UIEN | OIEN | /* over/under run */ cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */ DMEN; /* DMA : enable DMA */
} else { } else {
cr_mode = DIEN; /* PIO : enable Data interrupt */ cr_mode = DIEN; /* PIO : enable Data interrupt */
} }
cr = ssi->cr_own | /*
ssi->cr_clk | * TDM Extend Mode
cr_mode | * see
EN; * rsnd_ssiu_init_gen2()
*/
rsnd_mod_write(mod, SSICR, cr); wsr = ssi->wsr;
if (is_tdm) {
/* enable WS continue */ wsr |= WS_MODE;
if (rsnd_rdai_is_clk_master(rdai)) cr_own |= CHNL_8;
rsnd_mod_write(mod, SSIWSR, CONT);
/* clear error status */
rsnd_mod_write(mod, SSISR, 0);
ssi->usrcnt++;
dev_dbg(dev, "%s[%d] hw started\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
{
struct rsnd_mod *mod = rsnd_mod_get(ssi);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 cr;
if (0 == ssi->usrcnt) {
dev_err(dev, "%s called without starting\n", __func__);
return;
} }
ssi->usrcnt--; ssi->cr_own = cr_own;
ssi->cr_mode = cr_mode;
if (0 == ssi->usrcnt) { ssi->wsr = wsr;
/*
* disable all IRQ,
* and, wait all data was sent
*/
cr = ssi->cr_own |
ssi->cr_clk;
rsnd_mod_write(mod, SSICR, cr | EN);
rsnd_ssi_status_check(mod, DIRQ);
/*
* disable SSI,
* and, wait idle state
*/
rsnd_mod_write(mod, SSICR, cr); /* disabled all */
rsnd_ssi_status_check(mod, IIRQ);
if (rsnd_rdai_is_clk_master(rdai)) {
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
if (ssi_parent) return 0;
rsnd_ssi_hw_stop(io, ssi_parent);
else
rsnd_ssi_master_clk_stop(ssi);
}
rsnd_mod_power_off(mod);
ssi->chan = 0;
}
dev_dbg(dev, "%s[%d] hw stopped\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
} }
/* /*
...@@ -282,49 +369,30 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, ...@@ -282,49 +369,30 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int ret;
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 cr;
cr = FORCE; ssi->usrcnt++;
/* rsnd_mod_power_on(mod);
* always use 32bit system word for easy clock calculation.
* see also rsnd_ssi_master_clk_enable()
*/
cr |= SWL_32;
/* ret = rsnd_ssi_master_clk_start(ssi, io);
* init clock settings for SSICR if (ret < 0)
*/ return ret;
switch (runtime->sample_bits) {
case 16:
cr |= DWL_16;
break;
case 32:
cr |= DWL_24;
break;
default:
return -EIO;
}
if (rdai->bit_clk_inv) if (rsnd_ssi_is_parent(mod, io))
cr |= SCKP; return 0;
if (rdai->frm_clk_inv)
cr |= SWSP; ret = rsnd_ssi_config_init(ssi, io);
if (rdai->data_alignment) if (ret < 0)
cr |= SDTA; return ret;
if (rdai->sys_delay)
cr |= DEL;
if (rsnd_io_is_play(io))
cr |= TRMD;
/*
* set ssi parameter
*/
ssi->cr_own = cr;
ssi->err = -1; /* ignore 1st error */ ssi->err = -1; /* ignore 1st error */
/* clear error status */
rsnd_ssi_status_clear(mod);
rsnd_ssi_irq_enable(mod);
return 0; return 0;
} }
...@@ -335,6 +403,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, ...@@ -335,6 +403,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
if (rsnd_ssi_is_parent(mod, io))
goto rsnd_ssi_quit_end;
if (ssi->err > 0) if (ssi->err > 0)
dev_warn(dev, "%s[%d] under/over flow err = %d\n", dev_warn(dev, "%s[%d] under/over flow err = %d\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err); rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err);
...@@ -342,6 +413,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, ...@@ -342,6 +413,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
ssi->cr_own = 0; ssi->cr_own = 0;
ssi->err = 0; ssi->err = 0;
rsnd_ssi_irq_disable(mod);
rsnd_ssi_quit_end:
rsnd_ssi_master_clk_stop(ssi, io);
rsnd_mod_power_off(mod);
ssi->usrcnt--;
if (ssi->usrcnt < 0)
dev_err(dev, "%s[%d] usrcnt error\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
return 0; return 0;
} }
...@@ -351,14 +435,13 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, ...@@ -351,14 +435,13 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
int chan = params_channels(params); int chan = params_channels(params);
/* /*
* Already working. * Already working.
* It will happen if SSI has parent/child connection. * It will happen if SSI has parent/child connection.
*/ */
if (ssi->usrcnt) { if (ssi->usrcnt > 1) {
/* /*
* it is error if child <-> parent SSI uses * it is error if child <-> parent SSI uses
* different channels. * different channels.
...@@ -367,39 +450,83 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, ...@@ -367,39 +450,83 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
return -EIO; return -EIO;
} }
/* It will be removed on rsnd_ssi_hw_stop */
ssi->chan = chan; ssi->chan = chan;
if (ssi_parent)
return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
substream, params);
return 0; return 0;
} }
static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
{ {
struct rsnd_mod *mod = rsnd_mod_get(ssi); struct rsnd_mod *mod = rsnd_mod_get(ssi);
u32 status = rsnd_ssi_status_get(mod);
/* under/over flow error */ /* under/over flow error */
if (status & (UIRQ | OIRQ)) { if (status & (UIRQ | OIRQ))
ssi->err++; ssi->err++;
/* clear error status */ return status;
rsnd_mod_write(mod, SSISR, 0); }
}
static int __rsnd_ssi_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr;
cr = ssi->cr_own |
ssi->cr_clk |
ssi->cr_mode;
/*
* EN will be set via SSIU :: SSI_CONTROL
* if Multi channel mode
*/
if (!rsnd_ssi_multi_slaves(io))
cr |= EN;
rsnd_mod_write(mod, SSICR, cr);
rsnd_mod_write(mod, SSIWSR, ssi->wsr);
return 0;
} }
static int rsnd_ssi_start(struct rsnd_mod *mod, static int rsnd_ssi_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{
/*
* no limit to start
* see also
* rsnd_ssi_stop
* rsnd_ssi_interrupt
*/
return __rsnd_ssi_start(mod, io, priv);
}
static int __rsnd_ssi_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr;
rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io)); /*
* disable all IRQ,
* and, wait all data was sent
*/
cr = ssi->cr_own |
ssi->cr_clk;
rsnd_ssi_hw_start(ssi, io); rsnd_mod_write(mod, SSICR, cr | EN);
rsnd_ssi_status_check(mod, DIRQ);
rsnd_src_ssi_irq_enable(mod); /*
* disable SSI,
* and, wait idle state
*/
rsnd_mod_write(mod, SSICR, cr); /* disabled all */
rsnd_ssi_status_check(mod, IIRQ);
return 0; return 0;
} }
...@@ -410,15 +537,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, ...@@ -410,15 +537,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
rsnd_src_ssi_irq_disable(mod); /*
* don't stop if not last user
rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); * see also
* rsnd_ssi_start
rsnd_ssi_hw_stop(io, ssi); * rsnd_ssi_interrupt
*/
rsnd_src_ssiu_stop(mod, io); if (ssi->usrcnt > 1)
return 0;
return 0; return __rsnd_ssi_stop(mod, io, priv);
} }
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
...@@ -426,6 +554,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, ...@@ -426,6 +554,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
int is_dma = rsnd_ssi_is_dma_mode(mod); int is_dma = rsnd_ssi_is_dma_mode(mod);
u32 status; u32 status;
bool elapsed = false; bool elapsed = false;
...@@ -436,7 +565,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, ...@@ -436,7 +565,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io)) if (!rsnd_io_is_working(io))
goto rsnd_ssi_interrupt_out; goto rsnd_ssi_interrupt_out;
status = rsnd_mod_read(mod, SSISR); status = rsnd_ssi_record_error(ssi);
/* PIO only */ /* PIO only */
if (!is_dma && (status & DIRQ)) { if (!is_dma && (status & DIRQ)) {
...@@ -459,23 +588,24 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, ...@@ -459,23 +588,24 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
/* DMA only */ /* DMA only */
if (is_dma && (status & (UIRQ | OIRQ))) { if (is_dma && (status & (UIRQ | OIRQ))) {
struct device *dev = rsnd_priv_to_dev(priv);
/* /*
* restart SSI * restart SSI
*/ */
dev_dbg(dev, "%s[%d] restart\n", dev_dbg(dev, "%s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod)); rsnd_mod_name(mod), rsnd_mod_id(mod));
rsnd_ssi_stop(mod, io, priv); __rsnd_ssi_stop(mod, io, priv);
if (ssi->err < 1024) __rsnd_ssi_start(mod, io, priv);
rsnd_ssi_start(mod, io, priv);
else
dev_warn(dev, "no more SSI restart\n");
} }
rsnd_ssi_record_error(ssi, status); if (ssi->err > 1024) {
rsnd_ssi_irq_disable(mod);
dev_warn(dev, "no more %s[%d] restart\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
rsnd_ssi_status_clear(mod);
rsnd_ssi_interrupt_out: rsnd_ssi_interrupt_out:
spin_unlock(&priv->lock); spin_unlock(&priv->lock);
...@@ -495,15 +625,49 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) ...@@ -495,15 +625,49 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
/* /*
* SSI PIO * SSI PIO
*/ */
static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
struct rsnd_dai_stream *io, struct rsnd_dai_stream *io,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{
if (!__rsnd_ssi_is_pin_sharing(mod))
return;
switch (rsnd_mod_id(mod)) {
case 1:
case 2:
rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP);
break;
case 4:
rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP);
break;
case 8:
rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP);
break;
}
}
static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{ {
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int ret; int ret;
ret = devm_request_irq(dev, ssi->info->irq, /*
* SSIP/SSIU/IRQ are not needed on
* SSI Multi slaves
*/
if (rsnd_ssi_is_multi_slave(mod, io))
return 0;
rsnd_ssi_parent_attach(mod, io, priv);
ret = rsnd_ssiu_attach(io, mod);
if (ret < 0)
return ret;
ret = devm_request_irq(dev, ssi->irq,
rsnd_ssi_interrupt, rsnd_ssi_interrupt,
IRQF_SHARED, IRQF_SHARED,
dev_name(dev), mod); dev_name(dev), mod);
...@@ -513,7 +677,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, ...@@ -513,7 +677,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
static struct rsnd_mod_ops rsnd_ssi_pio_ops = { static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.name = SSI_NAME, .name = SSI_NAME,
.probe = rsnd_ssi_pio_probe, .probe = rsnd_ssi_common_probe,
.init = rsnd_ssi_init, .init = rsnd_ssi_init,
.quit = rsnd_ssi_quit, .quit = rsnd_ssi_quit,
.start = rsnd_ssi_start, .start = rsnd_ssi_start,
...@@ -526,20 +690,23 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, ...@@ -526,20 +690,23 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
struct rsnd_priv *priv) struct rsnd_priv *priv)
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv); int dma_id = 0; /* not needed */
int dma_id = ssi->info->dma_id;
int ret; int ret;
ret = devm_request_irq(dev, ssi->info->irq, /*
rsnd_ssi_interrupt, * SSIP/SSIU/IRQ/DMA are not needed on
IRQF_SHARED, * SSI Multi slaves
dev_name(dev), mod); */
if (rsnd_ssi_is_multi_slave(mod, io))
return 0;
ret = rsnd_ssi_common_probe(mod, io, priv);
if (ret) if (ret)
return ret; return ret;
ret = rsnd_dma_init( ssi->dma = rsnd_dma_attach(io, mod, dma_id);
io, rsnd_mod_to_dma(mod), if (IS_ERR(ssi->dma))
dma_id); return PTR_ERR(ssi->dma);
return ret; return ret;
} }
...@@ -550,9 +717,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, ...@@ -550,9 +717,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
{ {
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
int irq = ssi->info->irq; int irq = ssi->irq;
rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
/* PIO will request IRQ again */ /* PIO will request IRQ again */
devm_free_irq(dev, irq, mod); devm_free_irq(dev, irq, mod);
...@@ -581,32 +746,6 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, ...@@ -581,32 +746,6 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod,
return 0; return 0;
} }
static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
rsnd_dma_start(io, dma);
rsnd_ssi_start(mod, io, priv);
return 0;
}
static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
rsnd_ssi_stop(mod, io, priv);
rsnd_dma_stop(io, dma);
return 0;
}
static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
struct rsnd_mod *mod) struct rsnd_mod *mod)
{ {
...@@ -630,8 +769,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { ...@@ -630,8 +769,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.remove = rsnd_ssi_dma_remove, .remove = rsnd_ssi_dma_remove,
.init = rsnd_ssi_init, .init = rsnd_ssi_init,
.quit = rsnd_ssi_quit, .quit = rsnd_ssi_quit,
.start = rsnd_ssi_dma_start, .start = rsnd_ssi_start,
.stop = rsnd_ssi_dma_stop, .stop = rsnd_ssi_stop,
.fallback = rsnd_ssi_fallback, .fallback = rsnd_ssi_fallback,
.hw_params = rsnd_ssi_hw_params, .hw_params = rsnd_ssi_hw_params,
}; };
...@@ -652,110 +791,76 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { ...@@ -652,110 +791,76 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = {
/* /*
* ssi mod function * ssi mod function
*/ */
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) static void rsnd_ssi_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{ {
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
id = 0; enum rsnd_mod_type types[] = {
RSND_MOD_SSI,
return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id); RSND_MOD_SSIM1,
} RSND_MOD_SSIM2,
RSND_MOD_SSIM3,
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) };
{ enum rsnd_mod_type type;
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int i;
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
}
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
{
struct rsnd_mod *mod = rsnd_mod_get(ssi);
if (!__rsnd_ssi_is_pin_sharing(mod))
return;
switch (rsnd_mod_id(mod)) { /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
case 1: for (i = 0; i < ARRAY_SIZE(types); i++) {
case 2: type = types[i];
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); if (!rsnd_io_to_mod(io, type)) {
break; rsnd_dai_connect(mod, io, type);
case 4: rsnd_set_slot(rdai, 2 * (i + 1), (i + 1));
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3)); return;
break; }
case 8:
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7));
break;
} }
} }
void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
static void rsnd_of_parse_ssi(struct platform_device *pdev, struct device_node *playback,
const struct rsnd_of_data *of_data, struct device_node *capture)
struct rsnd_priv *priv)
{ {
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
struct device_node *node; struct device_node *node;
struct device_node *np; struct device_node *np;
struct rsnd_ssi_platform_info *ssi_info; struct rsnd_mod *mod;
struct rcar_snd_info *info = rsnd_priv_to_info(priv); int i;
struct device *dev = &pdev->dev;
int nr, i;
node = rsnd_ssi_of_node(priv); node = rsnd_ssi_of_node(priv);
if (!node) if (!node)
return; return;
nr = of_get_child_count(node); i = 0;
if (!nr)
goto rsnd_of_parse_ssi_end;
ssi_info = devm_kzalloc(dev,
sizeof(struct rsnd_ssi_platform_info) * nr,
GFP_KERNEL);
if (!ssi_info) {
dev_err(dev, "ssi info allocation error\n");
goto rsnd_of_parse_ssi_end;
}
info->ssi_info = ssi_info;
info->ssi_info_nr = nr;
i = -1;
for_each_child_of_node(node, np) { for_each_child_of_node(node, np) {
mod = rsnd_ssi_mod_get(priv, i);
if (np == playback)
rsnd_ssi_connect(mod, &rdai->playback);
if (np == capture)
rsnd_ssi_connect(mod, &rdai->capture);
i++; i++;
}
ssi_info = info->ssi_info + i; of_node_put(node);
}
/*
* pin settings
*/
if (of_get_property(np, "shared-pin", NULL))
ssi_info->flags |= RSND_SSI_CLK_PIN_SHARE;
/* struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
* irq {
*/ if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
ssi_info->irq = irq_of_parse_and_map(np, 0); id = 0;
/* return rsnd_mod_get(rsnd_ssi_get(priv, id));
* DMA }
*/
ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ?
0 : 1;
if (of_get_property(np, "no-busif", NULL)) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
ssi_info->flags |= RSND_SSI_NO_BUSIF; {
} struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
rsnd_of_parse_ssi_end: return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
of_node_put(node);
} }
int rsnd_ssi_probe(struct platform_device *pdev, int rsnd_ssi_probe(struct rsnd_priv *priv)
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{ {
struct rcar_snd_info *info = rsnd_priv_to_info(priv); struct device_node *node;
struct rsnd_ssi_platform_info *pinfo; struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv); struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops; struct rsnd_mod_ops *ops;
struct clk *clk; struct clk *clk;
...@@ -763,50 +868,73 @@ int rsnd_ssi_probe(struct platform_device *pdev, ...@@ -763,50 +868,73 @@ int rsnd_ssi_probe(struct platform_device *pdev,
char name[RSND_SSI_NAME_SIZE]; char name[RSND_SSI_NAME_SIZE];
int i, nr, ret; int i, nr, ret;
rsnd_of_parse_ssi(pdev, of_data, priv); node = rsnd_ssi_of_node(priv);
if (!node)
return -EINVAL;
nr = of_get_child_count(node);
if (!nr) {
ret = -EINVAL;
goto rsnd_ssi_probe_done;
}
/*
* init SSI
*/
nr = info->ssi_info_nr;
ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
if (!ssi) if (!ssi) {
return -ENOMEM; ret = -ENOMEM;
goto rsnd_ssi_probe_done;
}
priv->ssi = ssi; priv->ssi = ssi;
priv->ssi_nr = nr; priv->ssi_nr = nr;
for_each_rsnd_ssi(ssi, priv, i) { i = 0;
pinfo = &info->ssi_info[i]; for_each_child_of_node(node, np) {
ssi = rsnd_ssi_get(priv, i);
snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
SSI_NAME, i); SSI_NAME, i);
clk = devm_clk_get(dev, name); clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) if (IS_ERR(clk)) {
return PTR_ERR(clk); ret = PTR_ERR(clk);
goto rsnd_ssi_probe_done;
}
ssi->info = pinfo; if (of_get_property(np, "shared-pin", NULL))
ssi->flags |= RSND_SSI_CLK_PIN_SHARE;
if (of_get_property(np, "no-busif", NULL))
ssi->flags |= RSND_SSI_NO_BUSIF;
ssi->irq = irq_of_parse_and_map(np, 0);
if (!ssi->irq) {
ret = -EINVAL;
goto rsnd_ssi_probe_done;
}
ops = &rsnd_ssi_non_ops; ops = &rsnd_ssi_non_ops;
if (pinfo->dma_id > 0) if (of_get_property(np, "pio-transfer", NULL))
ops = &rsnd_ssi_dma_ops;
else if (rsnd_ssi_pio_available(ssi))
ops = &rsnd_ssi_pio_ops; ops = &rsnd_ssi_pio_ops;
else
ops = &rsnd_ssi_dma_ops;
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
RSND_MOD_SSI, i); RSND_MOD_SSI, i);
if (ret) if (ret)
return ret; goto rsnd_ssi_probe_done;
rsnd_ssi_parent_setup(priv, ssi); i++;
} }
return 0; ret = 0;
rsnd_ssi_probe_done:
of_node_put(node);
return ret;
} }
void rsnd_ssi_remove(struct platform_device *pdev, void rsnd_ssi_remove(struct rsnd_priv *priv)
struct rsnd_priv *priv)
{ {
struct rsnd_ssi *ssi; struct rsnd_ssi *ssi;
int i; int i;
......
/*
* Renesas R-Car SSIU support
*
* Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "rsnd.h"
#define SSIU_NAME "ssiu"
struct rsnd_ssiu {
struct rsnd_mod mod;
};
#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
#define for_each_rsnd_ssiu(pos, priv, i) \
for (i = 0; \
(i < rsnd_ssiu_nr(priv)) && \
((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \
i++)
static int rsnd_ssiu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
int use_busif = rsnd_ssi_use_busif(io);
int id = rsnd_mod_id(mod);
u32 mask1, val1;
u32 mask2, val2;
/*
* SSI_MODE0
*/
rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
/*
* SSI_MODE1
*/
mask1 = (1 << 4) | (1 << 20); /* mask sync bit */
mask2 = (1 << 4); /* mask sync bit */
val1 = val2 = 0;
if (rsnd_ssi_is_pin_sharing(io)) {
int shift = -1;
switch (id) {
case 1:
shift = 0;
break;
case 2:
shift = 2;
break;
case 4:
shift = 16;
break;
default:
return -EINVAL;
}
mask1 |= 0x3 << shift;
val1 = rsnd_rdai_is_clk_master(rdai) ?
0x2 << shift : 0x1 << shift;
} else if (multi_ssi_slaves) {
mask2 |= 0x00000007;
mask1 |= 0x0000000f;
switch (multi_ssi_slaves) {
case 0x0206: /* SSI0/1/2/9 */
val2 = (1 << 4) | /* SSI0129 sync */
rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1;
/* fall through */
case 0x0006: /* SSI0/1/2 */
val1 = rsnd_rdai_is_clk_master(rdai) ?
0xa : 0x5;
if (!val2) /* SSI012 sync */
val1 |= (1 << 4);
}
}
rsnd_mod_bset(mod, SSI_MODE1, mask1, val1);
rsnd_mod_bset(mod, SSI_MODE2, mask2, val2);
return 0;
}
static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
.name = SSIU_NAME,
.init = rsnd_ssiu_init,
};
static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int ret;
ret = rsnd_ssiu_init(mod, io, priv);
if (ret < 0)
return ret;
if (rsnd_get_slot_width(io) >= 6) {
/*
* TDM Extend Mode
* see
* rsnd_ssi_config_init()
*/
rsnd_mod_write(mod, SSI_MODE, 0x1);
}
if (rsnd_ssi_use_busif(io)) {
u32 val = rsnd_get_dalign(mod, io);
rsnd_mod_write(mod, SSI_BUSIF_ADINR,
rsnd_get_adinr_bit(mod, io) |
rsnd_get_adinr_chan(mod, io));
rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
}
return 0;
}
static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
if (!rsnd_ssi_use_busif(io))
return 0;
rsnd_mod_write(mod, SSI_CTRL, 0x1);
if (rsnd_ssi_multi_slaves(io))
rsnd_mod_write(mod, SSI_CONTROL, 0x1);
return 0;
}
static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
if (!rsnd_ssi_use_busif(io))
return 0;
rsnd_mod_write(mod, SSI_CTRL, 0);
if (rsnd_ssi_multi_slaves(io))
rsnd_mod_write(mod, SSI_CONTROL, 0);
return 0;
}
static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
.name = SSIU_NAME,
.init = rsnd_ssiu_init_gen2,
.start = rsnd_ssiu_start_gen2,
.stop = rsnd_ssiu_stop_gen2,
};
static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
{
if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
id = 0;
return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
}
int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *ssi_mod)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
rsnd_mod_confirm_ssi(ssi_mod);
return rsnd_dai_connect(mod, io, mod->type);
}
int rsnd_ssiu_probe(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ssiu *ssiu;
static struct rsnd_mod_ops *ops;
int i, nr, ret;
/* same number to SSI */
nr = priv->ssi_nr;
ssiu = devm_kzalloc(dev, sizeof(*ssiu) * nr, GFP_KERNEL);
if (!ssiu)
return -ENOMEM;
priv->ssiu = ssiu;
priv->ssiu_nr = nr;
if (rsnd_is_gen1(priv))
ops = &rsnd_ssiu_ops_gen1;
else
ops = &rsnd_ssiu_ops_gen2;
for_each_rsnd_ssiu(ssiu, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
ops, NULL, RSND_MOD_SSIU, i);
if (ret)
return ret;
}
return 0;
}
void rsnd_ssiu_remove(struct rsnd_priv *priv)
{
struct rsnd_ssiu *ssiu;
int i;
for_each_rsnd_ssiu(ssiu, priv, i) {
rsnd_mod_quit(rsnd_mod_get(ssiu));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment