Merge branch 'topic/rcar' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-of-graph

This commit is contained in:
Mark Brown 2017-06-13 21:57:18 +01:00
Родитель 37fae57931 a97a06c7ef
Коммит 03cf603a49
21 изменённых файлов: 1032 добавлений и 229 удалений

Просмотреть файл

@ -0,0 +1,117 @@
Audio-Graph-SCU-Card:
Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM".
It is based on common bindings for device graphs.
see ${LINUX}/Documentation/devicetree/bindings/graph.txt
Basically, Audio-Graph-SCU-Card property is same as
Simple-Card / Simple-SCU-Card / Audio-Graph-Card.
see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt
${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
Below are same as Simple-Card / Audio-Graph-Card.
- label
- dai-format
- frame-master
- bitclock-master
- bitclock-inversion
- frame-inversion
- dai-tdm-slot-num
- dai-tdm-slot-width
- clocks / system-clock-frequency
Below are same as Simple-SCU-Card.
- convert-rate
- convert-channels
- prefix
- routing
Required properties:
- compatible : "audio-graph-scu-card";
- dais : list of CPU DAI port{s}
Example 1. Sampling Rate Conversion
sound_card {
compatible = "audio-graph-scu-card";
label = "sound-card";
prefix = "codec";
routing = "codec Playback", "DAI0 Playback",
"codec Playback", "DAI1 Playback";
convert-rate = <48000>;
dais = <&cpu_port>;
};
audio-codec {
...
port {
codec_endpoint: endpoint {
remote-endpoint = <&cpu_endpoint>;
};
};
};
dai-controller {
...
cpu_port: port {
cpu_endpoint: endpoint {
remote-endpoint = <&codec_endpoint>;
dai-format = "left_j";
...
};
};
};
Example 2. 2 CPU 1 Codec (Mixing)
sound_card {
compatible = "audio-graph-scu-card";
label = "sound-card";
prefix = "codec";
routing = "codec Playback", "DAI0 Playback",
"codec Playback", "DAI1 Playback";
convert-rate = <48000>;
dais = <&cpu_port0
&cpu_port1>;
};
audio-codec {
...
port {
codec_endpoint: endpoint {
remote-endpoint = <&cpu_endpoint0>;
};
};
};
dai-controller {
...
ports {
cpu_port0: port {
cpu_endpoint0: endpoint {
remote-endpoint = <&codec_endpoint>;
dai-format = "left_j";
...
};
};
cpu_port1: port {
cpu_endpoint1: endpoint {
dai-format = "left_j";
...
};
};
};
};

Просмотреть файл

@ -83,11 +83,11 @@ SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
** Asynchronous mode
------------------
You need to use "renesas,rsrc-card" sound card for it.
You need to use "simple-scu-audio-card" sound card for it.
example)
sound {
compatible = "renesas,rsrc-card";
compatible = "simple-scu-audio-card";
...
/*
* SRC Asynchronous mode setting
@ -97,12 +97,12 @@ example)
* Inputed 48kHz data will be converted to
* system specified Hz
*/
convert-rate = <48000>;
simple-audio-card,convert-rate = <48000>;
...
cpu {
simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
};
codec {
simple-audio-card,codec {
...
};
};
@ -141,23 +141,23 @@ For more detail information, see below
${LINUX}/sound/soc/sh/rcar/ctu.c
- comment of header
You need to use "renesas,rsrc-card" sound card for it.
You need to use "simple-scu-audio-card" sound card for it.
example)
sound {
compatible = "renesas,rsrc-card";
compatible = "simple-scu-audio-card";
...
/*
* CTU setting
* All input data will be converted to 2ch
* as output data
*/
convert-channels = <2>;
simple-audio-card,convert-channels = <2>;
...
cpu {
simple-audio-card,cpu {
sound-dai = <&rcar_sound>;
};
codec {
simple-audio-card,codec {
...
};
};
@ -190,22 +190,22 @@ and these sounds will be merged by MIX.
aplay -D plughw:0,0 xxxx.wav &
aplay -D plughw:0,1 yyyy.wav
You need to use "renesas,rsrc-card" sound card for it.
You need to use "simple-scu-audio-card" sound card for it.
Ex)
[MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
|
[MEM] -> [SRC2] -> [CTU03] -+
sound {
compatible = "renesas,rsrc-card";
compatible = "simple-scu-audio-card";
...
cpu@0 {
simple-audio-card,cpu@0 {
sound-dai = <&rcar_sound 0>;
};
cpu@1 {
simple-audio-card,cpu@1 {
sound-dai = <&rcar_sound 1>;
};
codec {
simple-audio-card,codec {
...
};
};
@ -368,6 +368,10 @@ Required properties:
see below for detail.
- #sound-dai-cells : it must be 0 if your system is using single DAI
it must be 1 if your system is using multi DAI
- clocks : References to SSI/SRC/MIX/CTU/DVC/AUDIO_CLK clocks.
- clock-names : List of necessary clock names.
"ssi-all", "ssi.X", "src.X", "mix.X", "ctu.X",
"dvc.X", "clk_a", "clk_b", "clk_c", "clk_i"
Optional properties:
- #clock-cells : it must be 0 if your system has audio_clkout
@ -375,6 +379,9 @@ Optional properties:
- clock-frequency : for all audio_clkout0/1/2/3
- clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn
is asynchronizes with lr-clock.
- resets : References to SSI resets.
- reset-names : List of valid reset names.
"ssi-all", "ssi.X"
SSI subnode properties:
- interrupts : Should contain SSI interrupt for PIO transfer

Просмотреть файл

@ -38,7 +38,7 @@ Optional CPU/CODEC subnodes properties:
- clocks / system-clock-frequency : see simple-audio-card.txt
Example 1. Sampling Rate Covert
Example 1. Sampling Rate Conversion
sound {
compatible = "simple-scu-audio-card";

Просмотреть файл

@ -35,13 +35,16 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
char *prefix);
#define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \
asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai)
asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \
dai_link->cpu_dai_name)
#define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \
asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai)
asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\
dai_link->codec_dai_name)
int asoc_simple_card_parse_clk(struct device *dev,
struct device_node *node,
struct device_node *dai_of_node,
struct asoc_simple_dai *simple_dai);
struct asoc_simple_dai *simple_dai,
const char *name);
#define asoc_simple_card_parse_cpu(node, dai_link, \
list_name, cells_name, is_single_link) \

