hal: Enable direct playback support for AUDIO_FORMAT_PCM_FLOAT

-Add changes to support PCM_FLOAT format using direct track.
-For targets supporting true 32 bit, convert FLOAT pcm data
to 32 bit.
-Targets which dont support true 32 bit, convert FLOAT or
32 bit input to 24 bit Packed in HAL.

Change-Id: I8052c8b878bbd4d70c0453df054953d494773b0a
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index cb646cf..df20120 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -99,6 +99,7 @@
     STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT),
     STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED),
     STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_24_BIT),
+    STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT),
     STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT),
     STRING_TO_ENUM(AUDIO_FORMAT_MP3),
     STRING_TO_ENUM(AUDIO_FORMAT_AAC),
@@ -702,6 +703,124 @@
    return rv;
 }
 
+/*Translates ALSA formats to AOSP PCM formats*/
+audio_format_t alsa_format_to_hal(uint32_t alsa_format)
+{
+    audio_format_t format;
+
+    switch(alsa_format) {
+    case SNDRV_PCM_FORMAT_S16_LE:
+        format = AUDIO_FORMAT_PCM_16_BIT;
+        break;
+    case SNDRV_PCM_FORMAT_S24_3LE:
+        format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+        break;
+    case SNDRV_PCM_FORMAT_S24_LE:
+        format = AUDIO_FORMAT_PCM_8_24_BIT;
+        break;
+    case SNDRV_PCM_FORMAT_S32_LE:
+        format = AUDIO_FORMAT_PCM_32_BIT;
+        break;
+    default:
+        ALOGW("Incorrect ALSA format");
+        format = AUDIO_FORMAT_INVALID;
+    }
+    return format;
+}
+
+/*Translates hal format (AOSP) to alsa formats*/
+uint32_t hal_format_to_alsa(audio_format_t hal_format)
+{
+    uint32_t alsa_format;
+
+    switch (hal_format) {
+    case AUDIO_FORMAT_PCM_32_BIT: {
+        if (platform_supports_true_32bit())
+            alsa_format = SNDRV_PCM_FORMAT_S32_LE;
+        else
+            alsa_format = SNDRV_PCM_FORMAT_S24_3LE;
+        }
+        break;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        alsa_format = SNDRV_PCM_FORMAT_S8;
+        break;
+    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+        alsa_format = SNDRV_PCM_FORMAT_S24_3LE;
+        break;
+    case AUDIO_FORMAT_PCM_8_24_BIT: {
+        if (platform_supports_true_32bit())
+            alsa_format = SNDRV_PCM_FORMAT_S32_LE;
+        else
+            alsa_format = SNDRV_PCM_FORMAT_S24_3LE;
+        }
+        break;
+    case AUDIO_FORMAT_PCM_FLOAT:
+        alsa_format = SNDRV_PCM_FORMAT_S24_3LE;
+        break;
+    default:
+    case AUDIO_FORMAT_PCM_16_BIT:
+        alsa_format = SNDRV_PCM_FORMAT_S16_LE;
+        break;
+    }
+    return alsa_format;
+}
+
+uint32_t get_alsa_fragment_size(uint32_t bytes_per_sample,
+                                  uint32_t sample_rate,
+                                  uint32_t noOfChannels)
+{
+    uint32_t fragment_size = 0;
+    uint32_t pcm_offload_time = PCM_OFFLOAD_BUFFER_DURATION;
+
+    fragment_size = (pcm_offload_time
+                     * sample_rate
+                     * bytes_per_sample
+                     * noOfChannels)/1000;
+    if (fragment_size < MIN_PCM_OFFLOAD_FRAGMENT_SIZE)
+        fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE;
+    else if (fragment_size > MAX_PCM_OFFLOAD_FRAGMENT_SIZE)
+        fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE;
+    /*To have same PCM samples for all channels, the buffer size requires to
+     *be multiple of (number of channels * bytes per sample)
+     *For writes to succeed, the buffer must be written at address which is multiple of 32
+     */
+    fragment_size = ALIGN(fragment_size, (bytes_per_sample * noOfChannels * 32));
+
+    ALOGI("PCM offload Fragment size to %d bytes", fragment_size);
+    return fragment_size;
+}
+
+/* Calculates the fragment size required to configure compress session.
+ * Based on the alsa format selected, decide if conversion is needed in
+
+ * HAL ( e.g. convert AUDIO_FORMAT_PCM_FLOAT input format to
+ * AUDIO_FORMAT_PCM_24_BIT_PACKED before writing to the compress driver.
+ */
+void audio_extn_utils_update_direct_pcm_fragment_size(struct stream_out *out)
+{
+    audio_format_t dst_format = out->compr_pcm_config.hal_op_format;
+    audio_format_t src_format = out->compr_pcm_config.hal_ip_format;
+    uint32_t hal_op_bytes_per_sample = audio_bytes_per_sample(dst_format);
+    uint32_t hal_ip_bytes_per_sample = audio_bytes_per_sample(src_format);
+
+    out->compr_config.fragment_size =
+             get_alsa_fragment_size(hal_op_bytes_per_sample,
+                                      out->sample_rate,
+                                      popcount(out->channel_mask));
+
+    if ((src_format != dst_format) &&
+         hal_op_bytes_per_sample != hal_ip_bytes_per_sample) {
+
+        out->compr_pcm_config.hal_fragment_size =
+                  ((out->compr_config.fragment_size * hal_ip_bytes_per_sample) /
+                   hal_op_bytes_per_sample);
+        ALOGI("enable conversion hal_input_fragment_size is %d src_format %x dst_format %x",
+               out->compr_pcm_config.hal_fragment_size, src_format, dst_format);
+    } else {
+        out->compr_pcm_config.hal_fragment_size = out->compr_config.fragment_size;
+    }
+}
+
 void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
                                              struct audio_usecase *usecase)
 {