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

This patch creates intel_mid_hdmi_audio_if.c file.
This interface module of the driver communicates with client driver
(HDMI display module) for receiving buffer complete/over run interrupt.
ALSA sound card will be created dynamically when probe is called
and removed when remove is invoked. It implements power management.

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

diff --git a/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio_if.c 
b/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio_if.c
new file mode 100644
index 0000000..40b39e2
--- /dev/null
+++ b/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio_if.c
@@ -0,0 +1,307 @@
+/*
+ *   intel_mid_hdmi_audio_if.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.  This file contains
+ * interface functions exposed to HDMI Display driver and code to register
+ * with ALSA framework..
+ */
+
+#define pr_fmt(fmt)            "had: " fmt
+
+#include <sound/pcm.h>
+#include <sound/core.h>
+#include "intel_mid_hdmi_audio.h"
+
+#define PCM_INDEX              0
+#define MAX_PB_STREAMS         1
+#define MAX_CAP_STREAMS                0
+
+/*standard module options for ALSA. This module supports only one card*/
+static int hdmi_card_index = SNDRV_DEFAULT_IDX1;
+static char *hdmi_card_id = SNDRV_DEFAULT_STR1;
+
+module_param(hdmi_card_index, int, 0444);
+MODULE_PARM_DESC(hdmi_card_index,
+               "Index value for INTEL Intel HDMI Audio controller.");
+module_param(hdmi_card_id, charp, 0444);
+MODULE_PARM_DESC(hdmi_card_id,
+               "ID string for INTEL Intel HDMI Audio controller.");
+
+/**
+ * snd_intelhad_create - to crete alsa card instance
+ *
+ * @intelhaddata: pointer to internal context
+ * @card: pointer to card
+ *
+ * This function is called when the hdmi cable is plugged in
+ */
+static int __devinit snd_intelhad_create(
+               struct snd_intelhad *intelhaddata,
+               struct snd_card *card)
+{
+       int retval;
+       static struct snd_device_ops ops = {
+       };
+
+       BUG_ON(!intelhaddata);
+       /* ALSA api to register the device */
+       retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelhaddata, &ops);
+       return retval;
+}
+/**
+ * snd_intelhad_pcm_free - to free the memory allocated
+ *
+ * @pcm: pointer to pcm instance
+ * This function is called when the device is removed
+ */
+static void snd_intelhad_pcm_free(struct snd_pcm *pcm)
+{
+       pr_debug("Freeing PCM preallocated pages\n");
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+/**
+ * hdmi_audio_probe - to create sound card instance for HDMI audio playabck
+ *
+ *...@haddata: pointer to HAD private data
+ *...@card_id: card for which probe is called
+ *
+ * This function is called when the hdmi cable is plugged in. This function
+ * creates and registers the sound card with ALSA
+ */
+int hdmi_audio_probe(void *haddata, const int card_id)
+{
+
+       int retval;
+       struct snd_pcm *pcm;
+       struct snd_card *card;
+       struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata;
+
+       pr_debug("Hot plug event received\n");
+
+       if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED)
+               return -EEXIST;
+
+       mutex_lock(&intelhaddata->had_lock);
+       /* create a card instance with ALSA framework */
+       retval = snd_card_create(hdmi_card_index, hdmi_card_id,
+                               THIS_MODULE, 0, &card);
+       if (retval)
+               goto err;
+       intelhaddata->card = card;
+       intelhaddata->card_id = hdmi_card_id;
+       intelhaddata->card_index = card->number;
+       intelhaddata->playback_cnt = 0;
+       intelhaddata->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
+       strncpy(card->driver, INTEL_HAD, strlen(INTEL_HAD));
+       strncpy(card->shortname, INTEL_HAD, strlen(INTEL_HAD));
+
+       retval = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS,
+                                               MAX_CAP_STREAMS, &pcm);
+       if (retval)
+               goto err;
+
+       /* setup private data which can be retrieved when required */
+       pcm->private_data = intelhaddata;
+       pcm->private_free = snd_intelhad_pcm_free;
+       pcm->info_flags = 0;
+       strncpy(pcm->name, card->shortname, strlen(card->shortname));
+       /* setup the ops for palyabck */
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                           &snd_intelhad_playback_ops);
+       /* allocate dma pages for ALSA stream operations */
+       retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       snd_dma_continuous_data(GFP_KERNEL),
+                       HAD_MIN_BUFFER, HAD_MAX_BUFFER);
+       if (retval)
+               goto err;
+
+       /* internal function call to register device with ALSA */
+       retval = snd_intelhad_create(intelhaddata, card);
+       if (retval)
+               goto err;
+
+       card->private_data = &intelhaddata;
+       retval = snd_card_register(card);
+       if (retval)
+               goto err;
+
+       /* IEC958 controls */
+       retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958_mask,
+                                               intelhaddata));
+       if (retval < 0)
+               goto err;
+       retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958,
+                                               intelhaddata));
+       if (retval < 0)
+               goto err;
+       intelhaddata->query_ops.hdmi_audio_get_caps(
+                               HAD_GET_ELD, &intelhaddata->eeld);
+       intelhaddata->reg_ops.hdmi_audio_write_register(
+                                               AUD_HDMI_STATUS, 1);
+       intelhaddata->drv_status = HAD_DRV_CONNECTED;
+       mutex_unlock(&intelhaddata->had_lock);
+       return retval;
+err:
+       pr_err("Error returned from snd api %#x\n", retval);
+       snd_card_free(card);
+       mutex_unlock(&intelhaddata->had_lock);
+       return retval;
+}
+
+/**
+ * hdmi_audio_remove - removes the alsa card
+ *
+ *...@haddata: pointer to HAD private data
+ *
+ * This function is called when the hdmi cable is un-plugged. This function
+ * free the sound card.
+ */
+void  hdmi_audio_remove(void *haddata)
+{
+       struct snd_intelhad *intelhaddata = haddata;
+       struct hdmi_audio_query_set_ops query_ops;
+       int caps;
+
+       mutex_lock(&intelhaddata->had_lock);
+       if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) {
+               mutex_unlock(&intelhaddata->had_lock);
+               return -ENODEV;
+       }
+       if (intelhaddata->drv_status == HAD_DRV_RUNNING) {
+               query_ops = intelhaddata->query_ops;
+               caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
+               query_ops.hdmi_audio_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps);
+               query_ops.hdmi_audio_set_caps(HAD_SET_DISABLE_AUDIO, NULL);
+       }
+       snd_card_free(intelhaddata->card);
+       intelhaddata->card_index = SNDRV_DEFAULT_IDX1;
+       intelhaddata->drv_status = HAD_DRV_DISCONNECTED;
+       mutex_unlock(&intelhaddata->had_lock);
+}
+
+/**
+ * hdmi_audio_suspend - power management suspend function
+ *
+ *...@haddata: pointer to HAD private data
+ *...@event: pm event for which this method is invoked
+ *
+ * This function is called by client driver to suspend the
+ * hdmi audio.
+ */
+int hdmi_audio_suspend(void *haddata, pm_event_t event)
+{
+       int retval = 0;
+       struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata;
+       if (intelhaddata->playback_cnt > 0) {
+               pr_err("audio stream is active\n");
+               return -EBUSY;
+       }
+       intelhaddata->reg_ops.hdmi_audio_read_modify(AUD_CONFIG,
+                                       0, BIT(0));
+       mutex_lock(&intelhaddata->had_lock);
+       intelhaddata->drv_status = HAD_DRV_SUSPENDED;
+       mutex_unlock(&intelhaddata->had_lock);
+       return retval;
+}
+
+/**
+ * hdmi_audio_resume - power management resume function
+ *
+ *...@haddata: pointer to HAD private data
+ *
+ * This function is called by client driver to resume the
+ * hdmi audio.
+ */
+int hdmi_audio_resume(void *haddata)
+{
+       struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata;
+       if (HAD_DRV_SUSPENDED != intelhaddata->drv_status) {
+               pr_err("had is not in suspended state\n");
+               return 0;
+       }
+       mutex_lock(&intelhaddata->had_lock);
+       if (SNDRV_DEFAULT_IDX1 ==  intelhaddata->card_index)
+               intelhaddata->drv_status = HAD_DRV_DISCONNECTED;
+       else
+               intelhaddata->drv_status = HAD_DRV_CONNECTED;
+       mutex_unlock(&intelhaddata->had_lock);
+       return 0;
+}
+
+
+/**
+ * had_event_handler - Call back function to handle events
+ *
+ * @event_type: Event type to handle
+ * @data: data related to the event_type
+ *
+ * This function is invoked to handle HDMI events from client driver.
+ */
+int had_event_handler(enum had_event_type event_type, void *data)
+{
+       int retval = 0;
+       struct snd_intelhad *intelhaddata = data;
+       enum intel_had_aud_buf_type buf_id;
+       struct pcm_stream_info *stream;
+       u32 buf_size;
+
+       buf_id = intelhaddata->curr_buf;
+       switch (event_type) {
+       case HAD_EVENT_AUDIO_BUFFER_DONE:
+               stream = &intelhaddata->stream_info;
+               pr_debug("called _event_handler with BUFFER DONE event for\
+                                                        Buf#%d\n", buf_id);
+               buf_size =  intelhaddata->buf_info[buf_id].buf_size;
+               intelhaddata->stream_info.buffer_rendered += buf_size;
+               intelhaddata->buf_info[buf_id].is_valid = true;
+               if (intelhaddata->valid_buf_cnt-1 == buf_id)
+                       intelhaddata->curr_buf = HAD_BUF_TYPE_A;
+               else
+                       intelhaddata->curr_buf++;
+               stream->period_elapsed(stream->had_substream);
+               /*Reprogram the registers with addr and length*/
+               intelhaddata->reg_ops.hdmi_audio_write_register(
+                               AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH),
+                               buf_size);
+               intelhaddata->reg_ops.hdmi_audio_write_register(
+                               AUD_BUF_A_ADDR+(buf_id * HAD_REG_WIDTH),
+                               intelhaddata->buf_info[buf_id].buf_addr|
+                               BIT(0) | BIT(1));
+       break;
+
+       case HAD_EVENT_AUDIO_BUFFER_UNDERRUN:
+               pr_debug("called _event_handler with _UNDERRUN event for\
+                                       buf #%d\n", buf_id);
+               /*To be handle  error handling*/
+               retval = -EIO;
+       break;
+
+       default:
+               pr_debug("error un-handled event !!\n");
+               retval = -EINVAL;
+       break;
+
+       }
+       return retval;
+}
-- 
1.6.2.5

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

Reply via email to