ASoC: qdsp6: q6afe: Add support to MI2S ports
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Fri, 18 May 2018 12:55:58 +0000 (13:55 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 21 May 2018 14:31:00 +0000 (15:31 +0100)
This patch adds support to 4 MI2S ports on LPASS.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Reviewed-and-tested-by: Rohit kumar <rohitkr@codeaurora.org>
Reviewed-by: Banajit Goswami <bgoswami@codeaurora.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/qcom/qdsp6/q6afe.c
sound/soc/qcom/qdsp6/q6afe.h

index 735c0b5eb55a72ca4d0834daac1510dfd8d1cf06..0004369b3661a75e2960c06c3c7e7283c0eb26d9 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/delay.h>
 #include <linux/soc/qcom/apr.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include "q6dsp-errno.h"
 #include "q6core.h"
 #include "q6afe.h"
 #define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235
 
 #define AFE_PARAM_ID_SLIMBUS_CONFIG    0x00010212
+#define AFE_PARAM_ID_I2S_CONFIG        0x0001020D
+
+/* I2S config specific */
+#define AFE_API_VERSION_I2S_CONFIG     0x1
+#define AFE_PORT_I2S_SD0               0x1
+#define AFE_PORT_I2S_SD1               0x2
+#define AFE_PORT_I2S_SD2               0x3
+#define AFE_PORT_I2S_SD3               0x4
+#define AFE_PORT_I2S_SD0_MASK          BIT(0x1)
+#define AFE_PORT_I2S_SD1_MASK          BIT(0x2)
+#define AFE_PORT_I2S_SD2_MASK          BIT(0x3)
+#define AFE_PORT_I2S_SD3_MASK          BIT(0x4)
+#define AFE_PORT_I2S_SD0_1_MASK                GENMASK(2, 1)
+#define AFE_PORT_I2S_SD2_3_MASK                GENMASK(4, 3)
+#define AFE_PORT_I2S_SD0_1_2_MASK      GENMASK(3, 1)
+#define AFE_PORT_I2S_SD0_1_2_3_MASK    GENMASK(4, 1)
+#define AFE_PORT_I2S_QUAD01            0x5
+#define AFE_PORT_I2S_QUAD23            0x6
+#define AFE_PORT_I2S_6CHS              0x7
+#define AFE_PORT_I2S_8CHS              0x8
+#define AFE_PORT_I2S_MONO              0x0
+#define AFE_PORT_I2S_STEREO            0x1
+#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL    0x0
+#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL    0x1
+#define AFE_LINEAR_PCM_DATA                            0x0
+
 
 /* Port IDs */
 #define AFE_API_VERSION_HDMI_CONFIG    0x1
 #define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX      0x400c
 /* SLIMbus Tx port on channel 6. */
 #define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX      0x400d
+#define AFE_PORT_ID_PRIMARY_MI2S_RX         0x1000
+#define AFE_PORT_ID_PRIMARY_MI2S_TX         0x1001
+#define AFE_PORT_ID_SECONDARY_MI2S_RX       0x1002
+#define AFE_PORT_ID_SECONDARY_MI2S_TX       0x1003
+#define AFE_PORT_ID_TERTIARY_MI2S_RX        0x1004
+#define AFE_PORT_ID_TERTIARY_MI2S_TX        0x1005
+#define AFE_PORT_ID_QUATERNARY_MI2S_RX      0x1006
+#define AFE_PORT_ID_QUATERNARY_MI2S_TX      0x1007
+
+#define Q6AFE_LPASS_MODE_CLK1_VALID 1
+#define Q6AFE_LPASS_MODE_CLK2_VALID 2
+#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1
+#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
 
 #define TIMEOUT_MS 1000
 #define AFE_CMD_RESP_AVAIL     0
@@ -159,10 +202,21 @@ struct afe_param_id_slimbus_cfg {
  */
 } __packed;
 
+struct afe_param_id_i2s_cfg {
+       u32     i2s_cfg_minor_version;
+       u16     bit_width;
+       u16     channel_mode;
+       u16     mono_stereo;
+       u16     ws_src;
+       u32     sample_rate;
+       u16     data_format;
+       u16     reserved;
+} __packed;
 
 union afe_port_config {
        struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch;
        struct afe_param_id_slimbus_cfg           slim_cfg;
+       struct afe_param_id_i2s_cfg     i2s_cfg;
 } __packed;
 
 struct q6afe_port {
@@ -206,6 +260,22 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = {
                                SLIMBUS_5_RX, 1, 1},
        [SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX,
                                SLIMBUS_6_RX, 1, 1},
+       [PRIMARY_MI2S_RX] = { AFE_PORT_ID_PRIMARY_MI2S_RX,
+                               PRIMARY_MI2S_RX, 1, 1},
+       [PRIMARY_MI2S_TX] = { AFE_PORT_ID_PRIMARY_MI2S_TX,
+                               PRIMARY_MI2S_RX, 0, 1},
+       [SECONDARY_MI2S_RX] = { AFE_PORT_ID_SECONDARY_MI2S_RX,
+                               SECONDARY_MI2S_RX, 1, 1},
+       [SECONDARY_MI2S_TX] = { AFE_PORT_ID_SECONDARY_MI2S_TX,
+                               SECONDARY_MI2S_TX, 0, 1},
+       [TERTIARY_MI2S_RX] = { AFE_PORT_ID_TERTIARY_MI2S_RX,
+                               TERTIARY_MI2S_RX, 1, 1},
+       [TERTIARY_MI2S_TX] = { AFE_PORT_ID_TERTIARY_MI2S_TX,
+                               TERTIARY_MI2S_TX, 0, 1},
+       [QUATERNARY_MI2S_RX] = { AFE_PORT_ID_QUATERNARY_MI2S_RX,
+                               QUATERNARY_MI2S_RX, 1, 1},
+       [QUATERNARY_MI2S_TX] = { AFE_PORT_ID_QUATERNARY_MI2S_TX,
+                               QUATERNARY_MI2S_TX, 0, 1},
 };
 
 static void q6afe_port_free(struct kref *ref)
@@ -481,6 +551,149 @@ void q6afe_hdmi_port_prepare(struct q6afe_port *port,
 }
 EXPORT_SYMBOL_GPL(q6afe_hdmi_port_prepare);
 
+/**
+ * q6afe_i2s_port_prepare() - Prepare i2s afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: I2S configuration for the afe port
+ * Return: Will be an negative on error and zero on success.
+ */
+int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg)
+{
+       union afe_port_config *pcfg = &port->port_cfg;
+       struct device *dev = port->afe->dev;
+       int num_sd_lines;
+
+       pcfg->i2s_cfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG;
+       pcfg->i2s_cfg.sample_rate = cfg->sample_rate;
+       pcfg->i2s_cfg.bit_width = cfg->bit_width;
+       pcfg->i2s_cfg.data_format = AFE_LINEAR_PCM_DATA;
+
+       switch (cfg->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* CPU is slave */
+               pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL;
+               break;
+       default:
+               break;
+       }
+
+       num_sd_lines = hweight_long(cfg->sd_line_mask);
+
+       switch (num_sd_lines) {
+       case 0:
+               dev_err(dev, "no line is assigned\n");
+               return -EINVAL;
+       case 1:
+               switch (cfg->sd_line_mask) {
+               case AFE_PORT_I2S_SD0_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0;
+                       break;
+               case AFE_PORT_I2S_SD1_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD1;
+                       break;
+               case AFE_PORT_I2S_SD2_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2;
+                       break;
+               case AFE_PORT_I2S_SD3_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD3;
+                       break;
+               default:
+                       dev_err(dev, "Invalid SD lines\n");
+                       return -EINVAL;
+               }
+               break;
+       case 2:
+               switch (cfg->sd_line_mask) {
+               case AFE_PORT_I2S_SD0_1_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD01;
+                       break;
+               case AFE_PORT_I2S_SD2_3_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD23;
+                       break;
+               default:
+                       dev_err(dev, "Invalid SD lines\n");
+                       return -EINVAL;
+               }
+               break;
+       case 3:
+               switch (cfg->sd_line_mask) {
+               case AFE_PORT_I2S_SD0_1_2_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_6CHS;
+                       break;
+               default:
+                       dev_err(dev, "Invalid SD lines\n");
+                       return -EINVAL;
+               }
+               break;
+       case 4:
+               switch (cfg->sd_line_mask) {
+               case AFE_PORT_I2S_SD0_1_2_3_MASK:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_8CHS;
+
+                       break;
+               default:
+                       dev_err(dev, "Invalid SD lines\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               dev_err(dev, "Invalid SD lines\n");
+               return -EINVAL;
+       }
+
+       switch (cfg->num_channels) {
+       case 1:
+       case 2:
+               switch (pcfg->i2s_cfg.channel_mode) {
+               case AFE_PORT_I2S_QUAD01:
+               case AFE_PORT_I2S_6CHS:
+               case AFE_PORT_I2S_8CHS:
+                       pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0;
+                       break;
+               case AFE_PORT_I2S_QUAD23:
+                               pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2;
+                       break;
+               }
+
+               if (cfg->num_channels == 2)
+                       pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_STEREO;
+               else
+                       pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_MONO;
+
+               break;
+       case 3:
+       case 4:
+               if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_QUAD01) {
+                       dev_err(dev, "Invalid Channel mode\n");
+                       return -EINVAL;
+               }
+               break;
+       case 5:
+       case 6:
+               if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_6CHS) {
+                       dev_err(dev, "Invalid Channel mode\n");
+                       return -EINVAL;
+               }
+               break;
+       case 7:
+       case 8:
+               if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_8CHS) {
+                       dev_err(dev, "Invalid Channel mode\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare);
+
 /**
  * q6afe_port_start() - Start a afe port
  *
@@ -580,6 +793,17 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
        case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX:
                cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
                break;
+
+       case AFE_PORT_ID_PRIMARY_MI2S_RX:
+       case AFE_PORT_ID_PRIMARY_MI2S_TX:
+       case AFE_PORT_ID_SECONDARY_MI2S_RX:
+       case AFE_PORT_ID_SECONDARY_MI2S_TX:
+       case AFE_PORT_ID_TERTIARY_MI2S_RX:
+       case AFE_PORT_ID_TERTIARY_MI2S_TX:
+       case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+       case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+               cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+               break;
        default:
                dev_err(dev, "Invalid port id 0x%x\n", port_id);
                return ERR_PTR(-EINVAL);
index 5659966c6b1e882e7edfe61a271b493a43395fc0..3cb3bb4985a9b834ac844d183852e4d5f8b0fae7 100644 (file)
@@ -11,6 +11,8 @@
 #define MSM_AFE_PORT_TYPE_TX 1
 #define AFE_MAX_PORTS AFE_PORT_MAX
 
+#define Q6AFE_MAX_MI2S_LINES   4
+
 #define AFE_MAX_CHAN_COUNT     8
 #define AFE_PORT_MAX_AUDIO_CHAN_CNT    0x8
 
@@ -29,9 +31,19 @@ struct q6afe_slim_cfg {
        u8      ch_mapping[AFE_MAX_CHAN_COUNT];
 };
 
+struct q6afe_i2s_cfg {
+       u32     sample_rate;
+       u16     bit_width;
+       u16     data_format;
+       u16     num_channels;
+       u32     sd_line_mask;
+       int fmt;
+};
+
 struct q6afe_port_config {
        struct q6afe_hdmi_cfg hdmi;
        struct q6afe_slim_cfg slim;
+       struct q6afe_i2s_cfg i2s_cfg;
 };
 
 struct q6afe_port;
@@ -45,5 +57,6 @@ void q6afe_hdmi_port_prepare(struct q6afe_port *port,
                            struct q6afe_hdmi_cfg *cfg);
 void q6afe_slim_port_prepare(struct q6afe_port *port,
                          struct q6afe_slim_cfg *cfg);
+int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg);
 
 #endif /* __Q6AFE_H__ */