|
@@ -203,27 +203,6 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
|
|
|
return !!io->substream;
|
|
|
}
|
|
|
|
|
|
-void rsnd_set_slot(struct rsnd_dai *rdai,
|
|
|
- int slots, int num)
|
|
|
-{
|
|
|
- rdai->slots = slots;
|
|
|
- rdai->slots_num = num;
|
|
|
-}
|
|
|
-
|
|
|
-int rsnd_get_slot(struct rsnd_dai_stream *io)
|
|
|
-{
|
|
|
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
|
-
|
|
|
- return rdai->slots;
|
|
|
-}
|
|
|
-
|
|
|
-int rsnd_get_slot_num(struct rsnd_dai_stream *io)
|
|
|
-{
|
|
|
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
|
-
|
|
|
- return rdai->slots_num;
|
|
|
-}
|
|
|
-
|
|
|
int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
|
|
|
{
|
|
|
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
|
@@ -248,13 +227,14 @@ int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io)
|
|
|
|
|
|
int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
|
|
|
{
|
|
|
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
|
int chan = rsnd_io_is_play(io) ?
|
|
|
rsnd_runtime_channel_after_ctu(io) :
|
|
|
rsnd_runtime_channel_original(io);
|
|
|
|
|
|
/* Use Multi SSI */
|
|
|
if (rsnd_runtime_is_ssi_multi(io))
|
|
|
- chan /= rsnd_get_slot_num(io);
|
|
|
+ chan /= rsnd_rdai_ssi_lane_get(rdai);
|
|
|
|
|
|
/* TDM Extend Mode needs 8ch */
|
|
|
if (chan == 6)
|
|
@@ -265,12 +245,13 @@ int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
|
|
|
|
|
|
int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
|
|
|
{
|
|
|
- int slots = rsnd_get_slot_num(io);
|
|
|
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
|
+ int lane = rsnd_rdai_ssi_lane_get(rdai);
|
|
|
int chan = rsnd_io_is_play(io) ?
|
|
|
rsnd_runtime_channel_after_ctu(io) :
|
|
|
rsnd_runtime_channel_original(io);
|
|
|
|
|
|
- return (chan >= 6) && (slots > 1);
|
|
|
+ return (chan > 2) && (lane > 1);
|
|
|
}
|
|
|
|
|
|
int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
|
|
@@ -310,6 +291,24 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
|
|
|
u32 val = 0x76543210;
|
|
|
u32 mask = ~0;
|
|
|
|
|
|
+ /*
|
|
|
+ * *Hardware* L/R and *Software* L/R are inverted.
|
|
|
+ * We need to care about inversion timing to control
|
|
|
+ * Playback/Capture correctly.
|
|
|
+ * The point is [DVC] needs *Hardware* L/R, [MEM] needs *Software* L/R
|
|
|
+ *
|
|
|
+ * sL/R : software L/R
|
|
|
+ * hL/R : hardware L/R
|
|
|
+ * (*) : conversion timing
|
|
|
+ *
|
|
|
+ * Playback
|
|
|
+ * sL/R (*) hL/R hL/R hL/R hL/R hL/R
|
|
|
+ * [MEM] -> [SRC] -> [DVC] -> [CMD] -> [SSIU] -> [SSI] -> codec
|
|
|
+ *
|
|
|
+ * Capture
|
|
|
+ * hL/R hL/R hL/R hL/R hL/R (*) sL/R
|
|
|
+ * codec -> [SSI] -> [SSIU] -> [SRC] -> [DVC] -> [CMD] -> [MEM]
|
|
|
+ */
|
|
|
if (rsnd_io_is_play(io)) {
|
|
|
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
|
|
|
|
|
@@ -470,8 +469,7 @@ static int rsnd_status_update(u32 *status,
|
|
|
|
|
|
#define rsnd_dai_call(fn, io, param...) \
|
|
|
({ \
|
|
|
- struct rsnd_priv *priv = rsnd_io_to_priv(io); \
|
|
|
- struct device *dev = rsnd_priv_to_dev(priv); \
|
|
|
+ struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); \
|
|
|
struct rsnd_mod *mod; \
|
|
|
int is_play = rsnd_io_is_play(io); \
|
|
|
int ret = 0, i; \
|
|
@@ -532,6 +530,24 @@ static void rsnd_dai_disconnect(struct rsnd_mod *mod,
|
|
|
io->mod[type] = NULL;
|
|
|
}
|
|
|
|
|
|
+int rsnd_rdai_channels_ctrl(struct rsnd_dai *rdai,
|
|
|
+ int max_channels)
|
|
|
+{
|
|
|
+ if (max_channels > 0)
|
|
|
+ rdai->max_channels = max_channels;
|
|
|
+
|
|
|
+ return rdai->max_channels;
|
|
|
+}
|
|
|
+
|
|
|
+int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
|
|
|
+ int ssi_lane)
|
|
|
+{
|
|
|
+ if (ssi_lane > 0)
|
|
|
+ rdai->ssi_lane = ssi_lane;
|
|
|
+
|
|
|
+ return rdai->ssi_lane;
|
|
|
+}
|
|
|
+
|
|
|
struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
|
|
|
{
|
|
|
if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
|
|
@@ -551,40 +567,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;
|
|
@@ -602,15 +584,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)
|
|
@@ -749,9 +723,13 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
|
|
|
switch (slots) {
|
|
|
+ case 2:
|
|
|
case 6:
|
|
|
+ case 8:
|
|
|
+ case 16:
|
|
|
/* TDM Extend Mode */
|
|
|
- rsnd_set_slot(rdai, slots, 1);
|
|
|
+ rsnd_rdai_channels_set(rdai, slots);
|
|
|
+ rsnd_rdai_ssi_lane_set(rdai, 1);
|
|
|
break;
|
|
|
default:
|
|
|
dev_err(dev, "unsupported TDM slots (%d)\n", slots);
|
|
@@ -761,22 +739,177 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static unsigned int rsnd_soc_hw_channels_list[] = {
|
|
|
+ 2, 6, 8, 16,
|
|
|
+};
|
|
|
+
|
|
|
+static unsigned int rsnd_soc_hw_rate_list[] = {
|
|
|
+ 8000,
|
|
|
+ 11025,
|
|
|
+ 16000,
|
|
|
+ 22050,
|
|
|
+ 32000,
|
|
|
+ 44100,
|
|
|
+ 48000,
|
|
|
+ 64000,
|
|
|
+ 88200,
|
|
|
+ 96000,
|
|
|
+ 176400,
|
|
|
+ 192000,
|
|
|
+};
|
|
|
+
|
|
|
+static int rsnd_soc_hw_rule(struct rsnd_priv *priv,
|
|
|
+ unsigned int *list, int list_num,
|
|
|
+ struct snd_interval *baseline, struct snd_interval *iv)
|
|
|
+{
|
|
|
+ struct snd_interval p;
|
|
|
+ unsigned int rate;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ snd_interval_any(&p);
|
|
|
+ p.min = UINT_MAX;
|
|
|
+ p.max = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < list_num; i++) {
|
|
|
+
|
|
|
+ if (!snd_interval_test(iv, list[i]))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ rate = rsnd_ssi_clk_query(priv,
|
|
|
+ baseline->min, list[i], NULL);
|
|
|
+ if (rate > 0) {
|
|
|
+ p.min = min(p.min, list[i]);
|
|
|
+ p.max = max(p.max, list[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ rate = rsnd_ssi_clk_query(priv,
|
|
|
+ baseline->max, list[i], NULL);
|
|
|
+ if (rate > 0) {
|
|
|
+ p.min = min(p.min, list[i]);
|
|
|
+ p.max = max(p.max, list[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return snd_interval_refine(iv, &p);
|
|
|
+}
|
|
|
+
|
|
|
+static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params,
|
|
|
+ struct snd_pcm_hw_rule *rule)
|
|
|
+{
|
|
|
+ struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
|
|
+ struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
|
|
+ struct snd_interval ic;
|
|
|
+ struct snd_soc_dai *dai = rule->private;
|
|
|
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
|
|
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * possible sampling rate limitation is same as
|
|
|
+ * 2ch if it supports multi ssi
|
|
|
+ */
|
|
|
+ ic = *ic_;
|
|
|
+ if (1 < rsnd_rdai_ssi_lane_get(rdai)) {
|
|
|
+ ic.min = 2;
|
|
|
+ ic.max = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list,
|
|
|
+ ARRAY_SIZE(rsnd_soc_hw_rate_list),
|
|
|
+ &ic, ir);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
|
|
|
+ struct snd_pcm_hw_rule *rule)
|
|
|
+{
|
|
|
+ struct snd_interval *ic_ = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
|
|
|
+ struct snd_interval *ir = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
|
|
+ struct snd_interval ic;
|
|
|
+ struct snd_soc_dai *dai = rule->private;
|
|
|
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
|
|
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * possible sampling rate limitation is same as
|
|
|
+ * 2ch if it supports multi ssi
|
|
|
+ */
|
|
|
+ ic = *ic_;
|
|
|
+ if (1 < rsnd_rdai_ssi_lane_get(rdai)) {
|
|
|
+ ic.min = 2;
|
|
|
+ ic.max = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list,
|
|
|
+ ARRAY_SIZE(rsnd_soc_hw_channels_list),
|
|
|
+ ir, &ic);
|
|
|
+}
|
|
|
+
|
|
|
+static void rsnd_soc_hw_constraint(struct snd_pcm_runtime *runtime,
|
|
|
+ struct snd_soc_dai *dai)
|
|
|
+{
|
|
|
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
|
|
+ struct snd_pcm_hw_constraint_list *constraint = &rdai->constraint;
|
|
|
+ unsigned int max_channels = rsnd_rdai_channels_get(rdai);
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Channel Limitation
|
|
|
+ * It depends on Platform design
|
|
|
+ */
|
|
|
+ constraint->list = rsnd_soc_hw_channels_list;
|
|
|
+ constraint->count = 0;
|
|
|
+ constraint->mask = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(rsnd_soc_hw_channels_list); i++) {
|
|
|
+ if (rsnd_soc_hw_channels_list[i] > max_channels)
|
|
|
+ break;
|
|
|
+ constraint->count = i + 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ snd_pcm_hw_constraint_list(runtime, 0,
|
|
|
+ SNDRV_PCM_HW_PARAM_CHANNELS, constraint);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Sampling Rate / Channel Limitation
|
|
|
+ * It depends on Clock Master Mode
|
|
|
+ */
|
|
|
+ if (!rsnd_rdai_is_clk_master(rdai))
|
|
|
+ return;
|
|
|
+
|
|
|
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
|
|
+ rsnd_soc_hw_rule_rate, dai,
|
|
|
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
|
|
|
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
|
|
+ rsnd_soc_hw_rule_channels, dai,
|
|
|
+ SNDRV_PCM_HW_PARAM_RATE, -1);
|
|
|
+}
|
|
|
+
|
|
|
static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
|
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
|
|
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
|
|
|
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* rsnd_io_to_runtime() is not yet enabled here */
|
|
|
+ rsnd_soc_hw_constraint(substream->runtime, dai);
|
|
|
|
|
|
/*
|
|
|
* 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,
|
|
|
struct snd_soc_dai *dai)
|
|
|
{
|
|
|
struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
|
|
|
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
|
|
|
struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
|
|
|
|
|
|
/*
|
|
@@ -820,32 +953,132 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
|
|
|
of_node_put(node);
|
|
|
}
|
|
|
|
|
|
-static int rsnd_dai_probe(struct rsnd_priv *priv)
|
|
|
+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 *dai_np;
|
|
|
+ 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 *rdrv, *drv;
|
|
|
+ struct snd_soc_dai_driver *drv;
|
|
|
struct rsnd_dai *rdai;
|
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
- int nr, dai_i, io_i;
|
|
|
- int ret;
|
|
|
+ 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 = 16;
|
|
|
+ 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 = 16;
|
|
|
+ drv->capture.stream_name = rdai->capture.name;
|
|
|
+
|
|
|
+ rdai->playback.rdai = rdai;
|
|
|
+ rdai->capture.rdai = rdai;
|
|
|
+ rsnd_rdai_channels_set(rdai, 2); /* default 2ch */
|
|
|
+ rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */
|
|
|
+
|
|
|
+ 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;
|
|
|
|
|
|
- dai_node = rsnd_dai_of_node(priv);
|
|
|
- nr = of_get_child_count(dai_node);
|
|
|
- if (!nr) {
|
|
|
- ret = -EINVAL;
|
|
|
- goto rsnd_dai_probe_done;
|
|
|
+ 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 snd_soc_dai_driver *rdrv;
|
|
|
+ struct device *dev = rsnd_priv_to_dev(priv);
|
|
|
+ struct rsnd_dai *rdai;
|
|
|
+ int nr;
|
|
|
+ int is_graph;
|
|
|
+ int dai_i;
|
|
|
+
|
|
|
+ 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;
|
|
@@ -855,68 +1088,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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -965,12 +1148,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;
|
|
|
+
|
|
|
+ rsnd_dai_call(pointer, io, &pointer);
|
|
|
|
|
|
- return bytes_to_frames(runtime, io->byte_pos);
|
|
|
+ return pointer;
|
|
|
}
|
|
|
|
|
|
static struct snd_pcm_ops rsnd_pcm_ops = {
|
|
@@ -1033,6 +1218,9 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
|
|
|
struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
|
|
|
int i, change = 0;
|
|
|
|
|
|
+ if (!cfg->accept(cfg->io))
|
|
|
+ return 0;
|
|
|
+
|
|
|
for (i = 0; i < cfg->size; i++) {
|
|
|
if (cfg->texts) {
|
|
|
change |= (uc->value.enumerated.item[i] != cfg->val[i]);
|
|
@@ -1049,6 +1237,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
|
|
|
return change;
|
|
|
}
|
|
|
|
|
|
+int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io)
|
|
|
+{
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io)
|
|
|
+{
|
|
|
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
|
|
+
|
|
|
+ return !!runtime;
|
|
|
+}
|
|
|
+
|
|
|
struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
|
|
|
{
|
|
|
cfg->cfg.val = cfg->val;
|
|
@@ -1067,6 +1267,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
|
|
|
struct rsnd_dai_stream *io,
|
|
|
struct snd_soc_pcm_runtime *rtd,
|
|
|
const unsigned char *name,
|
|
|
+ int (*accept)(struct rsnd_dai_stream *io),
|
|
|
void (*update)(struct rsnd_dai_stream *io,
|
|
|
struct rsnd_mod *mod),
|
|
|
struct rsnd_kctrl_cfg *cfg,
|
|
@@ -1101,6 +1302,7 @@ int rsnd_kctrl_new(struct rsnd_mod *mod,
|
|
|
cfg->texts = texts;
|
|
|
cfg->max = max;
|
|
|
cfg->size = size;
|
|
|
+ cfg->accept = accept;
|
|
|
cfg->update = update;
|
|
|
cfg->card = card;
|
|
|
cfg->kctrl = kctrl;
|
|
@@ -1332,7 +1534,7 @@ static int rsnd_resume(struct device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static struct dev_pm_ops rsnd_pm_ops = {
|
|
|
+static const struct dev_pm_ops rsnd_pm_ops = {
|
|
|
.suspend = rsnd_suspend,
|
|
|
.resume = rsnd_resume,
|
|
|
};
|