|
@@ -90,6 +90,7 @@ struct rsnd_ssi {
|
|
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
|
|
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
|
|
#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */
|
|
#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */
|
|
#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */
|
|
#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */
|
|
|
|
+#define RSND_SSI_PROBED (1 << 4)
|
|
|
|
|
|
#define for_each_rsnd_ssi(pos, priv, i) \
|
|
#define for_each_rsnd_ssi(pos, priv, i) \
|
|
for (i = 0; \
|
|
for (i = 0; \
|
|
@@ -98,25 +99,27 @@ struct rsnd_ssi {
|
|
i++)
|
|
i++)
|
|
|
|
|
|
#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
|
|
#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
|
|
-#define rsnd_ssi_to_dma(mod) ((ssi)->dma)
|
|
|
|
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
|
|
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
|
|
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
|
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
|
-#define rsnd_ssi_mode_flags(p) ((p)->flags)
|
|
|
|
|
|
+#define rsnd_ssi_flags_has(p, f) ((p)->flags & f)
|
|
|
|
+#define rsnd_ssi_flags_set(p, f) ((p)->flags |= f)
|
|
|
|
+#define rsnd_ssi_flags_del(p, f) ((p)->flags = ((p)->flags & ~f))
|
|
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
|
|
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
|
|
#define rsnd_ssi_is_multi_slave(mod, io) \
|
|
#define rsnd_ssi_is_multi_slave(mod, io) \
|
|
(rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
|
|
(rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
|
|
#define rsnd_ssi_is_run_mods(mod, io) \
|
|
#define rsnd_ssi_is_run_mods(mod, io) \
|
|
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
|
|
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
|
|
|
|
+#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod))
|
|
|
|
|
|
int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io)
|
|
int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io)
|
|
{
|
|
{
|
|
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
|
|
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
|
- if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI0)
|
|
|
|
|
|
+ if (rsnd_ssi_flags_has(ssi, RSND_SSI_HDMI0))
|
|
return RSND_SSI_HDMI_PORT0;
|
|
return RSND_SSI_HDMI_PORT0;
|
|
|
|
|
|
- if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI1)
|
|
|
|
|
|
+ if (rsnd_ssi_flags_has(ssi, RSND_SSI_HDMI1))
|
|
return RSND_SSI_HDMI_PORT1;
|
|
return RSND_SSI_HDMI_PORT1;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -131,7 +134,7 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
|
|
if (!rsnd_ssi_is_dma_mode(mod))
|
|
if (!rsnd_ssi_is_dma_mode(mod))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
|
|
|
|
|
|
+ if (!(rsnd_ssi_flags_has(ssi, RSND_SSI_NO_BUSIF)))
|
|
use_busif = 1;
|
|
use_busif = 1;
|
|
if (rsnd_io_to_mod_src(io))
|
|
if (rsnd_io_to_mod_src(io))
|
|
use_busif = 1;
|
|
use_busif = 1;
|
|
@@ -256,7 +259,6 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
struct device *dev = rsnd_priv_to_dev(priv);
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
- struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
|
|
|
|
int chan = rsnd_runtime_channel_for_ssi(io);
|
|
int chan = rsnd_runtime_channel_for_ssi(io);
|
|
int idx, ret;
|
|
int idx, ret;
|
|
unsigned int main_rate;
|
|
unsigned int main_rate;
|
|
@@ -267,7 +269,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
|
|
if (!rsnd_rdai_is_clk_master(rdai))
|
|
if (!rsnd_rdai_is_clk_master(rdai))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
|
|
|
|
|
+ if (!rsnd_ssi_can_output_clk(mod))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
|
if (rsnd_ssi_is_multi_slave(mod, io))
|
|
@@ -318,12 +320,11 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
|
|
{
|
|
{
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
- struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
|
|
|
|
|
|
|
|
if (!rsnd_rdai_is_clk_master(rdai))
|
|
if (!rsnd_rdai_is_clk_master(rdai))
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
|
|
|
|
|
+ if (!rsnd_ssi_can_output_clk(mod))
|
|
return;
|
|
return;
|
|
|
|
|
|
if (ssi->usrcnt > 1)
|
|
if (ssi->usrcnt > 1)
|
|
@@ -346,6 +347,9 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod,
|
|
u32 wsr;
|
|
u32 wsr;
|
|
int is_tdm;
|
|
int is_tdm;
|
|
|
|
|
|
|
|
+ if (rsnd_ssi_is_parent(mod, io))
|
|
|
|
+ return;
|
|
|
|
+
|
|
is_tdm = rsnd_runtime_is_ssi_tdm(io);
|
|
is_tdm = rsnd_runtime_is_ssi_tdm(io);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -484,8 +488,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
- if (!rsnd_ssi_is_parent(mod, io))
|
|
|
|
- rsnd_ssi_config_init(mod, io);
|
|
|
|
|
|
+ rsnd_ssi_config_init(mod, io);
|
|
|
|
|
|
rsnd_ssi_register_setup(mod);
|
|
rsnd_ssi_register_setup(mod);
|
|
|
|
|
|
@@ -782,15 +785,47 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
|
|
/*
|
|
/*
|
|
* SSI might be called again as PIO fallback
|
|
* SSI might be called again as PIO fallback
|
|
* It is easy to manual handling for IRQ request/free
|
|
* It is easy to manual handling for IRQ request/free
|
|
|
|
+ *
|
|
|
|
+ * OTOH, this function might be called many times if platform is
|
|
|
|
+ * using MIX. It needs xxx_attach() many times on xxx_probe().
|
|
|
|
+ * Because of it, we can't control .probe/.remove calling count by
|
|
|
|
+ * mod->status.
|
|
|
|
+ * But it don't need to call request_irq() many times.
|
|
|
|
+ * Let's control it by RSND_SSI_PROBED flag.
|
|
*/
|
|
*/
|
|
- ret = request_irq(ssi->irq,
|
|
|
|
- rsnd_ssi_interrupt,
|
|
|
|
- IRQF_SHARED,
|
|
|
|
- dev_name(dev), mod);
|
|
|
|
|
|
+ if (!rsnd_ssi_flags_has(ssi, RSND_SSI_PROBED)) {
|
|
|
|
+ ret = request_irq(ssi->irq,
|
|
|
|
+ rsnd_ssi_interrupt,
|
|
|
|
+ IRQF_SHARED,
|
|
|
|
+ dev_name(dev), mod);
|
|
|
|
+
|
|
|
|
+ rsnd_ssi_flags_set(ssi, RSND_SSI_PROBED);
|
|
|
|
+ }
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int rsnd_ssi_common_remove(struct rsnd_mod *mod,
|
|
|
|
+ struct rsnd_dai_stream *io,
|
|
|
|
+ struct rsnd_priv *priv)
|
|
|
|
+{
|
|
|
|
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
+ struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io);
|
|
|
|
+
|
|
|
|
+ /* Do nothing if non SSI (= SSI parent, multi SSI) mod */
|
|
|
|
+ if (pure_ssi_mod != mod)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ /* PIO will request IRQ again */
|
|
|
|
+ if (rsnd_ssi_flags_has(ssi, RSND_SSI_PROBED)) {
|
|
|
|
+ free_irq(ssi->irq, mod);
|
|
|
|
+
|
|
|
|
+ rsnd_ssi_flags_del(ssi, RSND_SSI_PROBED);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int rsnd_ssi_pointer(struct rsnd_mod *mod,
|
|
static int rsnd_ssi_pointer(struct rsnd_mod *mod,
|
|
struct rsnd_dai_stream *io,
|
|
struct rsnd_dai_stream *io,
|
|
snd_pcm_uframes_t *pointer)
|
|
snd_pcm_uframes_t *pointer)
|
|
@@ -806,6 +841,7 @@ static int rsnd_ssi_pointer(struct rsnd_mod *mod,
|
|
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
|
static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
|
|
.name = SSI_NAME,
|
|
.name = SSI_NAME,
|
|
.probe = rsnd_ssi_common_probe,
|
|
.probe = rsnd_ssi_common_probe,
|
|
|
|
+ .remove = rsnd_ssi_common_remove,
|
|
.init = rsnd_ssi_init,
|
|
.init = rsnd_ssi_init,
|
|
.quit = rsnd_ssi_quit,
|
|
.quit = rsnd_ssi_quit,
|
|
.start = rsnd_ssi_start,
|
|
.start = rsnd_ssi_start,
|
|
@@ -840,23 +876,6 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
|
|
|
|
- struct rsnd_dai_stream *io,
|
|
|
|
- struct rsnd_priv *priv)
|
|
|
|
-{
|
|
|
|
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
- struct rsnd_mod *pure_ssi_mod = rsnd_io_to_mod_ssi(io);
|
|
|
|
-
|
|
|
|
- /* Do nothing if non SSI (= SSI parent, multi SSI) mod */
|
|
|
|
- if (pure_ssi_mod != mod)
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- /* PIO will request IRQ again */
|
|
|
|
- free_irq(ssi->irq, mod);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
|
|
static int rsnd_ssi_fallback(struct rsnd_mod *mod,
|
|
struct rsnd_dai_stream *io,
|
|
struct rsnd_dai_stream *io,
|
|
struct rsnd_priv *priv)
|
|
struct rsnd_priv *priv)
|
|
@@ -898,7 +917,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
|
|
.name = SSI_NAME,
|
|
.name = SSI_NAME,
|
|
.dma_req = rsnd_ssi_dma_req,
|
|
.dma_req = rsnd_ssi_dma_req,
|
|
.probe = rsnd_ssi_dma_probe,
|
|
.probe = rsnd_ssi_dma_probe,
|
|
- .remove = rsnd_ssi_dma_remove,
|
|
|
|
|
|
+ .remove = rsnd_ssi_common_remove,
|
|
.init = rsnd_ssi_init,
|
|
.init = rsnd_ssi_init,
|
|
.quit = rsnd_ssi_quit,
|
|
.quit = rsnd_ssi_quit,
|
|
.start = rsnd_ssi_start,
|
|
.start = rsnd_ssi_start,
|
|
@@ -984,13 +1003,13 @@ static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv,
|
|
ssi = rsnd_mod_to_ssi(mod);
|
|
ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
|
if (strstr(remote_ep->full_name, "hdmi0")) {
|
|
if (strstr(remote_ep->full_name, "hdmi0")) {
|
|
- ssi->flags |= RSND_SSI_HDMI0;
|
|
|
|
|
|
+ rsnd_ssi_flags_set(ssi, RSND_SSI_HDMI0);
|
|
dev_dbg(dev, "%s[%d] connected to HDMI0\n",
|
|
dev_dbg(dev, "%s[%d] connected to HDMI0\n",
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
|
}
|
|
}
|
|
|
|
|
|
if (strstr(remote_ep->full_name, "hdmi1")) {
|
|
if (strstr(remote_ep->full_name, "hdmi1")) {
|
|
- ssi->flags |= RSND_SSI_HDMI1;
|
|
|
|
|
|
+ rsnd_ssi_flags_set(ssi, RSND_SSI_HDMI1);
|
|
dev_dbg(dev, "%s[%d] connected to HDMI1\n",
|
|
dev_dbg(dev, "%s[%d] connected to HDMI1\n",
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
|
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
|
}
|
|
}
|
|
@@ -1023,7 +1042,7 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
|
|
{
|
|
{
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
|
|
|
|
|
- return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
|
|
|
|
|
|
+ return !!(rsnd_ssi_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE));
|
|
}
|
|
}
|
|
|
|
|
|
static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
|
|
static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
|
|
@@ -1101,18 +1120,20 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
|
|
clk = devm_clk_get(dev, name);
|
|
clk = devm_clk_get(dev, name);
|
|
if (IS_ERR(clk)) {
|
|
if (IS_ERR(clk)) {
|
|
ret = PTR_ERR(clk);
|
|
ret = PTR_ERR(clk);
|
|
|
|
+ of_node_put(np);
|
|
goto rsnd_ssi_probe_done;
|
|
goto rsnd_ssi_probe_done;
|
|
}
|
|
}
|
|
|
|
|
|
if (of_get_property(np, "shared-pin", NULL))
|
|
if (of_get_property(np, "shared-pin", NULL))
|
|
- ssi->flags |= RSND_SSI_CLK_PIN_SHARE;
|
|
|
|
|
|
+ rsnd_ssi_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE);
|
|
|
|
|
|
if (of_get_property(np, "no-busif", NULL))
|
|
if (of_get_property(np, "no-busif", NULL))
|
|
- ssi->flags |= RSND_SSI_NO_BUSIF;
|
|
|
|
|
|
+ rsnd_ssi_flags_set(ssi, RSND_SSI_NO_BUSIF);
|
|
|
|
|
|
ssi->irq = irq_of_parse_and_map(np, 0);
|
|
ssi->irq = irq_of_parse_and_map(np, 0);
|
|
if (!ssi->irq) {
|
|
if (!ssi->irq) {
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
|
|
+ of_node_put(np);
|
|
goto rsnd_ssi_probe_done;
|
|
goto rsnd_ssi_probe_done;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1123,8 +1144,10 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
|
|
|
|
|
|
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
|
|
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
|
|
rsnd_ssi_get_status, RSND_MOD_SSI, i);
|
|
rsnd_ssi_get_status, RSND_MOD_SSI, i);
|
|
- if (ret)
|
|
|
|
|
|
+ if (ret) {
|
|
|
|
+ of_node_put(np);
|
|
goto rsnd_ssi_probe_done;
|
|
goto rsnd_ssi_probe_done;
|
|
|
|
+ }
|
|
|
|
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|