Merge branch 'asoc-4.19' into asoc-4.20 for rcar dep
authorMark Brown <broonie@kernel.org>
Mon, 3 Sep 2018 13:37:28 +0000 (14:37 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 3 Sep 2018 13:37:28 +0000 (14:37 +0100)
1  2 
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/ssi.c

diff --combined sound/soc/sh/rcar/core.c
  #include "rsnd.h"
  
  #define RSND_RATES SNDRV_PCM_RATE_8000_192000
 -#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S8 |\
 +                 SNDRV_PCM_FMTBIT_S16_LE |\
 +                 SNDRV_PCM_FMTBIT_S24_LE)
  
  static const struct of_device_id rsnd_of_match[] = {
        { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
@@@ -282,8 -280,6 +282,8 @@@ u32 rsnd_get_adinr_bit(struct rsnd_mod 
        struct device *dev = rsnd_priv_to_dev(priv);
  
        switch (snd_pcm_format_width(runtime->format)) {
 +      case 8:
 +              return 16 << 16;
        case 16:
                return 8 << 16;
        case 24:
@@@ -335,7 -331,7 +335,7 @@@ u32 rsnd_get_dalign(struct rsnd_mod *mo
                target = cmd ? cmd : ssiu;
        }
  
 -      /* Non target mod or 24bit data needs normal DALIGN */
 +      /* Non target mod or non 16bit needs normal DALIGN */
        if ((snd_pcm_format_width(runtime->format) != 16) ||
            (mod != target))
                return 0x76543210;
@@@ -371,7 -367,7 +371,7 @@@ u32 rsnd_get_busif_shift(struct rsnd_da
         * HW    24bit data is located as 0x******00
         *
         */
 -      if (snd_pcm_format_width(runtime->format) == 16)
 +      if (snd_pcm_format_width(runtime->format) != 24)
                return 0;
  
        for (i = 0; i < ARRAY_SIZE(playback_mods); i++) {
@@@ -544,14 -540,6 +544,14 @@@ int rsnd_rdai_ssi_lane_ctrl(struct rsnd
        return rdai->ssi_lane;
  }
  
 +int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width)
 +{
 +      if (width > 0)
 +              rdai->chan_width = width;
 +
 +      return rdai->chan_width;
 +}
 +
  struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
  {
        if ((id < 0) || (id >= rsnd_rdai_nr(priv)))
@@@ -693,7 -681,6 +693,7 @@@ static int rsnd_soc_dai_set_fmt(struct 
                rdai->frm_clk_inv = 0;
                break;
        case SND_SOC_DAIFMT_LEFT_J:
 +      case SND_SOC_DAIFMT_DSP_B:
                rdai->sys_delay = 1;
                rdai->data_alignment = 0;
                rdai->frm_clk_inv = 1;
                rdai->data_alignment = 1;
                rdai->frm_clk_inv = 1;
                break;
 +      case SND_SOC_DAIFMT_DSP_A:
 +              rdai->sys_delay = 0;
 +              rdai->data_alignment = 0;
 +              rdai->frm_clk_inv = 1;
 +              break;
        }
  
        /* set clock inversion */
@@@ -738,16 -720,6 +738,16 @@@ static int rsnd_soc_set_dai_tdm_slot(st
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
        struct device *dev = rsnd_priv_to_dev(priv);
  
 +      switch (slot_width) {
 +      case 16:
 +      case 24:
 +      case 32:
 +              break;
 +      default:
 +              dev_err(dev, "unsupported slot width value: %d\n", slot_width);
 +              return -EINVAL;
 +      }
 +
        switch (slots) {
        case 2:
        case 6:
                /* TDM Extend Mode */
                rsnd_rdai_channels_set(rdai, slots);
                rsnd_rdai_ssi_lane_set(rdai, 1);
 +              rsnd_rdai_width_set(rdai, slot_width);
                break;
        default:
                dev_err(dev, "unsupported TDM slots (%d)\n", slots);
@@@ -784,7 -755,7 +784,7 @@@ static unsigned int rsnd_soc_hw_rate_li
        192000,
  };
  
 -static int rsnd_soc_hw_rule(struct rsnd_priv *priv,
 +static int rsnd_soc_hw_rule(struct rsnd_dai *rdai,
                            unsigned int *list, int list_num,
                            struct snd_interval *baseline, struct snd_interval *iv)
  {
                if (!snd_interval_test(iv, list[i]))
                        continue;
  
 -              rate = rsnd_ssi_clk_query(priv,
 +              rate = rsnd_ssi_clk_query(rdai,
                                          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,
 +              rate = rsnd_ssi_clk_query(rdai,
                                          baseline->max, list[i], NULL);
                if (rate > 0) {
                        p.min = min(p.min, 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,
 -                                 int is_play)
 +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);
 -      struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
 +      struct rsnd_dai_stream *io = rule->private;
 +      struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
  
        /*
         * possible sampling rate limitation is same as
        ic.min =
        ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params);
  
 -      return rsnd_soc_hw_rule(priv, rsnd_soc_hw_rate_list,
 +      return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_rate_list,
                                ARRAY_SIZE(rsnd_soc_hw_rate_list),
                                &ic, ir);
  }
  
 -static int rsnd_soc_hw_rule_rate_playback(struct snd_pcm_hw_params *params,
 -                               struct snd_pcm_hw_rule *rule)
 -{
 -      return __rsnd_soc_hw_rule_rate(params, rule, 1);
 -}
 -
 -static int rsnd_soc_hw_rule_rate_capture(struct snd_pcm_hw_params *params,
 -                                        struct snd_pcm_hw_rule *rule)
 -{
 -      return __rsnd_soc_hw_rule_rate(params, rule, 0);
 -}
 -
 -static int __rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
 -                                     struct snd_pcm_hw_rule *rule,
 -                                     int is_play)
 +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);
 -      struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
 +      struct rsnd_dai_stream *io = rule->private;
 +      struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
  
        /*
         * possible sampling rate limitation is same as
        ic.min =
        ic.max = rsnd_runtime_channel_for_ssi_with_params(io, params);
  
 -      return rsnd_soc_hw_rule(priv, rsnd_soc_hw_channels_list,
 +      return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_channels_list,
                                ARRAY_SIZE(rsnd_soc_hw_channels_list),
                                ir, &ic);
  }
  
 -static int rsnd_soc_hw_rule_channels_playback(struct snd_pcm_hw_params *params,
 -                                            struct snd_pcm_hw_rule *rule)
 -{
 -      return __rsnd_soc_hw_rule_channels(params, rule, 1);
 -}
 -
 -static int rsnd_soc_hw_rule_channels_capture(struct snd_pcm_hw_params *params,
 -                                           struct snd_pcm_hw_rule *rule)
 -{
 -      return __rsnd_soc_hw_rule_channels(params, rule, 0);
 -}
 -
  static const struct snd_pcm_hardware rsnd_pcm_hardware = {
        .info =         SNDRV_PCM_INFO_INTERLEAVED      |
                        SNDRV_PCM_INFO_MMAP             |
@@@ -921,12 -922,14 +921,12 @@@ static int rsnd_soc_dai_startup(struct 
                int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
  
                snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 -                                  is_play ? rsnd_soc_hw_rule_rate_playback :
 -                                            rsnd_soc_hw_rule_rate_capture,
 -                                  dai,
 +                                  rsnd_soc_hw_rule_rate,
 +                                  is_play ? &rdai->playback : &rdai->capture,
                                    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
                snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
 -                                  is_play ? rsnd_soc_hw_rule_channels_playback :
 -                                            rsnd_soc_hw_rule_channels_capture,
 -                                  dai,
 +                                  rsnd_soc_hw_rule_channels,
 +                                  is_play ? &rdai->playback : &rdai->capture,
                                    SNDRV_PCM_HW_PARAM_RATE, -1);
        }
  
@@@ -955,12 -958,23 +955,23 @@@ static void rsnd_soc_dai_shutdown(struc
        rsnd_dai_stream_quit(io);
  }
  
+ static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+ {
+       struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+       return rsnd_dai_call(prepare, io, priv);
+ }
  static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
        .startup        = rsnd_soc_dai_startup,
        .shutdown       = rsnd_soc_dai_shutdown,
        .trigger        = rsnd_soc_dai_trigger,
        .set_fmt        = rsnd_soc_dai_set_fmt,
        .set_tdm_slot   = rsnd_soc_set_dai_tdm_slot,
+       .prepare        = rsnd_soc_dai_prepare,
  };
  
  void rsnd_parse_connect_common(struct rsnd_dai *rdai,
@@@ -1069,7 -1083,6 +1080,7 @@@ static void __rsnd_dai_probe(struct rsn
        rdai->capture.rdai              = rdai;
        rsnd_rdai_channels_set(rdai, 2); /* default 2ch */
        rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */
 +      rsnd_rdai_width_set(rdai, 32);   /* default 32bit width */
  
        for (io_i = 0;; io_i++) {
                playback = of_parse_phandle(dai_np, "playback", io_i);
@@@ -1261,15 -1274,8 +1272,15 @@@ int rsnd_kctrl_accept_anytime(struct rs
  int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io)
  {
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 +      struct rsnd_priv *priv = rsnd_io_to_priv(io);
 +      struct device *dev = rsnd_priv_to_dev(priv);
  
 -      return !!runtime;
 +      if (!runtime) {
 +              dev_warn(dev, "Can't update kctrl when idle\n");
 +              return 0;
 +      }
 +
 +      return 1;
  }
  
  struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
diff --combined sound/soc/sh/rcar/rsnd.h
@@@ -156,30 -156,9 +156,30 @@@ enum rsnd_reg 
        RSND_REG_SSI_MODE2,
        RSND_REG_SSI_CONTROL,
        RSND_REG_SSI_CTRL,
 -      RSND_REG_SSI_BUSIF_MODE,
 -      RSND_REG_SSI_BUSIF_ADINR,
 -      RSND_REG_SSI_BUSIF_DALIGN,
 +      RSND_REG_SSI_BUSIF0_MODE,
 +      RSND_REG_SSI_BUSIF0_ADINR,
 +      RSND_REG_SSI_BUSIF0_DALIGN,
 +      RSND_REG_SSI_BUSIF1_MODE,
 +      RSND_REG_SSI_BUSIF1_ADINR,
 +      RSND_REG_SSI_BUSIF1_DALIGN,
 +      RSND_REG_SSI_BUSIF2_MODE,
 +      RSND_REG_SSI_BUSIF2_ADINR,
 +      RSND_REG_SSI_BUSIF2_DALIGN,
 +      RSND_REG_SSI_BUSIF3_MODE,
 +      RSND_REG_SSI_BUSIF3_ADINR,
 +      RSND_REG_SSI_BUSIF3_DALIGN,
 +      RSND_REG_SSI_BUSIF4_MODE,
 +      RSND_REG_SSI_BUSIF4_ADINR,
 +      RSND_REG_SSI_BUSIF4_DALIGN,
 +      RSND_REG_SSI_BUSIF5_MODE,
 +      RSND_REG_SSI_BUSIF5_ADINR,
 +      RSND_REG_SSI_BUSIF5_DALIGN,
 +      RSND_REG_SSI_BUSIF6_MODE,
 +      RSND_REG_SSI_BUSIF6_ADINR,
 +      RSND_REG_SSI_BUSIF6_DALIGN,
 +      RSND_REG_SSI_BUSIF7_MODE,
 +      RSND_REG_SSI_BUSIF7_ADINR,
 +      RSND_REG_SSI_BUSIF7_DALIGN,
        RSND_REG_SSI_INT_ENABLE,
        RSND_REG_SSI_SYS_STATUS0,
        RSND_REG_SSI_SYS_STATUS1,
@@@ -301,6 -280,9 +301,9 @@@ struct rsnd_mod_ops 
        int (*nolock_stop)(struct rsnd_mod *mod,
                    struct rsnd_dai_stream *io,
                    struct rsnd_priv *priv);
+       int (*prepare)(struct rsnd_mod *mod,
+                      struct rsnd_dai_stream *io,
+                      struct rsnd_priv *priv);
  };
  
  struct rsnd_dai_stream;
@@@ -330,6 -312,7 +333,7 @@@ struct rsnd_mod 
   * H  0: fallback
   * H  0: hw_params
   * H  0: pointer
+  * H  0: prepare
   */
  #define __rsnd_mod_shift_nolock_start 0
  #define __rsnd_mod_shift_nolock_stop  0
  #define __rsnd_mod_shift_fallback     28 /* always called */
  #define __rsnd_mod_shift_hw_params    28 /* always called */
  #define __rsnd_mod_shift_pointer      28 /* always called */
+ #define __rsnd_mod_shift_prepare      28 /* always called */
  
  #define __rsnd_mod_add_probe          0
  #define __rsnd_mod_add_remove         0
  #define __rsnd_mod_add_fallback               0
  #define __rsnd_mod_add_hw_params      0
  #define __rsnd_mod_add_pointer                0
+ #define __rsnd_mod_add_prepare                0
  
  #define __rsnd_mod_call_probe         0
  #define __rsnd_mod_call_remove                0
  #define __rsnd_mod_call_pointer               0
  #define __rsnd_mod_call_nolock_start  0
  #define __rsnd_mod_call_nolock_stop   1
+ #define __rsnd_mod_call_prepare               0
  
  #define rsnd_mod_to_priv(mod) ((mod)->priv)
  #define rsnd_mod_name(mod)    ((mod)->ops->name)
@@@ -452,7 -438,6 +459,7 @@@ struct rsnd_dai_stream 
        char name[RSND_DAI_NAME_SIZE];
        struct snd_pcm_substream *substream;
        struct rsnd_mod *mod[RSND_MOD_MAX];
 +      struct rsnd_mod *dma;
        struct rsnd_dai *rdai;
        struct device *dmac_dev; /* for IPMMU */
        u32 parent_ssi_status;
@@@ -482,7 -467,6 +489,7 @@@ struct rsnd_dai 
  
        int max_channels;       /* 2ch - 16ch */
        int ssi_lane;           /* 1lane - 4lane */
 +      int chan_width;         /* 16/24/32 bit width */
  
        unsigned int clk_master:1;
        unsigned int bit_clk_inv:1;
@@@ -516,11 -500,6 +523,11 @@@ int rsnd_rdai_channels_ctrl(struct rsnd
  int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai,
                            int ssi_lane);
  
 +#define rsnd_rdai_width_set(rdai, width) \
 +      rsnd_rdai_width_ctrl(rdai, width)
 +#define rsnd_rdai_width_get(rdai) \
 +      rsnd_rdai_width_ctrl(rdai, 0)
 +int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width);
  void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
  int rsnd_dai_connect(struct rsnd_mod *mod,
                     struct rsnd_dai_stream *io,
@@@ -713,7 -692,6 +720,7 @@@ void rsnd_ssi_remove(struct rsnd_priv *
  struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
  int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
  int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
 +int rsnd_ssi_get_busif(struct rsnd_dai_stream *io);
  u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
  
  #define RSND_SSI_HDMI_PORT0   0xf0
@@@ -731,7 -709,7 +738,7 @@@ int __rsnd_ssi_is_pin_sharing(struct rs
  void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
                            struct device_node *playback,
                            struct device_node *capture);
 -unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
 +unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai,
                       int param1, int param2, int *idx);
  
  /*
diff --combined sound/soc/sh/rcar/ssi.c
  #define       DWL_24          (5 << 19)       /* Data Word Length */
  #define       DWL_32          (6 << 19)       /* Data Word Length */
  
 +/*
 + * System word length
 + */
 +#define       SWL_16          (1 << 16)       /* R/W System Word Length */
 +#define       SWL_24          (2 << 16)       /* R/W System Word Length */
  #define       SWL_32          (3 << 16)       /* R/W System Word Length */
 +
  #define       SCKD            (1 << 15)       /* Serial Bit Clock Direction */
  #define       SWSD            (1 << 14)       /* Serial WS Direction */
  #define       SCKP            (1 << 13)       /* Serial Bit Clock Polarity */
@@@ -78,6 -72,7 +78,6 @@@
  
  struct rsnd_ssi {
        struct rsnd_mod mod;
 -      struct rsnd_mod *dma;
  
        u32 flags;
        u32 cr_own;
@@@ -150,11 -145,6 +150,11 @@@ int rsnd_ssi_use_busif(struct rsnd_dai_
        return use_busif;
  }
  
 +int rsnd_ssi_get_busif(struct rsnd_dai_stream *io)
 +{
 +      return 0; /* BUSIF0 only for now */
 +}
 +
  static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
  {
        rsnd_mod_write(mod, SSISR, 0);
@@@ -230,32 -220,14 +230,32 @@@ u32 rsnd_ssi_multi_slaves_runtime(struc
        return 0;
  }
  
 -unsigned int rsnd_ssi_clk_query(struct rsnd_priv *priv,
 +static u32 rsnd_rdai_width_to_swl(struct rsnd_dai *rdai)
 +{
 +      struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
 +      struct device *dev = rsnd_priv_to_dev(priv);
 +      int width = rsnd_rdai_width_get(rdai);
 +
 +      switch (width) {
 +      case 32: return SWL_32;
 +      case 24: return SWL_24;
 +      case 16: return SWL_16;
 +      }
 +
 +      dev_err(dev, "unsupported slot width value: %d\n", width);
 +      return 0;
 +}
 +
 +unsigned int rsnd_ssi_clk_query(struct rsnd_dai *rdai,
                       int param1, int param2, int *idx)
  {
 +      struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
        int ssi_clk_mul_table[] = {
                1, 2, 4, 8, 16, 6, 12,
        };
        int j, ret;
        unsigned int main_rate;
 +      int width = rsnd_rdai_width_get(rdai);
  
        for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
  
                if (j == 0)
                        continue;
  
 -              /*
 -               * this driver is assuming that
 -               * system word is 32bit x chan
 -               * see rsnd_ssi_init()
 -               */
 -              main_rate = 32 * param1 * param2 * ssi_clk_mul_table[j];
 +              main_rate = width * param1 * param2 * ssi_clk_mul_table[j];
  
                ret = rsnd_adg_clk_query(priv, main_rate);
                if (ret < 0)
@@@ -306,21 -283,16 +306,21 @@@ static int rsnd_ssi_master_clk_start(st
        if (rsnd_ssi_is_multi_slave(mod, io))
                return 0;
  
-       if (ssi->usrcnt > 1) {
+       if (ssi->rate) {
                if (ssi->rate != rate) {
                        dev_err(dev, "SSI parent/child should use same rate\n");
                        return -EINVAL;
                }
  
 +              if (ssi->chan != chan) {
 +                      dev_err(dev, "SSI parent/child should use same chan\n");
 +                      return -EINVAL;
 +              }
 +
                return 0;
        }
  
 -      main_rate = rsnd_ssi_clk_query(priv, rate, chan, &idx);
 +      main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx);
        if (!main_rate) {
                dev_err(dev, "unsupported clock rate\n");
                return -EIO;
         * SSICR  : FORCE, SCKD, SWSD
         * SSIWSR : CONT
         */
 -      ssi->cr_clk = FORCE | SWL_32 | SCKD | SWSD | CKDV(idx);
 +      ssi->cr_clk = FORCE | rsnd_rdai_width_to_swl(rdai) |
 +                      SCKD | SWSD | CKDV(idx);
        ssi->wsr = CONT;
        ssi->rate = rate;
 +      ssi->chan = chan;
  
        dev_dbg(dev, "%s[%d] outputs %u Hz\n",
                rsnd_mod_name(mod),
@@@ -370,7 -340,6 +370,7 @@@ static void rsnd_ssi_master_clk_stop(st
  
        ssi->cr_clk     = 0;
        ssi->rate       = 0;
 +      ssi->chan       = 0;
  
        rsnd_adg_ssi_clk_stop(mod);
  }
@@@ -388,7 -357,11 +388,7 @@@ static void rsnd_ssi_config_init(struc
  
        is_tdm = rsnd_runtime_is_ssi_tdm(io);
  
 -      /*
 -       * always use 32bit system word.
 -       * see also rsnd_ssi_master_clk_enable()
 -       */
 -      cr_own |= FORCE | SWL_32;
 +      cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai);
  
        if (rdai->bit_clk_inv)
                cr_own |= SCKP;
  
        cr_own &= ~DWL_MASK;
        switch (snd_pcm_format_width(runtime->format)) {
 +      case 8:
 +              cr_own |= DWL_8;
 +              break;
        case 16:
                cr_own |= DWL_16;
                break;
@@@ -464,7 -434,6 +464,6 @@@ static int rsnd_ssi_init(struct rsnd_mo
                         struct rsnd_priv *priv)
  {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       int ret;
  
        if (!rsnd_ssi_is_run_mods(mod, io))
                return 0;
  
        rsnd_mod_power_on(mod);
  
-       ret = rsnd_ssi_master_clk_start(mod, io);
-       if (ret < 0)
-               return ret;
        rsnd_ssi_config_init(mod, io);
  
        rsnd_ssi_register_setup(mod);
@@@ -523,16 -488,26 +518,16 @@@ static int rsnd_ssi_hw_params(struct rs
                              struct snd_pcm_substream *substream,
                              struct snd_pcm_hw_params *params)
  {
 -      struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 -      int chan = params_channels(params);
 +      struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
 +      unsigned int fmt_width = snd_pcm_format_width(params_format(params));
  
 -      /*
 -       * snd_pcm_ops::hw_params will be called *before*
 -       * snd_soc_dai_ops::trigger. Thus, ssi->usrcnt is 0
 -       * in 1st call.
 -       */
 -      if (ssi->usrcnt) {
 -              /*
 -               * Already working.
 -               * It will happen if SSI has parent/child connection.
 -               * it is error if child <-> parent SSI uses
 -               * different channels.
 -               */
 -              if (ssi->chan != chan)
 -                      return -EIO;
 -      }
 +      if (fmt_width > rdai->chan_width) {
 +              struct rsnd_priv *priv = rsnd_io_to_priv(io);
 +              struct device *dev = rsnd_priv_to_dev(priv);
  
 -      ssi->chan = chan;
 +              dev_err(dev, "invalid combination of slot-width and format-data-width\n");
 +              return -EINVAL;
 +      }
  
        return 0;
  }
@@@ -872,6 -847,13 +867,13 @@@ static int rsnd_ssi_pio_pointer(struct 
        return 0;
  }
  
+ static int rsnd_ssi_prepare(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           struct rsnd_priv *priv)
+ {
+       return rsnd_ssi_master_clk_start(mod, io);
+ }
  static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
        .probe  = rsnd_ssi_common_probe,
        .pointer = rsnd_ssi_pio_pointer,
        .pcm_new = rsnd_ssi_pcm_new,
        .hw_params = rsnd_ssi_hw_params,
+       .prepare = rsnd_ssi_prepare,
  };
  
  static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
                              struct rsnd_dai_stream *io,
                              struct rsnd_priv *priv)
  {
 -      struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        int ret;
  
        /*
                return ret;
  
        /* SSI probe might be called many times in MUX multi path */
 -      ret = rsnd_dma_attach(io, mod, &ssi->dma);
 +      ret = rsnd_dma_attach(io, mod, &io->dma);
  
        return ret;
  }
@@@ -959,6 -943,7 +962,7 @@@ static struct rsnd_mod_ops rsnd_ssi_dma
        .pcm_new = rsnd_ssi_pcm_new,
        .fallback = rsnd_ssi_fallback,
        .hw_params = rsnd_ssi_hw_params,
+       .prepare = rsnd_ssi_prepare,
  };
  
  int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)