struct snd_pcm_hardware {
        unsigned int info;              /* SNDRV_PCM_INFO_* */
        u64 formats;                    /* SNDRV_PCM_FMTBIT_* */
+       u32 subformats;                 /* for S32_LE, SNDRV_PCM_SUBFMTBIT_* */
        unsigned int rates;             /* SNDRV_PCM_RATE_* */
        unsigned int rate_min;          /* min rate */
        unsigned int rate_max;          /* max rate */
 #define SNDRV_PCM_FMTBIT_U20           SNDRV_PCM_FMTBIT_U20_BE
 #endif
 
+#define _SNDRV_PCM_SUBFMTBIT(fmt)      BIT((__force int)SNDRV_PCM_SUBFORMAT_##fmt)
+#define SNDRV_PCM_SUBFMTBIT_STD                _SNDRV_PCM_SUBFMTBIT(STD)
+#define SNDRV_PCM_SUBFMTBIT_MSBITS_MAX _SNDRV_PCM_SUBFMTBIT(MSBITS_MAX)
+#define SNDRV_PCM_SUBFMTBIT_MSBITS_20  _SNDRV_PCM_SUBFMTBIT(MSBITS_20)
+#define SNDRV_PCM_SUBFMTBIT_MSBITS_24  _SNDRV_PCM_SUBFMTBIT(MSBITS_24)
+
 struct snd_pcm_file {
        struct snd_pcm_substream *substream;
        int no_compat_mmap;
 
  *                                                                           *
  *****************************************************************************/
 
-#define SNDRV_PCM_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 15)
+#define SNDRV_PCM_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 16)
 
 typedef unsigned long snd_pcm_uframes_t;
 typedef signed long snd_pcm_sframes_t;
 
 typedef int __bitwise snd_pcm_subformat_t;
 #define        SNDRV_PCM_SUBFORMAT_STD         ((__force snd_pcm_subformat_t) 0)
-#define        SNDRV_PCM_SUBFORMAT_LAST        SNDRV_PCM_SUBFORMAT_STD
+#define        SNDRV_PCM_SUBFORMAT_MSBITS_MAX  ((__force snd_pcm_subformat_t) 1)
+#define        SNDRV_PCM_SUBFORMAT_MSBITS_20   ((__force snd_pcm_subformat_t) 2)
+#define        SNDRV_PCM_SUBFORMAT_MSBITS_24   ((__force snd_pcm_subformat_t) 3)
+#define        SNDRV_PCM_SUBFORMAT_LAST        SNDRV_PCM_SUBFORMAT_MSBITS_24
 
 #define SNDRV_PCM_INFO_MMAP            0x00000001      /* hardware supports mmap */
 #define SNDRV_PCM_INFO_MMAP_VALID      0x00000002      /* period data are valid during transfer */
 
 {
        const struct snd_interval *i;
        const struct snd_mask *m;
+       struct snd_mask *m_rw;
        int err;
 
        if (!params->msbits) {
                        params->msbits = snd_interval_value(i);
        }
 
+       if (params->msbits) {
+               m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+               if (snd_mask_single(m)) {
+                       snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+
+                       if (snd_pcm_format_linear(format) &&
+                           snd_pcm_format_width(format) != params->msbits) {
+                               m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+                               snd_mask_reset(m_rw,
+                                              (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+                               if (snd_mask_empty(m_rw))
+                                       return -EINVAL;
+                       }
+               }
+       }
+
        if (!params->rate_den) {
                i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
                if (snd_interval_single(i)) {
        return snd_interval_refine(hw_param_interval(params, rule->var), &t);
 }              
 
+static int snd_pcm_hw_rule_subformats(struct snd_pcm_hw_params *params,
+                                     struct snd_pcm_hw_rule *rule)
+{
+       struct snd_mask *sfmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+       struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       u32 *subformats = rule->private;
+       snd_pcm_format_t f;
+       struct snd_mask m;
+
+       snd_mask_none(&m);
+       /* All PCMs support at least the default STD subformat. */
+       snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD);
+
+       pcm_for_each_format(f) {
+               if (!snd_mask_test(fmask, (__force unsigned)f))
+                       continue;
+
+               if (f == SNDRV_PCM_FORMAT_S32_LE && *subformats)
+                       m.bits[0] |= *subformats;
+               else if (snd_pcm_format_linear(f))
+                       snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+       }
+
+       return snd_mask_refine(sfmask, &m);
+}
+
+static int snd_pcm_hw_constraint_subformats(struct snd_pcm_runtime *runtime,
+                                          unsigned int cond, u32 *subformats)
+{
+       return snd_pcm_hw_rule_add(runtime, cond, -1,
+                                  snd_pcm_hw_rule_subformats, (void *)subformats,
+                                  SNDRV_PCM_HW_PARAM_SUBFORMAT,
+                                  SNDRV_PCM_HW_PARAM_FORMAT, -1);
+}
+
 static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        if (err < 0)
                return err;
 
-       err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT,
-                                        PARAM_MASK_BIT(SNDRV_PCM_SUBFORMAT_STD));
+       err = snd_pcm_hw_constraint_subformats(runtime, 0, &hw->subformats);
        if (err < 0)
                return err;
 
 
  *                                                                           *
  *****************************************************************************/
 
-#define SNDRV_PCM_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 15)
+#define SNDRV_PCM_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 16)
 
 typedef unsigned long snd_pcm_uframes_t;
 typedef signed long snd_pcm_sframes_t;
 
 typedef int __bitwise snd_pcm_subformat_t;
 #define        SNDRV_PCM_SUBFORMAT_STD         ((__force snd_pcm_subformat_t) 0)
-#define        SNDRV_PCM_SUBFORMAT_LAST        SNDRV_PCM_SUBFORMAT_STD
+#define        SNDRV_PCM_SUBFORMAT_MSBITS_MAX  ((__force snd_pcm_subformat_t) 1)
+#define        SNDRV_PCM_SUBFORMAT_MSBITS_20   ((__force snd_pcm_subformat_t) 2)
+#define        SNDRV_PCM_SUBFORMAT_MSBITS_24   ((__force snd_pcm_subformat_t) 3)
+#define        SNDRV_PCM_SUBFORMAT_LAST        SNDRV_PCM_SUBFORMAT_MSBITS_24
 
 #define SNDRV_PCM_INFO_MMAP            0x00000001      /* hardware supports mmap */
 #define SNDRV_PCM_INFO_MMAP_VALID      0x00000002      /* period data are valid during transfer */