From: Ramesh Babu K V <[email protected]>

This patch creates intel_mid_hdmi_audio.c file.
It intereacts with ALSA framework to enable the audio playback
through HDMI interface. This code implements standard ALSA APIs.

Signed-off-by: Ramesh Babu K V <[email protected]>
Signed-off-by: Sailaja Bandarupalli <[email protected]>
---
 .../drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c  |  511 ++++++++++++++++++++
 1 files changed, 511 insertions(+), 0 deletions(-)
 create mode 100644 sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c

diff --git a/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c 
b/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c
new file mode 100644
index 0000000..e256b13
--- /dev/null
+++ b/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c
@@ -0,0 +1,511 @@
+/*
+ *   intel_mid_hdmi_audio.c - Intel HDMI audio driver for MID
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Authors:   Sailaja Bandarupalli <[email protected]>
+ *             Ramesh Babu K V <[email protected]>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * ALSA driver for Intel MID HDMI audio controller
+ */
+
+#define pr_fmt(fmt)    "had: " fmt
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include "intel_mid_hdmi_audio.h"
+
+MODULE_AUTHOR("Sailaja Bandarupalli <[email protected]>");
+MODULE_AUTHOR("Ramesh Babu K V <[email protected]>");
+MODULE_DESCRIPTION("Intel HDMI Audio driver");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{Intel,Intel_HAD}");
+MODULE_VERSION(HAD_DRIVER_VERSION);
+
+#define INFO_FRAME_WORD1       0x000a0184
+#define FIFO_THRESHOLD         0xFE
+#define BYTES_PER_WORD         0x4
+#define CH_STATUS_MAP_32KHZ    0x3
+#define CH_STATUS_MAP_44KHZ    0x0
+#define CH_STATUS_MAP_48KHZ    0x2
+#define MAX_SMPL_WIDTH_20      0x0
+#define MAX_SMPL_WIDTH_24      0x1
+#define SMPL_WIDTH_16BITS      0x1
+#define SMPL_WIDTH_24BITS      0x5
+#define CHANNEL_ALLOCATION     0x1F
+#define MASK_BYTE0             0x000000FF
+#define VALID_DIP_WORDS                3
+#define LAYOUT0                        0
+#define LAYOUT1                        1
+
+/* hardware capability structure */
+static const struct snd_pcm_hardware snd_intel_hadstream = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_DOUBLE |
+               SNDRV_PCM_INFO_MMAP|
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BATCH),
+       .formats = (SNDRV_PCM_FMTBIT_S24 |
+               SNDRV_PCM_FMTBIT_U24),
+       .rates = SNDRV_PCM_RATE_32000 |
+               SNDRV_PCM_RATE_44100 |
+               SNDRV_PCM_RATE_48000 |
+               SNDRV_PCM_RATE_64000 |
+               SNDRV_PCM_RATE_88200 |
+               SNDRV_PCM_RATE_96000 |
+               SNDRV_PCM_RATE_176400 |
+               SNDRV_PCM_RATE_192000,
+       .rate_min = HAD_MIN_RATE,
+       .rate_max = HAD_MAX_RATE,
+       .channels_min = HAD_MIN_CHANNEL,
+       .channels_max = HAD_MAX_CHANNEL,
+       .buffer_bytes_max = HAD_MAX_PERIOD_BYTES,
+       .period_bytes_min = HAD_MIN_PERIOD_BYTES,
+       .period_bytes_max = HAD_MAX_BUFFER,
+       .periods_min = HAD_MIN_PERIODS,
+       .periods_max = HAD_MAX_PERIODS,
+       .fifo_size = HAD_FIFO_SIZE,
+};
+
+/**
+* snd_intelhad_open - stream initializations are done here
+* @substream:substream for which the stream function is called
+*
+* This function is called whenever a PCM stream is opened
+*/
+static int snd_intelhad_open(struct snd_pcm_substream *substream)
+{
+       struct snd_intelhad *intelhaddata;
+       struct snd_pcm_runtime *runtime;
+       struct had_stream_pvt *stream;
+       int retval;
+
+       pr_debug("snd_intelhad_open called\n");
+       intelhaddata = snd_pcm_substream_chip(substream);
+
+       mutex_lock(&intelhaddata->had_lock);
+       if (intelhaddata->playback_cnt > 0) {
+               mutex_unlock(&intelhaddata->had_lock);
+               return -EBUSY;
+       } else
+               intelhaddata->playback_cnt++;
+
+       mutex_unlock(&intelhaddata->had_lock);
+
+       runtime = substream->runtime;
+       /* set the runtime hw parameter with local snd_pcm_hardware struct */
+       runtime->hw = snd_intel_hadstream;
+
+       stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+       if (!stream) {
+               retval = -ENOMEM;
+               goto exit_err;
+       }
+       stream->stream_status = STREAM_INIT;
+       runtime->private_data = stream;
+       intelhaddata->reg_ops.hdmi_audio_write_register(AUD_HDMI_STATUS, 0);
+       retval = snd_pcm_hw_constraint_integer(runtime,
+                        SNDRV_PCM_HW_PARAM_PERIODS);
+       if (retval < 0) {
+               goto exit_err;
+               kfree(stream);
+       }
+       mutex_lock(&intelhaddata->had_lock);
+       intelhaddata->drv_status = HAD_DRV_RUNNING;
+       mutex_unlock(&intelhaddata->had_lock);
+       return retval;
+exit_err:
+       intelhaddata->playback_cnt--;
+       return retval;
+}
+
+/**
+* had_period_elapsed - updates the hardware pointer status
+* @had_substream:substream for which the stream function is called
+*
+*/
+static void had_period_elapsed(void *had_substream)
+{
+       struct snd_pcm_substream *substream = had_substream;
+       struct had_stream_pvt *stream;
+
+       if (!substream || !substream->runtime)
+               return;
+       stream = substream->runtime->private_data;
+       if (!stream)
+               return;
+
+       if (stream->stream_status != STREAM_RUNNING)
+               return;
+       snd_pcm_period_elapsed(substream);
+       return;
+}
+
+/**
+* snd_intelhad_init_stream - internal function to initialize stream info
+* @substream:substream for which the stream function is called
+*
+*/
+static int snd_intelhad_init_stream(struct snd_pcm_substream *substream)
+{
+       struct snd_intelhad *intelhaddata = snd_pcm_substream_chip(substream);
+
+       pr_debug("setting buffer ptr param\n");
+       intelhaddata->stream_info.period_elapsed = had_period_elapsed;
+       intelhaddata->stream_info.had_substream = substream;
+       intelhaddata->stream_info.buffer_ptr = 0;
+       intelhaddata->stream_info.buffer_rendered = 0;
+       intelhaddata->stream_info.sfreq = substream->runtime->rate;
+       return 0;
+}
+
+/**
+ * snd_intelhad_close- to free parameteres when stream is stopped
+ *
+ * @substream:  substream for which the function is called
+ *
+ * This function is called by ALSA framework when stream is stopped
+ */
+static int snd_intelhad_close(struct snd_pcm_substream *substream)
+{
+       struct snd_intelhad *intelhaddata;
+       struct had_stream_pvt *stream;
+
+       stream = substream->runtime->private_data;
+
+       pr_debug("snd_intelhad_close called\n");
+       intelhaddata = snd_pcm_substream_chip(substream);
+       mutex_lock(&intelhaddata->had_lock);
+       if (intelhaddata->playback_cnt) {
+               intelhaddata->playback_cnt--;
+               intelhaddata->stream_info.str_id = 0;
+       }
+       intelhaddata->stream_info.buffer_rendered = 0;
+       intelhaddata->stream_info.buffer_ptr = 0;
+
+       if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED)
+               intelhaddata->drv_status = HAD_DRV_CONNECTED;
+       mutex_unlock(&intelhaddata->had_lock);
+       kfree(substream->runtime->private_data);
+       return 0;
+}
+
+/**
+ * snd_intelhad_hw_params- to setup the hardware parameters
+ * like allocating the buffers
+ *
+ * @substream:  substream for which the function is called
+ * @hw_params: hardware parameters
+ *
+ * This function is called by ALSA framework when hardware params are set
+ */
+static int snd_intelhad_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *hw_params)
+{
+       int retval;
+       struct snd_pcm_runtime *runtime;
+
+       BUG_ON(!hw_params);
+       pr_debug("snd_intelhad_hw_params called\n");
+
+       runtime = substream->runtime;
+
+       retval = snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+       if (retval < 0)
+               return -ENOMEM;
+       pr_debug("allocated memory = %d\n",
+                                       params_buffer_bytes(hw_params));
+       memset(substream->runtime->dma_area, 0,
+                       params_buffer_bytes(hw_params));
+
+       pr_debug("snd_intelhad_hw_params exited\n");
+       return retval;
+}
+
+/**
+ * snd_intelhad_hw_free- to release the resources allocated during
+ * hardware params setup
+ *
+ * @substream:  substream for which the function is called
+ *
+ * This function is called by ALSA framework before close callback.
+ *
+ */
+static int snd_intelhad_hw_free(struct snd_pcm_substream *substream)
+{
+       pr_debug("snd_intelhad_hw_free called\n");
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/**
+* snd_intelhad_pcm_trigger - stream activities are handled here
+* @substream:substream for which the stream function is called
+* @cmd:the stream commamd thats requested from upper layer
+* This function is called whenever an a stream activity is invoked
+*/
+static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream,
+                                       int cmd)
+{
+       int caps, retval = 0;
+       struct snd_intelhad *intelhaddata;
+       struct had_stream_pvt *stream;
+       struct hdmi_audio_registers_ops reg_ops;
+       struct hdmi_audio_query_set_ops query_ops;
+
+       intelhaddata = snd_pcm_substream_chip(substream);
+       stream = substream->runtime->private_data;
+       reg_ops = intelhaddata->reg_ops;
+       query_ops = intelhaddata->query_ops;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               pr_debug("Trigger Start\n");
+               caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
+               retval = query_ops.hdmi_audio_set_caps(
+                                       HAD_SET_ENABLE_AUDIO_INT, &caps);
+               if (retval)
+                       return retval;
+               retval = query_ops.hdmi_audio_set_caps(
+                                       HAD_SET_ENABLE_AUDIO, NULL);
+               if (retval)
+                       return retval;
+
+               reg_ops.hdmi_audio_read_modify(AUD_CONFIG,
+                                       1, BIT(0));
+               stream->substream = substream;
+               stream->stream_status = STREAM_RUNNING;
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               pr_debug("Trigger Stop\n");
+               caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
+               retval = query_ops.hdmi_audio_set_caps(
+                                       HAD_SET_DISABLE_AUDIO_INT, &caps);
+               if (retval)
+                       return retval;
+               retval = query_ops.hdmi_audio_set_caps(
+                                       HAD_SET_DISABLE_AUDIO, NULL);
+               if (retval)
+                       return retval;
+               stream->stream_status = STREAM_DROPPED;
+               break;
+
+       default:
+               retval = -EINVAL;
+       }
+       return retval;
+}
+
+/**
+* snd_intelhad_pcm_prepare- internal preparation before starting a stream
+*
+* @substream:  substream for which the function is called
+*
+* This function is called when a stream is started for internal preparation.
+*/
+static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct had_stream_pvt *stream;
+       int retval;
+       u32 disp_samp_freq, n_param;
+       struct snd_intelhad *intelhaddata;
+       struct snd_pcm_runtime *runtime;
+
+       pr_debug("pcm_prepare called\n");
+
+       runtime = substream->runtime;
+       pr_debug("period_size=%d\n",
+                               frames_to_bytes(runtime, runtime->period_size));
+       pr_debug("periods=%d\n", runtime->periods);
+       pr_debug("buffer_size=%d\n", snd_pcm_lib_buffer_bytes(substream));
+       pr_debug("rate=%d\n", runtime->rate);
+       pr_debug("channels=%d\n", runtime->channels);
+
+       stream = substream->runtime->private_data;
+       intelhaddata = snd_pcm_substream_chip(substream);
+       if (intelhaddata->stream_info.str_id) {
+               pr_debug("_prepare is called for existing str_id#%d\n",
+                                       intelhaddata->stream_info.str_id);
+               retval = snd_intelhad_pcm_trigger(substream,
+                                               SNDRV_PCM_TRIGGER_STOP);
+               return retval;
+       }
+
+       intelhaddata->stream_info.str_id = intelhaddata->playback_cnt;
+       snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+                       "%d", intelhaddata->stream_info.str_id);
+       retval = snd_intelhad_init_stream(substream);
+       if (retval)
+               goto prep_end;
+       /* Get N value in KHz */
+       retval = intelhaddata->query_ops.hdmi_audio_get_caps(
+                               HAD_GET_SAMPLING_FREQ, &disp_samp_freq);
+       if (retval) {
+               pr_err("querying display sampling freq failed %#x\n", retval);
+               goto prep_end;
+       } else
+               pr_debug("TMDS freq = %d kHz\n", disp_samp_freq);
+
+       retval = snd_intelhad_prog_n(substream->runtime->rate, &n_param,
+                                                               intelhaddata);
+       if (retval) {
+               pr_err("programming N value failed %#x\n", retval);
+               goto prep_end;
+       }
+       snd_intelhad_prog_cts(substream->runtime->rate/1000,
+                                       disp_samp_freq, n_param, intelhaddata);
+
+       snd_intelhad_prog_dip(substream, intelhaddata);
+
+       retval = snd_intelhad_init_audio_ctrl(substream, intelhaddata);
+       if (retval) {
+               pr_err("init audio controller regs failed %#x\n", retval);
+               goto prep_end;
+       }
+       retval = snd_intelhad_prog_ring_buf(substream, intelhaddata);
+       if (retval)
+               pr_err("init ring buffer regs failed %#x\n", retval);
+
+prep_end:
+       return retval;
+}
+
+/**
+ * snd_intelhad_pcm_pointer- to send the current buffer pointerprocessed by hw
+ *
+ * @substream:  substream for which the function is called
+ *
+ * This function is called by ALSA framework to get the current hw buffer ptr
+ * when a period is elapsed
+ */
+static snd_pcm_uframes_t snd_intelhad_pcm_pointer(
+                                       struct snd_pcm_substream *substream)
+{
+       struct had_stream_pvt *stream;
+       struct snd_intelhad *intelhaddata;
+       enum intel_had_aud_buf_type buf_id;
+       u32 bytes_rendered, bytes_dmaed;
+
+       intelhaddata = snd_pcm_substream_chip(substream);
+       stream = substream->runtime->private_data;
+       /*query the DMA buffer position*/
+       buf_id = intelhaddata->curr_buf;
+       intelhaddata->reg_ops.hdmi_audio_read_register(
+                               AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH),
+                               &bytes_dmaed);
+       bytes_dmaed = intelhaddata->buf_info[buf_id].buf_size - bytes_dmaed;
+       div_u64_rem(intelhaddata->stream_info.buffer_rendered + bytes_dmaed,
+                       intelhaddata->stream_info.ring_buf_size,
+                       &(bytes_rendered));
+       intelhaddata->stream_info.buffer_ptr = bytes_to_frames(
+                                               substream->runtime,
+                                               bytes_rendered);
+       return intelhaddata->stream_info.buffer_ptr;
+}
+
+/*PCM operations structure and the calls back for the same */
+struct snd_pcm_ops snd_intelhad_playback_ops = {
+       .open =         snd_intelhad_open,
+       .close =        snd_intelhad_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_intelhad_hw_params,
+       .hw_free =      snd_intelhad_hw_free,
+       .prepare =      snd_intelhad_pcm_prepare,
+       .trigger =      snd_intelhad_pcm_trigger,
+       .pointer =      snd_intelhad_pcm_pointer,
+};
+
+/*
+ *  * entry point for hdmi interface
+ *   */
+
+static struct snd_intel_had_interface had_driver = {
+       .name =         "hdmi-audio",
+       .probe =        hdmi_audio_probe,
+       .disconnect =   hdmi_audio_remove,
+       .suspend =      hdmi_audio_suspend,
+       .resume =       hdmi_audio_resume,
+};
+static struct snd_intelhad *had_pvt_data;
+
+/*
+* alsa_card_intelhad_init- driver init function
+* This function is called when driver module is inserted
+*/
+static int __init alsa_card_intelhad_init(void)
+{
+       int retval;
+       struct had_callback_ops ops_cb;
+       struct snd_intelhad *intelhaddata;
+
+       pr_debug("init called\n");
+       pr_info("******** HAD DRIVER loading.. Ver: %s\n",
+                                       HAD_DRIVER_VERSION);
+
+       /* allocate memory for saving internal context and working */
+       intelhaddata = kzalloc(sizeof(*intelhaddata), GFP_KERNEL);
+       if (!intelhaddata) {
+               pr_err("mem alloc failed\n");
+               return -ENOMEM;
+       }
+       had_pvt_data = intelhaddata;
+       ops_cb.intel_had_event_call_back = had_event_handler;
+
+       retval = display_register(&had_driver, intelhaddata);
+       if (retval) {
+               pr_err("registering with display driver failed %#x\n", retval);
+               goto free_allocs;
+       }
+
+       /* registering with display driver to get access to display APIs */
+       retval = intel_hdmi_audio_query_capabilities(
+                       ops_cb.intel_had_event_call_back,
+                       &(intelhaddata->reg_ops),
+                       &(intelhaddata->query_ops));
+       if (retval) {
+               pr_err("querying display driver APIs failed %#x\n", retval);
+               goto free_allocs;
+       }
+       mutex_init(&intelhaddata->had_lock);
+       mutex_lock(&intelhaddata->had_lock);
+       intelhaddata->drv_status = HAD_DRV_DISCONNECTED;
+       mutex_unlock(&intelhaddata->had_lock);
+       pr_debug("init complete\n");
+       return retval;
+free_allocs:
+       kfree(intelhaddata);
+       pr_err("driver init failed %#x\n", retval);
+       return retval;
+}
+
+/**
+* alsa_card_intelhad_exit- driver exit function
+* This function is called when driver module is removed
+*/
+static void __exit alsa_card_intelhad_exit(void)
+{
+       pr_debug("had_exit called\n");
+       kfree(had_pvt_data);
+}
+late_initcall(alsa_card_intelhad_init);
+module_exit(alsa_card_intelhad_exit);
--
1.6.2.5

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to