#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 
+#define LPASS_CPU_MAX_MI2S_LINES       4
+#define LPASS_CPU_I2S_SD0_MASK         BIT(0)
+#define LPASS_CPU_I2S_SD1_MASK         BIT(1)
+#define LPASS_CPU_I2S_SD2_MASK         BIT(2)
+#define LPASS_CPU_I2S_SD3_MASK         BIT(3)
+#define LPASS_CPU_I2S_SD0_1_MASK       GENMASK(1, 0)
+#define LPASS_CPU_I2S_SD2_3_MASK       GENMASK(3, 2)
+#define LPASS_CPU_I2S_SD0_1_2_MASK     GENMASK(2, 0)
+#define LPASS_CPU_I2S_SD0_1_2_3_MASK   GENMASK(3, 0)
+
 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
                unsigned int freq, int dir)
 {
        snd_pcm_format_t format = params_format(params);
        unsigned int channels = params_channels(params);
        unsigned int rate = params_rate(params);
+       unsigned int mode;
        unsigned int regval;
        int bitwidth, ret;
 
                return -EINVAL;
        }
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               switch (channels) {
-               case 1:
-                       regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-                       regval |= LPAIF_I2SCTL_SPKMONO_MONO;
-                       break;
-               case 2:
-                       regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-                       break;
-               case 4:
-                       regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
-                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-                       break;
-               case 6:
-                       regval |= LPAIF_I2SCTL_SPKMODE_6CH;
-                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               mode = drvdata->mi2s_playback_sd_mode[dai->driver->id];
+       else
+               mode = drvdata->mi2s_capture_sd_mode[dai->driver->id];
+
+       if (!mode) {
+               dev_err(dai->dev, "no line is assigned\n");
+               return -EINVAL;
+       }
+
+       switch (channels) {
+       case 1:
+       case 2:
+               switch (mode) {
+               case LPAIF_I2SCTL_MODE_QUAD01:
+               case LPAIF_I2SCTL_MODE_6CH:
+               case LPAIF_I2SCTL_MODE_8CH:
+                       mode = LPAIF_I2SCTL_MODE_SD0;
                        break;
-               case 8:
-                       regval |= LPAIF_I2SCTL_SPKMODE_8CH;
-                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+               case LPAIF_I2SCTL_MODE_QUAD23:
+                       mode = LPAIF_I2SCTL_MODE_SD2;
                        break;
-               default:
-                       dev_err(dai->dev, "invalid channels given: %u\n",
-                               channels);
+               }
+
+               break;
+       case 4:
+               if (mode < LPAIF_I2SCTL_MODE_QUAD01) {
+                       dev_err(dai->dev, "cannot configure 4 channels with mode %d\n",
+                               mode);
                        return -EINVAL;
                }
-       } else {
-               switch (channels) {
-               case 1:
-                       regval |= LPAIF_I2SCTL_MICMODE_SD0;
-                       regval |= LPAIF_I2SCTL_MICMONO_MONO;
-                       break;
-               case 2:
-                       regval |= LPAIF_I2SCTL_MICMODE_SD0;
-                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
-                       break;
-               case 4:
-                       regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
-                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
-                       break;
-               case 6:
-                       regval |= LPAIF_I2SCTL_MICMODE_6CH;
-                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+
+               switch (mode) {
+               case LPAIF_I2SCTL_MODE_6CH:
+               case LPAIF_I2SCTL_MODE_8CH:
+                       mode = LPAIF_I2SCTL_MODE_QUAD01;
                        break;
-               case 8:
-                       regval |= LPAIF_I2SCTL_MICMODE_8CH;
-                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+               }
+               break;
+       case 6:
+               if (mode < LPAIF_I2SCTL_MODE_6CH) {
+                       dev_err(dai->dev, "cannot configure 6 channels with mode %d\n",
+                               mode);
+                       return -EINVAL;
+               }
+
+               switch (mode) {
+               case LPAIF_I2SCTL_MODE_8CH:
+                       mode = LPAIF_I2SCTL_MODE_6CH;
                        break;
-               default:
-                       dev_err(dai->dev, "invalid channels given: %u\n",
-                               channels);
+               }
+               break;
+       case 8:
+               if (mode < LPAIF_I2SCTL_MODE_8CH) {
+                       dev_err(dai->dev, "cannot configure 8 channels with mode %d\n",
+                               mode);
                        return -EINVAL;
                }
+               break;
+       default:
+               dev_err(dai->dev, "invalid channels given: %u\n", channels);
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               regval |= LPAIF_I2SCTL_SPKMODE(mode);
+
+               if (channels >= 2)
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+               else
+                       regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+       } else {
+               regval |= LPAIF_I2SCTL_MICMODE(mode);
+
+               if (channels >= 2)
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+               else
+                       regval |= LPAIF_I2SCTL_MICMONO_MONO;
        }
 
        ret = regmap_write(drvdata->lpaif_map,
        .cache_type = REGCACHE_FLAT,
 };
 
+static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
+                                               struct device_node *node,
+                                               const char *name)
+{
+       unsigned int lines[LPASS_CPU_MAX_MI2S_LINES];
+       unsigned int sd_line_mask = 0;
+       int num_lines, i;
+
+       num_lines = of_property_read_variable_u32_array(node, name, lines, 0,
+                                                       LPASS_CPU_MAX_MI2S_LINES);
+       if (num_lines < 0)
+               return LPAIF_I2SCTL_MODE_NONE;
+
+       for (i = 0; i < num_lines; i++)
+               sd_line_mask |= BIT(lines[i]);
+
+       switch (sd_line_mask) {
+       case LPASS_CPU_I2S_SD0_MASK:
+               return LPAIF_I2SCTL_MODE_SD0;
+       case LPASS_CPU_I2S_SD1_MASK:
+               return LPAIF_I2SCTL_MODE_SD1;
+       case LPASS_CPU_I2S_SD2_MASK:
+               return LPAIF_I2SCTL_MODE_SD2;
+       case LPASS_CPU_I2S_SD3_MASK:
+               return LPAIF_I2SCTL_MODE_SD3;
+       case LPASS_CPU_I2S_SD0_1_MASK:
+               return LPAIF_I2SCTL_MODE_QUAD01;
+       case LPASS_CPU_I2S_SD2_3_MASK:
+               return LPAIF_I2SCTL_MODE_QUAD23;
+       case LPASS_CPU_I2S_SD0_1_2_MASK:
+               return LPAIF_I2SCTL_MODE_6CH;
+       case LPASS_CPU_I2S_SD0_1_2_3_MASK:
+               return LPAIF_I2SCTL_MODE_8CH;
+       default:
+               dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask);
+               return LPAIF_I2SCTL_MODE_NONE;
+       }
+}
+
+static void of_lpass_cpu_parse_dai_data(struct device *dev,
+                                       struct lpass_data *data)
+{
+       struct device_node *node;
+       int ret, id;
+
+       /* Allow all channels by default for backwards compatibility */
+       for (id = 0; id < data->variant->num_dai; id++) {
+               data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+               data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
+       }
+
+       for_each_child_of_node(dev->of_node, node) {
+               ret = of_property_read_u32(node, "reg", &id);
+               if (ret || id < 0 || id >= data->variant->num_dai) {
+                       dev_err(dev, "valid dai id not found: %d\n", ret);
+                       continue;
+               }
+
+               data->mi2s_playback_sd_mode[id] =
+                       of_lpass_cpu_parse_sd_lines(dev, node,
+                                                   "qcom,playback-sd-lines");
+               data->mi2s_capture_sd_mode[id] =
+                       of_lpass_cpu_parse_sd_lines(dev, node,
+                                                   "qcom,capture-sd-lines");
+       }
+}
+
 int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 {
        struct lpass_data *drvdata;
        drvdata->variant = (struct lpass_variant *)match->data;
        variant = drvdata->variant;
 
+       of_lpass_cpu_parse_dai_data(dev, drvdata);
+
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
 
        drvdata->lpaif = devm_ioremap_resource(dev, res);
 
 #define LPAIF_I2SCTL_SPKEN_DISABLE     (0 << LPAIF_I2SCTL_SPKEN_SHIFT)
 #define LPAIF_I2SCTL_SPKEN_ENABLE      (1 << LPAIF_I2SCTL_SPKEN_SHIFT)
 
+#define LPAIF_I2SCTL_MODE_NONE         0
+#define LPAIF_I2SCTL_MODE_SD0          1
+#define LPAIF_I2SCTL_MODE_SD1          2
+#define LPAIF_I2SCTL_MODE_SD2          3
+#define LPAIF_I2SCTL_MODE_SD3          4
+#define LPAIF_I2SCTL_MODE_QUAD01       5
+#define LPAIF_I2SCTL_MODE_QUAD23       6
+#define LPAIF_I2SCTL_MODE_6CH          7
+#define LPAIF_I2SCTL_MODE_8CH          8
+
 #define LPAIF_I2SCTL_SPKMODE_MASK      0x3C00
 #define LPAIF_I2SCTL_SPKMODE_SHIFT     10
-#define LPAIF_I2SCTL_SPKMODE_NONE      (0 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD0       (1 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD1       (2 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD2       (3 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_SD3       (4 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_QUAD01    (5 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_QUAD23    (6 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_6CH       (7 << LPAIF_I2SCTL_SPKMODE_SHIFT)
-#define LPAIF_I2SCTL_SPKMODE_8CH       (8 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE(mode)     ((mode) << LPAIF_I2SCTL_SPKMODE_SHIFT)
 
 #define LPAIF_I2SCTL_SPKMONO_MASK      0x0200
 #define LPAIF_I2SCTL_SPKMONO_SHIFT     9
 
 #define LPAIF_I2SCTL_MICMODE_MASK      GENMASK(7, 4)
 #define LPAIF_I2SCTL_MICMODE_SHIFT     4
-#define LPAIF_I2SCTL_MICMODE_NONE      (0 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD0       (1 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD1       (2 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD2       (3 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_SD3       (4 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_QUAD01    (5 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_QUAD23    (6 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_6CH       (7 << LPAIF_I2SCTL_MICMODE_SHIFT)
-#define LPAIF_I2SCTL_MICMODE_8CH       (8 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE(mode)     ((mode) << LPAIF_I2SCTL_MICMODE_SHIFT)
 
 #define LPAIF_I2SCTL_MIMONO_MASK       GENMASK(3, 3)
 #define LPAIF_I2SCTL_MICMONO_SHIFT     3