Merge remote-tracking branches 'asoc/topic/omap', 'asoc/topic/rcar' and 'asoc/topic/rockchip' into asoc-next
This commit is contained in:
Коммит
f3413783e4
|
@ -8,6 +8,8 @@ Required properties:
|
|||
- interrupts: Interrupt number for McPDM
|
||||
- interrupt-parent: The parent interrupt controller
|
||||
- ti,hwmods: Name of the hwmod associated to the McPDM
|
||||
- clocks: phandle for the pdmclk provider, likely <&twl6040>
|
||||
- clock-names: Must be "pdmclk"
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -19,3 +21,11 @@ mcpdm: mcpdm@40132000 {
|
|||
interrupt-parent = <&gic>;
|
||||
ti,hwmods = "mcpdm";
|
||||
};
|
||||
|
||||
In board DTS file the pdmclk needs to be added:
|
||||
|
||||
&mcpdm {
|
||||
clocks = <&twl6040>;
|
||||
clock-names = "pdmclk";
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -373,6 +373,8 @@ 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
|
||||
- clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn
|
||||
is asynchronizes with lr-clock.
|
||||
|
||||
SSI subnode properties:
|
||||
- interrupts : Should contain SSI interrupt for PIO transfer
|
||||
|
|
|
@ -23,6 +23,11 @@ Required properties:
|
|||
- rockchip,playback-channels: max playback channels, if not set, 8 channels default.
|
||||
- rockchip,capture-channels: max capture channels, if not set, 2 channels default.
|
||||
|
||||
Required properties for controller which support multi channels
|
||||
playback/capture:
|
||||
|
||||
- rockchip,grf: the phandle of the syscon node for GRF register.
|
||||
|
||||
Example for rk3288 I2S controller:
|
||||
|
||||
i2s@ff890000 {
|
||||
|
|
|
@ -13,16 +13,7 @@
|
|||
#define __SIMPLE_CARD_H
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
struct asoc_simple_dai {
|
||||
const char *name;
|
||||
unsigned int sysclk;
|
||||
int slots;
|
||||
int slot_width;
|
||||
unsigned int tx_slot_mask;
|
||||
unsigned int rx_slot_mask;
|
||||
struct clk *clk;
|
||||
};
|
||||
#include <sound/simple_card_utils.h>
|
||||
|
||||
struct asoc_simple_card_info {
|
||||
const char *name;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* simple_card_core.h
|
||||
*
|
||||
* Copyright (c) 2016 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 __SIMPLE_CARD_CORE_H
|
||||
#define __SIMPLE_CARD_CORE_H
|
||||
|
||||
#include <sound/soc.h>
|
||||
|
||||
struct asoc_simple_dai {
|
||||
const char *name;
|
||||
unsigned int sysclk;
|
||||
int slots;
|
||||
int slot_width;
|
||||
unsigned int tx_slot_mask;
|
||||
unsigned int rx_slot_mask;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
int asoc_simple_card_parse_daifmt(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt);
|
||||
int asoc_simple_card_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...);
|
||||
int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
||||
char *prefix);
|
||||
|
||||
#endif /* __SIMPLE_CARD_CORE_H */
|
|
@ -1,4 +1,8 @@
|
|||
config SND_SIMPLE_CARD_UTILS
|
||||
tristate
|
||||
|
||||
config SND_SIMPLE_CARD
|
||||
tristate "ASoC Simple sound card support"
|
||||
select SND_SIMPLE_CARD_UTILS
|
||||
help
|
||||
This option enables generic simple sound card support
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) := simple-card-utils.o
|
||||
|
||||
snd-soc-simple-card-objs := simple-card.o
|
||||
|
||||
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* simple-card-core.c
|
||||
*
|
||||
* Copyright (c) 2016 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 <linux/of.h>
|
||||
#include <sound/simple_card_utils.h>
|
||||
|
||||
int asoc_simple_card_parse_daifmt(struct device *dev,
|
||||
struct device_node *node,
|
||||
struct device_node *codec,
|
||||
char *prefix,
|
||||
unsigned int *retfmt)
|
||||
{
|
||||
struct device_node *bitclkmaster = NULL;
|
||||
struct device_node *framemaster = NULL;
|
||||
int prefix_len = prefix ? strlen(prefix) : 0;
|
||||
unsigned int daifmt;
|
||||
|
||||
daifmt = snd_soc_of_parse_daifmt(node, prefix,
|
||||
&bitclkmaster, &framemaster);
|
||||
daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
|
||||
|
||||
if (prefix_len && !bitclkmaster && !framemaster) {
|
||||
/*
|
||||
* No dai-link level and master setting was not found from
|
||||
* sound node level, revert back to legacy DT parsing and
|
||||
* take the settings from codec node.
|
||||
*/
|
||||
dev_dbg(dev, "Revert to legacy daifmt parsing\n");
|
||||
|
||||
daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
|
||||
(daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
|
||||
} else {
|
||||
if (codec == bitclkmaster)
|
||||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
|
||||
else
|
||||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
|
||||
}
|
||||
|
||||
of_node_put(bitclkmaster);
|
||||
of_node_put(framemaster);
|
||||
|
||||
*retfmt = daifmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
|
||||
|
||||
int asoc_simple_card_set_dailink_name(struct device *dev,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *name = NULL;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
va_start(ap, fmt);
|
||||
name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (name) {
|
||||
ret = 0;
|
||||
|
||||
dai_link->name = name;
|
||||
dai_link->stream_name = name;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
|
||||
|
||||
int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
|
||||
char *prefix)
|
||||
{
|
||||
char prop[128];
|
||||
int ret;
|
||||
|
||||
snprintf(prop, sizeof(prop), "%sname", prefix);
|
||||
|
||||
/* Parse the card name from DT */
|
||||
ret = snd_soc_of_parse_card_name(card, prop);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!card->name && card->dai_link)
|
||||
card->name = card->dai_link->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
|
|
@ -21,6 +21,12 @@
|
|||
#include <sound/soc-dai.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
struct asoc_simple_jack {
|
||||
struct snd_soc_jack jack;
|
||||
struct snd_soc_jack_pin pin;
|
||||
struct snd_soc_jack_gpio gpio;
|
||||
};
|
||||
|
||||
struct simple_card_data {
|
||||
struct snd_soc_card snd_card;
|
||||
struct simple_dai_props {
|
||||
|
@ -29,10 +35,8 @@ struct simple_card_data {
|
|||
unsigned int mclk_fs;
|
||||
} *dai_props;
|
||||
unsigned int mclk_fs;
|
||||
int gpio_hp_det;
|
||||
int gpio_hp_det_invert;
|
||||
int gpio_mic_det;
|
||||
int gpio_mic_det_invert;
|
||||
struct asoc_simple_jack hp_jack;
|
||||
struct asoc_simple_jack mic_jack;
|
||||
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
|
||||
};
|
||||
|
||||
|
@ -40,6 +44,69 @@ struct simple_card_data {
|
|||
#define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
|
||||
#define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
|
||||
|
||||
#define PREFIX "simple-audio-card,"
|
||||
|
||||
#define asoc_simple_card_init_hp(card, sjack, prefix)\
|
||||
asoc_simple_card_init_jack(card, sjack, 1, prefix)
|
||||
#define asoc_simple_card_init_mic(card, sjack, prefix)\
|
||||
asoc_simple_card_init_jack(card, sjack, 0, prefix)
|
||||
static int asoc_simple_card_init_jack(struct snd_soc_card *card,
|
||||
struct asoc_simple_jack *sjack,
|
||||
int is_hp, char *prefix)
|
||||
{
|
||||
struct device *dev = card->dev;
|
||||
enum of_gpio_flags flags;
|
||||
char prop[128];
|
||||
char *pin_name;
|
||||
char *gpio_name;
|
||||
int mask;
|
||||
int det;
|
||||
|
||||
sjack->gpio.gpio = -ENOENT;
|
||||
|
||||
if (is_hp) {
|
||||
snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
|
||||
pin_name = "Headphones";
|
||||
gpio_name = "Headphone detection";
|
||||
mask = SND_JACK_HEADPHONE;
|
||||
} else {
|
||||
snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
|
||||
pin_name = "Mic Jack";
|
||||
gpio_name = "Mic detection";
|
||||
mask = SND_JACK_MICROPHONE;
|
||||
}
|
||||
|
||||
det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
|
||||
if (det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (gpio_is_valid(det)) {
|
||||
sjack->pin.pin = pin_name;
|
||||
sjack->pin.mask = mask;
|
||||
|
||||
sjack->gpio.name = gpio_name;
|
||||
sjack->gpio.report = mask;
|
||||
sjack->gpio.gpio = det;
|
||||
sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
|
||||
sjack->gpio.debounce_time = 150;
|
||||
|
||||
snd_soc_card_jack_new(card, pin_name, mask,
|
||||
&sjack->jack,
|
||||
&sjack->pin, 1);
|
||||
|
||||
snd_soc_jack_add_gpios(&sjack->jack, 1,
|
||||
&sjack->gpio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asoc_simple_card_remove_jack(struct asoc_simple_jack *sjack)
|
||||
{
|
||||
if (gpio_is_valid(sjack->gpio.gpio))
|
||||
snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio);
|
||||
}
|
||||
|
||||
static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
|
@ -110,32 +177,6 @@ static struct snd_soc_ops asoc_simple_card_ops = {
|
|||
.hw_params = asoc_simple_card_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack simple_card_hp_jack;
|
||||
static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headphones",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
};
|
||||
static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
|
||||
.name = "Headphone detection",
|
||||
.report = SND_JACK_HEADPHONE,
|
||||
.debounce_time = 150,
|
||||
};
|
||||
|
||||
static struct snd_soc_jack simple_card_mic_jack;
|
||||
static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
|
||||
{
|
||||
.pin = "Mic Jack",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
};
|
||||
static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
|
||||
.name = "Mic detection",
|
||||
.report = SND_JACK_MICROPHONE,
|
||||
.debounce_time = 150,
|
||||
};
|
||||
|
||||
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
|
||||
struct asoc_simple_dai *set)
|
||||
{
|
||||
|
@ -184,30 +225,14 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (gpio_is_valid(priv->gpio_hp_det)) {
|
||||
snd_soc_card_jack_new(rtd->card, "Headphones",
|
||||
SND_JACK_HEADPHONE,
|
||||
&simple_card_hp_jack,
|
||||
simple_card_hp_jack_pins,
|
||||
ARRAY_SIZE(simple_card_hp_jack_pins));
|
||||
ret = asoc_simple_card_init_hp(rtd->card, &priv->hp_jack, PREFIX);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
|
||||
simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
|
||||
snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
|
||||
&simple_card_hp_jack_gpio);
|
||||
}
|
||||
ret = asoc_simple_card_init_mic(rtd->card, &priv->hp_jack, PREFIX);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (gpio_is_valid(priv->gpio_mic_det)) {
|
||||
snd_soc_card_jack_new(rtd->card, "Mic Jack",
|
||||
SND_JACK_MICROPHONE,
|
||||
&simple_card_mic_jack,
|
||||
simple_card_mic_jack_pins,
|
||||
ARRAY_SIZE(simple_card_mic_jack_pins));
|
||||
simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
|
||||
simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
|
||||
snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
|
||||
&simple_card_mic_jack_gpio);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -223,6 +248,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
|||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Get node via "sound-dai = <&phandle port>"
|
||||
* it will be used as xxx_of_node on soc_bind_dai_link()
|
||||
|
@ -238,9 +266,14 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
|||
*args_count = args.args_count;
|
||||
|
||||
/* Get dai->name */
|
||||
ret = snd_soc_of_get_dai_name(np, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (name) {
|
||||
ret = snd_soc_of_get_dai_name(np, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!dai)
|
||||
return 0;
|
||||
|
||||
/* Parse TDM slot */
|
||||
ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
|
||||
|
@ -275,48 +308,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_parse_daifmt(struct device_node *node,
|
||||
struct simple_card_data *priv,
|
||||
struct device_node *codec,
|
||||
char *prefix, int idx)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
struct device_node *bitclkmaster = NULL;
|
||||
struct device_node *framemaster = NULL;
|
||||
unsigned int daifmt;
|
||||
|
||||
daifmt = snd_soc_of_parse_daifmt(node, prefix,
|
||||
&bitclkmaster, &framemaster);
|
||||
daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
|
||||
|
||||
if (strlen(prefix) && !bitclkmaster && !framemaster) {
|
||||
/*
|
||||
* No dai-link level and master setting was not found from
|
||||
* sound node level, revert back to legacy DT parsing and
|
||||
* take the settings from codec node.
|
||||
*/
|
||||
dev_dbg(dev, "Revert to legacy daifmt parsing\n");
|
||||
|
||||
daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
|
||||
(daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
|
||||
} else {
|
||||
if (codec == bitclkmaster)
|
||||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
|
||||
else
|
||||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
|
||||
}
|
||||
|
||||
dai_link->dai_fmt = daifmt;
|
||||
|
||||
of_node_put(bitclkmaster);
|
||||
of_node_put(framemaster);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asoc_simple_card_dai_link_of(struct device_node *node,
|
||||
struct simple_card_data *priv,
|
||||
int idx,
|
||||
|
@ -328,7 +319,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
struct device_node *cpu = NULL;
|
||||
struct device_node *plat = NULL;
|
||||
struct device_node *codec = NULL;
|
||||
char *name;
|
||||
char prop[128];
|
||||
char *prefix = "";
|
||||
int ret, cpu_args;
|
||||
|
@ -336,7 +326,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
|
||||
/* For single DAI link & old style of DT node */
|
||||
if (is_top_level_node)
|
||||
prefix = "simple-audio-card,";
|
||||
prefix = PREFIX;
|
||||
|
||||
snprintf(prop, sizeof(prop), "%scpu", prefix);
|
||||
cpu = of_get_child_by_name(node, prop);
|
||||
|
@ -353,8 +343,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
ret = asoc_simple_card_parse_daifmt(node, priv,
|
||||
codec, prefix, idx);
|
||||
ret = asoc_simple_card_parse_daifmt(dev, node, codec,
|
||||
prefix, &dai_link->dai_fmt);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
|
@ -374,35 +364,28 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
|
|||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
ret = asoc_simple_card_sub_parse_of(plat, NULL,
|
||||
&dai_link->platform_of_node,
|
||||
NULL, NULL);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
|
||||
if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
|
||||
ret = -EINVAL;
|
||||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
if (plat) {
|
||||
struct of_phandle_args args;
|
||||
|
||||
ret = of_parse_phandle_with_args(plat, "sound-dai",
|
||||
"#sound-dai-cells", 0, &args);
|
||||
dai_link->platform_of_node = args.np;
|
||||
} else {
|
||||
/* Assumes platform == cpu */
|
||||
/* Assumes platform == cpu */
|
||||
if (!dai_link->platform_of_node)
|
||||
dai_link->platform_of_node = dai_link->cpu_of_node;
|
||||
}
|
||||
|
||||
/* DAI link name is created from CPU/CODEC dai name */
|
||||
name = devm_kzalloc(dev,
|
||||
strlen(dai_link->cpu_dai_name) +
|
||||
strlen(dai_link->codec_dai_name) + 2,
|
||||
GFP_KERNEL);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
||||
"%s-%s",
|
||||
dai_link->cpu_dai_name,
|
||||
dai_link->codec_dai_name);
|
||||
if (ret < 0)
|
||||
goto dai_link_of_err;
|
||||
}
|
||||
|
||||
sprintf(name, "%s-%s", dai_link->cpu_dai_name,
|
||||
dai_link->codec_dai_name);
|
||||
dai_link->name = dai_link->stream_name = name;
|
||||
dai_link->ops = &asoc_simple_card_ops;
|
||||
dai_link->init = asoc_simple_card_dai_init;
|
||||
|
||||
|
@ -438,42 +421,35 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
|||
struct simple_card_data *priv)
|
||||
{
|
||||
struct device *dev = simple_priv_to_dev(priv);
|
||||
enum of_gpio_flags flags;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
/* Parse the card name from DT */
|
||||
snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
|
||||
|
||||
/* The off-codec widgets */
|
||||
if (of_property_read_bool(node, "simple-audio-card,widgets")) {
|
||||
if (of_property_read_bool(node, PREFIX "widgets")) {
|
||||
ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
|
||||
"simple-audio-card,widgets");
|
||||
PREFIX "widgets");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* DAPM routes */
|
||||
if (of_property_read_bool(node, "simple-audio-card,routing")) {
|
||||
if (of_property_read_bool(node, PREFIX "routing")) {
|
||||
ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
|
||||
"simple-audio-card,routing");
|
||||
PREFIX "routing");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Factor to mclk, used in hw_params() */
|
||||
ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
|
||||
ret = of_property_read_u32(node, PREFIX "mclk-fs", &val);
|
||||
if (ret == 0)
|
||||
priv->mclk_fs = val;
|
||||
|
||||
dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
|
||||
priv->snd_card.name : "");
|
||||
|
||||
/* Single/Muti DAI link(s) & New style of DT node */
|
||||
if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
|
||||
if (of_get_child_by_name(node, PREFIX "dai-link")) {
|
||||
struct device_node *np = NULL;
|
||||
int i = 0;
|
||||
|
||||
|
@ -494,20 +470,9 @@ static int asoc_simple_card_parse_of(struct device_node *node,
|
|||
return ret;
|
||||
}
|
||||
|
||||
priv->gpio_hp_det = of_get_named_gpio_flags(node,
|
||||
"simple-audio-card,hp-det-gpio", 0, &flags);
|
||||
priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
|
||||
if (priv->gpio_hp_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
priv->gpio_mic_det = of_get_named_gpio_flags(node,
|
||||
"simple-audio-card,mic-det-gpio", 0, &flags);
|
||||
priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
|
||||
if (priv->gpio_mic_det == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!priv->snd_card.name)
|
||||
priv->snd_card.name = priv->snd_card.dai_link->name;
|
||||
ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -536,7 +501,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||
int num_links, ret;
|
||||
|
||||
/* Get the number of DAI links */
|
||||
if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
|
||||
if (np && of_get_child_by_name(np, PREFIX "dai-link"))
|
||||
num_links = of_get_child_count(np);
|
||||
else
|
||||
num_links = 1;
|
||||
|
@ -555,9 +520,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
|
|||
priv->snd_card.dai_link = dai_link;
|
||||
priv->snd_card.num_links = num_links;
|
||||
|
||||
priv->gpio_hp_det = -ENOENT;
|
||||
priv->gpio_mic_det = -ENOENT;
|
||||
|
||||
/* Get room for the other properties */
|
||||
priv->dai_props = devm_kzalloc(dev,
|
||||
sizeof(*priv->dai_props) * num_links,
|
||||
|
@ -624,12 +586,8 @@ static int asoc_simple_card_remove(struct platform_device *pdev)
|
|||
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
||||
struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
|
||||
|
||||
if (gpio_is_valid(priv->gpio_hp_det))
|
||||
snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
|
||||
&simple_card_hp_jack_gpio);
|
||||
if (gpio_is_valid(priv->gpio_mic_det))
|
||||
snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
|
||||
&simple_card_mic_jack_gpio);
|
||||
asoc_simple_card_remove_jack(&priv->hp_jack);
|
||||
asoc_simple_card_remove_jack(&priv->mic_jack);
|
||||
|
||||
return asoc_simple_card_unref(card);
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
|
|||
select SND_SOC_TWL6040
|
||||
select SND_SOC_DMIC
|
||||
select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
|
||||
select CLK_TWL6040
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on OMAP boards using
|
||||
ABE and twl6040 codec. This driver currently supports:
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of_device.h>
|
||||
|
@ -54,6 +55,7 @@ struct omap_mcpdm {
|
|||
unsigned long phys_base;
|
||||
void __iomem *io_base;
|
||||
int irq;
|
||||
struct clk *pdmclk;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
|
@ -66,6 +68,9 @@ struct omap_mcpdm {
|
|||
/* McPDM needs to be restarted due to runtime reconfiguration */
|
||||
bool restart;
|
||||
|
||||
/* pm state for suspend/resume handling */
|
||||
int pm_active_count;
|
||||
|
||||
struct snd_dmaengine_dai_dma_data dma_data[2];
|
||||
};
|
||||
|
||||
|
@ -173,6 +178,10 @@ static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
|
|||
*/
|
||||
static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
|
||||
{
|
||||
u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
|
||||
|
||||
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
|
||||
|
||||
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
|
||||
MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
|
||||
MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
|
||||
|
@ -258,12 +267,9 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
|
|||
|
||||
mutex_lock(&mcpdm->mutex);
|
||||
|
||||
if (!dai->active) {
|
||||
u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
|
||||
|
||||
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
|
||||
if (!dai->active)
|
||||
omap_mcpdm_open_streams(mcpdm);
|
||||
}
|
||||
|
||||
mutex_unlock(&mcpdm->mutex);
|
||||
|
||||
return 0;
|
||||
|
@ -384,6 +390,7 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
|
|||
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(mcpdm->pdmclk);
|
||||
pm_runtime_enable(mcpdm->dev);
|
||||
|
||||
/* Disable lines while request is ongoing */
|
||||
|
@ -418,15 +425,63 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
|
|||
|
||||
pm_runtime_disable(mcpdm->dev);
|
||||
|
||||
clk_disable_unprepare(mcpdm->pdmclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int omap_mcpdm_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
if (dai->active) {
|
||||
omap_mcpdm_stop(mcpdm);
|
||||
omap_mcpdm_close_streams(mcpdm);
|
||||
}
|
||||
|
||||
mcpdm->pm_active_count = 0;
|
||||
while (pm_runtime_active(mcpdm->dev)) {
|
||||
pm_runtime_put_sync(mcpdm->dev);
|
||||
mcpdm->pm_active_count++;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(mcpdm->pdmclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_mcpdm_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
clk_prepare_enable(mcpdm->pdmclk);
|
||||
|
||||
if (mcpdm->pm_active_count) {
|
||||
while (mcpdm->pm_active_count--)
|
||||
pm_runtime_get_sync(mcpdm->dev);
|
||||
|
||||
if (dai->active) {
|
||||
omap_mcpdm_open_streams(mcpdm);
|
||||
omap_mcpdm_start(mcpdm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define omap_mcpdm_suspend NULL
|
||||
#define omap_mcpdm_resume NULL
|
||||
#endif
|
||||
|
||||
#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
|
||||
#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
|
||||
|
||||
static struct snd_soc_dai_driver omap_mcpdm_dai = {
|
||||
.probe = omap_mcpdm_probe,
|
||||
.remove = omap_mcpdm_remove,
|
||||
.suspend = omap_mcpdm_suspend,
|
||||
.resume = omap_mcpdm_resume,
|
||||
.probe_order = SND_SOC_COMP_ORDER_LATE,
|
||||
.remove_order = SND_SOC_COMP_ORDER_EARLY,
|
||||
.playback = {
|
||||
|
@ -494,6 +549,15 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
|
|||
|
||||
mcpdm->dev = &pdev->dev;
|
||||
|
||||
mcpdm->pdmclk = devm_clk_get(&pdev->dev, "pdmclk");
|
||||
if (IS_ERR(mcpdm->pdmclk)) {
|
||||
if (PTR_ERR(mcpdm->pdmclk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_warn(&pdev->dev, "Error getting pdmclk (%ld)!\n",
|
||||
PTR_ERR(mcpdm->pdmclk));
|
||||
mcpdm->pdmclk = NULL;
|
||||
}
|
||||
|
||||
ret = devm_snd_soc_register_component(&pdev->dev,
|
||||
&omap_mcpdm_component,
|
||||
&omap_mcpdm_dai, 1);
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -23,6 +25,11 @@
|
|||
|
||||
#define DRV_NAME "rockchip-i2s"
|
||||
|
||||
struct rk_i2s_pins {
|
||||
u32 reg_offset;
|
||||
u32 shift;
|
||||
};
|
||||
|
||||
struct rk_i2s_dev {
|
||||
struct device *dev;
|
||||
|
||||
|
@ -33,6 +40,7 @@ struct rk_i2s_dev {
|
|||
struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap *grf;
|
||||
|
||||
/*
|
||||
* Used to indicate the tx/rx status.
|
||||
|
@ -42,6 +50,7 @@ struct rk_i2s_dev {
|
|||
bool tx_start;
|
||||
bool rx_start;
|
||||
bool is_master_mode;
|
||||
const struct rk_i2s_pins *pins;
|
||||
};
|
||||
|
||||
static int i2s_runtime_suspend(struct device *dev)
|
||||
|
@ -300,14 +309,38 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
|
||||
val);
|
||||
|
||||
if (!IS_ERR(i2s->grf) && i2s->pins) {
|
||||
regmap_read(i2s->regmap, I2S_TXCR, &val);
|
||||
val &= I2S_TXCR_CSR_MASK;
|
||||
|
||||
switch (val) {
|
||||
case I2S_CHN_4:
|
||||
val = I2S_IO_4CH_OUT_6CH_IN;
|
||||
break;
|
||||
case I2S_CHN_6:
|
||||
val = I2S_IO_6CH_OUT_4CH_IN;
|
||||
break;
|
||||
case I2S_CHN_8:
|
||||
val = I2S_IO_8CH_OUT_2CH_IN;
|
||||
break;
|
||||
default:
|
||||
val = I2S_IO_2CH_OUT_8CH_IN;
|
||||
break;
|
||||
}
|
||||
|
||||
val <<= i2s->pins->shift;
|
||||
val |= (I2S_IO_DIRECTION_MASK << i2s->pins->shift) << 16;
|
||||
regmap_write(i2s->grf, i2s->pins->reg_offset, val);
|
||||
}
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
|
||||
I2S_DMACR_TDL(16));
|
||||
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
|
||||
I2S_DMACR_RDL(16));
|
||||
|
||||
val = I2S_CKR_TRCM_TXRX;
|
||||
if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
|
||||
val = I2S_CKR_TRCM_TXSHARE;
|
||||
if (dai->driver->symmetric_rates && rtd->dai_link->symmetric_rates)
|
||||
val = I2S_CKR_TRCM_TXONLY;
|
||||
|
||||
regmap_update_bits(i2s->regmap, I2S_CKR,
|
||||
I2S_CKR_TRCM_MASK,
|
||||
|
@ -485,9 +518,23 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
|
|||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct rk_i2s_pins rk3399_i2s_pins = {
|
||||
.reg_offset = 0xe220,
|
||||
.shift = 11,
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_i2s_match[] = {
|
||||
{ .compatible = "rockchip,rk3066-i2s", },
|
||||
{ .compatible = "rockchip,rk3188-i2s", },
|
||||
{ .compatible = "rockchip,rk3288-i2s", },
|
||||
{ .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
|
||||
{},
|
||||
};
|
||||
|
||||
static int rockchip_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const struct of_device_id *of_id;
|
||||
struct rk_i2s_dev *i2s;
|
||||
struct snd_soc_dai_driver *soc_dai;
|
||||
struct resource *res;
|
||||
|
@ -501,6 +548,17 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2s->dev = &pdev->dev;
|
||||
|
||||
i2s->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
|
||||
if (!IS_ERR(i2s->grf)) {
|
||||
of_id = of_match_device(rockchip_i2s_match, &pdev->dev);
|
||||
if (!of_id || !of_id->data)
|
||||
return -EINVAL;
|
||||
|
||||
i2s->pins = of_id->data;
|
||||
}
|
||||
|
||||
/* try to prepare related clocks */
|
||||
i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
|
||||
if (IS_ERR(i2s->hclk)) {
|
||||
|
@ -540,7 +598,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
|
|||
i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
i2s->capture_dma_data.maxburst = 4;
|
||||
|
||||
i2s->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, i2s);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
@ -606,14 +663,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rockchip_i2s_match[] = {
|
||||
{ .compatible = "rockchip,rk3066-i2s", },
|
||||
{ .compatible = "rockchip,rk3188-i2s", },
|
||||
{ .compatible = "rockchip,rk3288-i2s", },
|
||||
{ .compatible = "rockchip,rk3399-i2s", },
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops rockchip_i2s_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
|
||||
NULL)
|
||||
|
|
|
@ -81,8 +81,8 @@
|
|||
#define I2S_CKR_TRCM_SHIFT 28
|
||||
#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_TRCM_TXSHARE (1 << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_TRCM_RXSHARE (2 << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
|
||||
#define I2S_CKR_MSS_SHIFT 27
|
||||
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
|
||||
|
@ -236,4 +236,11 @@ enum {
|
|||
#define I2S_TXDR (0x0024)
|
||||
#define I2S_RXDR (0x0028)
|
||||
|
||||
/* io direction cfg register */
|
||||
#define I2S_IO_DIRECTION_MASK (7)
|
||||
#define I2S_IO_8CH_OUT_2CH_IN (0)
|
||||
#define I2S_IO_6CH_OUT_4CH_IN (4)
|
||||
#define I2S_IO_4CH_OUT_6CH_IN (6)
|
||||
#define I2S_IO_2CH_OUT_8CH_IN (7)
|
||||
|
||||
#endif /* _ROCKCHIP_IIS_H */
|
||||
|
|
|
@ -34,13 +34,18 @@
|
|||
#define DRV_NAME "rockchip-snd-max98090"
|
||||
|
||||
static struct snd_soc_jack headset_jack;
|
||||
|
||||
/* Headset jack detection DAPM pins */
|
||||
static struct snd_soc_jack_pin headset_jack_pins[] = {
|
||||
{
|
||||
.pin = "Headset Jack",
|
||||
.mask = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
|
||||
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3,
|
||||
.pin = "Headphone",
|
||||
.mask = SND_JACK_HEADPHONE,
|
||||
},
|
||||
{
|
||||
.pin = "Headset Mic",
|
||||
.mask = SND_JACK_MICROPHONE,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
|
||||
|
@ -53,7 +58,7 @@ static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
|
|||
static const struct snd_soc_dapm_route rk_audio_map[] = {
|
||||
{"IN34", NULL, "Headset Mic"},
|
||||
{"IN34", NULL, "MICBIAS"},
|
||||
{"MICBIAS", NULL, "Headset Mic"},
|
||||
{"Headset Mic", NULL, "MICBIAS"},
|
||||
{"DMICL", NULL, "Int Mic"},
|
||||
{"Headphone", NULL, "HPL"},
|
||||
{"Headphone", NULL, "HPR"},
|
||||
|
@ -114,43 +119,27 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int rk_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
/* Enable Headset and 4 Buttons Jack detection */
|
||||
return snd_soc_card_jack_new(runtime->card, "Headset Jack",
|
||||
SND_JACK_HEADSET |
|
||||
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3,
|
||||
&headset_jack,
|
||||
headset_jack_pins,
|
||||
ARRAY_SIZE(headset_jack_pins));
|
||||
}
|
||||
|
||||
static int rk_98090_headset_init(struct snd_soc_component *component)
|
||||
{
|
||||
return ts3a227e_enable_jack_detect(component, &headset_jack);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops rk_aif1_ops = {
|
||||
.hw_params = rk_aif1_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_aux_dev rk_98090_headset_dev = {
|
||||
.name = "Headset Chip",
|
||||
.init = rk_98090_headset_init,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link rk_dailink = {
|
||||
.name = "max98090",
|
||||
.stream_name = "Audio",
|
||||
.codec_dai_name = "HiFi",
|
||||
.init = rk_init,
|
||||
.ops = &rk_aif1_ops,
|
||||
/* set max98090 as slave */
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
SND_SOC_DAIFMT_CBS_CFS,
|
||||
};
|
||||
|
||||
static int rk_98090_headset_init(struct snd_soc_component *component);
|
||||
|
||||
static struct snd_soc_aux_dev rk_98090_headset_dev = {
|
||||
.name = "Headset Chip",
|
||||
.init = rk_98090_headset_init,
|
||||
};
|
||||
|
||||
static struct snd_soc_card snd_soc_card_rk = {
|
||||
.name = "ROCKCHIP-I2S",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -166,6 +155,26 @@ static struct snd_soc_card snd_soc_card_rk = {
|
|||
.num_controls = ARRAY_SIZE(rk_mc_controls),
|
||||
};
|
||||
|
||||
static int rk_98090_headset_init(struct snd_soc_component *component)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Enable Headset and 4 Buttons Jack detection */
|
||||
ret = snd_soc_card_jack_new(&snd_soc_card_rk, "Headset Jack",
|
||||
SND_JACK_HEADSET |
|
||||
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
|
||||
SND_JACK_BTN_2 | SND_JACK_BTN_3,
|
||||
&headset_jack,
|
||||
headset_jack_pins,
|
||||
ARRAY_SIZE(headset_jack_pins));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ts3a227e_enable_jack_detect(component, &headset_jack);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int snd_rk_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
|
|
@ -101,21 +101,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
|
|||
int ret;
|
||||
|
||||
srate = params_rate(params);
|
||||
switch (srate) {
|
||||
case 32000:
|
||||
case 48000:
|
||||
case 96000:
|
||||
mclk = 96000 * 128; /* 12288000 hz */
|
||||
break;
|
||||
case 44100:
|
||||
mclk = 44100 * 256; /* 11289600 hz */
|
||||
break;
|
||||
case 192000:
|
||||
mclk = 192000 * 128; /* 24576000 hz */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
mclk = srate * 128;
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
|
@ -139,7 +125,6 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
|
|||
return ret;
|
||||
}
|
||||
|
||||
val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
|
||||
ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
|
||||
SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
|
||||
SDPIF_CFGR_VDW_MASK,
|
||||
|
|
|
@ -44,6 +44,7 @@ config SND_SOC_RCAR
|
|||
|
||||
config SND_SOC_RSRC_CARD
|
||||
tristate "Renesas Sampling Rate Convert Sound Card"
|
||||
select SND_SIMPLE_CARD_UTILS
|
||||
help
|
||||
This option enables simple sound if you need sampling rate convert
|
||||
|
||||
|
|
|
@ -33,11 +33,15 @@ struct rsnd_adg {
|
|||
struct clk *clkout[CLKOUTMAX];
|
||||
struct clk_onecell_data onecell;
|
||||
struct rsnd_mod mod;
|
||||
u32 flags;
|
||||
|
||||
int rbga_rate_for_441khz; /* RBGA */
|
||||
int rbgb_rate_for_48khz; /* RBGB */
|
||||
};
|
||||
|
||||
#define LRCLK_ASYNC (1 << 0)
|
||||
#define adg_mode_flags(adg) (adg->flags)
|
||||
|
||||
#define for_each_rsnd_clk(pos, adg, i) \
|
||||
for (i = 0; \
|
||||
(i < CLKMAX) && \
|
||||
|
@ -355,6 +359,16 @@ found_clock:
|
|||
|
||||
rsnd_adg_set_ssi_clk(ssi_mod, data);
|
||||
|
||||
if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
|
||||
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
|
||||
u32 ckr = 0;
|
||||
|
||||
if (0 == (rate % 8000))
|
||||
ckr = 0x80000000;
|
||||
|
||||
rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
|
||||
rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
|
||||
data, rate);
|
||||
|
@ -532,6 +546,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
|
|||
{
|
||||
struct rsnd_adg *adg;
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
|
||||
if (!adg) {
|
||||
|
@ -545,6 +560,9 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
|
|||
rsnd_adg_get_clkin(priv, adg);
|
||||
rsnd_adg_get_clkout(priv, adg);
|
||||
|
||||
if (of_get_property(np, "clkout-lr-asynchronous", NULL))
|
||||
adg->flags = LRCLK_ASYNC;
|
||||
|
||||
priv->adg = adg;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -206,7 +206,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
|
|||
*/
|
||||
static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
const static struct rsnd_regmap_field_conf conf_ssiu[] = {
|
||||
static const 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),
|
||||
|
@ -221,7 +221,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||
RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
|
||||
};
|
||||
|
||||
const static struct rsnd_regmap_field_conf conf_scu[] = {
|
||||
static const 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),
|
||||
|
@ -308,7 +308,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||
RSND_GEN_M_REG(DVC_VOL7R, 0xe44, 0x100),
|
||||
RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
|
||||
};
|
||||
const static struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
static const 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),
|
||||
|
@ -328,7 +328,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||
RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
|
||||
RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
|
||||
};
|
||||
const static struct rsnd_regmap_field_conf conf_ssi[] = {
|
||||
static const 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),
|
||||
|
@ -359,14 +359,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
|
|||
|
||||
static int rsnd_gen1_probe(struct rsnd_priv *priv)
|
||||
{
|
||||
const static struct rsnd_regmap_field_conf conf_adg[] = {
|
||||
static const 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),
|
||||
};
|
||||
const static struct rsnd_regmap_field_conf conf_ssi[] = {
|
||||
static const 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),
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <sound/jack.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
#include <sound/simple_card_utils.h>
|
||||
|
||||
struct rsrc_card_of_data {
|
||||
const char *prefix;
|
||||
|
@ -46,25 +47,13 @@ static const struct of_device_id rsrc_card_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
|
||||
|
||||
#define DAI_NAME_NUM 32
|
||||
struct rsrc_card_dai {
|
||||
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];
|
||||
};
|
||||
|
||||
#define IDX_CPU 0
|
||||
#define IDX_CODEC 1
|
||||
struct rsrc_card_priv {
|
||||
struct snd_soc_card snd_card;
|
||||
struct snd_soc_codec_conf codec_conf;
|
||||
struct rsrc_card_dai *dai_props;
|
||||
struct asoc_simple_dai *dai_props;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
int dai_num;
|
||||
u32 convert_rate;
|
||||
u32 convert_channels;
|
||||
};
|
||||
|
@ -77,7 +66,7 @@ static int rsrc_card_startup(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct rsrc_card_dai *dai_props =
|
||||
struct asoc_simple_dai *dai_props =
|
||||
rsrc_priv_to_props(priv, rtd->num);
|
||||
|
||||
return clk_prepare_enable(dai_props->clk);
|
||||
|
@ -87,7 +76,7 @@ static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct rsrc_card_dai *dai_props =
|
||||
struct asoc_simple_dai *dai_props =
|
||||
rsrc_priv_to_props(priv, rtd->num);
|
||||
|
||||
clk_disable_unprepare(dai_props->clk);
|
||||
|
@ -103,7 +92,7 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
|
|||
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
|
||||
struct snd_soc_dai *dai;
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct rsrc_card_dai *dai_props;
|
||||
struct asoc_simple_dai *dai_props;
|
||||
int num = rtd->num;
|
||||
int ret;
|
||||
|
||||
|
@ -159,44 +148,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rsrc_card_parse_daifmt(struct device_node *node,
|
||||
struct device_node *codec,
|
||||
struct rsrc_card_priv *priv,
|
||||
struct snd_soc_dai_link *dai_link,
|
||||
unsigned int *retfmt)
|
||||
{
|
||||
struct device_node *bitclkmaster = NULL;
|
||||
struct device_node *framemaster = NULL;
|
||||
unsigned int daifmt;
|
||||
|
||||
daifmt = snd_soc_of_parse_daifmt(node, NULL,
|
||||
&bitclkmaster, &framemaster);
|
||||
daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
|
||||
|
||||
if (!bitclkmaster && !framemaster)
|
||||
return -EINVAL;
|
||||
|
||||
if (codec == bitclkmaster)
|
||||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
|
||||
else
|
||||
daifmt |= (codec == framemaster) ?
|
||||
SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
|
||||
|
||||
of_node_put(bitclkmaster);
|
||||
of_node_put(framemaster);
|
||||
|
||||
*retfmt = daifmt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsrc_card_parse_links(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);
|
||||
struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
|
@ -232,9 +190,11 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set dai_name */
|
||||
snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
|
||||
dai_link->cpu_dai_name);
|
||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
||||
"fe.%s",
|
||||
dai_link->cpu_dai_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* In soc_bind_dai_link() will check cpu name after
|
||||
|
@ -248,7 +208,6 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
if (!args.args_count)
|
||||
dai_link->cpu_dai_name = NULL;
|
||||
} else {
|
||||
struct device *dev = rsrc_priv_to_dev(priv);
|
||||
const struct rsrc_card_of_data *of_data;
|
||||
|
||||
of_data = of_device_get_match_data(dev);
|
||||
|
@ -266,6 +225,12 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
|
||||
"be.%s",
|
||||
dai_link->codec_dai_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* additional name prefix */
|
||||
if (of_data) {
|
||||
priv->codec_conf.of_node = dai_link->codec_of_node;
|
||||
|
@ -276,18 +241,12 @@ static int rsrc_card_parse_links(struct device_node *np,
|
|||
dai_link->codec_of_node,
|
||||
"audio-prefix");
|
||||
}
|
||||
|
||||
/* set dai_name */
|
||||
snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s",
|
||||
dai_link->codec_dai_name);
|
||||
}
|
||||
|
||||
/* Simple Card assumes platform == cpu */
|
||||
dai_link->platform_of_node = dai_link->cpu_of_node;
|
||||
dai_link->dpcm_playback = 1;
|
||||
dai_link->dpcm_capture = 1;
|
||||
dai_link->name = dai_props->dai_name;
|
||||
dai_link->stream_name = dai_props->dai_name;
|
||||
dai_link->ops = &rsrc_card_ops;
|
||||
dai_link->init = rsrc_card_dai_init;
|
||||
|
||||
|
@ -299,7 +258,7 @@ static int rsrc_card_parse_clk(struct device_node *np,
|
|||
int idx, bool is_fe)
|
||||
{
|
||||
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
|
||||
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
struct clk *clk;
|
||||
struct device_node *of_np = is_fe ? dai_link->cpu_of_node :
|
||||
dai_link->codec_of_node;
|
||||
|
@ -336,7 +295,7 @@ static int rsrc_card_dai_sub_link_of(struct device_node *node,
|
|||
{
|
||||
struct device *dev = rsrc_priv_to_dev(priv);
|
||||
struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
|
||||
struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx);
|
||||
int ret;
|
||||
|
||||
ret = rsrc_card_parse_links(np, priv, idx, is_fe);
|
||||
|
@ -348,7 +307,7 @@ static int rsrc_card_dai_sub_link_of(struct device_node *node,
|
|||
return ret;
|
||||
|
||||
dev_dbg(dev, "\t%s / %04x / %d\n",
|
||||
dai_props->dai_name,
|
||||
dai_link->name,
|
||||
dai_link->dai_fmt,
|
||||
dai_props->sysclk);
|
||||
|
||||
|
@ -358,6 +317,7 @@ static int rsrc_card_dai_sub_link_of(struct device_node *node,
|
|||
static int rsrc_card_dai_link_of(struct device_node *node,
|
||||
struct rsrc_card_priv *priv)
|
||||
{
|
||||
struct device *dev = rsrc_priv_to_dev(priv);
|
||||
struct snd_soc_dai_link *dai_link;
|
||||
struct device_node *np;
|
||||
unsigned int daifmt = 0;
|
||||
|
@ -370,8 +330,8 @@ static int rsrc_card_dai_link_of(struct device_node *node,
|
|||
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);
|
||||
ret = asoc_simple_card_parse_daifmt(dev, node, np,
|
||||
NULL, &daifmt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
|
@ -402,7 +362,7 @@ static int rsrc_card_parse_of(struct device_node *node,
|
|||
struct device *dev)
|
||||
{
|
||||
const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);
|
||||
struct rsrc_card_dai *props;
|
||||
struct asoc_simple_dai *props;
|
||||
struct snd_soc_dai_link *links;
|
||||
int ret;
|
||||
int num;
|
||||
|
@ -418,7 +378,6 @@ static int rsrc_card_parse_of(struct device_node *node,
|
|||
|
||||
priv->dai_props = props;
|
||||
priv->dai_link = links;
|
||||
priv->dai_num = num;
|
||||
|
||||
/* Init snd_soc_card */
|
||||
priv->snd_card.owner = THIS_MODULE;
|
||||
|
@ -436,9 +395,6 @@ static int rsrc_card_parse_of(struct device_node *node,
|
|||
"audio-routing");
|
||||
}
|
||||
|
||||
/* Parse the card name from DT */
|
||||
snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
|
||||
|
||||
/* sampling rate convert */
|
||||
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
|
||||
|
||||
|
@ -454,8 +410,9 @@ static int rsrc_card_parse_of(struct device_node *node,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!priv->snd_card.name)
|
||||
priv->snd_card.name = priv->snd_card.dai_link->name;
|
||||
ret = asoc_simple_card_parse_card_name(&priv->snd_card, "card-");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче