diff --git a/Android.mk b/Android.mk
index 5b64934c0788f5e3d1e8958808bdc09bb12530f1..4f6db22651253e3072f79378d6ebf02130b0cc36 100644
--- a/Android.mk
+++ b/Android.mk
@@ -45,3 +45,12 @@ LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES:= external/tinyalsa/include
+LOCAL_SRC_FILES:= tinypcminfo.c
+LOCAL_MODULE := tinypcminfo
+LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h
index 6148361081b0a68bff5a1b078f04bca394c094d7..bb4005a954ccaf366a5307fa2c5fbeb7153d4b31 100644
--- a/include/tinyalsa/asoundlib.h
+++ b/include/tinyalsa/asoundlib.h
@@ -104,6 +104,23 @@ struct pcm_config {
     int avail_min;
 };
 
+/* PCM parameters */
+enum pcm_param
+{
+    PCM_PARAM_SAMPLE_BITS,
+    PCM_PARAM_FRAME_BITS,
+    PCM_PARAM_CHANNELS,
+    PCM_PARAM_RATE,
+    PCM_PARAM_PERIOD_TIME,
+    PCM_PARAM_PERIOD_SIZE,
+    PCM_PARAM_PERIOD_BYTES,
+    PCM_PARAM_PERIODS,
+    PCM_PARAM_BUFFER_TIME,
+    PCM_PARAM_BUFFER_SIZE,
+    PCM_PARAM_BUFFER_BYTES,
+    PCM_PARAM_TICK_TIME,
+};
+
 /* Mixer control types */
 enum mixer_ctl_type {
     MIXER_CTL_TYPE_BOOL,
@@ -123,6 +140,15 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
 int pcm_close(struct pcm *pcm);
 int pcm_is_ready(struct pcm *pcm);
 
+/* Obtain the parameters for a PCM */
+struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
+                                  unsigned int flags);
+void pcm_params_free(struct pcm_params *pcm_params);
+unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
+                                enum pcm_param param);
+unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
+                                enum pcm_param param);
+
 /* Set and get config */
 int pcm_get_config(struct pcm *pcm, struct pcm_config *config);
 int pcm_set_config(struct pcm *pcm, struct pcm_config *config);
diff --git a/pcm.c b/pcm.c
index d841bd9719b251af6fff4e50d07c358c98587b35..4b31c4d9204ab25360cb8108b0ade53c98008b78 100644
--- a/pcm.c
+++ b/pcm.c
@@ -93,6 +93,24 @@ static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
     }
 }
 
+static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        return i->min;
+    }
+    return 0;
+}
+
+static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
+{
+    if (param_is_interval(n)) {
+        struct snd_interval *i = param_to_interval(p, n);
+        return i->max;
+    }
+    return 0;
+}
+
 static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
 {
     if (param_is_interval(n)) {
@@ -130,6 +148,9 @@ static void param_init(struct snd_pcm_hw_params *p)
             i->min = 0;
             i->max = ~0;
     }
+    p->rmask = ~0U;
+    p->cmask = 0;
+    p->info = ~0U;
 }
 
 #define PCM_ERROR_MAX 128
@@ -433,6 +454,129 @@ static struct pcm bad_pcm = {
     .fd = -1,
 };
 
+struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
+                                  unsigned int flags)
+{
+    struct snd_pcm_hw_params *params;
+    char fn[256];
+    int fd;
+
+    snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+             flags & PCM_IN ? 'c' : 'p');
+
+    fd = open(fn, O_RDWR);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open device '%s'\n", fn);
+        goto err_open;
+    }
+
+    params = calloc(1, sizeof(struct snd_pcm_hw_params));
+    if (!params)
+        goto err_calloc;
+
+    param_init(params);
+    if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
+        fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
+        goto err_hw_refine;
+    }
+
+    close(fd);
+
+    return (struct pcm_params *)params;
+
+err_hw_refine:
+    free(params);
+err_calloc:
+    close(fd);
+err_open:
+    return NULL;
+}
+
+void pcm_params_free(struct pcm_params *pcm_params)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+
+    if (params)
+        free(params);
+}
+
+static int pcm_param_to_alsa(enum pcm_param param)
+{
+    switch (param) {
+    case PCM_PARAM_SAMPLE_BITS:
+        return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
+        break;
+    case PCM_PARAM_FRAME_BITS:
+        return SNDRV_PCM_HW_PARAM_FRAME_BITS;
+        break;
+    case PCM_PARAM_CHANNELS:
+        return SNDRV_PCM_HW_PARAM_CHANNELS;
+        break;
+    case PCM_PARAM_RATE:
+        return SNDRV_PCM_HW_PARAM_RATE;
+        break;
+    case PCM_PARAM_PERIOD_TIME:
+        return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
+        break;
+    case PCM_PARAM_PERIOD_SIZE:
+        return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
+        break;
+    case PCM_PARAM_PERIOD_BYTES:
+        return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
+        break;
+    case PCM_PARAM_PERIODS:
+        return SNDRV_PCM_HW_PARAM_PERIODS;
+        break;
+    case PCM_PARAM_BUFFER_TIME:
+        return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
+        break;
+    case PCM_PARAM_BUFFER_SIZE:
+        return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
+        break;
+    case PCM_PARAM_BUFFER_BYTES:
+        return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
+        break;
+    case PCM_PARAM_TICK_TIME:
+        return SNDRV_PCM_HW_PARAM_TICK_TIME;
+        break;
+
+    default:
+        return -1;
+    }
+}
+
+unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
+                                enum pcm_param param)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+    int p;
+
+    if (!params)
+        return 0;
+
+    p = pcm_param_to_alsa(param);
+    if (p < 0)
+        return 0;
+
+    return param_get_min(params, p);
+}
+
+unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
+                                enum pcm_param param)
+{
+    struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+    int p;
+
+    if (!params)
+        return 0;
+
+    p = pcm_param_to_alsa(param);
+    if (p < 0)
+        return 0;
+
+    return param_get_max(params, p);
+}
+
 int pcm_close(struct pcm *pcm)
 {
     if (pcm == &bad_pcm)
diff --git a/tinypcminfo.c b/tinypcminfo.c
new file mode 100644
index 0000000000000000000000000000000000000000..32821867037297a4fc50df4d9a1d44642f1195ad
--- /dev/null
+++ b/tinypcminfo.c
@@ -0,0 +1,97 @@
+/* tinypcminfo.c
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of The Android Open Source Project nor the names of
+**       its contributors may be used to endorse or promote products derived
+**       from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+    unsigned int device = 0;
+    unsigned int card = 0;
+    int i;
+
+    if (argc < 3) {
+        fprintf(stderr, "Usage: %s -D card -d device\n", argv[0]);
+        return 1;
+    }
+
+    /* parse command line arguments */
+    argv += 1;
+    while (*argv) {
+        if (strcmp(*argv, "-D") == 0) {
+            argv++;
+            if (*argv)
+                card = atoi(*argv);
+        }
+        if (strcmp(*argv, "-d") == 0) {
+            argv++;
+            if (*argv)
+                device = atoi(*argv);
+        }
+        if (*argv)
+            argv++;
+    }
+
+    printf("Info for card %d, device %d:\n", card, device);
+
+    for (i = 0; i < 2; i++) {
+        struct pcm_params *params;
+        unsigned int min;
+        unsigned int max;
+
+        printf("\nPCM %s:\n", i == 0 ? "out" : "in");
+
+        params = pcm_params_get(card, device, i == 0 ? PCM_OUT : PCM_IN);
+        if (params == NULL) {
+            printf("Device does not exist.\n");
+            continue;
+        }
+
+        min = pcm_params_get_min(params, PCM_PARAM_RATE);
+        max = pcm_params_get_max(params, PCM_PARAM_RATE);
+        printf("        Rate:\tmin=%uHz\tmax=%uHz\n", min, max);
+        min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
+        max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
+        printf("    Channels:\tmin=%u\t\tmax=%u\n", min, max);
+        min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
+        max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
+        printf(" Sample bits:\tmin=%u\t\tmax=%u\n", min, max);
+        min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
+        max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
+        printf(" Period size:\tmin=%u\t\tmax=%u\n", min, max);
+        min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
+        max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
+        printf("Period count:\tmin=%u\t\tmax=%u\n", min, max);
+
+        pcm_params_free(params);
+    }
+
+    return 0;
+}