diff --git a/include/pcm_rate.h b/include/pcm_rate.h
index d211e09..cd8f103 100644
--- a/include/pcm_rate.h
+++ b/include/pcm_rate.h
@@ -38,7 +38,7 @@ extern "C" {
 /**
  * Protocol version
  */
-#define SND_PCM_RATE_PLUGIN_VERSION	0x010001
+#define SND_PCM_RATE_PLUGIN_VERSION	0x010002
 
 /** hw_params information for a single side */
 typedef struct snd_pcm_rate_side_info {
@@ -98,6 +98,17 @@ typedef struct snd_pcm_rate_ops {
 	 * compute the frame size for output
 	 */
 	snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
+	/**
+	 * the protocol version the plugin supports;
+	 * new field since version 0x010002
+	 */
+	unsigned int version;
+	/**
+	 * return the supported min / max sample rates;
+	 * new ops since version 0x010002
+	 */
+	int (*get_supported_rates)(void *obj, unsigned int *rate_min,
+				   unsigned int *rate_max);
 } snd_pcm_rate_ops_t;
 
 /** open function type */
@@ -110,6 +121,26 @@ typedef int (*snd_pcm_rate_open_func_t)(unsigned int version, void **objp,
 #define SND_PCM_RATE_PLUGIN_ENTRY(name) _snd_pcm_rate_##name##_open
 
 
+#ifndef DOC_HIDDEN
+/* old rate_ops for protocol version 0x010001 */
+typedef struct snd_pcm_rate_old_ops {
+	void (*close)(void *obj);
+	int (*init)(void *obj, snd_pcm_rate_info_t *info);
+	void (*free)(void *obj);
+	void (*reset)(void *obj);
+	int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info);
+	void (*convert)(void *obj,
+			const snd_pcm_channel_area_t *dst_areas,
+			snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
+			const snd_pcm_channel_area_t *src_areas,
+			snd_pcm_uframes_t src_offset, unsigned int src_frames);
+	void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames,
+			    const int16_t *src, unsigned int src_frames);
+	snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames);
+	snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
+} snd_pcm_rate_old_ops_t;
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index a97a5de..9fc2685 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -69,12 +69,17 @@ struct _snd_pcm_rate {
 	int16_t *dst_buf;
 	int start_pending; /* start is triggered but not commited to slave */
 	snd_htimestamp_t trigger_tstamp;
+	unsigned int plugin_version;
+	unsigned int rate_min, rate_max;
 };
 
+#define SND_PCM_RATE_PLUGIN_VERSION_OLD	0x010001	/* old rate plugin */
+
 #endif /* DOC_HIDDEN */
 
 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
 {
+	snd_pcm_rate_t *rate = pcm->private_data;
 	int err;
 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
 	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
@@ -89,14 +94,18 @@ static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_
 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
 	if (err < 0)
 		return err;
-	err = _snd_pcm_hw_param_set_min(params,
-					SND_PCM_HW_PARAM_RATE, SND_PCM_PLUGIN_RATE_MIN, 0);
-	if (err < 0)
-		return err;
-	err = _snd_pcm_hw_param_set_max(params,
-					SND_PCM_HW_PARAM_RATE, SND_PCM_PLUGIN_RATE_MAX, 0);
-	if (err < 0)
-		return err;
+	if (rate->rate_min) {
+		err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
+						rate->rate_min, 0);
+		if (err < 0)
+			return err;
+	}
+	if (rate->rate_max) {
+		err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
+						rate->rate_max, 0);
+		if (err < 0)
+			return err;
+	}
 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
 	return 0;
 }
@@ -1264,6 +1273,7 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type)
 {
 	char open_name[64];
 	snd_pcm_rate_open_func_t open_func;
+	int err;
 
 	snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
 	open_func = snd_dlobj_cache_lookup(open_name);
@@ -1285,7 +1295,25 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type)
 		}
 		snd_dlobj_cache_add(open_name, h, open_func);
 	}
-	return open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
+
+	rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
+	rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
+	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
+
+	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
+	if (!err) {
+		rate->plugin_version = rate->ops.version;
+		if (rate->ops.get_supported_rates)
+			rate->ops.get_supported_rates(rate->obj,
+						      &rate->rate_min,
+						      &rate->rate_max);
+		return 0;
+	}
+
+	/* try to open with the old protocol version */
+	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
+	return open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
+			 &rate->obj, &rate->ops);
 }
 #endif
 
diff --git a/src/pcm/pcm_rate_linear.c b/src/pcm/pcm_rate_linear.c
index 20e119b..69cc2f2 100644
--- a/src/pcm/pcm_rate_linear.c
+++ b/src/pcm/pcm_rate_linear.c
@@ -405,6 +405,14 @@ static void linear_close(void *obj)
 	free(obj);
 }
 
+static int get_supported_rates(void *rate, unsigned int *rate_min,
+			       unsigned int *rate_max)
+{
+	*rate_min = SND_PCM_PLUGIN_RATE_MIN;
+	*rate_max = SND_PCM_PLUGIN_RATE_MAX;
+	return 0;
+}
+
 static const snd_pcm_rate_ops_t linear_ops = {
 	.close = linear_close,
 	.init = linear_init,
@@ -414,17 +422,14 @@ static const snd_pcm_rate_ops_t linear_ops = {
 	.convert = linear_convert,
 	.input_frames = input_frames,
 	.output_frames = output_frames,
+	.version = SND_PCM_RATE_PLUGIN_VERSION,
+	.get_supported_rates = get_supported_rates,
 };
 
 int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
 {
 	struct rate_linear *rate;
 
-	if (version != SND_PCM_RATE_PLUGIN_VERSION) {
-		SNDERR("Invalid plugin version %x\n", version);
-		return -EINVAL;
-	}
-
 	rate = calloc(1, sizeof(*rate));
 	if (! rate)
 		return -ENOMEM;