Просмотреть файл

@ -22,3 +22,12 @@ config SND_AUDIO_GRAPH_CARD
help
This option enables generic simple simple sound card support
with OF-graph DT bindings.
config SND_AUDIO_GRAPH_SCU_CARD
tristate "ASoC Audio Graph SCU sound card support"
depends on OF
select SND_SIMPLE_CARD_UTILS
help
This option enables generic simple SCU sound card support
with OF-graph DT bindings.
It supports DPCM of multi CPU single Codec ststem.

Просмотреть файл

@ -2,8 +2,10 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o
snd-soc-simple-card-objs := simple-card.o
snd-soc-simple-scu-card-objs := simple-scu-card.o
snd-soc-audio-graph-card-objs := audio-graph-card.o
snd-soc-audio-graph-scu-card-objs := audio-graph-scu-card.o
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o
obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o
obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-card.o

Просмотреть файл

@ -169,15 +169,6 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
dai_link->ops = &asoc_graph_card_ops;
dai_link->init = asoc_graph_card_dai_init;
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
dev_dbg(dev, "\tcpu : %s / %d\n",
dai_link->cpu_dai_name,
cpu_dai->sysclk);
dev_dbg(dev, "\tcodec : %s / %d\n",
dai_link->codec_dai_name,
codec_dai->sysclk);
asoc_simple_card_canonicalize_cpu(dai_link,
card->num_links == 1);

Просмотреть файл

