config: make volume control range configurable

Add optional configuration file settings to limit the
range of values over which a volume control will operate.

Change-Id: I2f717ade6678971fe682afaae10946fe01597705
Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
diff --git a/audio_config.c b/audio_config.c
index 8e76ea7..e26e02b 100644
--- a/audio_config.c
+++ b/audio_config.c
@@ -119,6 +119,14 @@
 struct stream_control {
     struct mixer_ctl    *ctl;
     uint                id;
+    uint                min;
+    uint                max;
+};
+
+struct volume_control {
+    struct stream_control control;
+    uint min;
+    uint max;
 };
 
 struct stream {
@@ -195,6 +203,8 @@
     e_attrib_rate,
     e_attrib_period_size,
     e_attrib_period_count,
+    e_attrib_min,
+    e_attrib_max,
 
     e_attrib_count
 };
@@ -521,27 +531,47 @@
  * Stream control
  *********************************************************************/
 
+static int set_vol_ctl( const struct stream_control *volctl, uint percent)
+{
+    uint val;
+    uint range;
+
+    switch (percent) {
+    case 0:
+        val = volctl->min;
+        break;
+
+    case 100:
+        val = volctl->max;
+        break;
+
+    default:
+        range = volctl->max - volctl->min;
+        val = volctl->min + ((percent * range)/100);
+        break;
+    }
+
+    ALOGW("set '%s' = %u", mixer_ctl_get_name(volctl->ctl), val);
+    mixer_ctl_set_value(volctl->ctl, volctl->id, val);
+    return 0;
+}
+
 int set_hw_volume( const struct hw_stream *stream, int left_pc, int right_pc)
 {
     struct stream *s = (struct stream *)stream;
     int ret = -ENOSYS;
 
-
     if (s->controls.volume_left.ctl) {
         if (!s->controls.volume_right.ctl) {
             /* Control is mono so average left and right */
             left_pc = (left_pc + right_pc) / 2;
         }
-        mixer_ctl_set_percent(s->controls.volume_left.ctl,
-                                s->controls.volume_left.id,
-                                left_pc);
-        ret = 0;
+
+        ret = set_vol_ctl(&s->controls.volume_left, left_pc);
     }
+
     if (s->controls.volume_right.ctl) {
-        mixer_ctl_set_percent(s->controls.volume_right.ctl,
-                                s->controls.volume_right.id,
-                                right_pc);
-        ret = 0;
+        ret = set_vol_ctl(&s->controls.volume_right, right_pc);
     }
 
     ALOGV_IF(ret == 0, "set_hw_volume: L=%d%% R=%d%%", left_pc, right_pc);
@@ -835,7 +865,8 @@
     [e_elem_stream_ctl] =    {
         .name = "ctl",
         .valid_attribs = BIT(e_attrib_name) | BIT(e_attrib_function)
-                            | BIT(e_attrib_index),
+                            | BIT(e_attrib_index)
+                            | BIT(e_attrib_min) | BIT(e_attrib_max),
         .required_attribs = BIT(e_attrib_name) | BIT(e_attrib_function),
         .valid_subelem = 0,
         .start_fn = parse_stream_ctl_start,
@@ -883,8 +914,10 @@
     [e_attrib_instances] =  {"instances"},
     [e_attrib_rate] =       {"rate"},
     [e_attrib_period_size] = {"period_size"},
-    [e_attrib_period_count] = {"period_count"}
-};
+    [e_attrib_period_count] = {"period_count"},
+    [e_attrib_min] = {"min"},
+    [e_attrib_max] = {"max"}
+ };
 
 static const struct parse_device device_table[] = {
     {"global",      0}, /* special dummy device for global settings */
@@ -1522,7 +1555,10 @@
     const char *function = state->attribs.value[e_attrib_function];
     const char *index = state->attribs.value[e_attrib_index];
     struct mixer_ctl *ctl;
+    struct stream_control *streamctl;
     uint idx_val = 0;
+    uint32_t v;
+    int r;
 
     ctl = mixer_get_ctl_by_name(state->cm->mixer, name);
     if (!ctl) {
@@ -1539,21 +1575,59 @@
     if (0 == strcmp(function, "leftvol")) {
         ALOGE_IF(state->current.stream->controls.volume_left.ctl,
                                 "Left volume control specified again");
-        state->current.stream->controls.volume_left.ctl = ctl;
-        state->current.stream->controls.volume_left.id = idx_val;
-
+        streamctl = &(state->current.stream->controls.volume_left);
     } else if (0 == strcmp(function, "rightvol")) {
         ALOGE_IF(state->current.stream->controls.volume_right.ctl,
                                 "Right volume control specified again");
-        state->current.stream->controls.volume_right.ctl = ctl;
-        state->current.stream->controls.volume_right.id = idx_val;
-
+        streamctl = &(state->current.stream->controls.volume_right);
     } else {
         ALOGE("'%s' is not a valid control function", function);
         return -EINVAL;
     }
 
-    ALOGV("Added control '%s' function '%s'", name, function);
+    streamctl->ctl = ctl;
+    streamctl->id = idx_val;
+
+    switch (attrib_to_uint(&v, state, e_attrib_min)) {
+    case -EINVAL:
+        return -EINVAL;
+
+    case -ENOENT:
+        /* Not specified, get control's min value */
+        r = mixer_ctl_get_range_min(ctl);
+        if (r < 0) {
+            ALOGE("Failed to get control min");
+            return r;
+        }
+        streamctl->min = (uint)r;
+        break;
+
+    default:
+        streamctl->min = v;
+        break;
+    }
+
+    switch (attrib_to_uint(&v, state, e_attrib_max)) {
+    case -EINVAL:
+        return -EINVAL;
+
+    case -ENOENT:
+        /* Not specified, get control's max value */
+        r = mixer_ctl_get_range_max(ctl);
+        if (r < 0) {
+            ALOGE("Failed to get control max");
+            return r;
+        }
+        streamctl->max = (uint)r;
+        break;
+
+    default:
+        streamctl->max = v;
+        break;
+    }
+
+    ALOGV("Added control '%s' function '%s' range %u-%u", name, function,
+                streamctl->min, streamctl->max);
 
     return 0;
 }