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:
commit
81b6863cae
20
Documentation/devicetree/bindings/sound/inno-rk3036.txt
Normal file
20
Documentation/devicetree/bindings/sound/inno-rk3036.txt
Normal 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>;
|
||||
};
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
490
sound/soc/codecs/inno_rk3036.c
Normal file
490
sound/soc/codecs/inno_rk3036.c
Normal 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, ®val);
|
||||
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");
|
123
sound/soc/codecs/inno_rk3036.h
Normal file
123
sound/soc/codecs/inno_rk3036.h
Normal 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
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
171
sound/soc/sh/rcar/cmd.c
Normal 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));
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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);
|
||||
|
@ -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
225
sound/soc/sh/rcar/ssiu.c
Normal 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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user