@ -0,0 +1,411 @@
/*
* ASoC audio graph SCU sound card support
*
* Copyright (C) 2017 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* based on
* ${LINUX}/sound/soc/generic/simple-scu-card.c
* ${LINUX}/sound/soc/generic/audio-graph-card.c
*
* 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/clk.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <sound/jack.h>
#include <sound/simple_card_utils.h>
struct graph_card_data {
struct snd_soc_card snd_card;
struct snd_soc_codec_conf codec_conf;
struct asoc_simple_dai *dai_props;
struct snd_soc_dai_link *dai_link;
u32 convert_rate;
u32 convert_channels;
};
#define graph_priv_to_card(priv) (&(priv)->snd_card)
#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
return clk_prepare_enable(dai_props->clk);
}
static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->clk);
}
static struct snd_soc_ops asoc_graph_card_ops = {
.startup = asoc_graph_card_startup,
.shutdown = asoc_graph_card_shutdown,
};
static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *dai;
struct snd_soc_dai_link *dai_link;
struct asoc_simple_dai *dai_props;
int num = rtd->num;
dai_link = graph_priv_to_link(priv, num);
dai_props = graph_priv_to_props(priv, num);
dai = dai_link->dynamic ?
rtd->cpu_dai :
rtd->codec_dai;
return asoc_simple_card_init_dai(dai, dai_props);
}
static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
if (priv->convert_rate)
rate->min =
rate->max = priv->convert_rate;
if (priv->convert_channels)
channels->min =
channels->max = priv->convert_channels;
return 0;
}
static int asoc_graph_card_dai_link_of(struct device_node *ep,
struct graph_card_data *priv,
unsigned int daifmt,
int idx, int is_fe)
{
struct device *dev = graph_priv_to_dev(priv);
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, idx);
struct snd_soc_card *card = graph_priv_to_card(priv);
int ret;
if (is_fe) {
/* BE is dummy */
dai_link->codec_of_node = NULL;
dai_link->codec_dai_name = "snd-soc-dummy-dai";
dai_link->codec_name = "snd-soc-dummy";
/* FE settings */
dai_link->dynamic = 1;
dai_link->dpcm_merged_format = 1;
ret = asoc_simple_card_parse_graph_cpu(ep, dai_link);
if (ret)
return ret;
ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai_props);
if (ret < 0)
return ret;
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
"fe.%s",
dai_link->cpu_dai_name);
if (ret < 0)
return ret;
/* card->num_links includes Codec */
asoc_simple_card_canonicalize_cpu(dai_link,
(card->num_links - 1) == 1);
} else {
/* FE is dummy */
dai_link->cpu_of_node = NULL;
dai_link->cpu_dai_name = "snd-soc-dummy-dai";
dai_link->cpu_name = "snd-soc-dummy";
/* BE settings */
dai_link->no_pcm = 1;
dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup;
ret = asoc_simple_card_parse_graph_codec(ep, dai_link);
if (ret < 0)
return ret;
ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai_props);
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;
snd_soc_of_parse_audio_prefix(card,
&priv->codec_conf,
dai_link->codec_of_node,
"prefix");
}
ret = snd_soc_of_parse_tdm_slot(ep,
&dai_props->tx_slot_mask,
&dai_props->rx_slot_mask,
&dai_props->slots,
&dai_props->slot_width);
if (ret)
return ret;
ret = asoc_simple_card_canonicalize_dailink(dai_link);
if (ret < 0)
return ret;
dai_link->dai_fmt = daifmt;
dai_link->dpcm_playback = 1;
dai_link->dpcm_capture = 1;
dai_link->ops = &asoc_graph_card_ops;
dai_link->init = asoc_graph_card_dai_init;
return 0;
}
static int asoc_graph_card_parse_of(struct graph_card_data *priv)
{
struct of_phandle_iterator it;
struct device *dev = graph_priv_to_dev(priv);
struct snd_soc_card *card = graph_priv_to_card(priv);
struct device_node *node = dev->of_node;
struct device_node *cpu_port;
struct device_node *cpu_ep;
struct device_node *codec_ep;
struct device_node *rcpu_ep;
unsigned int daifmt = 0;
int dai_idx, ret;
int rc, codec;
if (!node)
return -EINVAL;
/*
* we need to consider "widgets", "mclk-fs" around here
* see simple-card
*/
ret = snd_soc_of_parse_audio_routing(card, "routing");
if (ret)
return ret;
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
/* channels transfer */
of_property_read_u32(node, "convert-channels", &priv->convert_channels);
/*
* it supports multi CPU, single CODEC only here
* see asoc_graph_get_dais_count
*/
/* find 1st codec */
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
cpu_port = it.node;
cpu_ep = of_get_next_child(cpu_port, NULL);
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
of_node_put(cpu_port);
of_node_put(cpu_ep);
of_node_put(codec_ep);
of_node_put(rcpu_ep);
if (!codec_ep)
continue;
if (rcpu_ep != cpu_ep) {
dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n",
cpu_ep->name, codec_ep->name, rcpu_ep->name);
ret = -EINVAL;
goto parse_of_err;
}
ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
NULL, &daifmt);
if (ret < 0)
goto parse_of_err;
}
dai_idx = 0;
for (codec = 0; codec < 2; codec++) {
/*
* To listup valid sounds continuously,
* detect all CPU-dummy first, and
* detect all dummy-Codec second
*/
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
cpu_port = it.node;
cpu_ep = of_get_next_child(cpu_port, NULL);
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
of_node_put(cpu_port);
of_node_put(cpu_ep);
of_node_put(codec_ep);
if (codec) {
if (!codec_ep)
continue;
/* Back-End (= Codec) */
ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0);
if (ret < 0)
goto parse_of_err;
} else {
/* Front-End (= CPU) */
ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1);
if (ret < 0)
goto parse_of_err;
}
}
}
ret = asoc_simple_card_parse_card_name(card, NULL);
if (ret)
goto parse_of_err;
dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
ret = 0;
parse_of_err:
return ret;
}
static int asoc_graph_get_dais_count(struct device *dev)
{
struct of_phandle_iterator it;
struct device_node *node = dev->of_node;
struct device_node *cpu_port;
struct device_node *cpu_ep;
struct device_node *codec_ep;
int count = 0;
int rc;
of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
cpu_port = it.node;
cpu_ep = of_get_next_child(cpu_port, NULL);
codec_ep = of_graph_get_remote_endpoint(cpu_ep);
of_node_put(cpu_port);
of_node_put(cpu_ep);
of_node_put(codec_ep);
if (cpu_ep)
count++;
if (codec_ep)
count++;
}
return count;
}
static int asoc_graph_card_probe(struct platform_device *pdev)
{
struct graph_card_data *priv;
struct snd_soc_dai_link *dai_link;
struct asoc_simple_dai *dai_props;
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
int num, ret;
/* Allocate the private data and the DAI link array */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
num = asoc_graph_get_dais_count(dev);
if (num == 0)
return -EINVAL;
dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
if (!dai_props || !dai_link)
return -ENOMEM;
priv->dai_props = dai_props;
priv->dai_link = dai_link;
/* Init snd_soc_card */
card = graph_priv_to_card(priv);
card->owner = THIS_MODULE;
card->dev = dev;
card->dai_link = priv->dai_link;
card->num_links = num;
card->codec_conf = &priv->codec_conf;
card->num_configs = 1;
ret = asoc_graph_card_parse_of(priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);
goto err;
}
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0)
goto err;
return 0;
err:
asoc_simple_card_clean_reference(card);
return ret;
}
static int asoc_graph_card_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
return asoc_simple_card_clean_reference(card);
}
static const struct of_device_id asoc_graph_of_match[] = {
{ .compatible = "audio-graph-scu-card", },
{},
};
MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
static struct platform_driver asoc_graph_card = {
.driver = {
.name = "asoc-audio-graph-scu-card",
.of_match_table = asoc_graph_of_match,
},
.probe = asoc_graph_card_probe,
.remove = asoc_graph_card_remove,
};
module_platform_driver(asoc_graph_card);
MODULE_ALIAS("platform:asoc-audio-graph-scu-card");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

Просмотреть файл

@ -51,6 +51,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev,
*retfmt = daifmt;
dev_dbg(dev, "format : %04x\n", daifmt);
return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
@ -72,6 +74,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev,
dai_link->name = name;
dai_link->stream_name = name;
dev_dbg(dev, "name : %s\n", name);
}
return ret;
@ -100,6 +104,8 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
if (!card->name && card->dai_link)
card->name = card->dai_link->name;
dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : "");
return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
@ -107,7 +113,8 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
int asoc_simple_card_parse_clk(struct device *dev,
struct device_node *node,
struct device_node *dai_of_node,
struct asoc_simple_dai *simple_dai)
struct asoc_simple_dai *simple_dai,
const char *name)
{
struct clk *clk;
u32 val;
@ -130,6 +137,8 @@ int asoc_simple_card_parse_clk(struct device *dev,
simple_dai->sysclk = clk_get_rate(clk);
}
dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk);
return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);

