#include <sound/initval.h>
#include "intel_mid_hdmi_audio.h"
+#include <psb_powermgmt.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;
-static struct snd_intelhad *had_pvt_data;
+static struct snd_intelhad *had_data;
module_param(hdmi_card_index, int, 0444);
MODULE_PARM_DESC(hdmi_card_index,
.fifo_size = HAD_FIFO_SIZE,
};
+/* Register access functions */
+
+inline int had_get_hwstate(struct snd_intelhad *intelhaddata)
+{
+ /* Check for device presence -SW state */
+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) {
+ pr_debug("%s:Device not connected:%d\n", __func__,
+ intelhaddata->drv_status);
+ return -ENODEV;
+ }
+
+ /* Check for device presence -HW state */
+ if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND,
+ OSPM_UHB_ONLY_IF_ON)) {
+ pr_err("%s:Device not connected\n", __func__);
+ dump_stack();
+ /* HOT_UNPLUG event can be sent to
+ * maintain correct state within HAD
+ * had_event_handler(HAD_EVENT_HOT_UNPLUG, intelhaddata);
+ * Drop all acuired locks before executing this.
+ */
+ return -ENODEV;
+ }
+
+ ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
+ return 0;
+}
+
+inline int had_get_caps(enum had_caps_list query, void *caps)
+{
+ int retval;
+ struct snd_intelhad *intelhaddata = had_data;
+
+ retval = had_get_hwstate(intelhaddata);
+ if (!retval)
+ retval = intelhaddata->query_ops.hdmi_audio_get_caps(query,
+ caps);
+
+ return retval;
+}
+
+inline int had_set_caps(enum had_caps_list set_element , void *caps)
+{
+ int retval;
+ struct snd_intelhad *intelhaddata = had_data;
+
+ retval = had_get_hwstate(intelhaddata);
+ if (!retval)
+ retval = intelhaddata->query_ops.hdmi_audio_set_caps(
+ set_element, caps);
+
+ return retval;
+}
+
+inline int had_read_register(uint32_t reg_addr, uint32_t *data)
+{
+ int retval;
+ struct snd_intelhad *intelhaddata = had_data;
+
+ retval = had_get_hwstate(intelhaddata);
+ if (!retval)
+ retval = intelhaddata->reg_ops.hdmi_audio_read_register(
+ reg_addr, data);
+
+ return retval;
+}
+
+inline int had_write_register(uint32_t reg_addr, uint32_t data)
+{
+ int retval;
+ struct snd_intelhad *intelhaddata = had_data;
+
+ retval = had_get_hwstate(intelhaddata);
+ if (!retval)
+ retval = intelhaddata->reg_ops.hdmi_audio_write_register(
+ reg_addr, data);
+
+ return retval;
+}
+
+inline int had_read_modify(uint32_t reg_addr, uint32_t data, uint32_t mask)
+{
+ int retval;
+ struct snd_intelhad *intelhaddata = had_data;
+
+ retval = had_get_hwstate(intelhaddata);
+ if (!retval)
+ retval = intelhaddata->reg_ops.hdmi_audio_read_modify(
+ reg_addr, data, mask);
+
+ return retval;
+}
+
/**
* snd_intelhad_init_audio_ctrl - to initialize audio channel status
* registers and confgiuration registers
break;
}
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_CH_STATUS_0, ch_stat0.status_0_regval);
+ had_write_register(AUD_CH_STATUS_0, ch_stat0.status_0_regval);
if (flag_silence)
format = SNDRV_PCM_FORMAT_S24_LE;
ch_stat1.status_1_regx.max_wrd_len = 0;
ch_stat1.status_1_regx.wrd_len = 0;
}
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_CH_STATUS_1, ch_stat1.status_1_regval);
+ had_write_register(AUD_CH_STATUS_1, ch_stat1.status_1_regval);
buf_cfg.buf_cfg_regx.fifo_width = FIFO_THRESHOLD;
buf_cfg.buf_cfg_regx.aud_delay = 0;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_CONFIG, buf_cfg.buf_cfgval);
+ had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval);
if (flag_silence)
channels = HAD_MIN_CHANNEL;
}
cfg_val.cfg_regx.val_bit = 1;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_CONFIG, cfg_val.cfg_regval);
+ had_write_register(AUD_CONFIG, cfg_val.cfg_regval);
return 0;
}
union aud_info_frame3 frame3 = {.fr3_val = 0};
u8 checksum = 0;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_CNTL_ST, ctrl_state.ctrl_val);
+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val);
if (flag_silence)
frame2.fr2_regx.chnl_cnt = HAD_MIN_CHANNEL - 1;
frame2.fr2_regx.chksum = -(checksum);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_HDMIW_INFOFR, INFO_FRAME_WORD1);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_HDMIW_INFOFR, frame2.fr2_val);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_HDMIW_INFOFR, frame3.fr3_val);
+ had_write_register(AUD_HDMIW_INFOFR, INFO_FRAME_WORD1);
+ had_write_register(AUD_HDMIW_INFOFR, frame2.fr2_val);
+ had_write_register(AUD_HDMIW_INFOFR, frame3.fr3_val);
+
/* program remaining DIP words with zero */
for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++)
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_HDMIW_INFOFR, 0x0);
+ had_write_register(AUD_HDMIW_INFOFR, 0x0);
ctrl_state.ctrl_regx.dip_freq = 1;
ctrl_state.ctrl_regx.dip_en_sta = 1;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_CNTL_ST, ctrl_state.ctrl_val);
+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val);
}
/**
struct snd_pcm_substream *substream;
substream = intelhaddata->stream_info.had_substream;
- ring_buf_addr = virt_to_phys(substream->runtime->dma_area);
+ ring_buf_addr = substream->runtime->dma_addr;
ring_buf_size = snd_pcm_lib_buffer_bytes(substream);
intelhaddata->stream_info.ring_buf_size = ring_buf_size;
period_bytes = frames_to_bytes(substream->runtime,
substream->runtime->period_size);
num_periods = substream->runtime->periods;
- pr_debug("Ring buffer address = %#x\n", ring_buf_addr);
- pr_debug("Ring buffer size = %#x\n", ring_buf_size);
- pr_debug("period size in bytes = %d\n", period_bytes);
-
/* buffer addr should be 64 byte aligned, period bytes
will be used to calculate addr offset*/
period_bytes &= ~0x3F;
- pr_debug("period size in bytes after align = %d\n", period_bytes);
/* Hardware supports MAX_PERIODS buffers */
if (end >= HAD_MAX_PERIODS)
intelhaddata->buf_info[i].buf_size = ring_buf_size -
(period_bytes*i);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
intelhaddata->buf_info[i].buf_addr |
BIT(0) | BIT(1));
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
period_bytes);
intelhaddata->buf_info[i].is_valid = true;
- pr_debug("buf[%d] addr=%#x and size=%d\n", i,
- intelhaddata->buf_info[i].buf_addr,
- intelhaddata->buf_info[i].buf_size);
}
+ pr_debug("%s:buf[%d-%d] addr=%#x and size=%d\n", __func__, start, end,
+ intelhaddata->buf_info[start].buf_addr,
+ intelhaddata->buf_info[start].buf_size);
intelhaddata->valid_buf_cnt = num_periods;
return 0;
}
-inline void snd_intelhad_read_len(struct snd_intelhad *intelhaddata)
+int snd_intelhad_invd_buffer(int start, int end)
{
int i;
- u32 len;
- for (i = 0; i < 4 ; i++) {
+
+ /* Hardware supports MAX_PERIODS buffers */
+ if (end >= HAD_MAX_PERIODS)
+ return -EINVAL;
+
+ for (i = start; i <= end; i++) {
/* Program the buf registers with addr and len */
- intelhaddata->reg_ops.hdmi_audio_read_register(
- AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
- &len);
- pr_debug("buf[%d] size=%d\n", i, len);
+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH), 0);
+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), 0);
+ }
+ pr_debug("buf[%d-%d] invalidated\n", start, end);
+ return 0;
+}
+
+inline int snd_intelhad_read_len(struct snd_intelhad *intelhaddata)
+{
+ int i, retval = 0;
+ u32 len[4];
+
+ for (i = 0; i < 4 ; i++) {
+ had_read_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
+ &len[i]);
+ if (!len[i])
+ retval++;
}
+ if (retval != 1) {
+ for (i = 0; i < 4 ; i++)
+ pr_debug("buf[%d] size=%d\n", i, len[i]);
+ }
+
+ return retval;
}
/**
dividend = (u64)tmds * n_param*1000;
divisor = 128 * aud_samp_freq;
cts_val = div64_u64(dividend, divisor);
- pr_debug("CTS Value=%d\n", cts_val);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_HDMI_CTS, (BIT(20) | cts_val));
+ pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n",
+ tmds, n_param, cts_val);
+ had_write_register(AUD_HDMI_CTS, (BIT(20) | cts_val));
}
/**
}
if (retval)
return retval;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_N_ENABLE, (BIT(20) | n_val));
+ had_write_register(AUD_N_ENABLE, (BIT(20) | n_val));
*n_param = n_val;
return retval;
}
struct snd_intelhad *intelhaddata;
struct snd_pcm_runtime *runtime;
struct had_stream_pvt *stream;
+ struct had_pvt_data *had_stream;
int retval;
pr_debug("snd_intelhad_open called\n");
intelhaddata = snd_pcm_substream_chip(substream);
+ had_stream = intelhaddata->private_data;
+ if (had_stream->process_trigger != NO_TRIGGER) {
+ pr_err("%s:Yet to process some trigger\n", __func__);
+ return -ENODEV;
+ }
- mutex_lock(&intelhaddata->had_lock);
- if (intelhaddata->drv_status != HAD_DRV_CONNECTED) {
- pr_err("had in disconnected/suspended state :%d\n",
- intelhaddata->drv_status);
- mutex_unlock(&intelhaddata->had_lock);
+ if (had_get_hwstate(intelhaddata)) {
+ pr_err("%s: HDMI cable plugged-out\n", __func__);
return -ENODEV;
}
- if (intelhaddata->playback_cnt > 0) {
- mutex_unlock(&intelhaddata->had_lock);
- return -EBUSY;
- } else
- intelhaddata->playback_cnt++;
+ runtime = substream->runtime;
- mutex_unlock(&intelhaddata->had_lock);
+ /* Check, if device already in use */
+ if (runtime->private_data) {
+ pr_err("Device already in use\n");
+ return -EBUSY;
+ }
- runtime = substream->runtime;
/* set the runtime hw parameter with local snd_pcm_hardware struct */
runtime->hw = snd_intel_hadstream;
}
stream->stream_status = STREAM_INIT;
runtime->private_data = stream;
+
retval = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (retval < 0) {
kfree(stream);
goto exit_err;
}
- mutex_lock(&intelhaddata->had_lock);
- intelhaddata->drv_status = HAD_DRV_RUNNING;
- mutex_unlock(&intelhaddata->had_lock);
+
return retval;
exit_err:
- mutex_lock(&intelhaddata->had_lock);
- intelhaddata->playback_cnt--;
- mutex_unlock(&intelhaddata->had_lock);
+ runtime->private_data = NULL;
return retval;
}
static int snd_intelhad_close(struct snd_pcm_substream *substream)
{
struct snd_intelhad *intelhaddata;
+ struct snd_pcm_runtime *runtime;
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--;
+ runtime = substream->runtime;
intelhaddata->stream_info.buffer_rendered = 0;
intelhaddata->stream_info.buffer_ptr = 0;
intelhaddata->stream_info.str_id = 0;
+ /* Check if following drv_status modification is required - VA */
if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED)
intelhaddata->drv_status = HAD_DRV_CONNECTED;
- mutex_unlock(&intelhaddata->had_lock);
- kfree(substream->runtime->private_data);
+ kfree(runtime->private_data);
+ runtime->private_data = NULL;
return 0;
}
u32 buf_size;
BUG_ON(!hw_params);
- pr_debug("snd_intelhad_hw_params called\n");
buf_size = params_buffer_bytes(hw_params);
retval = snd_pcm_lib_malloc_pages(substream, buf_size);
if (retval < 0)
return retval;
- pr_debug("allocated memory = %d\n", buf_size);
+ pr_debug("%s:allocated memory = %d\n", __func__, buf_size);
memset(substream->runtime->dma_area, 0, buf_size);
- pr_debug("snd_intelhad_hw_params exited\n");
return retval;
}
int retval = 0;
u32 disp_samp_freq, n_param;
- pr_debug("Enter %s\n", __func__);
/* Get N value in KHz */
- retval = intelhaddata->query_ops.hdmi_audio_get_caps(
- HAD_GET_SAMPLING_FREQ, &disp_samp_freq);
+ retval = had_get_caps(HAD_GET_SAMPLING_FREQ, &disp_samp_freq);
if (retval) {
pr_err("querying display sampling freq failed %#x\n", retval);
goto out;
- } else
- pr_debug("%s:TMDS freq = %d kHz\n", __func__, disp_samp_freq);
+ }
retval = snd_intelhad_prog_n(AUD_SAMPLE_RATE_44_1, &n_param,
intelhaddata);
{
int i, retval = 0;
u32 buf_addr;
-
- pr_debug("Enter %s\n", __func__);
+ unsigned long flag_irqs;
+ struct had_pvt_data *had_stream;
buf_addr = virt_to_phys(intelhaddata->flat_data);
intelhaddata->buf_info[i].buf_addr = buf_addr;
intelhaddata->buf_info[i].buf_size = MAX_SZ_ZERO_BUF;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
buf_addr | BIT(0) | BIT(1));
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
MAX_SZ_ZERO_BUF);
intelhaddata->buf_info[i].is_valid = true;
- pr_debug("buf[%d] addr=%#x and size=%d\n", i,
- intelhaddata->buf_info[i].buf_addr,
- intelhaddata->buf_info[i].buf_size);
}
+ pr_debug("%s:buf[%d-%d] addr=%#x and size=%d\n", __func__,
+ HAD_BUF_TYPE_C, HAD_BUF_TYPE_D,
+ intelhaddata->buf_info[HAD_BUF_TYPE_C].buf_addr,
+ intelhaddata->buf_info[HAD_BUF_TYPE_C].buf_size);
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
intelhaddata->valid_buf_cnt = HAD_MAX_PERIODS;
intelhaddata->curr_buf = HAD_BUF_TYPE_C;
+ had_stream = intelhaddata->private_data;
+ had_stream->stream_status = HAD_RUNNING_SILENCE;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
- intelhaddata->reg_ops.hdmi_audio_read_modify(AUD_CONFIG, 1,
- BIT(0));
+ had_read_modify(AUD_CONFIG, 1, BIT(0));
return retval;
}
int snd_intelhad_stop_silence(struct snd_intelhad *intelhaddata)
{
int i, retval = 0;
+ unsigned long flag_irqs;
+ struct had_pvt_data *had_stream;
pr_debug("Enter %s\n", __func__);
- intelhaddata->reg_ops.hdmi_audio_read_modify(AUD_CONFIG, 0,
- BIT(0));
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ had_stream = intelhaddata->private_data;
+ had_stream->stream_status = HAD_INIT;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+
+ had_read_modify(AUD_CONFIG, 0, BIT(0));
/* Invalidate Audio buffers C & D */
for (i = HAD_BUF_TYPE_C; i < HAD_MAX_PERIODS; i++) {
/* Program the buf registers with addr and len */
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
- 0);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
- 0);
+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH), 0);
+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), 0);
}
/* Reset buffer pointers */
- intelhaddata->reg_ops.hdmi_audio_write_register(AUD_HDMI_STATUS, 1);
- intelhaddata->reg_ops.hdmi_audio_write_register(AUD_HDMI_STATUS, 0);
+ had_write_register(AUD_HDMI_STATUS, 1);
+ had_write_register(AUD_HDMI_STATUS, 0);
return retval;
}
void snd_process_stop_trigger(struct snd_intelhad *intelhaddata)
{
- int buf_id, buff_done, i;
+ int buf_id, i;
u32 buf_addr;
- buff_done = intelhaddata->buff_done;
- if (intelhaddata->valid_buf_cnt-1 == buff_done)
- buf_id = HAD_BUF_TYPE_A;
- else
- buf_id = buff_done + 1;
+ buf_id = intelhaddata->curr_buf;
pr_debug("%s:buf_id=%d\n", __func__, buf_id);
/* Invalidate Audio buffers */
if (i < HAD_BUF_TYPE_C) {
/* invalidate */
intelhaddata->buf_info[i].buf_size = 0;
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
- 0);
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
- 0);
+ had_write_register(AUD_BUF_A_ADDR +
+ (i * HAD_REG_WIDTH), 0);
+ had_write_register(AUD_BUF_A_LENGTH +
+ (i * HAD_REG_WIDTH), 0);
} else {
/* Program with silence buffer */
buf_addr = virt_to_phys(intelhaddata->flat_data);
intelhaddata->buf_info[i].buf_addr = buf_addr;
intelhaddata->buf_info[i].buf_size = MAX_SZ_ZERO_BUF;
- intelhaddata->reg_ops.hdmi_audio_write_register(
+ had_write_register(
AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
buf_addr | BIT(0) | BIT(1));
- intelhaddata->reg_ops.hdmi_audio_write_register(
+ had_write_register(
AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
(MAX_SZ_ZERO_BUF));
intelhaddata->buf_info[i].is_valid = true;
int cmd)
{
int retval = 0;
- int buf_id;
unsigned long flag_irq;
struct snd_intelhad *intelhaddata;
struct had_stream_pvt *stream;
- struct hdmi_audio_registers_ops reg_ops;
+ struct had_pvt_data *had_stream;
intelhaddata = snd_pcm_substream_chip(substream);
stream = substream->runtime->private_data;
- reg_ops = intelhaddata->reg_ops;
+ had_stream = intelhaddata->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
pr_debug("Trigger Start\n");
stream->substream = substream;
stream->stream_status = STREAM_RUNNING;
- snd_intelhad_read_len(intelhaddata);
/* Disable local INTRs till register prgmng is done */
- spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq);
- if (intelhaddata->buff_done == HAD_BUF_TYPE_D)
- buf_id = HAD_BUF_TYPE_C;
- else
- buf_id = HAD_BUF_TYPE_D;
-
- /* Re-program silence buffers */
- if (buf_id == HAD_BUF_TYPE_C) {
- /* Disable Buffer D*/
- reg_ops.hdmi_audio_write_register(AUD_BUF_A_ADDR +
- (HAD_BUF_TYPE_D * HAD_REG_WIDTH), 0);
- reg_ops.hdmi_audio_write_register(AUD_BUF_A_LENGTH +
- (HAD_BUF_TYPE_D * HAD_REG_WIDTH), 0);
+ if (had_get_hwstate(intelhaddata)) {
+ pr_err("_START: HDMI cable plugged-out\n");
+ retval = -ENODEV;
+ break;
}
- retval = snd_intelhad_prog_buffer(intelhaddata, HAD_BUF_TYPE_A,
- HAD_BUF_TYPE_B);
- /* Start reporting BUFFER_DONE/UNDERRUN to above layers*/
- intelhaddata->pcm_active = 1;
- intelhaddata->start_trigger = 1;
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq);
+ had_stream->process_trigger = PRE_START;
spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq);
- pr_debug("Processed _Start, buf_id = %d\n", buf_id);
+ pr_debug("Processed _Start\n");
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("Trigger Stop\n");
- snd_intelhad_read_len(intelhaddata);
spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq);
intelhaddata->stream_info.str_id = 0;
- intelhaddata->send_data = 0;
/* Stop reporting BUFFER_DONE/UNDERRUN to above layers*/
- intelhaddata->pcm_active = 0;
- intelhaddata->stop_trigger = 1;
+ had_stream->process_trigger = STOP_TRIGGER;
/* Send zero filled data */
- if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED)
- snd_process_stop_trigger(intelhaddata);
- spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq);
- cancel_delayed_work(&intelhaddata->dummy_audio);
-
+ if (had_stream->stream_status == HAD_RUNNING_DUMMY) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock,
+ flag_irq);
+ cancel_delayed_work(&intelhaddata->dummy_audio);
+ } else if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock,
+ flag_irq);
+ if (!had_get_hwstate(intelhaddata))
+ snd_process_stop_trigger(intelhaddata);
+ } else
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock,
+ flag_irq);
stream->stream_status = STREAM_DROPPED;
break;
u32 disp_samp_freq, n_param;
struct snd_intelhad *intelhaddata;
struct snd_pcm_runtime *runtime;
+ struct had_pvt_data *had_stream;
+ unsigned long flag_irqs;
pr_debug("pcm_prepare called\n");
+ intelhaddata = snd_pcm_substream_chip(substream);
runtime = substream->runtime;
+ had_stream = intelhaddata->private_data;
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ if (had_stream->process_trigger != NO_TRIGGER) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ pr_err("%s:Yet to process some trigger\n", __func__);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+
pr_debug("period_size=%d\n",
frames_to_bytes(runtime, runtime->period_size));
pr_debug("periods=%d\n", runtime->periods);
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;
+
+ if (had_get_hwstate(intelhaddata)) {
+ pr_err("%s: HDMI cable plugged-out\n", __func__);
+ retval = -ENODEV;
+ goto prep_end;
+ }
+
/* Get N value in KHz */
- retval = intelhaddata->query_ops.hdmi_audio_get_caps(
- HAD_GET_SAMPLING_FREQ, &disp_samp_freq);
+ retval = had_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);
struct snd_pcm_substream *substream)
{
struct snd_intelhad *intelhaddata;
- u32 bytes_rendered;
+ u32 bytes_rendered = 0;
intelhaddata = snd_pcm_substream_chip(substream);
return SNDRV_PCM_POS_XRUN;
}
- div_u64_rem(intelhaddata->stream_info.buffer_rendered,
+ if (intelhaddata->stream_info.buffer_rendered)
+ div_u64_rem(intelhaddata->stream_info.buffer_rendered,
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;
}
+/**
+* snd_intelhad_pcm_mmap- mmaps a kernel buffer to user space for copying data
+*
+* @substream: substream for which the function is called
+* @vma: struct instance of memory VMM memory area
+*
+* This function is called by OS when a user space component
+* tries to get mmap memory from driver
+*/
+static int snd_intelhad_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ pr_debug("entry with prot:%s\n", __func__);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
int hdmi_audio_mode_change(struct snd_pcm_substream *substream)
{
int retval = 0;
intelhaddata = snd_pcm_substream_chip(substream);
/* Disable Audio */
- intelhaddata->reg_ops.hdmi_audio_read_modify(
- AUD_CONFIG, 0, BIT(0));
+ had_read_modify(AUD_CONFIG, 0, BIT(0));
/* Update CTS value */
- retval = intelhaddata->query_ops.hdmi_audio_get_caps(
- HAD_GET_SAMPLING_FREQ, &disp_samp_freq);
+ retval = had_get_caps(HAD_GET_SAMPLING_FREQ, &disp_samp_freq);
if (retval) {
pr_err("querying display sampling freq failed %#x\n", retval);
goto out;
- } else
- pr_debug("TMDS freq = %d kHz\n", disp_samp_freq);
+ }
retval = snd_intelhad_prog_n(substream->runtime->rate, &n_param,
intelhaddata);
disp_samp_freq, n_param, intelhaddata);
/* Enable Audio */
- intelhaddata->reg_ops.hdmi_audio_read_modify(
- AUD_CONFIG, 1, BIT(0));
+ had_read_modify(AUD_CONFIG, 1, BIT(0));
out:
return retval;
void dummy_audio_play(struct work_struct *work)
{
- struct snd_intelhad *intelhaddata = had_pvt_data;
+ struct snd_intelhad *intelhaddata = had_data;
+ struct had_pvt_data *had_stream;
+ unsigned long flag_irqs;
- mutex_lock(&intelhaddata->had_lock);
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) {
- mutex_unlock(&intelhaddata->had_lock);
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_debug("HDMI device still connected\n");
return;
}
+ had_stream = intelhaddata->private_data;
- if (!intelhaddata->send_data) {
- mutex_unlock(&intelhaddata->had_lock);
+ /* In case _STOP, silence data, return */
+ if (had_stream->stream_status < HAD_RUNNING_STREAM) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_debug("HDMI device_flag is reset\n");
return;
}
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
had_event_handler(HAD_EVENT_AUDIO_BUFFER_DONE, intelhaddata);
- mutex_unlock(&intelhaddata->had_lock);
schedule_delayed_work(&intelhaddata->dummy_audio, intelhaddata->timer);
}
.prepare = snd_intelhad_pcm_prepare,
.trigger = snd_intelhad_pcm_trigger,
.pointer = snd_intelhad_pcm_pointer,
+ .mmap = snd_intelhad_pcm_mmap,
};
/**
struct snd_ctl_elem_value *ucontrol)
{
struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol);
- mutex_lock(&intelhaddata->had_lock);
ucontrol->value.iec958.status[0] = (intelhaddata->aes_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (intelhaddata->aes_bits >> 8) & 0xff;
ucontrol->value.iec958.status[2] =
(intelhaddata->aes_bits >> 16) & 0xff;
ucontrol->value.iec958.status[3] =
(intelhaddata->aes_bits >> 24) & 0xff;
- mutex_unlock(&intelhaddata->had_lock);
return 0;
}
static int had_iec958_mask_get(struct snd_kcontrol *kcontrol,
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- mutex_lock(&intelhaddata->had_lock);
if (intelhaddata->aes_bits != val) {
intelhaddata->aes_bits = val;
- mutex_unlock(&intelhaddata->had_lock);
return 1;
}
- mutex_unlock(&intelhaddata->had_lock);
return 1;
}
struct snd_card *card;
struct had_callback_ops ops_cb;
struct snd_intelhad *intelhaddata;
+ struct had_pvt_data *had_stream;
pr_debug("Enter %s\n", __func__);
return -ENOMEM;
}
- had_pvt_data = intelhaddata;
+ had_stream = kzalloc(sizeof(*had_stream), GFP_KERNEL);
+ if (!had_stream) {
+ pr_err("mem alloc failed\n");
+ retval = -ENOMEM;
+ goto free_haddata;
+ }
+
+ had_data = intelhaddata;
ops_cb.intel_had_event_call_back = had_event_handler;
/* registering with display driver to get access to display APIs */
&(intelhaddata->query_ops));
if (retval) {
pr_err("querying display driver APIs failed %#x\n", retval);
- goto free_haddata;
+ goto free_hadstream;
}
mutex_lock(&had_mutex);
- mutex_init(&intelhaddata->had_lock);
spin_lock_init(&intelhaddata->had_spinlock);
intelhaddata->drv_status = HAD_DRV_DISCONNECTED;
/* create a card instance with ALSA framework */
intelhaddata->card = card;
intelhaddata->card_id = hdmi_card_id;
intelhaddata->card_index = card->number;
- intelhaddata->playback_cnt = 0;
+ intelhaddata->private_data = had_stream;
intelhaddata->flag_underrun = 0;
intelhaddata->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF;
strncpy(card->driver, INTEL_HAD, strlen(INTEL_HAD));
&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),
+ SNDRV_DMA_TYPE_DEV, card->dev,
HAD_MIN_BUFFER, HAD_MAX_BUFFER);
if (retval)
goto err;
if (retval) {
pr_err("registering with display driver failed %#x\n", retval);
snd_card_free(card);
- goto free_haddata;
+ goto free_hadstream;
}
return retval;
err:
- pr_err("Error returned from %s api %#x\n", __func__, retval);
snd_card_free(card);
unlock_mutex:
mutex_unlock(&had_mutex);
+free_hadstream:
+ kfree(had_stream);
free_haddata:
kfree(intelhaddata);
intelhaddata = NULL;
+ pr_err("Error returned from %s api %#x\n", __func__, retval);
return retval;
}
*/
static int __devexit hdmi_audio_remove(struct platform_device *devptr)
{
- struct snd_intelhad *intelhaddata = had_pvt_data;
+ struct snd_intelhad *intelhaddata = had_data;
int caps;
pr_debug("Enter %s\n", __func__);
if (!intelhaddata)
return 0;
- mutex_lock(&intelhaddata->had_lock);
if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) {
caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
- intelhaddata->query_ops.hdmi_audio_set_caps(
- HAD_SET_DISABLE_AUDIO_INT, &caps);
- intelhaddata->query_ops.hdmi_audio_set_caps(
- HAD_SET_DISABLE_AUDIO, NULL);
+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps);
+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL);
snd_intelhad_stop_silence(intelhaddata);
}
kfree(intelhaddata->flat_data);
snd_card_free(intelhaddata->card);
- mutex_unlock(&intelhaddata->had_lock);
+ kfree(intelhaddata->private_data);
kfree(intelhaddata);
return 0;
}
int hdmi_audio_suspend(void *haddata, pm_event_t event)
{
int caps, retval = 0;
+ struct had_pvt_data *had_stream;
+ unsigned long flag_irqs;
struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata;
+
pr_debug("Enter:%s", __func__);
- if (intelhaddata->playback_cnt > 0) {
+ had_stream = intelhaddata->private_data;
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ if (had_stream->stream_status > HAD_RUNNING_SILENCE) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_err("audio stream is active\n");
- return -EBUSY;
+ return -EAGAIN;
}
- mutex_lock(&intelhaddata->had_lock);
if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_debug("had not connected\n");
- mutex_unlock(&intelhaddata->had_lock);
+ return retval;
+ }
+
+ if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ pr_debug("had already suspended\n");
return retval;
}
intelhaddata->drv_status = HAD_DRV_SUSPENDED;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
- intelhaddata->query_ops.hdmi_audio_set_caps(
- HAD_SET_DISABLE_AUDIO_INT, &caps);
- intelhaddata->query_ops.hdmi_audio_set_caps(
- HAD_SET_DISABLE_AUDIO, NULL);
+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps);
+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL);
retval = snd_intelhad_stop_silence(intelhaddata);
- mutex_unlock(&intelhaddata->had_lock);
+ pr_debug("Exit:%s", __func__);
return retval;
}
{
int caps, retval = 0;
struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata;
+ unsigned long flag_irqs;
+
pr_debug("Enter:%s", __func__);
- mutex_lock(&intelhaddata->had_lock);
- if (HAD_DRV_DISCONNECTED == intelhaddata->drv_status) {
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_debug("had not connected\n");
- mutex_unlock(&intelhaddata->had_lock);
return 0;
}
if (HAD_DRV_SUSPENDED != intelhaddata->drv_status) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_err("had is not in suspended state\n");
- mutex_unlock(&intelhaddata->had_lock);
return 0;
}
- if (SNDRV_DEFAULT_IDX1 == intelhaddata->card_index)
- intelhaddata->drv_status = HAD_DRV_DISCONNECTED;
+ if (had_get_hwstate(intelhaddata)) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ pr_err("Failed to resume. Device not accessible\n");
+ return -ENODEV;
+ }
+
+ intelhaddata->drv_status = HAD_DRV_CONNECTED;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps);
+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL);
+ retval = snd_intelhad_configure_silence(intelhaddata);
+ retval = snd_intelhad_start_silence(intelhaddata);
+ pr_debug("Exit:%s", __func__);
+ return retval;
+}
+
+static inline int had_chk_intrmiss(struct snd_intelhad *intelhaddata,
+ enum intel_had_aud_buf_type buf_id)
+{
+ int i, intr_count = 0;
+ enum intel_had_aud_buf_type buff_done;
+ u32 buf_size, buf_addr;
+ struct had_pvt_data *had_stream;
+ unsigned long flag_irqs;
+
+ had_stream = intelhaddata->private_data;
+
+ buff_done = buf_id;
+
+ intr_count = snd_intelhad_read_len(intelhaddata);
+ if (intr_count > 1) {
+ /* In case of active playback */
+ pr_err("Driver detected %d missed buffer done interrupt(s)!!!!\n",
+ (intr_count - 1));
+ if (intr_count > 3)
+ return intr_count;
+
+ buf_id += (intr_count - 1);
+ /* Reprogram registers*/
+ for (i = buff_done; i < buf_id; i++) {
+ i = i % 4;
+ buf_size = intelhaddata->buf_info[i].buf_size;
+ buf_addr = intelhaddata->buf_info[i].buf_addr;
+ had_write_register(AUD_BUF_A_LENGTH +
+ (i * HAD_REG_WIDTH), buf_size);
+ had_write_register(
+ AUD_BUF_A_ADDR+(i * HAD_REG_WIDTH),
+ (buf_addr | BIT(0) | BIT(1)));
+ }
+ buf_id = buf_id % 4;
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ intelhaddata->buff_done = buf_id;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ }
+
+ return intr_count;
+}
+
+static inline int had_start_dummy_playback(struct snd_intelhad *intelhaddata)
+{
+ int retval = 0;
+ enum intel_had_aud_buf_type buf_id;
+ u32 buf_size;
+ struct snd_pcm_substream *substream;
+ struct had_pvt_data *had_stream;
+ u32 msecs, rate, channels;
+ unsigned long flag_irqs;
+
+ pr_debug("Enter:%s", __func__);
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ had_stream = intelhaddata->private_data;
+ buf_id = intelhaddata->curr_buf;
+ substream = intelhaddata->stream_info.had_substream;
+
+ if ((had_stream->stream_status < HAD_RUNNING_DUMMY) && substream
+ && substream->runtime) {
+ had_stream->stream_status = HAD_RUNNING_DUMMY;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ pr_debug("Steam active, start dummy_playback\n");
+ /* In case substream is active, start reporting
+ * dummy buffer_done interrupts to above ALSA layer.
+ */
+ buf_size = intelhaddata->buf_info[buf_id].buf_size;
+ rate = substream->runtime->rate;
+ channels = substream->runtime->channels;
+ msecs = (buf_size*1000)/(rate*channels*4);
+ intelhaddata->timer = msecs_to_jiffies(msecs);
+ schedule_delayed_work(
+ &intelhaddata->dummy_audio,
+ intelhaddata->timer);
+ }
+
+ return retval;
+}
+
+static inline int had_process_pre_start(struct snd_intelhad *intelhaddata,
+ enum intel_had_aud_buf_type buf_id)
+{
+ int retval = 0;
+ struct had_pvt_data *had_stream;
+ enum intel_had_aud_buf_type prg_start, prg_end;
+ enum intel_had_aud_buf_type inv_start;
+ unsigned long flag_irqs;
+
+ had_stream = intelhaddata->private_data;
+
+ if (intelhaddata->valid_buf_cnt-1 == buf_id) {
+ if (had_stream->stream_status >= HAD_RUNNING_STREAM)
+ buf_id = HAD_BUF_TYPE_A;
+ else /* Use only silence buffers C & D */
+ buf_id = HAD_BUF_TYPE_C;
+ } else
+ buf_id = buf_id + 1;
+
+ /* Program all buffers in case of Buf == A */
+ if (buf_id == HAD_BUF_TYPE_A) {
+ prg_start = buf_id + 1;
+ prg_end = HAD_BUF_TYPE_D;
+ } else {
+ prg_start = HAD_BUF_TYPE_A;
+ prg_end = buf_id - 1;
+ }
+ retval = snd_intelhad_prog_buffer(intelhaddata,
+ prg_start, prg_end);
+ /* Invalidate all buffs after buf_id */
+ inv_start = buf_id + 1;
+ if ((buf_id != HAD_BUF_TYPE_A) && (buf_id != HAD_BUF_TYPE_D))
+ snd_intelhad_invd_buffer(inv_start, HAD_BUF_TYPE_D);
+
+ /* Start reporting BUFFER_DONE/UNDERRUN to above layers*/
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ intelhaddata->curr_buf = buf_id;
+
+ if (buf_id != HAD_BUF_TYPE_A)
+ had_stream->process_trigger = START_TRIGGER;
else {
- intelhaddata->drv_status = HAD_DRV_CONNECTED;
- caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
- retval = intelhaddata->query_ops.hdmi_audio_set_caps(
- HAD_SET_ENABLE_AUDIO_INT, &caps);
- retval = intelhaddata->query_ops.hdmi_audio_set_caps(
- HAD_SET_ENABLE_AUDIO, NULL);
- retval = snd_intelhad_configure_silence(intelhaddata);
- retval = snd_intelhad_start_silence(intelhaddata);
+ had_stream->stream_status = HAD_RUNNING_STREAM;
+ had_stream->process_trigger = NO_TRIGGER;
+ }
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+
+ pr_debug("Processed %s, buf_id = %d\n", __func__, buf_id);
+
+ return retval;
+}
+
+static inline int had_process_start_trigger(struct snd_intelhad *intelhaddata)
+{
+ int retval = 0;
+ struct had_pvt_data *had_stream;
+ enum intel_had_aud_buf_type prg_start;
+ unsigned long flag_irqs;
+
+ had_stream = intelhaddata->private_data;
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ /* Check for device state
+ * In case, _DISCONNECTED, start dummy_playback
+ * if not done
+ */
+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ pr_debug("Cable plugged-out\n");
+ retval = had_start_dummy_playback(intelhaddata);
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ had_stream->process_trigger = NO_TRIGGER;
+ intelhaddata->curr_buf = HAD_BUF_TYPE_A;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ return retval;
+ }
+
+ had_stream->stream_status = HAD_RUNNING_STREAM;
+ had_stream->process_trigger = NO_TRIGGER;
+ intelhaddata->curr_buf = HAD_BUF_TYPE_A;
+ prg_start = intelhaddata->buff_done;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ retval = snd_intelhad_prog_buffer(intelhaddata,
+ prg_start, HAD_BUF_TYPE_D);
+
+ return retval;
+}
+
+static inline int had_process_stop_trigger(struct snd_intelhad *intelhaddata)
+{
+ int i, retval = 0;
+ struct had_pvt_data *had_stream;
+ enum intel_had_aud_buf_type buf_id;
+ u32 buf_addr;
+ unsigned long flag_irqs;
+
+ had_stream = intelhaddata->private_data;
+
+ buf_id = intelhaddata->curr_buf;
+ /* If device disconnected, ignore this interrupt */
+ if (had_stream->stream_status == HAD_RUNNING_DUMMY) {
+ had_stream->stream_status = HAD_INIT;
+ return retval;
+ }
+
+ /* All successive intr for Silence stream */
+ had_stream->stream_status = HAD_RUNNING_SILENCE;
+ had_stream->process_trigger = NO_TRIGGER;
+
+ /* If buf_id < HAD_BUF_TYPE_C, ignore */
+ if (buf_id < HAD_BUF_TYPE_C) {
+ intelhaddata->curr_buf = HAD_BUF_TYPE_C;
+ return retval;
}
- mutex_unlock(&intelhaddata->had_lock);
+ if (buf_id == HAD_BUF_TYPE_C)
+ intelhaddata->curr_buf = HAD_BUF_TYPE_D;
+ else
+ intelhaddata->curr_buf = HAD_BUF_TYPE_C;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+
+ /* _STOP -> _START -> _STOP occured
+ * invalidate Buff_A, Buff_B
+ */
+ snd_intelhad_invd_buffer(HAD_BUF_TYPE_A, HAD_BUF_TYPE_B);
+ i = buf_id;
+ buf_addr = virt_to_phys(intelhaddata->flat_data);
+ /* Program the buf registers with addr and len */
+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
+ buf_addr | BIT(0) | BIT(1));
+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
+ (MAX_SZ_ZERO_BUF));
+
+ intelhaddata->buf_info[i].buf_addr = buf_addr;
+ intelhaddata->buf_info[i].buf_size = MAX_SZ_ZERO_BUF;
+ intelhaddata->buf_info[i].is_valid = true;
+ pr_debug("buf[%d] addr=%#x and size=%d\n", i,
+ intelhaddata->buf_info[i].buf_addr,
+ intelhaddata->buf_info[i].buf_size);
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+
return retval;
}
int had_process_buffer_done(struct snd_intelhad *intelhaddata)
{
- int i = 0, retval = 0;
+ int retval = 0;
+ u32 len = 1;
enum intel_had_aud_buf_type buf_id;
+ enum intel_had_aud_buf_type buff_done;
struct pcm_stream_info *stream;
u32 buf_size;
- struct snd_pcm_substream *substream;
- struct hdmi_audio_registers_ops reg_ops;
- struct hdmi_audio_query_set_ops query_ops;
- u32 buf_addr;
- int pcm_active;
+ struct had_pvt_data *had_stream;
+ int intr_count;
+ enum had_stream_status stream_status;
+ enum had_process_trigger process_trigger;
+ unsigned long flag_irqs;
+
+ had_stream = intelhaddata->private_data;
+ stream = &intelhaddata->stream_info;
+ intr_count = 1;
- spin_lock(&intelhaddata->had_spinlock);
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
buf_id = intelhaddata->curr_buf;
intelhaddata->buff_done = buf_id;
- pcm_active = intelhaddata->pcm_active;
- spin_unlock(&intelhaddata->had_spinlock);
-
- substream = intelhaddata->stream_info.had_substream;
- reg_ops = intelhaddata->reg_ops;
- query_ops = intelhaddata->query_ops;
+ buff_done = intelhaddata->buff_done;
+ buf_size = intelhaddata->buf_info[buf_id].buf_size;
+ stream_status = had_stream->stream_status;
+ process_trigger = had_stream->process_trigger;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
pr_debug("Enter:%s buf_id=%d", __func__, buf_id);
- stream = &intelhaddata->stream_info;
- buf_size = intelhaddata->buf_info[buf_id].buf_size;
+
+ /* Every debug statement has an implication
+ * of ~5msec. Thus, avoid having >3 debug statements
+ * for each buffer_done handling.
+ */
+
+ /* Check for any intr_miss in case of active playback */
+ if ((stream_status == HAD_RUNNING_STREAM) &&
+ (process_trigger == NO_TRIGGER) &&
+ !flag_en_allbufs) {
+ intr_count = had_chk_intrmiss(intelhaddata, buf_id);
+ if (!intr_count || (intr_count > 3)) {
+ pr_err("HAD SW state in non-recoverable!!! mode\n");
+ pr_err("Already played stale data\n");
+ return retval;
+ }
+ buf_id += (intr_count - 1);
+ buf_id = buf_id % 4;
+ }
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ /* Acknowledge _START trigger recieved */
+ if (had_stream->process_trigger == PRE_START) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ retval = had_process_pre_start(intelhaddata, buf_id);
+ return retval;
+ }
/* Program actual data buffer, in case _START trigger recieved */
- if (intelhaddata->start_trigger) {
- spin_lock(&intelhaddata->had_spinlock);
- intelhaddata->start_trigger = 0;
- intelhaddata->curr_buf = HAD_BUF_TYPE_A;
- spin_unlock(&intelhaddata->had_spinlock);
- retval = snd_intelhad_prog_buffer(intelhaddata,
- HAD_BUF_TYPE_C, HAD_BUF_TYPE_D);
+ if (had_stream->process_trigger == START_TRIGGER) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ retval = had_process_start_trigger(intelhaddata);
return retval;
}
/* Program Silence buffer, in case _STOP trigger recieved */
- if (intelhaddata->stop_trigger) {
- pr_debug("processing stop trigger in hanlder:bufid=%d\n",
+ if (had_stream->process_trigger == STOP_TRIGGER) {
+ /* Process with spin_lock acquired
+ * Can be optimized later
+ */
+ retval = had_process_stop_trigger(intelhaddata);
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ pr_debug("Processed stop trigger in hanlder:bufid=%d\n",
buf_id);
- spin_lock(&intelhaddata->had_spinlock);
- intelhaddata->stop_trigger = 0;
- /* If buf_id < HAD_BUF_TYPE_C, ignore */
- if (buf_id < HAD_BUF_TYPE_C) {
- intelhaddata->curr_buf = HAD_BUF_TYPE_C;
- spin_unlock(&intelhaddata->had_spinlock);
- return retval;
- }
- spin_unlock(&intelhaddata->had_spinlock);
- i = buf_id;
- buf_addr = virt_to_phys(intelhaddata->flat_data);
- /* Program the buf registers with addr and len */
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH),
- buf_addr | BIT(0) | BIT(1));
- intelhaddata->reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH),
- (MAX_SZ_ZERO_BUF));
-
- intelhaddata->buf_info[i].buf_addr = buf_addr;
- intelhaddata->buf_info[i].buf_size = MAX_SZ_ZERO_BUF;
- intelhaddata->buf_info[i].is_valid = true;
- if (buf_id == HAD_BUF_TYPE_C)
- intelhaddata->curr_buf = HAD_BUF_TYPE_D;
- else
- intelhaddata->curr_buf = HAD_BUF_TYPE_C;
- pr_debug("buf[%d] addr=%#x and size=%d\n", i,
- intelhaddata->buf_info[i].buf_addr,
- intelhaddata->buf_info[i].buf_size);
return retval;
}
- /* In case of actual data, report buffer_done to above ALSA layer */
- if (pcm_active) {
- intelhaddata->stream_info.buffer_rendered += buf_size;
- stream->period_elapsed(stream->had_substream);
- }
-
intelhaddata->buf_info[buf_id].is_valid = true;
if (intelhaddata->valid_buf_cnt-1 == buf_id) {
- if (pcm_active)
+ if (had_stream->stream_status >= HAD_RUNNING_STREAM)
intelhaddata->curr_buf = HAD_BUF_TYPE_A;
else /* Use only silence buffers C & D */
intelhaddata->curr_buf = HAD_BUF_TYPE_C;
} else
- intelhaddata->curr_buf++;
+ intelhaddata->curr_buf = buf_id + 1;
+
+ if (had_stream->stream_status == HAD_RUNNING_DUMMY) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ goto exit;
+ }
+
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
- if (intelhaddata->send_data)
+ if (had_get_hwstate(intelhaddata)) {
+ pr_err("HDMI cable plugged-out\n");
return retval;
+ }
- if (flag_en_allbufs &&
- intelhaddata->curr_buf == HAD_BUF_TYPE_A) {
- pr_debug("special case:enable all bufs\n");
- for (i = 0; i < intelhaddata->valid_buf_cnt; i++) {
- pr_debug("Enabling buf[%d]\n", i);
- reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH +
- (i * HAD_REG_WIDTH), buf_size);
- reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_ADDR+(i * HAD_REG_WIDTH),
- intelhaddata->buf_info[i].buf_addr |
- BIT(0) | BIT(1));
- }
+ if (flag_en_allbufs) {
+ pr_debug("Enabling Top half buffers after _PLUG\n");
+ retval = snd_intelhad_prog_buffer(intelhaddata,
+ HAD_BUF_TYPE_A, (buf_id-1));
flag_en_allbufs = 0;
- } else {
- /*Reprogram the registers with addr and length*/
- reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH +
- (buf_id * HAD_REG_WIDTH), buf_size);
- 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));
+ }
+ /*Reprogram the registers with addr and length*/
+ had_write_register(AUD_BUF_A_LENGTH +
+ (buf_id * HAD_REG_WIDTH), buf_size);
+ had_write_register(AUD_BUF_A_ADDR+(buf_id * HAD_REG_WIDTH),
+ intelhaddata->buf_info[buf_id].buf_addr|
+ BIT(0) | BIT(1));
+
+ had_read_register(AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH),
+ &len);
+ pr_debug("%s:Enabled buf[%d]\n", __func__, buf_id);
+exit:
+ /* In case of actual/dummy data,
+ * report buffer_done to above ALSA layer
+ */
+ buf_size = intelhaddata->buf_info[buf_id].buf_size;
+ if (stream_status >= HAD_RUNNING_STREAM) {
+ intelhaddata->stream_info.buffer_rendered +=
+ (intr_count * buf_size);
+ stream->period_elapsed(stream->had_substream);
}
return retval;
int had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
{
- int caps, i = 0, retval = 0;
+ int i = 0, retval = 0;
enum intel_had_aud_buf_type buf_id;
struct pcm_stream_info *stream;
- struct hdmi_audio_registers_ops reg_ops;
- struct hdmi_audio_query_set_ops query_ops;
+ struct had_pvt_data *had_stream;
+ enum had_stream_status stream_status;
+ enum had_process_trigger process_trigger;
u32 hdmi_status;
- int pcm_active;
+ unsigned long flag_irqs;
- spin_lock(&intelhaddata->had_spinlock);
+ had_stream = intelhaddata->private_data;
+ stream = &intelhaddata->stream_info;
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
buf_id = intelhaddata->curr_buf;
+ stream_status = had_stream->stream_status;
+ process_trigger = had_stream->process_trigger;
intelhaddata->buff_done = buf_id;
- pcm_active = intelhaddata->pcm_active;
- spin_unlock(&intelhaddata->had_spinlock);
- reg_ops = intelhaddata->reg_ops;
- query_ops = intelhaddata->query_ops;
+ if (stream_status == HAD_RUNNING_STREAM)
+ intelhaddata->curr_buf = HAD_BUF_TYPE_A;
+ else /* Use only silence buffers C & D */
+ intelhaddata->curr_buf = HAD_BUF_TYPE_C;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
- pr_debug("Enter:%s buf_id=%d", __func__, buf_id);
- stream = &intelhaddata->stream_info;
- if (intelhaddata->pcm_active) {
- /* Report UNDERRUN error to above layers */
- intelhaddata->flag_underrun = 1;
- stream->period_elapsed(stream->had_substream);
- } else
- pr_debug("_UNDERRUN occured during _STOP\n");
+ pr_debug("Enter:%s buf_id=%d, stream_status=%d, process_trigger=%d\n",
+ __func__, buf_id, stream_status, process_trigger);
+
+ if (stream_status == HAD_RUNNING_DUMMY) {
+ pr_err("_UNDERRUN occured during dummy playback\n");
+ return retval;
+ }
/* Handle Underrun interrupt within Audio Unit */
- reg_ops.hdmi_audio_write_register(
- AUD_CONFIG, 0);
+ had_write_register(AUD_CONFIG, 0);
/* Reset buffer pointers */
- reg_ops.hdmi_audio_write_register(AUD_HDMI_STATUS, 1);
- reg_ops.hdmi_audio_write_register(AUD_HDMI_STATUS, 0);
- if (pcm_active)
- intelhaddata->curr_buf = HAD_BUF_TYPE_A;
- else /* Use only silence buffers C & D */
- intelhaddata->curr_buf = HAD_BUF_TYPE_C;
-
+ had_write_register(AUD_HDMI_STATUS, 1);
+ had_write_register(AUD_HDMI_STATUS, 0);
/**
* The interrupt status 'sticky' bits might not be cleared by
* setting '1' to that bit once...
*/
do { /* clear bit30, 31 AUD_HDMI_STATUS */
- reg_ops.hdmi_audio_read_register(
- AUD_HDMI_STATUS, &hdmi_status);
+ had_read_register(AUD_HDMI_STATUS, &hdmi_status);
pr_debug("HDMI status =0x%x\n", hdmi_status);
if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) {
i++;
hdmi_status &= (AUD_CONFIG_MASK_SRDBG |
AUD_CONFIG_MASK_FUNCRST);
hdmi_status |= ~AUD_CONFIG_MASK_UNDERRUN;
- reg_ops.hdmi_audio_write_register(
- AUD_HDMI_STATUS, hdmi_status);
+ had_write_register(AUD_HDMI_STATUS, hdmi_status);
} else
break;
} while (i < MAX_CNT);
if (i >= MAX_CNT)
pr_err("Unable to clear UNDERRUN bits\n");
- if (!intelhaddata->pcm_active) {
- /* In case _STOP=1, send silence data */
- caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
- retval = query_ops.hdmi_audio_set_caps(
- HAD_SET_ENABLE_AUDIO_INT, &caps);
- retval = query_ops.hdmi_audio_set_caps(
- HAD_SET_ENABLE_AUDIO, NULL);
-
- retval = snd_intelhad_configure_silence(intelhaddata);
- retval = snd_intelhad_start_silence(intelhaddata);
+ if (stream_status == HAD_RUNNING_STREAM) {
+ if (process_trigger == NO_TRIGGER) {
+ /* Report UNDERRUN error to above layers */
+ intelhaddata->flag_underrun = 1;
+ stream->period_elapsed(stream->had_substream);
+ had_read_modify(AUD_CONFIG, 1, BIT(0));
+ retval = snd_intelhad_prog_buffer(intelhaddata,
+ HAD_BUF_TYPE_A, HAD_BUF_TYPE_D);
+ } else if (process_trigger == STOP_TRIGGER) {
+ spin_lock_irqsave(&intelhaddata->had_spinlock,
+ flag_irqs);
+ had_stream->process_trigger = NO_TRIGGER;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock,
+ flag_irqs);
+ /* Invalidate A & B */
+ snd_intelhad_invd_buffer(HAD_BUF_TYPE_A,
+ HAD_BUF_TYPE_B);
+ /* Start sending silence data */
+ retval = snd_intelhad_start_silence(intelhaddata);
+ return retval;
+ } else {
+ pr_err("%s:Should never come here\n", __func__);
+ pr_err("stream_status=%d,process_trigger=%d\n",
+ stream_status, process_trigger);
+ return retval;
+ }
+ } else if (stream_status == HAD_RUNNING_SILENCE) {
+ if (process_trigger < START_TRIGGER)
+ retval = snd_intelhad_start_silence(intelhaddata);
+ else if (process_trigger == START_TRIGGER) {
+ spin_lock_irqsave(&intelhaddata->had_spinlock,
+ flag_irqs);
+ intelhaddata->curr_buf = HAD_BUF_TYPE_A;
+ had_stream->process_trigger = NO_TRIGGER;
+ had_stream->stream_status = HAD_RUNNING_STREAM;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock,
+ flag_irqs);
+ had_read_modify(AUD_CONFIG, 1, BIT(0));
+ retval = snd_intelhad_prog_buffer(intelhaddata,
+ HAD_BUF_TYPE_A, HAD_BUF_TYPE_D);
+ /* Pre start already processed */
+ } else {
+ pr_err("%s:Should never come here\n", __func__);
+ pr_err("stream_status=%d,process_trigger=%d\n",
+ stream_status, process_trigger);
+ return retval;
+ }
}
return retval;
int had_process_hot_plug(struct snd_intelhad *intelhaddata)
{
- int caps, i = 0, retval = 0;
+ int caps, retval = 0;
enum intel_had_aud_buf_type buf_id;
- u32 buf_size;
struct snd_pcm_substream *substream;
- struct hdmi_audio_registers_ops reg_ops;
- struct hdmi_audio_query_set_ops query_ops;
+ struct had_pvt_data *had_stream;
+ enum had_stream_status stream_status;
+ unsigned long flag_irqs;
- spin_lock(&intelhaddata->had_spinlock);
- buf_id = intelhaddata->curr_buf;
- intelhaddata->buff_done = buf_id;
- spin_unlock(&intelhaddata->had_spinlock);
- substream = intelhaddata->stream_info.had_substream;
- reg_ops = intelhaddata->reg_ops;
- query_ops = intelhaddata->query_ops;
+ pr_debug("Enter:%s", __func__);
+ substream = intelhaddata->stream_info.had_substream;
+ had_stream = intelhaddata->private_data;
- pr_debug("Enter:%s", __func__);
- mutex_lock(&intelhaddata->had_lock);
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ if (intelhaddata->drv_status == HAD_DRV_CONNECTED) {
+ pr_debug("Device already connected\n");
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ return retval;
+ }
+ buf_id = intelhaddata->curr_buf;
+ intelhaddata->buff_done = buf_id;
intelhaddata->drv_status = HAD_DRV_CONNECTED;
- buf_size = intelhaddata->buf_info[buf_id].buf_size;
buf_id = intelhaddata->curr_buf;
- intelhaddata->send_data = 0;
- caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
- retval = query_ops.hdmi_audio_set_caps(
- HAD_SET_ENABLE_AUDIO_INT, &caps);
- retval = query_ops.hdmi_audio_set_caps(
- HAD_SET_ENABLE_AUDIO, NULL);
+ stream_status = had_stream->stream_status;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
- if (!intelhaddata->stream_info.str_id) {
+ pr_debug("Processing HOT_PLUG, buf_id = %d\n", buf_id);
+ if (stream_status == HAD_INIT) {
+ /* Start sending silence data */
+ pr_debug("Start sending SILENCE\n");
+ caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps);
+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL);
retval = snd_intelhad_configure_silence(intelhaddata);
retval = snd_intelhad_start_silence(intelhaddata);
- mutex_unlock(&intelhaddata->had_lock);
- pr_err("nothing to do in hot plug\n");
return retval;
}
- mutex_unlock(&intelhaddata->had_lock);
- cancel_delayed_work(&intelhaddata->dummy_audio);
+ if (stream_status == HAD_RUNNING_DUMMY) {
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ had_stream->stream_status = HAD_RUNNING_STREAM;
+ flag_en_allbufs = 1;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ cancel_delayed_work(&intelhaddata->dummy_audio);
+ }
+ caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps);
+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL);
/* Reset HW within Audio unit */
- reg_ops.hdmi_audio_write_register(
- AUD_CONFIG, 0);
- reg_ops.hdmi_audio_write_register(
- AUD_HDMI_STATUS, 1);
- reg_ops.hdmi_audio_write_register(
- AUD_HDMI_STATUS, 0);
+ had_write_register(AUD_CONFIG, 0);
+ had_write_register(AUD_HDMI_STATUS, 1);
+ had_write_register(AUD_HDMI_STATUS, 0);
/*Reprogram the registers with addr and length*/
- for (i = buf_id; i < intelhaddata->valid_buf_cnt; i++) {
- pr_debug("Enabling buf[%d]\n", i);
- reg_ops.hdmi_audio_write_register(
- AUD_BUF_A_LENGTH +
- (buf_id * HAD_REG_WIDTH), buf_size);
- 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));
- }
+ pr_debug("Enabling Bottom half buffers after _PLUG\n");
+ /* If all buffs already enabled,
+ * No need to enable Top half buffers
+ */
+ if (buf_id == HAD_BUF_TYPE_A)
+ flag_en_allbufs = 0;
+ retval = snd_intelhad_prog_buffer(intelhaddata, buf_id, HAD_BUF_TYPE_D);
+
if (substream) { /* continue transfer */
snd_intelhad_init_audio_ctrl(substream,
intelhaddata, 0);
hdmi_audio_mode_change(substream);
}
- flag_en_allbufs = 1;
-
+ if (buf_id == HAD_BUF_TYPE_D) { /* Enable all remaining buffs now*/
+ retval = snd_intelhad_prog_buffer(intelhaddata, HAD_BUF_TYPE_A,
+ (buf_id-1));
+ flag_en_allbufs = 0;
+ }
return retval;
}
{
int caps, retval = 0;
enum intel_had_aud_buf_type buf_id;
- u32 buf_size;
struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- struct hdmi_audio_query_set_ops query_ops;
- u32 msecs, rate, channels;
-
- spin_lock(&intelhaddata->had_spinlock);
- buf_id = intelhaddata->curr_buf;
- intelhaddata->buff_done = buf_id;
- spin_unlock(&intelhaddata->had_spinlock);
+ struct had_pvt_data *had_stream;
+ unsigned long flag_irqs;
+ pr_debug("Enter:%s", __func__);
+ had_stream = intelhaddata->private_data;
+ buf_id = intelhaddata->curr_buf;
substream = intelhaddata->stream_info.had_substream;
- query_ops = intelhaddata->query_ops;
- pr_debug("Enter:%s", __func__);
- mutex_lock(&intelhaddata->had_lock);
- if (intelhaddata->stream_info.str_id) {
- buf_size = intelhaddata->buf_info[buf_id].buf_size;
- /* In case substream is active, start reporting
- * dummy buffer_done interrupts to above ALSA layer.
- */
- if (substream) { /* substream is active */
- runtime = substream->runtime;
- rate = runtime->rate;
- channels = runtime->channels;
- msecs = (buf_size*1000)/(rate*channels*4);
- intelhaddata->timer = msecs_to_jiffies(msecs);
- intelhaddata->send_data = 1;
- schedule_delayed_work(
- &intelhaddata->dummy_audio,
- intelhaddata->timer);
- }
+
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) {
+ pr_debug("Device already disconnected\n");
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ return retval;
} else {
+ /* Disable Audio */
caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
- retval = query_ops.hdmi_audio_set_caps(
- HAD_SET_DISABLE_AUDIO_INT, &caps);
- retval = query_ops.hdmi_audio_set_caps(
- HAD_SET_DISABLE_AUDIO, NULL);
- retval = snd_intelhad_stop_silence(intelhaddata);
+ retval = had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps);
+ retval = had_set_caps(HAD_SET_DISABLE_AUDIO, NULL);
+ had_read_modify(AUD_CONFIG, 0, BIT(0));
}
+
intelhaddata->drv_status = HAD_DRV_DISCONNECTED;
- mutex_unlock(&intelhaddata->had_lock);
+
+ if (had_stream->stream_status == HAD_RUNNING_STREAM) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ had_start_dummy_playback(intelhaddata);
+ } else {
+ had_stream->stream_status = HAD_INIT;
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ }
return retval;
}
struct snd_intelhad *intelhaddata = data;
enum intel_had_aud_buf_type buf_id;
struct snd_pcm_substream *substream;
- struct hdmi_audio_registers_ops reg_ops;
- struct hdmi_audio_query_set_ops query_ops;
+ struct had_pvt_data *had_stream;
+ unsigned long flag_irqs;
- spin_lock(&intelhaddata->had_spinlock);
buf_id = intelhaddata->curr_buf;
- intelhaddata->buff_done = buf_id;
- spin_unlock(&intelhaddata->had_spinlock);
-
+ had_stream = intelhaddata->private_data;
+
+ /* Switching to a function can drop atomicity even in INTR context.
+ * Thus, a big lock is acquired to maintain atomicity.
+ * This can be optimized later.
+ * Currently, only buffer_done/_underrun executes in INTR context.
+ * Also, locking is implemented seperately to avoid real contention
+ * of data(struct intelhaddata) between IRQ/SOFT_IRQ/PROCESS context.
+ */
substream = intelhaddata->stream_info.had_substream;
- reg_ops = intelhaddata->reg_ops;
- query_ops = intelhaddata->query_ops;
switch (event_type) {
case HAD_EVENT_AUDIO_BUFFER_DONE:
retval = had_process_buffer_done(intelhaddata);
case HAD_EVENT_MODE_CHANGING:
pr_debug(" called _event_handler with _MODE_CHANGE event\n");
- mutex_lock(&intelhaddata->had_lock);
- if (intelhaddata->stream_info.str_id && substream)
+ /* Process only if stream is active & cable Plugged-in */
+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs);
+ if (intelhaddata->drv_status >= HAD_DRV_DISCONNECTED) {
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock,
+ flag_irqs);
+ break;
+ }
+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs);
+ if ((had_stream->stream_status == HAD_RUNNING_STREAM)
+ && substream)
retval = hdmi_audio_mode_change(substream);
- mutex_unlock(&intelhaddata->had_lock);
break;
default: