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
Examples with soctypes are:
- "renesas,rcar_sound-r8a7778" (R-Car M1A)
- "renesas,rcar_sound-r8a7779" (R-Car H1)
- "renesas,rcar_sound-r8a7790" (R-Car H2)
- "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)
- reg : Should contain the register physical address.
required register is
@ -34,6 +37,8 @@ Required properties:
see below for detail.
- #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
Optional properties:
- #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
- 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:
- compatible : "renesas,rsrc-card,<board>"
Examples with soctypes are:
- compatible : "renesas,rsrc-card{,<board>}"
Examples with boards are:
- "renesas,rsrc-card"
- "renesas,rsrc-card,lager"
- "renesas,rsrc-card,koelsch"

View file

@ -19,6 +19,7 @@ Required properties:
- clock-names: should contain followings:
- "i2s_hclk": clock for I2S BUS
- "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.
Example for rk3288 I2S controller:
@ -31,5 +32,6 @@ i2s@ff890000 {
dma-names = "tx", "rx";
clock-names = "i2s_hclk", "i2s_clk";
clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
rockchip,playback-channels = <8>;
rockchip,capture-channels = <2>;
};

View file

@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_GTM601
select SND_SOC_HDAC_HDMI
select SND_SOC_ICS43432
select SND_SOC_INNO_RK3036
select SND_SOC_ISABELLE if I2C
select SND_SOC_JZ4740_CODEC
select SND_SOC_LM4857 if I2C
@ -492,6 +493,9 @@ config SND_SOC_HDAC_HDMI
config SND_SOC_ICS43432
tristate
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
config SND_SOC_ISABELLE
tristate

View file

@ -63,6 +63,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
snd-soc-gtm601-objs := gtm601.o
snd-soc-hdac-hdmi-objs := hdac_hdmi.o
snd-soc-ics43432-objs := ics43432.o
snd-soc-inno-rk3036-objs := inno_rk3036.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.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_HDAC_HDMI) += snd-soc-hdac-hdmi.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_JZ4740_CODEC) += snd-soc-jz4740-codec.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;
sspa_mclk = params_rate(params) * 64;
}
sspa_div = freq_out;
do_div(sspa_div, sspa_mclk);
sspa_div = freq_out / sspa_mclk;
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);

View file

@ -355,6 +355,7 @@ static struct regmap_config lpass_cpu_regmap_config = {
.readable_reg = lpass_cpu_regmap_readable,
.volatile_reg = lpass_cpu_regmap_volatile,
.cache_type = REGCACHE_FLAT,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)

View file

@ -34,13 +34,7 @@ struct rk_i2s_dev {
struct regmap *regmap;
/*
* 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;
bool is_master_mode;
};
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);
regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
i2s->tx_start = true;
I2S_XFER_TXS_START,
I2S_XFER_TXS_START);
} else {
i2s->tx_start = false;
regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
if (!i2s->rx_start) {
regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP);
regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START,
I2S_XFER_TXS_STOP);
regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC | I2S_CLR_RXC,
I2S_CLR_TXC | I2S_CLR_RXC);
regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC,
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);
/* Should wait for clear operation to finish */
while (val) {
regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
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);
regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
i2s->rx_start = true;
I2S_XFER_RXS_START,
I2S_XFER_RXS_START);
} else {
i2s->rx_start = false;
regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
if (!i2s->tx_start) {
regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START |
I2S_XFER_RXS_START,
I2S_XFER_TXS_STOP |
I2S_XFER_RXS_STOP);
regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_RXS_START,
I2S_XFER_RXS_STOP);
regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC | I2S_CLR_RXC,
I2S_CLR_TXC | I2S_CLR_RXC);
regmap_update_bits(i2s->regmap, I2S_CLR,
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);
/* Should wait for clear operation to finish */
while (val) {
regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
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:
/* Set source clock in Master mode */
val = I2S_CKR_MSS_MASTER;
i2s->is_master_mode = true;
break;
case SND_SOC_DAIFMT_CBM_CFM:
val = I2S_CKR_MSS_SLAVE;
i2s->is_master_mode = false;
break;
default:
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 snd_soc_pcm_runtime *rtd = substream->private_data;
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)) {
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 rk_i2s_dev *i2s;
struct snd_soc_dai_driver *soc_dai;
struct resource *res;
void __iomem *regs;
int ret;
@ -511,17 +512,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
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 (val >= 2 && val <= 8)
rockchip_i2s_dai.capture.channels_max = val;
else
rockchip_i2s_dai.capture.channels_max = 2;
soc_dai->capture.channels_max = val;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component,
&rockchip_i2s_dai, 1);
soc_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI\n");
goto err_suspend;

View file

@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:

View file

@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:

View file