Просмотреть файл

@ -307,15 +307,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
dai_link->ops = &asoc_simple_card_ops;
dai_link->init = asoc_simple_card_dai_init;
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
dev_dbg(dev, "\tcpu : %s / %d\n",
dai_link->cpu_dai_name,
dai_props->cpu_dai.sysclk);
dev_dbg(dev, "\tcodec : %s / %d\n",
dai_link->codec_dai_name,
dai_props->codec_dai.sysclk);
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
dai_link_of_err:
@ -503,8 +494,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card);
if (ret >= 0)
return ret;
if (ret < 0)
goto err;
return 0;
err:
asoc_simple_card_clean_reference(card);

Просмотреть файл

@ -189,21 +189,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
dai_link->ops = &asoc_simple_card_ops;
dai_link->init = asoc_simple_card_dai_init;
dev_dbg(dev, "\t%s / %04x / %d\n",
dai_link->name,
dai_link->dai_fmt,
dai_props->sysclk);
return 0;
}
static int asoc_simple_card_parse_of(struct device_node *node,
struct simple_card_data *priv)
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *np;
struct snd_soc_card *card = simple_priv_to_card(priv);
struct device_node *node = dev->of_node;
unsigned int daifmt = 0;
bool is_fe;
int ret, i;
@ -246,8 +241,6 @@ static int asoc_simple_card_parse_of(struct device_node *node,
if (ret < 0)
return ret;
dev_dbg(dev, "New card: %s\n",
card->name ? card->name : "");
dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
@ -288,7 +281,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
card->codec_conf = &priv->codec_conf;
card->num_configs = 1;
ret = asoc_simple_card_parse_of(np, priv);
ret = asoc_simple_card_parse_of(priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);
@ -298,8 +291,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card);
if (ret >= 0)
return ret;
if (ret < 0)
goto err;
return 0;
err:
asoc_simple_card_clean_reference(card);

Просмотреть файл

@ -38,7 +38,7 @@ config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
depends on COMMON_CLK
depends on OF || COMPILE_TEST
select SND_SIMPLE_CARD
select SND_SIMPLE_CARD_UTILS
select REGMAP_MMIO
help
This option enables R-Car SRU/SCU/SSIU/SSI sound support

Просмотреть файл

