Merge remote-tracking branches 'asoc/topic/pxa', 'asoc/topic/qcom', 'asoc/topic/rcar', 'asoc/topic/rk3036' and 'asoc/topic/rockchip' into asoc-next

This commit is contained in:
Mark Brown 2015-12-23 00:23:46 +00:00
29 changed files with 2859 additions and 2297 deletions

View File

@ -0,0 +1,20 @@
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>;
};

View File

@ -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:
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 {
}; };
}; };
}; };
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>;
};
};
};

View File

@ -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"

View File

@ -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 {
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>;
}; };

View File

@ -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
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

View File

@ -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
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

View File

@ -0,0 +1,490 @@
/*
* 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");

View File

@ -0,0 +1,123 @@
/*
* 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

View File

@ -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);

View File

@ -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)

View File

@ -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)
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);
/* Should wait for clear operation to finish */
while (val & I2S_CLR_TXC) {
regmap_read(i2s->regmap, I2S_CLR, &val); regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
/* Should wait for clear operation to finish */ if (!retry) {
while (val) { dev_warn(i2s->dev, "fail to clear\n");
regmap_read(i2s->regmap, I2S_CLR, &val); break;
retry--;
if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
break;
}
} }
} }
} }
@ -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);
/* Should wait for clear operation to finish */
while (val & I2S_CLR_RXC) {
regmap_read(i2s->regmap, I2S_CLR, &val); regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
/* Should wait for clear operation to finish */ if (!retry) {
while (val) { dev_warn(i2s->dev, "fail to clear\n");
regmap_read(i2s->regmap, I2S_CLR, &val); break;
retry--;
if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
break;
}
} }
} }
} }
@ -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,
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)
{ {
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)
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;

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -1,4 +1,4 @@
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

View File

@ -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)
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,
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)
} }
} }
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)
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,
[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,
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,
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,
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);
}
}

171
sound/soc/sh/rcar/cmd.c Normal file
View File

@ -0,0 +1,171 @@
/*
* 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));
}
}

View File

@ -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)
} }
} }
/*
* 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,
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_each_rsnd_dai(rdai, priv, i) {
io = &rdai->playback;
if (mod == io->mod[mod->type])
callback(mod, io);
for (i = 0; i < RSND_MOD_MAX; i++) { io = &rdai->capture;
io = &rdai->playback; if (mod == io->mod[mod->type])
if (mod == io->mod[i]) callback(mod, io);
callback(mod, io);
io = &rdai->capture;
if (mod == io->mod[i])
callback(mod, 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)
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)
*/ */
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)
/* /*
* 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)
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,
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,
} }
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,
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,
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,
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)
return 0; return 0;
} }
static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
u32 tx_mask, u32 rx_mask,
int slots, int slot_width)
{
struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct device *dev = rsnd_priv_to_dev(priv);
switch (slots) {
case 6:
/* TDM Extend Mode */
rsnd_set_slot(rdai, slots, 1);
break;
default:
dev_err(dev, "unsupported TDM slots (%d)\n", slots);
return -EINVAL;
}
return 0;
}
static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
.trigger = rsnd_soc_dai_trigger, .trigger = rsnd_soc_dai_trigger,
.set_fmt = rsnd_soc_dai_set_fmt, .set_fmt = rsnd_soc_dai_set_fmt,
.set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
}; };
#define rsnd_path_add(priv, io, type) \ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
({ \ struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct rsnd_mod *mod; \ struct device_node *node,
int ret = 0; \ struct device_node *playback,
int id = -1; \ struct device_node *capture)
\
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_rdai_to_priv(rdai);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); struct device_node *np;
struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *mod;
struct rsnd_mod *cmd; int i;
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
/* Gen1 is not supported */ if (!node)
if (rsnd_is_gen1(priv))
return; return;
if (!mix && !dvc) i = 0;
return; for_each_child_of_node(node, np) {
mod = mod_get(priv, i);
if (mix) { if (np == playback)
struct rsnd_dai *rdai; rsnd_dai_connect(mod, &rdai->playback, mod->type);
int i; if (np == capture)
u32 path[] = { rsnd_dai_connect(mod, &rdai->capture, mod->type);
[0] = 0, i++;
[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); of_node_put(node);
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 int rsnd_dai_probe(struct rsnd_priv *priv)
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
{ {
struct device_node *dai_node;
struct device_node *dai_np;
struct device_node *playback, *capture;
struct rsnd_dai_stream *io_playback;
struct rsnd_dai_stream *io_capture;
struct snd_soc_dai_driver *rdrv, *drv;
struct rsnd_dai *rdai;
struct device *dev = rsnd_priv_to_dev(priv);
int nr, dai_i, io_i;
int ret; int ret;
/* dai_node = rsnd_dai_of_node(priv);
* 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 */
ret = rsnd_path_add(priv, io, ctu);
if (ret < 0)
return ret;
/* MIX */
ret = rsnd_path_add(priv, io, mix);
if (ret < 0)
return ret;
/* DVC */
ret = rsnd_path_add(priv, io, dvc);
if (ret < 0)
return ret;
return ret;
}
static void rsnd_of_parse_dai(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct device_node *dai_node, *dai_np;
struct device_node *ssi_node, *ssi_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 rsnd_dai_platform_info *dai_info;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr, i;
int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i;
if (!of_data)
return;
dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai");
if (!dai_node)
return;
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,
sizeof(struct rsnd_dai_platform_info) * nr,
GFP_KERNEL);
if (!dai_info) {
dev_err(dev, "dai info allocation error\n");
return;
} }
info->dai_info_nr = nr; rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
info->dai_info = dai_info; rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
if (!rdrv || !rdai) {
ret = -ENOMEM;
goto rsnd_dai_probe_done;
}
ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); priv->rdai_nr = nr;
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); priv->daidrv = rdrv;
ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu"); priv->rdai = rdai;
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;
io_playback = &rdai->playback;
io_capture = &rdai->capture;
for (i = 0;; i++) { snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
playback = of_parse_phandle(dai_np, "playback", i); rdai->priv = priv;
capture = of_parse_phandle(dai_np, "capture", i); 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, dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
const struct rsnd_of_data *of_data, rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
struct rsnd_priv *priv) rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
{
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;
if (!dai_nr) {
dev_err(dev, "no dai\n");
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; rsnd_dai_probe_done:
priv->daidrv = drv; of_node_put(dai_node);
priv->rdai = rdai;
for (i = 0; i < dai_nr; i++) { return ret;
pmod = info->dai_info[i].playback.ssi;
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,
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,
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,
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,
*/ */
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)
} }
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)
{ {
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)
} }
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);