@ -36,7 +36,6 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on DMA_OF
depends on COMMON_CLK
select SND_SIMPLE_CARD
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
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)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
int id = rsnd_mod_id(mod);
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
int id = rsnd_mod_id(ssi_mod);
int ws = id;
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;
}
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_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_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;
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);
}
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)
{
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)
{
/*
* "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, 0);
rsnd_adg_set_ssi_clk(ssi_mod, 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 device *dev = rsnd_priv_to_dev(priv);
struct clk *clk;
@ -394,14 +328,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
found_clock:
/*
* This "mod" = "ssi" here.
* we can get "ssi id" from mod
*/
rsnd_adg_set_ssi_clk(mod, data);
rsnd_adg_set_ssi_clk(ssi_mod, data);
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);
return 0;
@ -418,15 +348,20 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
[CLKC] = "clk_c",
[CLKI] = "clk_i",
};
int i;
int i, ret;
for (i = 0; i < CLKMAX; i++) {
clk = devm_clk_get(dev, clk_name[i]);
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));
}
}
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_node *np = dev->of_node;
u32 ckr, rbgx, rbga, rbgb;
u32 rate, req_rate, div;
u32 rate, req_rate = 0, div;
uint32_t count = 0;
unsigned long req_48kHz_rate, req_441kHz_rate;
int i;
@ -572,9 +507,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
ckr, rbga, rbgb);
}
int rsnd_adg_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
int rsnd_adg_probe(struct rsnd_priv *priv)
{
struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv);
@ -600,3 +533,14 @@ int rsnd_adg_probe(struct platform_device *pdev,
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_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[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
{ .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
{ .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
{ .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */
{},
};
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)
{
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)
{
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_dai_stream *io;
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->playback;
if (mod == io->mod[i])
callback(mod, io);
io = &rdai->capture;
if (mod == io->mod[i])
callback(mod, io);
}
io = &rdai->capture;
if (mod == io->mod[mod->type])
callback(mod, io);
}
}
@ -214,6 +191,43 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
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
*/
@ -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 snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 adinr = runtime->channels;
switch (runtime->sample_bits) {
case 16:
adinr |= (8 << 16);
break;
return 8 << 16;
case 32:
adinr |= (0 << 16);
break;
default:
dev_warn(dev, "not supported sample bits\n");
return 0;
return 0 << 16;
}
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)
@ -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)
{
struct rsnd_mod *src = rsnd_io_to_mod_src(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);
u32 val = 0x76543210;
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;
val = val & mask;
@ -300,20 +319,22 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
/*
* 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_mod *mod = (io)->mod[idx]; \
struct device *dev = rsnd_priv_to_dev(priv); \
u32 *status = (io)->mod_status + idx; \
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); \
int ret = 0; \
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
mod->status = (mod->status & ~mask) + \
*status = (*status & ~mask) + \
(add << __rsnd_mod_shift_##func); \
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), \
mod->status, call ? #func : ""); \
*status, call ? #func : ""); \
if (call) \
ret = (mod)->ops->func(mod, io, param); \
ret; \
@ -327,13 +348,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
mod = (io)->mod[i]; \
if (!mod) \
continue; \
ret |= rsnd_mod_call(mod, io, fn, param); \
ret |= rsnd_mod_call(i, io, fn, param); \
} \
ret; \
})
static int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type)
{
struct rsnd_priv *priv;
struct device *dev;
@ -341,10 +363,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
if (!mod)
return -EIO;
if (io->mod[type])
return -EINVAL;
priv = rsnd_mod_to_priv(mod);
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",
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,
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)
@ -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_dai *rdai = rsnd_dai_to_rdai(dai);
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;
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:
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);
if (ret < 0)
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_platform_call(priv, dai, stop, ssi_id);
rsnd_dai_stream_quit(io);
break;
default:
@ -567,332 +586,157 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
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 = {
.trigger = rsnd_soc_dai_trigger,
.set_fmt = rsnd_soc_dai_set_fmt,
.set_tdm_slot = rsnd_soc_set_dai_tdm_slot,
};
#define rsnd_path_add(priv, io, type) \
({ \
struct rsnd_mod *mod; \
int ret = 0; \
int id = -1; \
\
if (rsnd_is_enable_path(io, type)) { \
id = rsnd_info_id(priv, io, type); \
if (id >= 0) { \
mod = rsnd_##type##_mod_get(priv, id); \
ret = rsnd_dai_connect(mod, io); \
} \
} \
ret; \
})
#define rsnd_path_remove(priv, io, type) \
{ \
struct rsnd_mod *mod; \
int id = -1; \
\
if (rsnd_is_enable_path(io, type)) { \
id = rsnd_info_id(priv, io, type); \
if (id >= 0) { \
mod = rsnd_##type##_mod_get(priv, id); \
rsnd_dai_disconnect(mod, io); \
} \
} \
}
void rsnd_path_parse(struct rsnd_priv *priv,
struct rsnd_dai_stream *io)
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)
{
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 rsnd_mod *cmd;
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
struct device_node *np;
struct rsnd_mod *mod;
int i;
/* Gen1 is not supported */
if (rsnd_is_gen1(priv))
if (!node)
return;
if (!mix && !dvc)
return;
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)];
}
/*
* 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;
i = 0;
for_each_child_of_node(node, np) {
mod = mod_get(priv, i);
if (np == playback)
rsnd_dai_connect(mod, &rdai->playback, mod->type);
if (np == capture)
rsnd_dai_connect(mod, &rdai->capture, mod->type);
i++;
}
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
rsnd_mod_write(cmd, CMD_CTRL, 0x10);
of_node_put(node);
}
static int rsnd_path_init(struct rsnd_priv *priv,
struct rsnd_dai *rdai,
struct rsnd_dai_stream *io)
static int rsnd_dai_probe(struct rsnd_priv *priv)
{
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;
/*
* 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;
dai_node = rsnd_dai_of_node(priv);
nr = of_get_child_count(dai_node);
if (!nr)
return;
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;
if (!nr) {
ret = -EINVAL;
goto rsnd_dai_probe_done;
}
info->dai_info_nr = nr;
info->dai_info = dai_info;
rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
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");
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
#define mod_parse(name) \
if (name##_node) { \
struct rsnd_##name##_platform_info *name##_info; \
\
name##_i = 0; \
for_each_child_of_node(name##_node, name##_np) { \
name##_info = info->name##_info + name##_i; \
\
if (name##_np == playback) \
dai_info->playback.name = name##_info; \
if (name##_np == capture) \
dai_info->capture.name = name##_info; \
\
name##_i++; \
} \
}
priv->rdai_nr = nr;
priv->daidrv = rdrv;
priv->rdai = rdai;
/*
* parse all dai
*/
dai_i = 0;
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);
capture = of_parse_phandle(dai_np, "capture", i);
rdai->priv = priv;
drv->name = rdai->name;
drv->ops = &rsnd_soc_dai_ops;
snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
"DAI%d Playback", dai_i);
drv->playback.rates = RSND_RATES;
drv->playback.formats = RSND_FMTS;
drv->playback.channels_min = 2;
drv->playback.channels_max = 6;
drv->playback.stream_name = rdai->playback.name;
snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
"DAI%d Capture", dai_i);
drv->capture.rates = RSND_RATES;
drv->capture.formats = RSND_FMTS;
drv->capture.channels_min = 2;
drv->capture.channels_max = 6;
drv->capture.stream_name = rdai->capture.name;
rdai->playback.rdai = rdai;
rdai->capture.rdai = rdai;
rsnd_set_slot(rdai, 2, 1); /* default */
for (io_i = 0;; io_i++) {
playback = of_parse_phandle(dai_np, "playback", io_i);
capture = of_parse_phandle(dai_np, "capture", io_i);
if (!playback && !capture)
break;
mod_parse(ssi);
mod_parse(src);
mod_parse(ctu);
mod_parse(mix);
mod_parse(dvc);
rsnd_parse_connect_ssi(rdai, playback, capture);
rsnd_parse_connect_src(rdai, playback, capture);
rsnd_parse_connect_ctu(rdai, playback, capture);
rsnd_parse_connect_mix(rdai, playback, capture);
rsnd_parse_connect_dvc(rdai, playback, capture);
of_node_put(playback);
of_node_put(capture);
}
dai_i++;
}
}
static int rsnd_dai_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
{
struct snd_soc_dai_driver *drv;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct rsnd_dai *rdai;
struct rsnd_ssi_platform_info *pmod, *cmod;
struct device *dev = rsnd_priv_to_dev(priv);
int dai_nr;
int i;
rsnd_of_parse_dai(pdev, of_data, priv);
dai_nr = info->dai_info_nr;
if (!dai_nr) {
dev_err(dev, "no dai\n");
return -EIO;
dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
}
drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL);
rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL);
if (!drv || !rdai) {
dev_err(dev, "dai allocate failed\n");
return -ENOMEM;
}
ret = 0;
priv->rdai_nr = dai_nr;
priv->daidrv = drv;
priv->rdai = rdai;
rsnd_dai_probe_done:
of_node_put(dai_node);
for (i = 0; i < dai_nr; i++) {
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;
return ret;
}
/*
@ -1076,10 +920,14 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
void (*update)(struct rsnd_dai_stream *io,
struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg,
int ch_size,
u32 max)
{
if (ch_size > RSND_DVC_CHANNELS)
return -EINVAL;
_cfg->cfg.max = max;
_cfg->cfg.size = RSND_DVC_CHANNELS;
_cfg->cfg.size = ch_size;
_cfg->cfg.val = _cfg->val;
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);
if (ret == -EAGAIN) {
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
int i;
/*
* Fallback to PIO mode
*/
@ -1174,10 +1025,12 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *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);
rsnd_path_remove(priv, io, dvc);
for (i = 0; i < RSND_MOD_MAX; i++)
rsnd_dai_disconnect((io)->mod[i], io, i);
rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
/*
* fallback
@ -1199,33 +1052,25 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
*/
static int rsnd_probe(struct platform_device *pdev)
{
struct rcar_snd_info *info;
struct rsnd_priv *priv;
struct device *dev = &pdev->dev;
struct rsnd_dai *rdai;
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 platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv) = {
int (*probe_func[])(struct rsnd_priv *priv) = {
rsnd_gen_probe,
rsnd_dma_probe,
rsnd_ssi_probe,
rsnd_ssiu_probe,
rsnd_src_probe,
rsnd_ctu_probe,
rsnd_mix_probe,
rsnd_dvc_probe,
rsnd_cmd_probe,
rsnd_adg_probe,
rsnd_dai_probe,
};
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
*/
@ -1236,14 +1081,14 @@ static int rsnd_probe(struct platform_device *pdev)
}
priv->pdev = pdev;
priv->info = info;
priv->flags = (unsigned long)of_id->data;
spin_lock_init(&priv->lock);
/*
* init each module
*/
for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
ret = probe_func[i](pdev, of_data, priv);
ret = probe_func[i](priv);
if (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_dai *rdai;
void (*remove_func[])(struct platform_device *pdev,
struct rsnd_priv *priv) = {
void (*remove_func[])(struct rsnd_priv *priv) = {
rsnd_ssi_remove,
rsnd_ssiu_remove,
rsnd_src_remove,
rsnd_ctu_remove,
rsnd_mix_remove,
rsnd_dvc_remove,
rsnd_cmd_remove,
rsnd_adg_remove,
};
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++)
remove_func[i](pdev, priv);
remove_func[i](priv);
snd_soc_unregister_component(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev);

View file

@ -13,7 +13,6 @@
#define CTU_NAME "ctu"
struct rsnd_ctu {
struct rsnd_ctu_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
};
@ -24,6 +23,7 @@ struct rsnd_ctu {
((pos) = (struct rsnd_ctu *)(priv)->ctu + 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_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
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);
}
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,
struct rsnd_dai_stream *io,
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 = {
.name = CTU_NAME,
.probe = rsnd_ctu_probe_,
.init = rsnd_ctu_init,
.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)))
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,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
int rsnd_ctu_probe(struct rsnd_priv *priv)
{
struct device_node *node;
struct rsnd_ctu_platform_info *ctu_info;
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_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ctu *ctu;
struct clk *clk;
@ -121,20 +91,30 @@ int rsnd_ctu_probe(struct platform_device *pdev,
if (rsnd_is_gen1(priv))
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;
if (!nr)
return 0;
nr = of_get_child_count(node);
if (!nr) {
ret = -EINVAL;
goto rsnd_ctu_probe_done;
}
ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
if (!ctu)
return -ENOMEM;
if (!ctu) {
ret = -ENOMEM;
goto rsnd_ctu_probe_done;
}
priv->ctu_nr = nr;
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
* CTU10, CTU11, CTU12, CTU13 => CTU1
@ -143,22 +123,27 @@ int rsnd_ctu_probe(struct platform_device *pdev,
CTU_NAME, i / 4);
clk = devm_clk_get(dev, name);
if (IS_ERR(clk))
return PTR_ERR(clk);
ctu->info = &info->ctu_info[i];
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto rsnd_ctu_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
clk, RSND_MOD_CTU, i);
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,
struct rsnd_priv *priv)
void rsnd_ctu_remove(struct rsnd_priv *priv)
{
struct rsnd_ctu *ctu;
int i;

View file

@ -22,21 +22,36 @@
/* PDMACHCR */
#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 {
void __iomem *base;
int dmaen_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_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
@ -77,18 +92,24 @@ static void rsnd_dmaen_complete(void *data)
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);
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_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
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) {
dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
return;
return -EIO;
}
desc->callback = rsnd_dmaen_complete;
desc->callback_param = mod;
desc->callback_param = rsnd_mod_get(dma);
if (dmaengine_submit(desc) < 0) {
dev_err(dev, "dmaengine_submit() fail\n");
return;
return -EIO;
}
dma_async_issue_pending(dmaen->chan);
return 0;
}
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);
}
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_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_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 dma_slave_config cfg = {};
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.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
dev_dbg(dev, "%s %pad -> %pad\n",
dma->ops->name,
dev_dbg(dev, "%s[%d] %pad -> %pad\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
&cfg.src_addr, &cfg.dst_addr);
ret = dmaengine_slave_config(dmaen->chan, &cfg);
if (ret < 0)
goto rsnd_dma_init_err;
goto rsnd_dma_attach_err;
dmac->dmaen_num++;
return 0;
rsnd_dma_init_err:
rsnd_dma_quit(io, dma);
rsnd_dma_attach_err:
rsnd_dmaen_remove(mod, io, priv);
rsnd_dma_channel_err:
/*
@ -214,22 +256,11 @@ rsnd_dma_channel_err:
return -EAGAIN;
}
static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
{
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 = {
static struct rsnd_mod_ops rsnd_dmaen_ops = {
.name = "audmac",
.start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop,
.init = rsnd_dmaen_init,
.quit = rsnd_dmaen_quit,
.remove = rsnd_dmaen_remove,
};
/*
@ -307,7 +338,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
(0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
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_dma_ctrl *dmac = rsnd_priv_to_dmac(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)
{
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_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
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;
rsnd_dmapp_write(dma, 0, PDMACHCR);
for (i = 0; i < 1024; i++) {
if (0 == rsnd_dmapp_read(dma, PDMACHCR))
return;
return 0;
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);
rsnd_dmapp_write(dma, dma->src_addr, PDMASAR);
rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR);
rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR);
return 0;
}
static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
struct rsnd_dma *dma, int id,
struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
{
struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
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++;
rsnd_dmapp_stop(io, dma);
dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
return 0;
}
static struct rsnd_dma_ops rsnd_dmapp_ops = {
static struct rsnd_mod_ops rsnd_dmapp_ops = {
.name = "audmac-pp",
.start = rsnd_dmapp_start,
.stop = rsnd_dmapp_stop,
.init = rsnd_dmapp_init,
.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 */
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,
int is_play,
struct rsnd_mod **mod_from,
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 *src = rsnd_io_to_mod_src(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_priv *priv = rsnd_mod_to_priv(this);
struct device *dev = rsnd_priv_to_dev(priv);
int nr, i;
int nr, i, idx;
if (!ssi)
return;
@ -542,23 +579,24 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
mod_start = (is_play) ? NULL : ssi;
mod_end = (is_play) ? ssi : NULL;
mod[0] = mod_start;
idx = 0;
mod[idx++] = mod_start;
for (i = 1; i < nr; i++) {
if (src) {
mod[i] = src;
mod[idx++] = src;
src = NULL;
} else if (ctu) {
mod[i] = ctu;
mod[idx++] = ctu;
ctu = NULL;
} else if (mix) {
mod[i] = mix;
mod[idx++] = mix;
mix = NULL;
} else if (dvc) {
mod[i] = dvc;
mod[idx++] = dvc;
dvc = NULL;
}
}
mod[i] = mod_end;
mod[idx] = mod_end;
/*
* | SSI | SRC |
@ -567,8 +605,8 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
* !is_play | * | o |
*/
if ((this == ssi) == (is_play)) {
*mod_from = mod[nr - 1];
*mod_to = mod[nr];
*mod_from = mod[idx - 1];
*mod_to = mod[idx];
} else {
*mod_from = mod[0];
*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",
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",
rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]),
(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)
{
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 *rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, int id)
{
struct rsnd_mod *dma_mod;
struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL;
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
struct rsnd_dma *dma;
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 ret, dma_id;
/*
* 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()
*/
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->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
/* for Gen2 */
if (mod_from && mod_to)
dma->ops = &rsnd_dmapp_ops;
else
dma->ops = &rsnd_dmaen_ops;
if (mod_from && mod_to) {
ops = &rsnd_dmapp_ops;
attach = rsnd_dmapp_attach;
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 */
if (rsnd_is_gen1(priv))
dma->ops = &rsnd_dmaen_ops;
if (rsnd_is_gen1(priv)) {
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->ops->name,
dma_mod = rsnd_mod_get(dma);
ret = rsnd_mod_init(priv, dma_mod,
ops, NULL, type, dma_id);
if (ret < 0)
return ERR_PTR(ret);
dev_dbg(dev, "%s[%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_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,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
int rsnd_dma_probe(struct rsnd_priv *priv)
{
struct platform_device *pdev = rsnd_priv_to_pdev(priv);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dma_ctrl *dmac;
struct resource *res;

View file

@ -15,7 +15,6 @@
#define DVC_NAME "dvc"
struct rsnd_dvc {
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
struct rsnd_kctrl_cfg_m volume;
struct rsnd_kctrl_cfg_m mute;
@ -24,6 +23,7 @@ struct rsnd_dvc {
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_of_node(priv) \
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 */
};
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, 1);
}
#define rsnd_dvc_initialize_lock(mod) __rsnd_dvc_initialize_lock(mod, 1)
#define rsnd_dvc_initialize_unlock(mod) __rsnd_dvc_initialize_lock(mod, 0)
static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable)
static void rsnd_dvc_halt(struct rsnd_mod *mod)
{
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,
struct rsnd_mod *mod)
#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val)
#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);
u32 val[RSND_DVC_CHANNELS];
u32 dvucr = 0;
u32 mute = 0;
int i;
for (i = 0; i < dvc->mute.cfg.size; i++)
mute |= (!!dvc->mute.cfg.val[i]) << i;
/* Enable Ramp */
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 */
rsnd_mod_write(mod, DVC_DVUER, 0);
/* Enable Digital Volume */
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 */
if (dvc->ren.val) {
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 !!
* use scale-downed Digital Volume
* as Volume Ramp
* 7F FFFF -> 3FF
*/
rsnd_mod_write(mod, DVC_VRDBR,
0x3ff - (dvc->volume.val[0] >> 13));
} else {
for (i = 0; i < RSND_DVC_CHANNELS; i++)
val[i] = dvc->volume.val[i];
vrctr = 0xff;
vrpdr = rsnd_dvc_get_vrpdr(dvc);
vrdbr = rsnd_dvc_get_vrdbr(dvc);
}
/* Enable Digital Volume */
dvucr |= 0x100;
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);
}
/* Initialize operation */
rsnd_mod_write(mod, DVC_DVUIR, 1);
/* General Information */
rsnd_mod_write(mod, DVC_ADINR, adinr);
rsnd_mod_write(mod, DVC_DVUCR, dvucr);
/* Volume Ramp Parameter */
rsnd_mod_write(mod, DVC_VRCTR, vrctr);
rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
/* Digital Volume Function Parameter */
rsnd_dvc_volume_parameter(io, mod);
/* cancel operation */
rsnd_mod_write(mod, DVC_DVUIR, 0);
}
static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 zcmcr = 0;
u32 vrpdr = 0;
u32 vrdbr = 0;
int i;
for (i = 0; i < dvc->mute.cfg.size; i++)
zcmcr |= (!!dvc->mute.cfg.val[i]) << i;
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 */
rsnd_mod_write(mod, DVC_DVUER, 1);
}
static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
static int rsnd_dvc_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_dvc_remove_(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
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_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_adg_set_cmd_timsel_gen2(mod, io);
return 0;
}
@ -175,37 +231,20 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_dvc_halt(mod);
rsnd_mod_power_off(mod);
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,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
int is_play = rsnd_io_is_play(io);
int slots = rsnd_get_slot(io);
int ret;
/* Volume */
@ -213,7 +252,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
is_play ?
"DVC Out Playback Volume" : "DVC In Capture Volume",
rsnd_dvc_volume_update,
&dvc->volume, 0x00800000 - 1);
&dvc->volume, slots,
0x00800000 - 1);
if (ret < 0)
return ret;
@ -222,7 +262,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
is_play ?
"DVC Out Mute Switch" : "DVC In Mute Switch",
rsnd_dvc_volume_update,
&dvc->mute, 1);
&dvc->mute, slots,
1);
if (ret < 0)
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 = {
.name = DVC_NAME,
.dma_req = rsnd_dvc_dma_req,
.remove = rsnd_dvc_remove_gen2,
.probe = rsnd_dvc_probe_,
.remove = rsnd_dvc_remove_,
.init = rsnd_dvc_init,
.quit = rsnd_dvc_quit,
.start = rsnd_dvc_start,
.stop = rsnd_dvc_stop,
.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)))
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,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
int rsnd_dvc_probe(struct rsnd_priv *priv)
{
struct device_node *node;
struct rsnd_dvc_platform_info *dvc_info;
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_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dvc *dvc;
struct clk *clk;
@ -336,40 +339,54 @@ int rsnd_dvc_probe(struct platform_device *pdev,
if (rsnd_is_gen1(priv))
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;
if (!nr)
return 0;
nr = of_get_child_count(node);
if (!nr) {
ret = -EINVAL;
goto rsnd_dvc_probe_done;
}
dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
if (!dvc)
return -ENOMEM;
if (!dvc) {
ret = -ENOMEM;
goto rsnd_dvc_probe_done;
}
priv->dvc_nr = nr;
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",
DVC_NAME, i);
clk = devm_clk_get(dev, name);
if (IS_ERR(clk))
return PTR_ERR(clk);
dvc->info = &info->dvc_info[i];
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto rsnd_dvc_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
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,
struct rsnd_priv *priv)
void rsnd_dvc_remove(struct rsnd_priv *priv)
{
struct rsnd_dvc *dvc;
int i;

View file

@ -31,29 +31,33 @@ struct rsnd_gen {
/* RSND_REG_MAX base */
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_reg_name(gen, id) ((gen)->reg_name[id])
struct rsnd_regmap_field_conf {
int idx;
unsigned int reg_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, \
.reg_offset = offset, \
.id_offset = _id_offset, \
.reg_name = n, \
}
/* single address mapping */
#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 */
#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
@ -83,8 +87,9 @@ u32 rsnd_read(struct rsnd_priv *priv,
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
rsnd_reg_name(gen, reg), reg, val);
return val;
}
@ -99,10 +104,11 @@ void rsnd_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
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);
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,
@ -115,10 +121,11 @@ void rsnd_force_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
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);
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,
@ -130,11 +137,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
if (!rsnd_is_accessible_reg(priv, gen, reg))
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),
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)
@ -150,7 +159,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
int id_size,
int reg_id,
const char *name,
struct rsnd_regmap_field_conf *conf,
const struct rsnd_regmap_field_conf *conf,
int conf_size)
{
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 */
gen->regs[conf[i].idx] = regs;
gen->reg_name[conf[i].idx] = conf[i].reg_name;
}
return 0;
@ -211,25 +221,31 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
/*
* Gen2
*/
static int rsnd_gen2_probe(struct platform_device *pdev,
struct rsnd_priv *priv)
static int rsnd_gen2_probe(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_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 */
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 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_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_ROUTE_MODE0, 0xc, 0x20),
RSND_GEN_M_REG(SRC_CTRL, 0x10, 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_CTRL, 0x190, 0x20),
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_VOL0R, 0xe28, 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),
};
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(BRRB, 0x04),
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(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(SSISR, 0x04, 0x40),
RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
@ -317,65 +339,30 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
* Gen1
*/
static int rsnd_gen1_probe(struct platform_device *pdev,
struct rsnd_priv *priv)
static int rsnd_gen1_probe(struct rsnd_priv *priv)
{
struct rsnd_regmap_field_conf conf_sru[] = {
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[] = {
const static struct rsnd_regmap_field_conf conf_adg[] = {
RSND_GEN_S_REG(BRRA, 0x00),
RSND_GEN_S_REG(BRRB, 0x04),
RSND_GEN_S_REG(SSICKR, 0x08),
RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
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(SSISR, 0x04, 0x40),
RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
};
int ret_sru;
int ret_adg;
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_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
if (ret_sru < 0 ||
ret_adg < 0 ||
if (ret_adg < 0 ||
ret_ssi < 0)
return ret_sru | ret_adg | ret_ssi;
return ret_adg | ret_ssi;
return 0;
}
@ -383,28 +370,12 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
/*
* Gen
*/
static void rsnd_of_parse_gen(struct platform_device *pdev,
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)
int rsnd_gen_probe(struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen;
int ret;
rsnd_of_parse_gen(pdev, of_data, priv);
gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
if (!gen) {
dev_err(dev, "GEN allocate failed\n");
@ -415,9 +386,9 @@ int rsnd_gen_probe(struct platform_device *pdev,
ret = -ENODEV;
if (rsnd_is_gen1(priv))
ret = rsnd_gen1_probe(pdev, priv);
ret = rsnd_gen1_probe(priv);
else if (rsnd_is_gen2(priv))
ret = rsnd_gen2_probe(pdev, priv);
ret = rsnd_gen2_probe(priv);
if (ret < 0)
dev_err(dev, "unknown generation R-Car sound device\n");

View file

@ -13,10 +13,10 @@
#define MIX_NAME "mix"
struct rsnd_mix {
struct rsnd_mix_platform_info *info; /* rcar_snd.h */
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 for_each_rsnd_mix(pos, priv, i) \
for ((i) = 0; \
@ -24,58 +24,77 @@ struct rsnd_mix {
((pos) = (struct rsnd_mix *)(priv)->mix + i); \
i++)
static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
static void rsnd_mix_activation(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, MIX_SWRSR, 0);
rsnd_mod_write(mod, MIX_SWRSR, 1);
}
#define rsnd_mix_initialize_lock(mod) __rsnd_mix_initialize_lock(mod, 1)
#define rsnd_mix_initialize_unlock(mod) __rsnd_mix_initialize_lock(mod, 0)
static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
static void rsnd_mix_halt(struct rsnd_mod *mod)
{
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,
struct rsnd_mod *mod)
{
/* Disable MIX dB setting */
rsnd_mod_write(mod, MIX_MDBER, 0);
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);
/* common volume parameter */
rsnd_mix_volume_parameter(io, mod);
/* Enable MIX dB setting */
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,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_power_on(mod);
rsnd_mix_soft_reset(mod);
rsnd_mix_activation(mod);
rsnd_mix_initialize_lock(mod);
rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
rsnd_path_parse(priv, io);
/* volume step */
rsnd_mod_write(mod, MIX_MIXMR, 0);
rsnd_mod_write(mod, MIX_MVPDR, 0);
rsnd_mix_volume_init(io, mod);
rsnd_mix_volume_update(io, mod);
rsnd_mix_initialize_unlock(mod);
return 0;
}
@ -83,6 +102,8 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mix_halt(mod);
rsnd_mod_power_off(mod);
return 0;
@ -90,6 +111,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
static struct rsnd_mod_ops rsnd_mix_ops = {
.name = MIX_NAME,
.probe = rsnd_mix_probe_,
.init = rsnd_mix_init,
.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)))
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,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
int rsnd_mix_probe(struct rsnd_priv *priv)
{
struct device_node *node;
struct rsnd_mix_platform_info *mix_info;
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_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mix *mix;
struct clk *clk;
@ -154,40 +138,54 @@ int rsnd_mix_probe(struct platform_device *pdev,
if (rsnd_is_gen1(priv))
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;
if (!nr)
return 0;
nr = of_get_child_count(node);
if (!nr) {
ret = -EINVAL;
goto rsnd_mix_probe_done;
}
mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
if (!mix)
return -ENOMEM;
if (!mix) {
ret = -ENOMEM;
goto rsnd_mix_probe_done;
}
priv->mix_nr = nr;
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",
MIX_NAME, i);
clk = devm_clk_get(dev, name);
if (IS_ERR(clk))
return PTR_ERR(clk);
mix->info = &info->mix_info[i];
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto rsnd_mix_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
clk, RSND_MOD_MIX, i);
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,
struct rsnd_priv *priv)
void rsnd_mix_remove(struct rsnd_priv *priv)
{
struct rsnd_mix *mix;
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/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
@ -34,10 +43,19 @@
* see gen1/gen2 for detail
*/
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_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_SWRSR,
RSND_REG_SRC_SRCIR,
@ -45,9 +63,29 @@ enum rsnd_reg {
RSND_REG_SRC_IFSCR,
RSND_REG_SRC_IFSVR,
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_STATUS1, /* Gen2 only */
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_CMDOUT_TIMSEL, /* Gen2 only */
RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR,
RSND_REG_MIX_SWRSR,
@ -67,14 +105,25 @@ enum rsnd_reg {
RSND_REG_DVC_ZCMCR,
RSND_REG_DVC_VOL0R,
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_VRCTR, /* Gen2 only */
RSND_REG_DVC_VRPDR, /* Gen2 only */
RSND_REG_DVC_VRDBR, /* Gen2 only */
/* ADG */
RSND_REG_BRRA,
RSND_REG_BRRB,
RSND_REG_SSICKR,
RSND_REG_DIV_EN, /* Gen2 only */
RSND_REG_AUDIO_CLK_SEL0,
RSND_REG_AUDIO_CLK_SEL1,
RSND_REG_AUDIO_CLK_SEL2, /* Gen2 only */
/* SSI */
RSND_REG_SSICR,
@ -83,83 +132,9 @@ enum rsnd_reg {
RSND_REG_SSIRDR,
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,
};
/* 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_mod;
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_chan(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
*/
struct rsnd_dma;
struct rsnd_dmaen {
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 rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
struct rsnd_mod *mod, int id);
int rsnd_dma_probe(struct rsnd_priv *priv);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
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
*/
enum rsnd_mod_type {
RSND_MOD_DVC = 0,
RSND_MOD_AUDMAPP,
RSND_MOD_AUDMA,
RSND_MOD_DVC,
RSND_MOD_MIX,
RSND_MOD_CTU,
RSND_MOD_CMD,
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_SSIU,
RSND_MOD_MAX,
};
@ -278,10 +231,8 @@ struct rsnd_mod {
int id;
enum rsnd_mod_type type;
struct rsnd_mod_ops *ops;
struct rsnd_dma dma;
struct rsnd_priv *priv;
struct clk *clk;
u32 status;
};
/*
* status
@ -328,7 +279,6 @@ struct rsnd_mod {
#define __rsnd_mod_call_hw_params 0
#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_power_on(mod) clk_enable((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 (*callback)(struct rsnd_mod *mod,
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
@ -358,6 +319,7 @@ struct rsnd_dai_stream {
struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai;
u32 mod_status[RSND_MOD_MAX];
int byte_pos;
int period_pos;
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_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_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_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_priv(io) (rsnd_rdai_to_priv(rsnd_io_to_rdai(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_priv *priv;
int slots;
int slots_num;
unsigned int clk_master:1;
unsigned int bit_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);
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_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
*/
int rsnd_gen_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
int rsnd_gen_probe(struct rsnd_priv *priv);
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg);
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
*/
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_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
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_probe(struct rsnd_priv *priv);
void rsnd_adg_remove(struct rsnd_priv *priv);
int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
unsigned int src_rate,
@ -442,15 +404,14 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
/*
* R-Car sound priv
*/
struct rsnd_of_data {
u32 flags;
};
struct rsnd_priv {
struct platform_device *pdev;
struct rcar_snd_info *info;
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()
@ -473,6 +434,12 @@ struct rsnd_priv {
void *ssi;
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()
*/
@ -497,6 +464,12 @@ struct rsnd_priv {
void *dvc;
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()
*/
@ -507,7 +480,9 @@ struct rsnd_priv {
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
#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
@ -523,7 +498,7 @@ struct rsnd_kctrl_cfg {
struct snd_kcontrol *kctrl;
};
#define RSND_DVC_CHANNELS 2
#define RSND_DVC_CHANNELS 8
struct rsnd_kctrl_cfg_m {
struct rsnd_kctrl_cfg cfg;
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,
struct rsnd_mod *mod),
struct rsnd_kctrl_cfg_m *_cfg,
int ch_size,
u32 max);
int rsnd_kctrl_new_s(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
@ -566,70 +542,93 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod,
/*
* R-Car SSI
*/
int rsnd_ssi_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
void rsnd_ssi_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
int rsnd_ssi_probe(struct rsnd_priv *priv);
void rsnd_ssi_remove(struct rsnd_priv *priv);
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_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
#define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
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
*/
int rsnd_src_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
void rsnd_src_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
int rsnd_src_probe(struct rsnd_priv *priv);
void rsnd_src_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime);
int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io,
int use_busif);
int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
struct rsnd_dai_stream *io);
int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod);
int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
#define rsnd_parse_connect_src(rdai, playback, capture) \
rsnd_parse_connect_common(rdai, rsnd_src_mod_get, \
rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
playback, capture)
/*
* R-Car CTU
*/
int rsnd_ctu_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
void rsnd_ctu_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
int rsnd_ctu_probe(struct rsnd_priv *priv);
void rsnd_ctu_remove(struct rsnd_priv *priv);
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
*/
int rsnd_mix_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
void rsnd_mix_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
int rsnd_mix_probe(struct rsnd_priv *priv);
void rsnd_mix_remove(struct rsnd_priv *priv);
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
*/
int rsnd_dvc_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
void rsnd_dvc_remove(struct platform_device *pdev,
struct rsnd_priv *priv);
int rsnd_dvc_probe(struct rsnd_priv *priv);
void rsnd_dvc_remove(struct rsnd_priv *priv);
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
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
struct rsrc_card_dai {
unsigned int fmt;
unsigned int sysclk;
unsigned int tx_slot_mask;
unsigned int rx_slot_mask;
int slots;
int slot_width;
struct clk *clk;
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->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) {
ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
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;
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,
struct device_node *np,
struct device_node *codec,
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 *framemaster = NULL;
struct device_node *codec = is_fe ? NULL : np;
unsigned int daifmt;
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) ?
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
dai_props->fmt = daifmt;
of_node_put(bitclkmaster);
of_node_put(framemaster);
*retfmt = daifmt;
return 0;
}
@ -198,6 +204,15 @@ static int rsrc_card_parse_links(struct device_node *np,
if (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) {
/* BE is dummy */
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->dpcm_merged_format = 1;
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 */
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->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
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 */
if (of_data) {
@ -305,23 +324,16 @@ static int rsrc_card_parse_clk(struct device_node *np,
return 0;
}
static int rsrc_card_dai_link_of(struct device_node *node,
struct device_node *np,
struct rsrc_card_priv *priv,
int idx)
static int rsrc_card_dai_sub_link_of(struct device_node *node,
struct device_node *np,
struct rsrc_card_priv *priv,
int idx, bool is_fe)
{
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);
bool is_fe = false;
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);
if (ret < 0)
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",
dai_props->dai_name,
dai_props->fmt,
dai_link->dai_fmt,
dai_props->sysclk);
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,
struct rsrc_card_priv *priv,
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);
struct rsrc_card_dai *props;
struct snd_soc_dai_link *links;
struct device_node *np;
int ret;
int i, num;
int num;
if (!node)
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->convert_rate);
i = 0;
for_each_child_of_node(node, np) {
ret = rsrc_card_dai_link_of(node, np, priv, i);
if (ret < 0)
return ret;
i++;
}
ret = rsrc_card_dai_link_of(node, priv);
if (ret < 0)
return ret;
if (!priv->snd_card.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));
}
}