@ -480,6 +480,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
if (req_rate[0] % 48000 == 0)
adg->flags = AUDIO_OUT_48;
if (of_get_property(np, "clkout-lr-asynchronous", NULL))
adg->flags = LRCLK_ASYNC;
/*
* This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
* have 44.1kHz or 48kHz base clocks for now.
@ -507,7 +510,8 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
rbga = rbgx;
adg->rbga_rate_for_441khz = rate / div;
ckr |= brg_table[i] << 20;
if (req_441kHz_rate)
if (req_441kHz_rate &&
!(adg_mode_flags(adg) & AUDIO_OUT_48))
parent_clk_name = __clk_get_name(clk);
}
}
@ -522,7 +526,8 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
rbgb = rbgx;
adg->rbgb_rate_for_48khz = rate / div;
ckr |= brg_table[i] << 16;
if (req_48kHz_rate)
if (req_48kHz_rate &&
(adg_mode_flags(adg) & AUDIO_OUT_48))
parent_clk_name = __clk_get_name(clk);
}
}
@ -553,7 +558,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
clk = clk_register_fixed_rate(dev, clkout_name[i],
parent_clk_name, 0,
req_rate[0]);
adg->clkout[i] = ERR_PTR(-ENOENT);
if (!IS_ERR(clk))
adg->clkout[i] = clk;
}
@ -578,7 +582,6 @@ 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;
int ret;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
@ -595,9 +598,6 @@ 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;
rsnd_adg_clk_enable(priv);

Просмотреть файл

@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
[9] = 0x2,
};
if (unlikely(!src))
return -EIO;
data = path[rsnd_mod_id(src)] |
cmd_case[rsnd_mod_id(src)] << 16;
}
@ -89,6 +92,7 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
dev_dbg(dev, "ctu/mix path = 0x%08x", data);
rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
rsnd_mod_write(mod, CMD_BUSIF_MODE, rsnd_get_busif_shift(io, mod) | 1);
rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
rsnd_adg_set_cmd_timsel_gen2(mod, io);

Просмотреть файл

@ -343,6 +343,57 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
return 0x76543210;
}
u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
{
enum rsnd_mod_type playback_mods[] = {
RSND_MOD_SRC,
RSND_MOD_CMD,
RSND_MOD_SSIU,
};
enum rsnd_mod_type capture_mods[] = {
RSND_MOD_CMD,
RSND_MOD_SRC,
RSND_MOD_SSIU,
};
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_mod *tmod = NULL;
enum rsnd_mod_type *mods =
rsnd_io_is_play(io) ?
playback_mods : capture_mods;
int i;
/*
* This is needed for 24bit data
* We need to shift 8bit
*
* Linux 24bit data is located as 0x00******
* HW 24bit data is located as 0x******00
*
*/
switch (runtime->sample_bits) {
case 16:
return 0;
case 32:
break;
}
for (i = 0; i < ARRAY_SIZE(playback_mods); i++) {
tmod = rsnd_io_to_mod(io, mods[i]);
if (tmod)
break;
}
if (tmod != mod)
return 0;
if (rsnd_io_is_play(io))
return (0 << 20) | /* shift to Left */
(8 << 16); /* 8bit */
else
return (1 << 20) | /* shift to Right */
(8 << 16); /* 8bit */
}
/*
* rsnd_dai functions
*/
@ -500,40 +551,6 @@ static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
/*
* rsnd_soc_dai functions
*/
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
{
struct snd_pcm_substream *substream = io->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
int pos = io->byte_pos + additional;
pos %= (runtime->periods * io->byte_per_period);
return pos;
}
bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
{
io->byte_pos += byte;
if (io->byte_pos >= io->next_period_byte) {
struct snd_pcm_substream *substream = io->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
io->period_pos++;
io->next_period_byte += io->byte_per_period;
if (io->period_pos >= runtime->periods) {
io->byte_pos = 0;
io->period_pos = 0;
io->next_period_byte = io->byte_per_period;
}
return true;
}
return false;
}
void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
{
struct snd_pcm_substream *substream = io->substream;
@ -551,15 +568,7 @@ void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io)
static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
io->substream = substream;
io->byte_pos = 0;
io->period_pos = 0;
io->byte_per_period = runtime->period_size *
runtime->channels *
samples_to_bytes(runtime, 1);
io->next_period_byte = io->byte_per_period;
}
static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
@ -715,11 +724,16 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
{
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
int ret;
/*
* call rsnd_dai_call without spinlock
*/
return rsnd_dai_call(nolock_start, io, priv);
ret = rsnd_dai_call(nolock_start, io, priv);
if (ret < 0)
rsnd_dai_call(nolock_stop, io, priv);
return ret;
}
static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream,
@ -769,32 +783,131 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
of_node_put(node);
}
static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv,
int *is_graph)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
struct device_node *dai_node;
struct device_node *ret;
*is_graph = 0;
/*
* parse both previous dai (= rcar_sound,dai), and
* graph dai (= ports/port)
*/
dai_node = of_get_child_by_name(np, RSND_NODE_DAI);
if (dai_node) {
ret = dai_node;
goto of_node_compatible;
}
ret = np;
dai_node = of_graph_get_next_endpoint(np, NULL);
if (dai_node)
goto of_node_graph;
return NULL;
of_node_graph:
*is_graph = 1;
of_node_compatible:
of_node_put(dai_node);
return ret;
}
static void __rsnd_dai_probe(struct rsnd_priv *priv,
struct device_node *dai_np,
int dai_i, int is_graph)
{
struct device_node *playback, *capture;
struct rsnd_dai_stream *io_playback;
struct rsnd_dai_stream *io_capture;
struct snd_soc_dai_driver *drv;
struct rsnd_dai *rdai;
struct device *dev = rsnd_priv_to_dev(priv);
int io_i;
rdai = rsnd_rdai_get(priv, dai_i);
drv = priv->daidrv + dai_i;
io_playback = &rdai->playback;
io_capture = &rdai->capture;
snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
rdai->priv = priv;
drv->name = rdai->name;
drv->ops = &rsnd_soc_dai_ops;
snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
"DAI%d Playback", dai_i);
drv->playback.rates = RSND_RATES;
drv->playback.formats = RSND_FMTS;
drv->playback.channels_min = 2;
drv->playback.channels_max = 6;
drv->playback.stream_name = rdai->playback.name;
snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
"DAI%d Capture", dai_i);
drv->capture.rates = RSND_RATES;
drv->capture.formats = RSND_FMTS;
drv->capture.channels_min = 2;
drv->capture.channels_max = 6;
drv->capture.stream_name = rdai->capture.name;
rdai->playback.rdai = rdai;
rdai->capture.rdai = rdai;
rsnd_set_slot(rdai, 2, 1); /* default */
for (io_i = 0;; io_i++) {
playback = of_parse_phandle(dai_np, "playback", io_i);
capture = of_parse_phandle(dai_np, "capture", io_i);
if (!playback && !capture)
break;
rsnd_parse_connect_ssi(rdai, playback, capture);
rsnd_parse_connect_src(rdai, playback, capture);
rsnd_parse_connect_ctu(rdai, playback, capture);
rsnd_parse_connect_mix(rdai, playback, capture);
rsnd_parse_connect_dvc(rdai, playback, capture);
of_node_put(playback);
of_node_put(capture);
}
dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
}
static int rsnd_dai_probe(struct rsnd_priv *priv)
{
struct device_node *dai_node;
struct device_node *dai_np;
struct device_node *playback, *capture;
struct rsnd_dai_stream *io_playback;
struct rsnd_dai_stream *io_capture;
struct snd_soc_dai_driver *rdrv, *drv;
struct rsnd_dai *rdai;
struct snd_soc_dai_driver *rdrv;
struct device *dev = rsnd_priv_to_dev(priv);
int nr, dai_i, io_i;
int ret;
struct rsnd_dai *rdai;
int nr;
int is_graph;
int dai_i;
dai_node = rsnd_dai_of_node(priv);
nr = of_get_child_count(dai_node);
if (!nr) {
ret = -EINVAL;
goto rsnd_dai_probe_done;
}
dai_node = rsnd_dai_of_node(priv, &is_graph);
if (is_graph)
nr = of_graph_get_endpoint_count(dai_node);
else
nr = of_get_child_count(dai_node);
if (!nr)
return -EINVAL;
rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
if (!rdrv || !rdai) {
ret = -ENOMEM;
goto rsnd_dai_probe_done;
}
if (!rdrv || !rdai)
return -ENOMEM;
priv->rdai_nr = nr;
priv->daidrv = rdrv;
@ -804,68 +917,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
* parse all dai
*/
dai_i = 0;
for_each_child_of_node(dai_node, dai_np) {
rdai = rsnd_rdai_get(priv, dai_i);
drv = rdrv + dai_i;
io_playback = &rdai->playback;
io_capture = &rdai->capture;
snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
rdai->priv = priv;
drv->name = rdai->name;
drv->ops = &rsnd_soc_dai_ops;
snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
"DAI%d Playback", dai_i);
drv->playback.rates = RSND_RATES;
drv->playback.formats = RSND_FMTS;
drv->playback.channels_min = 2;
drv->playback.channels_max = 6;
drv->playback.stream_name = rdai->playback.name;
snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
"DAI%d Capture", dai_i);
drv->capture.rates = RSND_RATES;
drv->capture.formats = RSND_FMTS;
drv->capture.channels_min = 2;
drv->capture.channels_max = 6;
drv->capture.stream_name = rdai->capture.name;
rdai->playback.rdai = rdai;
rdai->capture.rdai = rdai;
rsnd_set_slot(rdai, 2, 1); /* default */
for (io_i = 0;; io_i++) {
playback = of_parse_phandle(dai_np, "playback", io_i);
capture = of_parse_phandle(dai_np, "capture", io_i);
if (!playback && !capture)
break;
rsnd_parse_connect_ssi(rdai, playback, capture);
rsnd_parse_connect_src(rdai, playback, capture);
rsnd_parse_connect_ctu(rdai, playback, capture);
rsnd_parse_connect_mix(rdai, playback, capture);
rsnd_parse_connect_dvc(rdai, playback, capture);
of_node_put(playback);
of_node_put(capture);
if (is_graph) {
for_each_endpoint_of_node(dai_node, dai_np) {
__rsnd_dai_probe(priv, dai_np, dai_i, is_graph);
rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i);
dai_i++;
}
dai_i++;
dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ",
rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- ");
} else {
for_each_child_of_node(dai_node, dai_np)
__rsnd_dai_probe(priv, dai_np, dai_i++, is_graph);
}
ret = 0;
rsnd_dai_probe_done:
of_node_put(dai_node);
return ret;
return 0;
}
/*
@ -914,12 +977,14 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream,
static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
snd_pcm_uframes_t pointer = 0;
return bytes_to_frames(runtime, io->byte_pos);
rsnd_dai_call(pointer, io, &pointer);
return pointer;
}
static struct snd_pcm_ops rsnd_pcm_ops = {

Просмотреть файл

@ -25,6 +25,7 @@
struct rsnd_dmaen {
struct dma_chan *chan;
dma_cookie_t cookie;
dma_addr_t dma_buf;
unsigned int dma_len;
unsigned int dma_period;
@ -103,10 +104,6 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
* In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
* But, Audio-DMAC-peri-peri doesn't have interrupt,
* and this driver is assuming that here.
*
* If Audio-DMAC-peri-peri has interrpt,
* rsnd_dai_pointer_update() will be called twice,
* ant it will breaks io->byte_pos
*/
spin_lock_irqsave(&priv->lock, flags);
@ -121,7 +118,7 @@ static void __rsnd_dmaen_complete(struct rsnd_mod *mod,
*/
rsnd_dmaen_sync(dmaen, io, dmaen->dma_cnt + 2);
elapsed = rsnd_dai_pointer_update(io, io->byte_per_period);
elapsed = true;
dmaen->dma_cnt++;
}
@ -292,7 +289,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod,
for (i = 0; i < 2; i++)
rsnd_dmaen_sync(dmaen, io, i);
if (dmaengine_submit(desc) < 0) {
dmaen->cookie = dmaengine_submit(desc);
if (dmaen->cookie < 0) {
dev_err(dev, "dmaengine_submit() fail\n");
return -EIO;
}
@ -348,12 +346,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
return 0;
}
static int rsnd_dmaen_pointer(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
snd_pcm_uframes_t *pointer)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
struct dma_tx_state state;
enum dma_status status;
unsigned int pos = 0;
status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state);
if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
if (state.residue > 0 && state.residue <= dmaen->dma_len)
pos = dmaen->dma_len - state.residue;
}
*pointer = bytes_to_frames(runtime, pos);
return 0;
}
static struct rsnd_mod_ops rsnd_dmaen_ops = {
.name = "audmac",
.nolock_start = rsnd_dmaen_nolock_start,
.nolock_stop = rsnd_dmaen_nolock_stop,
.start = rsnd_dmaen_start,
.stop = rsnd_dmaen_stop,
.pointer= rsnd_dmaen_pointer,
};
/*

Просмотреть файл

@ -219,6 +219,8 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884),
RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c),
RSND_GEN_S_REG(HDMI0_SEL, 0x9e0),
RSND_GEN_S_REG(HDMI1_SEL, 0x9e4),
/* FIXME: it needs SSI_MODE2/3 in the future */
RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
@ -236,6 +238,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18, 0x20),
RSND_GEN_M_REG(CMD_BUSIF_MODE, 0x184, 0x20),
RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188, 0x20),
RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20),
RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20),

