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
