]> git.proxmox.com Git - mirror_ubuntu-disco-kernel.git/commitdiff
ASoC: rsnd: add HDMI output support
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 18 May 2017 01:28:22 +0000 (01:28 +0000)
committerMark Brown <broonie@kernel.org>
Fri, 19 May 2017 17:36:25 +0000 (18:36 +0100)
Renesas R-Car Gen3 can output HDMI sound if SSIU/SSI are connected to
R-Car built-in HDMI device (R-Car Gen3 built-in HDMI device will be
controlled by DRM/KMS driver).
If SSIx was connected to HDMI0/1 on DT, SSI driver will detect it
automatically by this patch.
Note is that now Renesas R-Car sound driver is assuming that it is
using OF-graph base simple card for HDMI sound.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sh/Kconfig
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c

index 147ebecfed9438068cb4c65560e937a34dbbe065..1aa5cd77ca24a06f75d28ed2e6aec23baf0011ba 100644 (file)
@@ -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
index 41b2e782b0bf82c391e1d61e01c43cd784f32649..0804315431417e61ed54c0abff6a3df1f730e55e 100644 (file)
@@ -909,8 +909,11 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
         */
        dai_i = 0;
        if (is_graph) {
-               for_each_endpoint_of_node(dai_node, dai_np)
-                       __rsnd_dai_probe(priv, dai_np, dai_i++, 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++;
+               }
        } else {
                for_each_child_of_node(dai_node, dai_np)
                        __rsnd_dai_probe(priv, dai_np, dai_i++, is_graph);
index 63b6d3c28021024b1f06278c5c4f217a394faf8c..99f93a17511e57ad2244466a09244c7a270cbe79 100644 (file)
@@ -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),
index 4d8e0584b6445d18d2fe49f68a6f8265828c102f..037e33ffa69dfed9bc5a1945227d2b16bd214fbc 100644 (file)
@@ -170,6 +170,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,
@@ -646,6 +648,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);
index 135c5669f7963bd228c9a1187bc6f5dfd13bc04e..d0602c1897360c81055e95173d3725a39fa53a6a 100644 (file)
@@ -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
@@ -81,6 +82,8 @@ struct rsnd_ssi {
 /* 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 +102,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);
@@ -835,6 +852,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)))
index 14fafdaf1395f9737191df18599ee58fc4f858fd..13d648ef1ed6fb56a97f14d29c063c29fbb8fcef 100644 (file)
@@ -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);
@@ -149,6 +150,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
                               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;
 }