Просмотреть файл

@ -18,6 +18,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/of_irq.h>
#include <linux/sh_dma.h>
#include <linux/workqueue.h>
@ -73,6 +74,7 @@ enum rsnd_reg {
RSND_REG_SCU_SYS_INT_EN0,
RSND_REG_SCU_SYS_INT_EN1,
RSND_REG_CMD_CTRL,
RSND_REG_CMD_BUSIF_MODE,
RSND_REG_CMD_BUSIF_DALIGN,
RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_CMDOUT_TIMSEL,
@ -169,6 +171,8 @@ enum rsnd_reg {
RSND_REG_SSI_SYS_STATUS5,
RSND_REG_SSI_SYS_STATUS6,
RSND_REG_SSI_SYS_STATUS7,
RSND_REG_HDMI0_SEL,
RSND_REG_HDMI1_SEL,
/* SSI */
RSND_REG_SSICR,
@ -204,6 +208,7 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 mask, u32 data);
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
/*
* R-Car DMA
@ -266,6 +271,9 @@ struct rsnd_mod_ops {
struct rsnd_dai_stream *io,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params);
int (*pointer)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
snd_pcm_uframes_t *pointer);
int (*fallback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
@ -303,6 +311,7 @@ struct rsnd_mod {
* H 0: pcm_new
* H 0: fallback
* H 0: hw_params
* H 0: pointer
*/
#define __rsnd_mod_shift_nolock_start 0
#define __rsnd_mod_shift_nolock_stop 0
@ -316,6 +325,7 @@ struct rsnd_mod {
#define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 28 /* always called */
#define __rsnd_mod_shift_pointer 28 /* always called */
#define __rsnd_mod_add_probe 0
#define __rsnd_mod_add_remove 0
@ -329,6 +339,7 @@ struct rsnd_mod {
#define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_add_pointer 0
#define __rsnd_mod_call_probe 0
#define __rsnd_mod_call_remove 0
@ -340,6 +351,7 @@ struct rsnd_mod {
#define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
#define __rsnd_mod_call_pointer 0
#define __rsnd_mod_call_nolock_start 0
#define __rsnd_mod_call_nolock_stop 1
@ -418,13 +430,8 @@ struct rsnd_dai_stream {
char name[RSND_DAI_NAME_SIZE];
struct snd_pcm_substream *substream;
struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai;
u32 parent_ssi_status;
int byte_pos;
int period_pos;
int byte_per_period;
int next_period_byte;
};
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
@ -469,13 +476,10 @@ struct rsnd_dai {
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
int rsnd_dai_connect(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
enum rsnd_mod_type type);
#define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI)
/*
* R-Car Gen1/Gen2
@ -646,6 +650,13 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
#define RSND_SSI_HDMI_PORT0 0xf0
#define RSND_SSI_HDMI_PORT1 0xf1
int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io);
void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
struct device_node *endpoint,
int dai_i);
#define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);

Просмотреть файл

@ -12,10 +12,6 @@
#define SRC_NAME "src"
/* SRCx_STATUS */
#define OUF_SRCO ((1 << 12) | (1 << 13))
#define OUF_SRCI ((1 << 9) | (1 << 8))
/* SCU_SYSTEM_STATUS0/1 */
#define OUF_SRC(id) ((1 << (id + 16)) | (1 << id))
@ -55,20 +51,6 @@ struct rsnd_src {
*
*/
/*
* src.c is caring...
*
* Gen1
*
* [mem] -> [SRU] -> [SSI]
* |--------|
*
* Gen2
*
* [mem] -> [SRC] -> [SSIU] -> [SSI]
* |-----------------|
*/
static void rsnd_src_activation(struct rsnd_mod *mod)
{
rsnd_mod_write(mod, SRC_SWRSR, 0);
@ -190,11 +172,13 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int is_play = rsnd_io_is_play(io);
int use_src = 0;
u32 fin, fout;
u32 ifscr, fsrate, adinr;
u32 cr, route;
u32 bsdsr, bsisr;
u32 i_busif, o_busif, tmp;
uint ratio;
if (!runtime)
@ -270,6 +254,11 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
break;
}
/* BUSIF_MODE */
tmp = rsnd_get_busif_shift(io, mod);
i_busif = ( is_play ? tmp : 0) | 1;
o_busif = (!is_play ? tmp : 0) | 1;
rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
@ -281,8 +270,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, SRC_BSISR, bsisr);
rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif);
rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif);
rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);

Просмотреть файл

@ -11,6 +11,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <sound/simple_card_utils.h>
#include <linux/delay.h>
#include "rsnd.h"
#define RSND_SSI_NAME_SIZE 16
@ -76,11 +77,18 @@ struct rsnd_ssi {
int rate;
int irq;
unsigned int usrcnt;
int byte_pos;
int period_pos;
int byte_per_period;
int next_period_byte;
};
/* flags */
#define RSND_SSI_CLK_PIN_SHARE (1 << 0)
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */
#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */
#define for_each_rsnd_ssi(pos, priv, i) \
for (i = 0; \
@ -99,6 +107,20 @@ struct rsnd_ssi {
#define rsnd_ssi_is_run_mods(mod, io) \
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI0)
return RSND_SSI_HDMI_PORT0;
if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI1)
return RSND_SSI_HDMI_PORT1;
return 0;
}
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
@ -302,7 +324,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
* always use 32bit system word.
* see also rsnd_ssi_master_clk_enable()
*/
cr_own = FORCE | SWL_32 | PDTA;
cr_own = FORCE | SWL_32;
if (rdai->bit_clk_inv)
cr_own |= SCKP;
@ -357,6 +379,59 @@ static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
ssi->cr_mode); /* without EN */
}
static void rsnd_ssi_pointer_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
ssi->byte_pos = 0;
ssi->period_pos = 0;
ssi->byte_per_period = runtime->period_size *
runtime->channels *
samples_to_bytes(runtime, 1);
ssi->next_period_byte = ssi->byte_per_period;
}
static int rsnd_ssi_pointer_offset(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
int additional)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int pos = ssi->byte_pos + additional;
pos %= (runtime->periods * ssi->byte_per_period);
return pos;
}
static bool rsnd_ssi_pointer_update(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
int byte)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
ssi->byte_pos += byte;
if (ssi->byte_pos >= ssi->next_period_byte) {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
ssi->period_pos++;
ssi->next_period_byte += ssi->byte_per_period;
if (ssi->period_pos >= runtime->periods) {
ssi->byte_pos = 0;
ssi->period_pos = 0;
ssi->next_period_byte = ssi->byte_per_period;
}
return true;
}
return false;
}
/*
* SSI mod common functions
*/
@ -370,6 +445,8 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
if (!rsnd_ssi_is_run_mods(mod, io))
return 0;
rsnd_ssi_pointer_init(mod, io);
ssi->usrcnt++;
rsnd_mod_power_on(mod);
@ -549,7 +626,14 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
if (!is_dma && (status & DIRQ)) {
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
u32 *buf = (u32 *)(runtime->dma_area +
rsnd_dai_pointer_offset(io, 0));
rsnd_ssi_pointer_offset(mod, io, 0));
int shift = 0;
switch (runtime->sample_bits) {
case 32:
shift = 8;
break;
}
/*
* 8/16/32 data can be assesse to TDR/RDR register
@ -557,11 +641,11 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
* see rsnd_ssi_init()
*/
if (rsnd_io_is_play(io))
rsnd_mod_write(mod, SSITDR, *buf);
rsnd_mod_write(mod, SSITDR, (*buf) << shift);
else
*buf = rsnd_mod_read(mod, SSIRDR);
*buf = (rsnd_mod_read(mod, SSIRDR) >> shift);
elapsed = rsnd_dai_pointer_update(io, sizeof(*buf));
elapsed = rsnd_ssi_pointer_update(mod, io, sizeof(*buf));
}
/* DMA only */
@ -668,6 +752,18 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
return ret;
}
static int rsnd_ssi_pointer(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
snd_pcm_uframes_t *pointer)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
*pointer = bytes_to_frames(runtime, ssi->byte_pos);
return 0;
}
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.name = SSI_NAME,
.probe = rsnd_ssi_common_probe,
@ -676,6 +772,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
.irq = rsnd_ssi_irq,
.pointer= rsnd_ssi_pointer,
.pcm_new = rsnd_ssi_pcm_new,
.hw_params = rsnd_ssi_hw_params,
};
@ -709,6 +806,11 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
/* Do nothing for SSI parent mod */
if (ssi_parent_mod == mod)
return 0;
/* PIO will request IRQ again */
free_irq(ssi->irq, mod);
@ -774,13 +876,6 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
}
/*
* Non SSI
*/
static struct rsnd_mod_ops rsnd_ssi_non_ops = {
.name = SSI_NAME,
};
/*
* ssi mod function
*/
@ -835,6 +930,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
of_node_put(node);
}
static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct device_node *remote_ep)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssi *ssi;
if (!mod)
return;
ssi = rsnd_mod_to_ssi(mod);
if (strstr(remote_ep->full_name, "hdmi0")) {
ssi->flags |= RSND_SSI_HDMI0;
dev_dbg(dev, "%s[%d] connected to HDMI0\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
if (strstr(remote_ep->full_name, "hdmi1")) {
ssi->flags |= RSND_SSI_HDMI1;
dev_dbg(dev, "%s[%d] connected to HDMI1\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
}
void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
struct device_node *endpoint,
int dai_i)
{
struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i);
struct device_node *remote_ep;
remote_ep = of_graph_get_remote_endpoint(endpoint);
if (!remote_ep)
return;
__rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep);
__rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep);
}
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
{
if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
@ -940,7 +1076,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
goto rsnd_ssi_probe_done;
}
ops = &rsnd_ssi_non_ops;
if (of_property_read_bool(np, "pio-transfer"))
ops = &rsnd_ssi_pio_ops;
else

