未验证 提交 7fa89d61 编写于 作者: M Mark Brown

Merge remote-tracking branches 'asoc/topic/twl4030', 'asoc/topic/twl6040',...

Merge remote-tracking branches 'asoc/topic/twl4030', 'asoc/topic/twl6040', 'asoc/topic/uda134x', 'asoc/topic/uda1380' and 'asoc/topic/uniphier' into asoc-next
Socionext UniPhier SoC audio driver
The Socionext UniPhier audio subsystem consists of I2S and S/PDIF blocks in
the same register space.
Required properties:
- compatible : should be one of the following:
"socionext,uniphier-ld11-aio"
"socionext,uniphier-ld20-aio"
"socionext,uniphier-pxs2-aio"
- reg : offset and length of the register set for the device.
- interrupts : should contain I2S or S/PDIF interrupt.
- pinctrl-names : should be "default".
- pinctrl-0 : defined I2S signal pins for an external codec chip.
- clock-names : should include following entries:
"aio"
- clocks : a list of phandle, should contain an entry for each
entry in clock-names.
- reset-names : should include following entries:
"aio"
- resets : a list of phandle, should contain an entry for each
entry in reset-names.
- #sound-dai-cells: should be 1.
Optional properties:
- socionext,syscon: a phandle, should contain soc-glue.
The soc-glue is used for changing mode of S/PDIF signal pin
to Output from Hi-Z. This property is optional if you use
I2S signal pins only.
Example:
audio {
compatible = "socionext,uniphier-ld20-aio";
reg = <0x56000000 0x80000>;
interrupts = <0 144 4>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_aout>;
clock-names = "aio";
clocks = <&sys_clk 40>;
reset-names = "aio";
resets = <&sys_rst 40>;
#sound-dai-cells = <1>;
socionext,syscon = <&sg>;
};
此差异已折叠。
此差异已折叠。
......@@ -34,11 +34,11 @@ enum twl6040_trim {
#define TWL6040_HSF_TRIM_LEFT(x) (x & 0x0f)
#define TWL6040_HSF_TRIM_RIGHT(x) ((x >> 4) & 0x0f)
int twl6040_get_dl1_gain(struct snd_soc_codec *codec);
void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
int twl6040_get_dl1_gain(struct snd_soc_component *component);
void twl6040_hs_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int report);
int twl6040_get_clk_id(struct snd_soc_codec *codec);
int twl6040_get_trim_value(struct snd_soc_codec *codec, enum twl6040_trim trim);
int twl6040_get_hs_step_size(struct snd_soc_codec *codec);
int twl6040_get_clk_id(struct snd_soc_component *component);
int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim);
int twl6040_get_hs_step_size(struct snd_soc_component *component);
#endif /* End of __TWL6040_H__ */
......@@ -110,9 +110,9 @@ static int uda134x_regmap_write(void *context, unsigned int reg,
return 0;
}
static inline void uda134x_reset(struct snd_soc_codec *codec)
static inline void uda134x_reset(struct snd_soc_component *component)
{
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
unsigned int mask = 1<<6;
regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask);
......@@ -122,7 +122,7 @@ static inline void uda134x_reset(struct snd_soc_codec *codec)
static int uda134x_mute(struct snd_soc_dai *dai, int mute)
{
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(dai->codec);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component);
unsigned int mask = 1<<2;
unsigned int val;
......@@ -139,8 +139,8 @@ static int uda134x_mute(struct snd_soc_dai *dai, int mute)
static int uda134x_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct snd_pcm_runtime *master_runtime;
if (uda134x->master_substream) {
......@@ -168,8 +168,8 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
static void uda134x_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
if (uda134x->master_substream == substream)
uda134x->master_substream = uda134x->slave_substream;
......@@ -181,8 +181,8 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
unsigned int hw_params = 0;
if (substream == uda134x->slave_substream) {
......@@ -248,8 +248,8 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = codec_dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
clk_id, freq, dir);
......@@ -270,8 +270,8 @@ static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = codec_dai->component;
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
pr_debug("%s fmt: %08X\n", __func__, fmt);
......@@ -294,10 +294,10 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static int uda134x_set_bias_level(struct snd_soc_codec *codec,
static int uda134x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct uda134x_platform_data *pd = uda134x->pd;
pr_debug("%s bias level %d\n", __func__, level);
......@@ -446,10 +446,10 @@ static struct snd_soc_dai_driver uda134x_dai = {
.ops = &uda134x_dai_ops,
};
static int uda134x_soc_probe(struct snd_soc_codec *codec)
static int uda134x_soc_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
struct uda134x_platform_data *pd = uda134x->pd;
const struct snd_soc_dapm_widget *widgets;
unsigned num_widgets;
......@@ -473,7 +473,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
if (pd->power)
pd->power(1);
uda134x_reset(codec);
uda134x_reset(component);
if (pd->model == UDA134X_UDA1341) {
widgets = uda1341_dapm_widgets;
......@@ -493,15 +493,15 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
ret = snd_soc_add_codec_controls(codec, uda1340_snd_controls,
ret = snd_soc_add_component_controls(component, uda1340_snd_controls,
ARRAY_SIZE(uda1340_snd_controls));
break;
case UDA134X_UDA1341:
ret = snd_soc_add_codec_controls(codec, uda1341_snd_controls,
ret = snd_soc_add_component_controls(component, uda1341_snd_controls,
ARRAY_SIZE(uda1341_snd_controls));
break;
case UDA134X_UDA1345:
ret = snd_soc_add_codec_controls(codec, uda1345_snd_controls,
ret = snd_soc_add_component_controls(component, uda1345_snd_controls,
ARRAY_SIZE(uda1345_snd_controls));
break;
default:
......@@ -518,17 +518,18 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
return 0;
}
static const struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.probe = uda134x_soc_probe,
.set_bias_level = uda134x_set_bias_level,
.suspend_bias_off = true,
.component_driver = {
.dapm_widgets = uda134x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
.dapm_routes = uda134x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
},
static const struct snd_soc_component_driver soc_component_dev_uda134x = {
.probe = uda134x_soc_probe,
.set_bias_level = uda134x_set_bias_level,
.dapm_widgets = uda134x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
.dapm_routes = uda134x_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static const struct regmap_config uda134x_regmap_config = {
......@@ -571,14 +572,8 @@ static int uda134x_codec_probe(struct platform_device *pdev)
if (IS_ERR(uda134x->regmap))
return PTR_ERR(uda134x->regmap);
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_uda134x, &uda134x_dai, 1);
}
static int uda134x_codec_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
return devm_snd_soc_register_component(&pdev->dev,
&soc_component_dev_uda134x, &uda134x_dai, 1);
}
static struct platform_driver uda134x_codec_driver = {
......@@ -586,7 +581,6 @@ static struct platform_driver uda134x_codec_driver = {
.name = "uda134x-codec",
},
.probe = uda134x_codec_probe,
.remove = uda134x_codec_remove,
};
module_platform_driver(uda134x_codec_driver);
......
......@@ -34,7 +34,7 @@
/* codec private data */
struct uda1380_priv {
struct snd_soc_codec *codec;
struct snd_soc_component *component;
unsigned int dac_clk;
struct work_struct work;
struct i2c_client *i2c;
......@@ -61,10 +61,10 @@ static unsigned long uda1380_cache_dirty;
/*
* read uda1380 register cache
*/
static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
static inline unsigned int uda1380_read_reg_cache(struct snd_soc_component *component,
unsigned int reg)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u16 *cache = uda1380->reg_cache;
if (reg == UDA1380_RESET)
......@@ -77,10 +77,10 @@ static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
/*
* write uda1380 register cache
*/
static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
static inline void uda1380_write_reg_cache(struct snd_soc_component *component,
u16 reg, unsigned int value)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u16 *cache = uda1380->reg_cache;
if (reg >= UDA1380_CACHEREGNUM)
......@@ -93,10 +93,10 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
/*
* write to the UDA1380 register space
*/
static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
static int uda1380_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
u8 data[3];
/* data is
......@@ -108,12 +108,12 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
data[1] = (value & 0xff00) >> 8;
data[2] = value & 0x00ff;
uda1380_write_reg_cache(codec, reg, value);
uda1380_write_reg_cache(component, reg, value);
/* the interpolator & decimator regs must only be written when the
* codec DAI is active.
*/
if (!snd_soc_codec_is_active(codec) && (reg >= UDA1380_MVOL))
if (!snd_soc_component_is_active(component) && (reg >= UDA1380_MVOL))
return 0;
pr_debug("uda1380: hw write %x val %x\n", reg, value);
if (i2c_master_send(uda1380->i2c, data, 3) == 3) {
......@@ -133,9 +133,9 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
static void uda1380_sync_cache(struct snd_soc_codec *codec)
static void uda1380_sync_cache(struct snd_soc_component *component)
{
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int reg;
u8 data[3];
u16 *cache = uda1380->reg_cache;
......@@ -146,15 +146,15 @@ static void uda1380_sync_cache(struct snd_soc_codec *codec)
data[1] = (cache[reg] & 0xff00) >> 8;
data[2] = cache[reg] & 0x00ff;
if (i2c_master_send(uda1380->i2c, data, 3) != 3)
dev_err(codec->dev, "%s: write to reg 0x%x failed\n",
dev_err(component->dev, "%s: write to reg 0x%x failed\n",
__func__, reg);
}
}
static int uda1380_reset(struct snd_soc_codec *codec)
static int uda1380_reset(struct snd_soc_component *component)
{
struct uda1380_platform_data *pdata = codec->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_platform_data *pdata = component->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
if (gpio_is_valid(pdata->gpio_reset)) {
gpio_set_value(pdata->gpio_reset, 1);
......@@ -168,7 +168,7 @@ static int uda1380_reset(struct snd_soc_codec *codec)
data[2] = 0;
if (i2c_master_send(uda1380->i2c, data, 3) != 3) {
dev_err(codec->dev, "%s: failed\n", __func__);
dev_err(component->dev, "%s: failed\n", __func__);
return -EIO;
}
}
......@@ -179,15 +179,15 @@ static int uda1380_reset(struct snd_soc_codec *codec)
static void uda1380_flush_work(struct work_struct *work)
{
struct uda1380_priv *uda1380 = container_of(work, struct uda1380_priv, work);
struct snd_soc_codec *uda1380_codec = uda1380->codec;
struct snd_soc_component *uda1380_component = uda1380->component;
int bit, reg;
for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
reg = 0x10 + bit;
pr_debug("uda1380: flush reg %x val %x:\n", reg,
uda1380_read_reg_cache(uda1380_codec, reg));
uda1380_write(uda1380_codec, reg,
uda1380_read_reg_cache(uda1380_codec, reg));
uda1380_read_reg_cache(uda1380_component, reg));
uda1380_write(uda1380_component, reg,
uda1380_read_reg_cache(uda1380_component, reg));
clear_bit(bit, &uda1380_cache_dirty);
}
......@@ -420,11 +420,11 @@ static const struct snd_soc_dapm_route uda1380_dapm_routes[] = {
static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
......@@ -442,7 +442,7 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
uda1380_write_reg_cache(codec, UDA1380_IFACE, iface);
uda1380_write_reg_cache(component, UDA1380_IFACE, iface);
return 0;
}
......@@ -450,11 +450,11 @@ static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~R01_SFORI_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
......@@ -472,7 +472,7 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
uda1380_write(codec, UDA1380_IFACE, iface);
uda1380_write(component, UDA1380_IFACE, iface);
return 0;
}
......@@ -480,11 +480,11 @@ static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_component *component = codec_dai->component;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface = uda1380_read_reg_cache(component, UDA1380_IFACE);
iface &= ~(R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
......@@ -501,7 +501,7 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
iface |= R01_SIM;
uda1380_write(codec, UDA1380_IFACE, iface);
uda1380_write(component, UDA1380_IFACE, iface);
return 0;
}
......@@ -509,20 +509,20 @@ static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
struct snd_soc_component *component = dai->component;
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int mixer = uda1380_read_reg_cache(component, UDA1380_MIXER);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
uda1380_write_reg_cache(component, UDA1380_MIXER,
mixer & ~R14_SILENCE);
schedule_work(&uda1380->work);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
uda1380_write_reg_cache(component, UDA1380_MIXER,
mixer | R14_SILENCE);
schedule_work(&uda1380->work);
break;
......@@ -534,13 +534,13 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
struct snd_soc_component *component = dai->component;
u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
if (clk & R00_DAC_CLK) {
int rate = params_rate(params);
u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
u16 pm = uda1380_read_reg_cache(component, UDA1380_PM);
clk &= ~0x3; /* clear SEL_LOOP_DIV */
switch (rate) {
case 6250 ... 12500:
......@@ -556,7 +556,7 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
clk |= 0x3;
break;
}
uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
uda1380_write(component, UDA1380_PM, R02_PON_PLL | pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
......@@ -564,20 +564,20 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
else
clk |= R00_EN_ADC | R00_EN_DEC;
uda1380_write(codec, UDA1380_CLK, clk);
uda1380_write(component, UDA1380_CLK, clk);
return 0;
}
static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
struct snd_soc_component *component = dai->component;
u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK);
/* shut down WSPLL power if running from this clock */
if (clk & R00_DAC_CLK) {
u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
u16 pm = uda1380_read_reg_cache(component, UDA1380_PM);
uda1380_write(component, UDA1380_PM, ~R02_PON_PLL & pm);
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
......@@ -585,33 +585,33 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
else
clk &= ~(R00_EN_ADC | R00_EN_DEC);
uda1380_write(codec, UDA1380_CLK, clk);
uda1380_write(component, UDA1380_CLK, clk);
}
static int uda1380_set_bias_level(struct snd_soc_codec *codec,
static int uda1380_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
int pm = uda1380_read_reg_cache(component, UDA1380_PM);
int reg;
struct uda1380_platform_data *pdata = codec->dev->platform_data;
struct uda1380_platform_data *pdata = component->dev->platform_data;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
/* ADC, DAC on */
uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
uda1380_write(component, UDA1380_PM, R02_PON_BIAS | pm);
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 1);
mdelay(1);
uda1380_reset(codec);
uda1380_reset(component);
}
uda1380_sync_cache(codec);
uda1380_sync_cache(component);
}
uda1380_write(codec, UDA1380_PM, 0x0);
uda1380_write(component, UDA1380_PM, 0x0);
break;
case SND_SOC_BIAS_OFF:
if (!gpio_is_valid(pdata->gpio_power))
......@@ -694,16 +694,16 @@ static struct snd_soc_dai_driver uda1380_dai[] = {
},
};
static int uda1380_probe(struct snd_soc_codec *codec)
static int uda1380_probe(struct snd_soc_component *component)
{
struct uda1380_platform_data *pdata =codec->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_codec_get_drvdata(codec);
struct uda1380_platform_data *pdata =component->dev->platform_data;
struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component);
int ret;
uda1380->codec = codec;
uda1380->component = component;
if (!gpio_is_valid(pdata->gpio_power)) {
ret = uda1380_reset(codec);
ret = uda1380_reset(component);
if (ret)
return ret;
}
......@@ -713,10 +713,10 @@ static int uda1380_probe(struct snd_soc_codec *codec)
/* set clock input */
switch (pdata->dac_clk) {
case UDA1380_DAC_CLK_SYSCLK:
uda1380_write_reg_cache(codec, UDA1380_CLK, 0);
uda1380_write_reg_cache(component, UDA1380_CLK, 0);
break;
case UDA1380_DAC_CLK_WSPLL:
uda1380_write_reg_cache(codec, UDA1380_CLK,
uda1380_write_reg_cache(component, UDA1380_CLK,
R00_DAC_CLK);
break;
}
......@@ -724,21 +724,22 @@ static int uda1380_probe(struct snd_soc_codec *codec)
return 0;
}
static const struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
.probe = uda1380_probe,
.read = uda1380_read_reg_cache,
.write = uda1380_write,
.set_bias_level = uda1380_set_bias_level,
.suspend_bias_off = true,
.component_driver = {
.controls = uda1380_snd_controls,
.num_controls = ARRAY_SIZE(uda1380_snd_controls),
.dapm_widgets = uda1380_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
.dapm_routes = uda1380_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
},
static const struct snd_soc_component_driver soc_component_dev_uda1380 = {
.probe = uda1380_probe,
.read = uda1380_read_reg_cache,
.write = uda1380_write,
.set_bias_level = uda1380_set_bias_level,
.controls = uda1380_snd_controls,
.num_controls = ARRAY_SIZE(uda1380_snd_controls),
.dapm_widgets = uda1380_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
.dapm_routes = uda1380_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
.suspend_bias_off = 1,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static int uda1380_i2c_probe(struct i2c_client *i2c,
......@@ -780,17 +781,11 @@ static int uda1380_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, uda1380);
uda1380->i2c = i2c;
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai));
return ret;
}
static int uda1380_i2c_remove(struct i2c_client *i2c)
{
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static const struct i2c_device_id uda1380_i2c_id[] = {
{ "uda1380", 0 },
{ }
......@@ -809,7 +804,6 @@ static struct i2c_driver uda1380_i2c_driver = {
.of_match_table = uda1380_of_match,
},
.probe = uda1380_i2c_probe,
.remove = uda1380_i2c_remove,
.id_table = uda1380_i2c_id,
};
......
......@@ -52,7 +52,7 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
int clk_id, freq;
int ret;
clk_id = twl6040_get_clk_id(rtd->codec);
clk_id = twl6040_get_clk_id(codec_dai->component);
if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
freq = priv->mclk_freq;
else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
......@@ -166,7 +166,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_component *component = rtd->codec_dai->component;
struct snd_soc_card *card = rtd->card;
struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int hs_trim;
......@@ -176,7 +176,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
* Configure McPDM offset cancellation based on the HSOTRIM value from
* twl6040.
*/
hs_trim = twl6040_get_trim_value(codec, TWL6040_TRIM_HSOTRIM);
hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM);
omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim),
TWL6040_HSF_TRIM_RIGHT(hs_trim));
......@@ -189,7 +189,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
if (ret)
return ret;
twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET);
}
return 0;
......
......@@ -8,6 +8,39 @@ config SND_SOC_UNIPHIER
audio interfaces to support below.
If unsure select "N".
config SND_SOC_UNIPHIER_AIO
tristate "UniPhier AIO DAI Driver"
select REGMAP_MMIO
select SND_SOC_COMPRESS
depends on SND_SOC_UNIPHIER
help
This adds ASoC driver support for Socionext UniPhier
'AIO' Audio Input/Output subsystem.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_LD11
tristate "UniPhier LD11/LD20 Device Driver"
depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
select SND_SOC_UNIPHIER_AIO_DMA
help
This adds ASoC driver for Socionext UniPhier LD11/LD20
input and output that can be used with other codecs.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_PXS2
tristate "UniPhier PXs2 Device Driver"
depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
select SND_SOC_UNIPHIER_AIO_DMA
help
This adds ASoC driver for Socionext UniPhier PXs2
input and output that can be used with other codecs.
Select Y if you use such device.
If unsure select "N".
config SND_SOC_UNIPHIER_EVEA_CODEC
tristate "UniPhier SoC internal audio codec"
depends on SND_SOC_UNIPHIER
......
# SPDX-License-Identifier: GPL-2.0
snd-soc-uniphier-aio-cpu-objs := aio-core.o aio-dma.o aio-cpu.o aio-compress.o
snd-soc-uniphier-aio-ld11-objs := aio-ld11.o
snd-soc-uniphier-aio-pxs2-objs := aio-pxs2.o
obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio-cpu.o
obj-$(CONFIG_SND_SOC_UNIPHIER_LD11) += snd-soc-uniphier-aio-ld11.o
obj-$(CONFIG_SND_SOC_UNIPHIER_PXS2) += snd-soc-uniphier-aio-pxs2.o
snd-soc-uniphier-evea-objs := evea.o
obj-$(CONFIG_SND_SOC_UNIPHIER_EVEA_CODEC) += snd-soc-uniphier-evea.o
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO Compress Audio driver.
//
// Copyright (c) 2017-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/bitfield.h>
#include <linux/circ_buf.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "aio.h"
static int uniphier_aio_compr_prepare(struct snd_compr_stream *cstream);
static int uniphier_aio_compr_hw_free(struct snd_compr_stream *cstream);
static int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_compr *compr = rtd->compr;
struct device *dev = compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
size_t size = AUD_RING_SIZE;
int dma_dir = DMA_FROM_DEVICE, ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
sub->compr_area = kzalloc(size, GFP_KERNEL);
if (!sub->compr_area)
return -ENOMEM;
if (sub->swm->dir == PORT_DIR_OUTPUT)
dma_dir = DMA_TO_DEVICE;
sub->compr_addr = dma_map_single(dev, sub->compr_area, size, dma_dir);
if (dma_mapping_error(dev, sub->compr_addr)) {
kfree(sub->compr_area);
sub->compr_area = NULL;
return -ENOMEM;
}
sub->compr_bytes = size;
return 0;
}
static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd)
{
struct snd_compr *compr = rtd->compr;
struct device *dev = compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
int dma_dir = DMA_FROM_DEVICE;
if (sub->swm->dir == PORT_DIR_OUTPUT)
dma_dir = DMA_TO_DEVICE;
dma_unmap_single(dev, sub->compr_addr, sub->compr_bytes, dma_dir);
kfree(sub->compr_area);
sub->compr_area = NULL;
return 0;
}
static int uniphier_aio_compr_open(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int ret;
if (sub->cstream)
return -EBUSY;
sub->cstream = cstream;
sub->pass_through = 1;
sub->use_mmap = false;
ret = uniphier_aio_comprdma_new(rtd);
if (ret)
return ret;
ret = aio_init(sub);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_free(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int ret;
ret = uniphier_aio_compr_hw_free(cstream);
if (ret)
return ret;
ret = uniphier_aio_comprdma_free(rtd);
if (ret)
return ret;
sub->cstream = NULL;
return 0;
}
static int uniphier_aio_compr_get_params(struct snd_compr_stream *cstream,
struct snd_codec *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
*params = sub->cparams.codec;
return 0;
}
static int uniphier_aio_compr_set_params(struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
int ret;
if (params->codec.id != SND_AUDIOCODEC_IEC61937) {
dev_err(dev, "Codec ID is not supported(%d)\n",
params->codec.id);
return -EINVAL;
}
if (params->codec.profile != SND_AUDIOPROFILE_IEC61937_SPDIF) {
dev_err(dev, "Codec profile is not supported(%d)\n",
params->codec.profile);
return -EINVAL;
}
/* IEC frame type will be changed after received valid data */
sub->iec_pc = IEC61937_PC_AAC;
sub->cparams = *params;
sub->setting = 1;
aio_port_reset(sub);
aio_src_reset(sub);
ret = uniphier_aio_compr_prepare(cstream);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_hw_free(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
sub->setting = 0;
return 0;
}
static int uniphier_aio_compr_prepare(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int bytes = runtime->fragment_size;
unsigned long flags;
int ret;
ret = aiodma_ch_set_param(sub);
if (ret)
return ret;
spin_lock_irqsave(&sub->lock, flags);
ret = aiodma_rb_set_buffer(sub, sub->compr_addr,
sub->compr_addr + sub->compr_bytes,
bytes);
spin_unlock_irqrestore(&sub->lock, flags);
if (ret)
return ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
if (ret)
return ret;
ret = aio_oport_set_stream_type(sub, sub->iec_pc);
if (ret)
return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through);
if (ret)
return ret;
return 0;
}
static int uniphier_aio_compr_trigger(struct snd_compr_stream *cstream,
int cmd)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
struct device *dev = &aio->chip->pdev->dev;
int bytes = runtime->fragment_size, ret = 0;
unsigned long flags;
spin_lock_irqsave(&sub->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
aiodma_ch_set_enable(sub, 1);
sub->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
sub->running = 0;
aiodma_ch_set_enable(sub, 0);
break;
default:
dev_warn(dev, "Unknown trigger(%d)\n", cmd);
ret = -EINVAL;
}
spin_unlock_irqrestore(&sub->lock, flags);
return ret;
}
static int uniphier_aio_compr_pointer(struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
int bytes = runtime->fragment_size;
unsigned long flags;
u32 pos;
spin_lock_irqsave(&sub->lock, flags);
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT) {
pos = sub->rd_offs;
/* Size of AIO output format is double of IEC61937 */
tstamp->copied_total = sub->rd_total / 2;
} else {
pos = sub->wr_offs;
tstamp->copied_total = sub->rd_total;
}
tstamp->byte_offset = pos;
spin_unlock_irqrestore(&sub->lock, flags);
return 0;
}
static int aio_compr_send_to_hw(struct uniphier_aio_sub *sub,
char __user *buf, size_t dstsize)
{
u32 __user *srcbuf = (u32 __user *)buf;
u32 *dstbuf = (u32 *)(sub->compr_area + sub->wr_offs);
int src = 0, dst = 0, ret;
u32 frm, frm_a, frm_b;
while (dstsize > 0) {
ret = get_user(frm, srcbuf + src);
if (ret)
return ret;
src++;
frm_a = frm & 0xffff;
frm_b = (frm >> 16) & 0xffff;
if (frm == IEC61937_HEADER_SIGN) {
frm_a |= 0x01000000;
/* Next data is Pc and Pd */
sub->iec_header = true;
} else {
u16 pc = be16_to_cpu((__be16)frm_a);
if (sub->iec_header && sub->iec_pc != pc) {
/* Force overwrite IEC frame type */
sub->iec_pc = pc;
ret = aio_oport_set_stream_type(sub, pc);
if (ret)
return ret;
}
sub->iec_header = false;
}
dstbuf[dst++] = frm_a;
dstbuf[dst++] = frm_b;
dstsize -= sizeof(u32) * 2;
}
return 0;
}
static int uniphier_aio_compr_copy(struct snd_compr_stream *cstream,
char __user *buf, size_t count)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_compr_runtime *runtime = cstream->runtime;
struct device *carddev = rtd->compr->card->dev;
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2);
int bytes = runtime->fragment_size;
unsigned long flags;
size_t s;
int ret;
if (cnt < sizeof(u32))
return 0;
if (sub->swm->dir == PORT_DIR_OUTPUT) {
dma_addr_t dmapos = sub->compr_addr + sub->wr_offs;
/* Size of AIO output format is double of IEC61937 */
s = cnt * 2;
dma_sync_single_for_cpu(carddev, dmapos, s, DMA_TO_DEVICE);
ret = aio_compr_send_to_hw(sub, buf, s);
dma_sync_single_for_device(carddev, dmapos, s, DMA_TO_DEVICE);
} else {
dma_addr_t dmapos = sub->compr_addr + sub->rd_offs;
s = cnt;
dma_sync_single_for_cpu(carddev, dmapos, s, DMA_FROM_DEVICE);
ret = copy_to_user(buf, sub->compr_area + sub->rd_offs, s);
dma_sync_single_for_device(carddev, dmapos, s, DMA_FROM_DEVICE);
}
if (ret)
return -EFAULT;
spin_lock_irqsave(&sub->lock, flags);
sub->threshold = 2 * bytes;
aiodma_rb_set_threshold(sub, sub->compr_bytes, 2 * bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT) {
sub->wr_offs += s;
if (sub->wr_offs >= sub->compr_bytes)
sub->wr_offs -= sub->compr_bytes;
} else {
sub->rd_offs += s;
if (sub->rd_offs >= sub->compr_bytes)
sub->rd_offs -= sub->compr_bytes;
}
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
spin_unlock_irqrestore(&sub->lock, flags);
return cnt;
}
static int uniphier_aio_compr_get_caps(struct snd_compr_stream *cstream,
struct snd_compr_caps *caps)
{
caps->num_codecs = 1;
caps->min_fragment_size = AUD_MIN_FRAGMENT_SIZE;
caps->max_fragment_size = AUD_MAX_FRAGMENT_SIZE;
caps->min_fragments = AUD_MIN_FRAGMENT;
caps->max_fragments = AUD_MAX_FRAGMENT;
caps->codecs[0] = SND_AUDIOCODEC_IEC61937;
return 0;
}
static const struct snd_compr_codec_caps caps_iec = {
.num_descriptors = 1,
.descriptor[0].max_ch = 8,
.descriptor[0].num_sample_rates = 0,
.descriptor[0].num_bitrates = 0,
.descriptor[0].profiles = SND_AUDIOPROFILE_IEC61937_SPDIF,
.descriptor[0].modes = SND_AUDIOMODE_IEC_AC3 |
SND_AUDIOMODE_IEC_MPEG1 |
SND_AUDIOMODE_IEC_MP3 |
SND_AUDIOMODE_IEC_DTS,
.descriptor[0].formats = 0,
};
static int uniphier_aio_compr_get_codec_caps(struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec)
{
if (codec->codec == SND_AUDIOCODEC_IEC61937)
*codec = caps_iec;
else
return -EINVAL;
return 0;
}
const struct snd_compr_ops uniphier_aio_compr_ops = {
.open = uniphier_aio_compr_open,
.free = uniphier_aio_compr_free,
.get_params = uniphier_aio_compr_get_params,
.set_params = uniphier_aio_compr_set_params,
.trigger = uniphier_aio_compr_trigger,
.pointer = uniphier_aio_compr_pointer,
.copy = uniphier_aio_compr_copy,
.get_caps = uniphier_aio_compr_get_caps,
.get_codec_caps = uniphier_aio_compr_get_codec_caps,
};
此差异已折叠。
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO ALSA CPU DAI driver.
//
// Copyright (c) 2016-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "aio.h"
static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id)
{
struct device *dev = &chip->pdev->dev;
if (pll_id < 0 || chip->num_plls <= pll_id) {
dev_err(dev, "PLL(%d) is not supported\n", pll_id);
return false;
}
return chip->plls[pll_id].enable;
}
static bool match_spec(const struct uniphier_aio_spec *spec,
const char *name, int dir)
{
if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
spec->swm.dir != PORT_DIR_OUTPUT) {
return false;
}
if (dir == SNDRV_PCM_STREAM_CAPTURE &&
spec->swm.dir != PORT_DIR_INPUT) {
return false;
}
if (spec->name && strcmp(spec->name, name) == 0)
return true;
if (spec->gname && strcmp(spec->gname, name) == 0)
return true;
return false;
}
/**
* find_spec - find HW specification info by name
* @aio: the AIO device pointer
* @name: name of device
* @direction: the direction of substream, SNDRV_PCM_STREAM_*
*
* Find hardware specification information from list by device name. This
* information is used for telling the difference of SoCs to driver.
*
* Specification list is array of 'struct uniphier_aio_spec' which is defined
* in each drivers (see: aio-i2s.c).
*
* Return: The pointer of hardware specification of AIO if successful,
* otherwise NULL on error.
*/
static const struct uniphier_aio_spec *find_spec(struct uniphier_aio *aio,
const char *name,
int direction)
{
const struct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec;
int i;
for (i = 0; i < chip_spec->num_specs; i++) {
const struct uniphier_aio_spec *spec = &chip_spec->specs[i];
if (match_spec(spec, name, direction))
return spec;
}
return NULL;
}
/**
* find_divider - find clock divider by frequency
* @aio: the AIO device pointer
* @pll_id: PLL ID, should be AUD_PLL_XX
* @freq: required frequency
*
* Find suitable clock divider by frequency.
*
* Return: The ID of PLL if successful, otherwise negative error value.
*/
static int find_divider(struct uniphier_aio *aio, int pll_id, unsigned int freq)
{
struct uniphier_aio_pll *pll;
int mul[] = { 1, 1, 1, 2, };
int div[] = { 2, 3, 1, 3, };
int i;
if (!is_valid_pll(aio->chip, pll_id))
return -EINVAL;
pll = &aio->chip->plls[pll_id];
for (i = 0; i < ARRAY_SIZE(mul); i++)
if (pll->freq * mul[i] / div[i] == freq)
return i;
return -ENOTSUPP;
}
static int uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
bool pll_auto = false;
int pll_id, div_id;
switch (clk_id) {
case AUD_CLK_IO:
return -ENOTSUPP;
case AUD_CLK_A1:
pll_id = AUD_PLL_A1;
break;
case AUD_CLK_F1:
pll_id = AUD_PLL_F1;
break;
case AUD_CLK_A2:
pll_id = AUD_PLL_A2;
break;
case AUD_CLK_F2:
pll_id = AUD_PLL_F2;
break;
case AUD_CLK_A:
pll_id = AUD_PLL_A1;
pll_auto = true;
break;
case AUD_CLK_F:
pll_id = AUD_PLL_F1;
pll_auto = true;
break;
case AUD_CLK_APLL:
pll_id = AUD_PLL_APLL;
break;
case AUD_CLK_RX0:
pll_id = AUD_PLL_RX0;
break;
case AUD_CLK_USB0:
pll_id = AUD_PLL_USB0;
break;
case AUD_CLK_HSC0:
pll_id = AUD_PLL_HSC0;
break;
default:
dev_err(dev, "Sysclk(%d) is not supported\n", clk_id);
return -EINVAL;
}
if (pll_auto) {
for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
div_id = find_divider(aio, pll_id, freq);
if (div_id >= 0) {
aio->plldiv = div_id;
break;
}
}
if (pll_id == aio->chip->num_plls) {
dev_err(dev, "Sysclk frequency is not supported(%d)\n",
freq);
return -EINVAL;
}
}
if (dir == SND_SOC_CLOCK_OUT)
aio->pll_out = pll_id;
else
aio->pll_in = pll_id;
return 0;
}
static int uniphier_aio_set_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in,
unsigned int freq_out)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
int ret;
if (!is_valid_pll(aio->chip, pll_id))
return -EINVAL;
if (!aio->chip->plls[pll_id].enable) {
dev_err(dev, "PLL(%d) is not implemented\n", pll_id);
return -ENOTSUPP;
}
ret = aio_chip_set_pll(aio->chip, pll_id, freq_out);
if (ret < 0)
return ret;
return 0;
}
static int uniphier_aio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct device *dev = &aio->chip->pdev->dev;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_LEFT_J:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_I2S:
aio->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
dev_err(dev, "Format is not supported(%d)\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
return 0;
}
static int uniphier_aio_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int ret;
sub->substream = substream;
sub->pass_through = 0;
sub->use_mmap = true;
ret = aio_init(sub);
if (ret)
return ret;
return 0;
}
static void uniphier_aio_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->substream = NULL;
}
static int uniphier_aio_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
struct device *dev = &aio->chip->pdev->dev;
int freq, ret;
switch (params_rate(params)) {
case 48000:
case 32000:
case 24000:
freq = 12288000;
break;
case 44100:
case 22050:
freq = 11289600;
break;
default:
dev_err(dev, "Rate is not supported(%d)\n",
params_rate(params));
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
freq, SND_SOC_CLOCK_OUT);
if (ret)
return ret;
sub->params = *params;
sub->setting = 1;
aio_port_reset(sub);
aio_src_reset(sub);
return 0;
}
static int uniphier_aio_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
sub->setting = 0;
return 0;
}
static int uniphier_aio_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
if (ret)
return ret;
ret = aio_src_set_param(sub, &sub->params);
if (ret)
return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through);
if (ret)
return ret;
if (sub->swm->type == PORT_TYPE_CONV) {
ret = aio_srcif_set_param(sub);
if (ret)
return ret;
ret = aio_srcch_set_param(sub);
if (ret)
return ret;
aio_srcch_set_enable(sub, 1);
}
return 0;
}
const struct snd_soc_dai_ops uniphier_aio_i2s_ops = {
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.set_fmt = uniphier_aio_set_fmt,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops);
const struct snd_soc_dai_ops uniphier_aio_spdif_ops = {
.set_sysclk = uniphier_aio_set_sysclk,
.set_pll = uniphier_aio_set_pll,
.startup = uniphier_aio_startup,
.shutdown = uniphier_aio_shutdown,
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
};
EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops);
int uniphier_aio_dai_probe(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int i;
for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
struct uniphier_aio_sub *sub = &aio->sub[i];
const struct uniphier_aio_spec *spec;
spec = find_spec(aio, dai->name, i);
if (!spec)
continue;
sub->swm = &spec->swm;
sub->spec = spec;
}
aio_iecout_set_enable(aio->chip, true);
aio_chip_init(aio->chip);
aio->chip->active = 1;
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe);
int uniphier_aio_dai_remove(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
aio->chip->active = 0;
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove);
int uniphier_aio_dai_suspend(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
reset_control_assert(aio->chip->rst);
clk_disable_unprepare(aio->chip->clk);
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_suspend);
int uniphier_aio_dai_resume(struct snd_soc_dai *dai)
{
struct uniphier_aio *aio = uniphier_priv(dai);
int ret, i;
if (!aio->chip->active)
return 0;
ret = clk_prepare_enable(aio->chip->clk);
if (ret)
return ret;
ret = reset_control_deassert(aio->chip->rst);
if (ret)
goto err_out_clock;
aio_iecout_set_enable(aio->chip, true);
aio_chip_init(aio->chip);
for (i = 0; i < ARRAY_SIZE(aio->sub); i++) {
struct uniphier_aio_sub *sub = &aio->sub[i];
if (!sub->spec || !sub->substream)
continue;
ret = aio_init(sub);
if (ret)
goto err_out_clock;
if (!sub->setting)
continue;
aio_port_reset(sub);
aio_src_reset(sub);
}
return 0;
err_out_clock:
clk_disable_unprepare(aio->chip->clk);
return ret;
}
EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume);
static const struct snd_soc_component_driver uniphier_aio_component = {
.name = "uniphier-aio",
};
int uniphier_aio_probe(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip;
struct device *dev = &pdev->dev;
int ret, i, j;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->chip_spec = of_device_get_match_data(dev);
if (!chip->chip_spec)
return -EINVAL;
chip->regmap_sg = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
if (IS_ERR(chip->regmap_sg)) {
if (PTR_ERR(chip->regmap_sg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
chip->regmap_sg = NULL;
}
chip->clk = devm_clk_get(dev, "aio");
if (IS_ERR(chip->clk))
return PTR_ERR(chip->clk);
chip->rst = devm_reset_control_get_shared(dev, "aio");
if (IS_ERR(chip->rst))
return PTR_ERR(chip->rst);
chip->num_aios = chip->chip_spec->num_dais;
chip->aios = devm_kzalloc(dev,
sizeof(struct uniphier_aio) * chip->num_aios,
GFP_KERNEL);
if (!chip->aios)
return -ENOMEM;
chip->num_plls = chip->chip_spec->num_plls;
chip->plls = devm_kzalloc(dev, sizeof(struct uniphier_aio_pll) *
chip->num_plls, GFP_KERNEL);
if (!chip->plls)
return -ENOMEM;
memcpy(chip->plls, chip->chip_spec->plls,
sizeof(struct uniphier_aio_pll) * chip->num_plls);
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio *aio = &chip->aios[i];
aio->chip = chip;
aio->fmt = SND_SOC_DAIFMT_I2S;
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
sub->aio = aio;
spin_lock_init(&sub->lock);
}
}
chip->pdev = pdev;
platform_set_drvdata(pdev, chip);
ret = clk_prepare_enable(chip->clk);
if (ret)
return ret;
ret = reset_control_deassert(chip->rst);
if (ret)
goto err_out_clock;
ret = devm_snd_soc_register_component(dev, &uniphier_aio_component,
chip->chip_spec->dais,
chip->chip_spec->num_dais);
if (ret) {
dev_err(dev, "Register component failed.\n");
goto err_out_reset;
}
ret = uniphier_aiodma_soc_register_platform(pdev);
if (ret) {
dev_err(dev, "Register platform failed.\n");
goto err_out_reset;
}
return 0;
err_out_reset:
reset_control_assert(chip->rst);
err_out_clock:
clk_disable_unprepare(chip->clk);
return ret;
}
EXPORT_SYMBOL_GPL(uniphier_aio_probe);
int uniphier_aio_remove(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
reset_control_assert(chip->rst);
clk_disable_unprepare(chip->clk);
return 0;
}
EXPORT_SYMBOL_GPL(uniphier_aio_remove);
MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>");
MODULE_DESCRIPTION("UniPhier AIO CPU DAI driver.");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
//
// Socionext UniPhier AIO DMA driver.
//
// Copyright (c) 2016-2018 Socionext Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; version 2
// of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include "aio.h"
static struct snd_pcm_hardware uniphier_aiodma_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED,
.period_bytes_min = 256,
.period_bytes_max = 4096,
.periods_min = 4,
.periods_max = 1024,
.buffer_bytes_max = 128 * 1024,
};
static void aiodma_pcm_irq(struct uniphier_aio_sub *sub)
{
struct snd_pcm_runtime *runtime = sub->substream->runtime;
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
int ret;
spin_lock(&sub->lock);
ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes,
sub->threshold + bytes);
if (!ret)
sub->threshold += bytes;
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
aiodma_rb_clear_irq(sub);
spin_unlock(&sub->lock);
snd_pcm_period_elapsed(sub->substream);
}
static void aiodma_compr_irq(struct uniphier_aio_sub *sub)
{
struct snd_compr_runtime *runtime = sub->cstream->runtime;
int bytes = runtime->fragment_size;
int ret;
spin_lock(&sub->lock);
ret = aiodma_rb_set_threshold(sub, sub->compr_bytes,
sub->threshold + bytes);
if (!ret)
sub->threshold += bytes;
aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
aiodma_rb_clear_irq(sub);
spin_unlock(&sub->lock);
snd_compr_fragment_elapsed(sub->cstream);
}
static irqreturn_t aiodma_irq(int irq, void *p)
{
struct platform_device *pdev = p;
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
irqreturn_t ret = IRQ_NONE;
int i, j;
for (i = 0; i < chip->num_aios; i++) {
struct uniphier_aio *aio = &chip->aios[i];
for (j = 0; j < ARRAY_SIZE(aio->sub); j++) {
struct uniphier_aio_sub *sub = &aio->sub[j];
/* Skip channel that does not trigger */
if (!sub->running || !aiodma_rb_is_irq(sub))
continue;
if (sub->substream)
aiodma_pcm_irq(sub);
if (sub->cstream)
aiodma_compr_irq(sub);
ret = IRQ_HANDLED;
}
}
return ret;
}
static int uniphier_aiodma_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw);
return snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256);
}
static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
substream->runtime->dma_bytes = params_buffer_bytes(params);
return 0;
}
static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream)
{
snd_pcm_set_runtime_buffer(substream, NULL);
substream->runtime->dma_bytes = 0;
return 0;
}
static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
unsigned long flags;
int ret;
ret = aiodma_ch_set_param(sub);
if (ret)
return ret;
spin_lock_irqsave(&sub->lock, flags);
ret = aiodma_rb_set_buffer(sub, runtime->dma_addr,
runtime->dma_addr + runtime->dma_bytes,
bytes);
spin_unlock_irqrestore(&sub->lock, flags);
if (ret)
return ret;
return 0;
}
static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
struct device *dev = &aio->chip->pdev->dev;
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
unsigned long flags;
spin_lock_irqsave(&sub->lock, flags);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes,
bytes);
aiodma_ch_set_enable(sub, 1);
sub->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
sub->running = 0;
aiodma_ch_set_enable(sub, 0);
break;
default:
dev_warn(dev, "Unknown trigger(%d) ignored\n", cmd);
break;
}
spin_unlock_irqrestore(&sub->lock, flags);
return 0;
}
static snd_pcm_uframes_t uniphier_aiodma_pointer(
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai);
struct uniphier_aio_sub *sub = &aio->sub[substream->stream];
int bytes = runtime->period_size *
runtime->channels * samples_to_bytes(runtime, 1);
unsigned long flags;
snd_pcm_uframes_t pos;
spin_lock_irqsave(&sub->lock, flags);
aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes);
if (sub->swm->dir == PORT_DIR_OUTPUT)
pos = bytes_to_frames(runtime, sub->rd_offs);
else
pos = bytes_to_frames(runtime, sub->wr_offs);
spin_unlock_irqrestore(&sub->lock, flags);
return pos;
}
static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
static const struct snd_pcm_ops uniphier_aiodma_ops = {
.open = uniphier_aiodma_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = uniphier_aiodma_hw_params,
.hw_free = uniphier_aiodma_hw_free,
.prepare = uniphier_aiodma_prepare,
.trigger = uniphier_aiodma_trigger,
.pointer = uniphier_aiodma_pointer,
.mmap = uniphier_aiodma_mmap,
};
static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd)
{
struct device *dev = rtd->card->snd_card->dev;
struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
return snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, dev,
uniphier_aiodma_hw.buffer_bytes_max,
uniphier_aiodma_hw.buffer_bytes_max);
}
static void uniphier_aiodma_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static const struct snd_soc_component_driver uniphier_soc_platform = {
.pcm_new = uniphier_aiodma_new,
.pcm_free = uniphier_aiodma_free,
.ops = &uniphier_aiodma_ops,
.compr_ops = &uniphier_aio_compr_ops,
};
static const struct regmap_config aiodma_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0x7fffc,
.cache_type = REGCACHE_NONE,
};
/**
* uniphier_aiodma_soc_register_platform - register the AIO DMA
* @pdev: the platform device
*
* Register and setup the DMA of AIO to transfer the sound data to device.
* This function need to call once at driver startup and need NOT to call
* unregister function.
*
* Return: Zero if successful, otherwise a negative value on error.
*/
int uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
{
struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
struct resource *res;
void __iomem *preg;
int irq, ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
preg = devm_ioremap_resource(dev, res);
if (IS_ERR(preg))
return PTR_ERR(preg);
chip->regmap = devm_regmap_init_mmio(dev, preg,
&aiodma_regmap_config);
if (IS_ERR(chip->regmap))
return PTR_ERR(chip->regmap);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "Could not get irq.\n");
return irq;
}
ret = devm_request_irq(dev, irq, aiodma_irq,
IRQF_SHARED, dev_name(dev), pdev);
if (ret)
return ret;
return devm_snd_soc_register_component(dev, &uniphier_soc_platform,
NULL, 0);
}
EXPORT_SYMBOL_GPL(uniphier_aiodma_soc_register_platform);
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
// SPDX-License-Identifier: GPL-2.0
/*
* Socionext UniPhier EVEA ADC/DAC codec driver.
*
* Copyright (c) 2016-2017 Socionext Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
//
// Socionext UniPhier EVEA ADC/DAC codec driver.
//
// Copyright (c) 2016-2017 Socionext Inc.
#include <linux/clk.h>
#include <linux/module.h>
......@@ -32,6 +18,8 @@
#define AADCPOW(n) (0x0078 + 0x04 * (n))
#define AADCPOW_AADC_POWD BIT(0)
#define ALINSW1 0x0088
#define ALINSW1_SEL1_SHIFT 3
#define AHPOUTPOW 0x0098
#define AHPOUTPOW_HP_ON BIT(4)
#define ALINEPOW 0x009c
......@@ -220,8 +208,8 @@ static void evea_update_switch_all(struct evea_priv *evea)
static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_lin;
......@@ -231,8 +219,8 @@ static int evea_get_switch_lin(struct snd_kcontrol *kcontrol,
static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_lin == ucontrol->value.integer.value[0])
return 0;
......@@ -245,8 +233,8 @@ static int evea_set_switch_lin(struct snd_kcontrol *kcontrol,
static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_lo;
......@@ -256,8 +244,8 @@ static int evea_get_switch_lo(struct snd_kcontrol *kcontrol,
static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_lo == ucontrol->value.integer.value[0])
return 0;
......@@ -270,8 +258,8 @@ static int evea_set_switch_lo(struct snd_kcontrol *kcontrol,
static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = evea->switch_hp;
......@@ -281,8 +269,8 @@ static int evea_get_switch_hp(struct snd_kcontrol *kcontrol,
static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
if (evea->switch_hp == ucontrol->value.integer.value[0])
return 0;
......@@ -292,7 +280,16 @@ static int evea_set_switch_hp(struct snd_kcontrol *kcontrol,
return evea_update_switch_hp(evea);
}
static const struct snd_kcontrol_new eva_controls[] = {
static const char * const linsw1_sel1_text[] = {
"LIN1", "LIN2", "LIN3"
};
static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum,
ALINSW1, ALINSW1_SEL1_SHIFT,
linsw1_sel1_text);
static const struct snd_kcontrol_new evea_controls[] = {
SOC_ENUM("Line Capture Source", linsw1_sel1_enum),
SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0,
evea_get_switch_lin, evea_set_switch_lin),
SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0,
......@@ -301,9 +298,9 @@ static const struct snd_kcontrol_new eva_controls[] = {
evea_get_switch_hp, evea_set_switch_hp),
};
static int evea_codec_probe(struct snd_soc_codec *codec)
static int evea_codec_probe(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
evea->switch_lin = 1;
evea->switch_lo = 1;
......@@ -315,9 +312,9 @@ static int evea_codec_probe(struct snd_soc_codec *codec)
return 0;
}
static int evea_codec_suspend(struct snd_soc_codec *codec)
static int evea_codec_suspend(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
evea_set_power_state_off(evea);
......@@ -331,9 +328,9 @@ static int evea_codec_suspend(struct snd_soc_codec *codec)
return 0;
}
static int evea_codec_resume(struct snd_soc_codec *codec)
static int evea_codec_resume(struct snd_soc_component *component)
{
struct evea_priv *evea = snd_soc_codec_get_drvdata(codec);
struct evea_priv *evea = snd_soc_component_get_drvdata(component);
int ret;
ret = clk_prepare_enable(evea->clk);
......@@ -376,19 +373,20 @@ static int evea_codec_resume(struct snd_soc_codec *codec)
return ret;
}
static struct snd_soc_codec_driver soc_codec_evea = {
.probe = evea_codec_probe,
.suspend = evea_codec_suspend,
.resume = evea_codec_resume,
.component_driver = {
.dapm_widgets = evea_widgets,
.num_dapm_widgets = ARRAY_SIZE(evea_widgets),
.dapm_routes = evea_routes,
.num_dapm_routes = ARRAY_SIZE(evea_routes),
.controls = eva_controls,
.num_controls = ARRAY_SIZE(eva_controls),
},
static struct snd_soc_component_driver soc_codec_evea = {
.probe = evea_codec_probe,
.suspend = evea_codec_suspend,
.resume = evea_codec_resume,
.dapm_widgets = evea_widgets,
.num_dapm_widgets = ARRAY_SIZE(evea_widgets),
.dapm_routes = evea_routes,
.num_dapm_routes = ARRAY_SIZE(evea_routes),
.controls = evea_controls,
.num_controls = ARRAY_SIZE(evea_controls),
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
.non_legacy_dai_naming = 1,
};
static struct snd_soc_dai_driver soc_dai_evea[] = {
......@@ -505,7 +503,7 @@ static int evea_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, evea);
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_evea,
ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_evea,
soc_dai_evea, ARRAY_SIZE(soc_dai_evea));
if (ret)
goto err_out_reset_adamv;
......@@ -534,8 +532,6 @@ static int evea_remove(struct platform_device *pdev)
{
struct evea_priv *evea = platform_get_drvdata(pdev);
snd_soc_unregister_codec(&pdev->dev);
reset_control_assert(evea->rst_adamv);
reset_control_assert(evea->rst_exiv);
reset_control_assert(evea->rst);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册