View File

@ -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 {
((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)
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,
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)
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,
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,
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;

View File

@ -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)
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)
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,
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,
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 @@ rsnd_dma_channel_err:
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,
(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)
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,
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,
} }
#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,
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,
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,
* !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,
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,
} }
} }
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)
* 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;
}
dev_dbg(dev, "%s %s[%d] -> %s[%d]\n", dma_mod = rsnd_mod_get(dma);
dma->ops->name,
ret = rsnd_mod_init(priv, dma_mod,
ops, NULL, type, dma_id);
if (ret < 0)
return ERR_PTR(ret);
dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
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;

View File

@ -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 {
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[] = {
"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]);
/* Enable Mute */
if (mute) {
dvucr |= 0x1;
rsnd_mod_write(mod, DVC_ZCMCR, mute);
}
/* General Information */
rsnd_mod_write(mod, DVC_ADINR, adinr);
rsnd_mod_write(mod, DVC_DVUCR, dvucr); 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;
if (dvc->ren.val) {
vrpdr = rsnd_dvc_get_vrpdr(dvc);
vrdbr = rsnd_dvc_get_vrdbr(dvc);
}
/* 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,
{ {
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_dvc_volume_init(io, mod);
rsnd_path_parse(priv, io);
rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io));
/* 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,37 +231,20 @@ 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_dvc_halt(mod);
rsnd_mod_power_off(mod); rsnd_mod_power_off(mod);
return 0; return 0;
} }
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;
}
static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, static int rsnd_dvc_pcm_new(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)
{ {
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,
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,
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,
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)
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,
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;

View File

@ -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,
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,
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,
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,
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,
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,
/* 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,
/* /*
* 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,
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,
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,
* 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,
/* /*
* 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,
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");

View File

@ -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 {
((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, 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_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_mix_volume_init(io, mod);
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
rsnd_path_parse(priv, io);
/* volume step */
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,
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,
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)
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,
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;

View File

@ -1,117 +0,0 @@
/*
* 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

View File

@ -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 @@
* 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 {
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 {
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 {
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,
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,
* 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 {
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 {
#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,
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 {
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 {
}; };
#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 {
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);
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,
/* /*
* 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 {
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 {
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 {
#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 {
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,
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,
/* /*
* 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);

View File

@ -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,14 +113,6 @@ 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) {
ret = snd_soc_dai_set_fmt(dai, dai_props->fmt);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "set_fmt error\n");
goto err;
}
}
if (dai_props->sysclk) { if (dai_props->sysclk) {
ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
if (ret && ret != -ENOTSUPP) { if (ret && ret != -ENOTSUPP) {
@ -126,6 +121,18 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
} }
} }
if (dai_props->slots) {
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) {
dev_err(dai->dev, "set_tdm_slot error\n");
goto err;
}
}
ret = 0; ret = 0;
err: err:
@ -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,
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,
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,
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,
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,
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,
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,
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,
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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

225
sound/soc/sh/rcar/ssiu.c Normal file
View File

@ -0,0 +1,225 @@
/*
* 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));
}
}