Просмотреть файл

@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
int hdmi = rsnd_ssi_hdmi_port(io);
int ret;
ret = rsnd_ssiu_init(mod, io, priv);
@ -144,11 +145,48 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
(rsnd_io_is_play(io) ?
rsnd_runtime_channel_after_ctu(io) :
rsnd_runtime_channel_original(io)));
rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
rsnd_mod_write(mod, SSI_BUSIF_MODE,
rsnd_get_busif_shift(io, mod) | 1);
rsnd_mod_write(mod, SSI_BUSIF_DALIGN,
rsnd_get_dalign(mod, io));
}
if (hdmi) {
enum rsnd_mod_type rsnd_ssi_array[] = {
RSND_MOD_SSIM1,
RSND_MOD_SSIM2,
RSND_MOD_SSIM3,
};
struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
struct rsnd_mod *pos;
u32 val;
int i, shift;
i = rsnd_mod_id(ssi_mod);
/* output all same SSI as default */
val = i << 16 |
i << 20 |
i << 24 |
i << 28 |
i;
for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
shift = (i * 4) + 16;
val = (val & ~(0xF << shift)) |
rsnd_mod_id(pos) << shift;
}
switch (hdmi) {
case RSND_SSI_HDMI_PORT0:
rsnd_mod_write(mod, HDMI0_SEL, val);
break;
case RSND_SSI_HDMI_PORT1:
rsnd_mod_write(mod, HDMI1_SEL, val);
break;
}
}
return 0;
}