diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-02-03 17:46:28 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-02-03 17:46:28 +0100 |
commit | c1a7c40cff65719e0a5a2f101aa59868ff5eb8ce (patch) | |
tree | 9564bba0de6c9aa79b6aa23d0dba2e8ebe49bb7d /sound | |
parent | 374a504025a0913d3d639cec7fbb46a5800fc447 (diff) | |
parent | 91b0cb0cc07bcb5114df2897531f4ea41c148c8e (diff) | |
download | linux-c1a7c40cff65719e0a5a2f101aa59868ff5eb8ce.tar.bz2 |
Merge branch 'topic/intel-lpe-audio' into for-next
Lots of cleanups and refactoring of Intel LPE audio driver.
Diffstat (limited to 'sound')
-rw-r--r-- | sound/x86/Makefile | 4 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_audio.c | 1901 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_audio.h | 117 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_audio_if.c | 548 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_lpe_audio.c | 665 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_lpe_audio.h | 470 |
6 files changed, 1026 insertions, 2679 deletions
diff --git a/sound/x86/Makefile b/sound/x86/Makefile index 85ea22a2cf28..7ff919808320 100644 --- a/sound/x86/Makefile +++ b/sound/x86/Makefile @@ -1,6 +1,4 @@ snd-hdmi-lpe-audio-objs += \ - intel_hdmi_audio.o \ - intel_hdmi_audio_if.o \ - intel_hdmi_lpe_audio.o + intel_hdmi_audio.o obj-$(CONFIG_HDMI_LPE_AUDIO) += snd-hdmi-lpe-audio.o diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 5ce139c1b21d..c83f02c2593e 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -21,29 +21,27 @@ * ALSA driver for Intel HDMI audio */ -#define pr_fmt(fmt) "had: " fmt - +#include <linux/types.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/acpi.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <asm/cacheflush.h> -#include <sound/pcm.h> #include <sound/core.h> +#include <sound/asoundef.h> +#include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/control.h> -#include <sound/initval.h> +#include <drm/drm_edid.h> +#include <drm/intel_lpe_audio.h> #include "intel_hdmi_audio.h" -static DEFINE_MUTEX(had_mutex); - /*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_data; -static int underrun_count; module_param_named(index, hdmi_card_index, int, 0444); MODULE_PARM_DESC(index, @@ -55,7 +53,7 @@ MODULE_PARM_DESC(id, /* * ELD SA bits in the CEA Speaker Allocation data block */ -static int eld_speaker_allocation_bits[] = { +static const int eld_speaker_allocation_bits[] = { [0] = FL | FR, [1] = LFE, [2] = FC, @@ -118,7 +116,7 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { { .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, }; -static struct channel_map_table map_tables[] = { +static const struct channel_map_table map_tables[] = { { SNDRV_CHMAP_FL, 0x00, FL }, { SNDRV_CHMAP_FR, 0x01, FR }, { SNDRV_CHMAP_RL, 0x04, RL }, @@ -158,89 +156,101 @@ static const struct snd_pcm_hardware snd_intel_hadstream = { .fifo_size = HAD_FIFO_SIZE, }; -/* Register access functions */ - -int had_get_hwstate(struct snd_intelhad *intelhaddata) +/* Get the active PCM substream; + * Call had_substream_put() for unreferecing. + * Don't call this inside had_spinlock, as it takes by itself + */ +static struct snd_pcm_substream * +had_substream_get(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; - } + struct snd_pcm_substream *substream; + unsigned long flags; - return 0; + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + substream = intelhaddata->stream_info.substream; + if (substream) + intelhaddata->stream_info.substream_refcount++; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + return substream; } -int had_get_caps(enum had_caps_list query, void *caps) +/* Unref the active PCM substream; + * Don't call this inside had_spinlock, as it takes by itself + */ +static void had_substream_put(struct snd_intelhad *intelhaddata) { - 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); + unsigned long flags; - return retval; + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + intelhaddata->stream_info.substream_refcount--; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); } -int had_set_caps(enum had_caps_list set_element, void *caps) +/* Register access functions */ +static inline void +mid_hdmi_audio_read(struct snd_intelhad *ctx, u32 reg, u32 *val) { - 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; + *val = ioread32(ctx->mmio_start + ctx->had_config_offset + reg); } -int had_read_register(u32 offset, u32 *data) +static inline void +mid_hdmi_audio_write(struct snd_intelhad *ctx, u32 reg, u32 val) { - int retval; - struct snd_intelhad *intelhaddata = had_data; + iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg); +} - retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = intelhaddata->reg_ops.hdmi_audio_read_register( - offset + intelhaddata->audio_cfg_offset, data); +static int had_read_register(struct snd_intelhad *intelhaddata, + u32 offset, u32 *data) +{ + if (!intelhaddata->connected) + return -ENODEV; - return retval; + mid_hdmi_audio_read(intelhaddata, offset, data); + return 0; } -int had_write_register(u32 offset, u32 data) +static void fixup_dp_config(struct snd_intelhad *intelhaddata, + u32 offset, u32 *data) { - int retval; - struct snd_intelhad *intelhaddata = had_data; + if (intelhaddata->dp_output) { + if (offset == AUD_CONFIG && (*data & AUD_CONFIG_VALID_BIT)) + *data |= AUD_CONFIG_DP_MODE | AUD_CONFIG_BLOCK_BIT; + } +} - retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = intelhaddata->reg_ops.hdmi_audio_write_register( - offset + intelhaddata->audio_cfg_offset, data); +static int had_write_register(struct snd_intelhad *intelhaddata, + u32 offset, u32 data) +{ + if (!intelhaddata->connected) + return -ENODEV; - return retval; + fixup_dp_config(intelhaddata, offset, &data); + mid_hdmi_audio_write(intelhaddata, offset, data); + return 0; } -int had_read_modify(u32 offset, u32 data, u32 mask) +static int had_read_modify(struct snd_intelhad *intelhaddata, u32 offset, + u32 data, u32 mask) { - int retval; - struct snd_intelhad *intelhaddata = had_data; + u32 val_tmp; + + if (!intelhaddata->connected) + return -ENODEV; - retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = intelhaddata->reg_ops.hdmi_audio_read_modify( - offset + intelhaddata->audio_cfg_offset, - data, mask); + mid_hdmi_audio_read(intelhaddata, offset, &val_tmp); + val_tmp &= ~mask; + val_tmp |= (data & mask); - return retval; + fixup_dp_config(intelhaddata, offset, &val_tmp); + mid_hdmi_audio_write(intelhaddata, offset, val_tmp); + return 0; } -/** - * function to read-modify - * AUD_CONFIG register on VLV2.The had_read_modify() function should not - * directly be used on VLV2 for updating AUD_CONFIG register. + +/* + * enable / disable audio configuration + * + * The had_read_modify() function should not directly be used on VLV2 for + * updating AUD_CONFIG register. * This is because: * Bit6 of AUD_CONFIG register is writeonly due to a silicon bug on VLV2 * HDMI IP. As a result a read-modify of AUD_CONFIG regiter will always @@ -250,206 +260,147 @@ int had_read_modify(u32 offset, u32 data, u32 mask) * causes the "channels" field to be updated as 0xy binary resulting in * bad audio. The fix is to always write the AUD_CONFIG[6:4] with * appropriate value when doing read-modify of AUD_CONFIG register. - * - * @substream: the current substream or NULL if no active substream - * @data : data to be written - * @mask : mask - * */ -static int had_read_modify_aud_config_v2(struct snd_pcm_substream *substream, - u32 data, u32 mask) +static void snd_intelhad_enable_audio(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata, + bool enable) { - union aud_cfg cfg_val = {.cfg_regval = 0}; - u8 channels; + union aud_cfg cfg_val = {.regval = 0}; + u8 channels, data, mask; /* * If substream is NULL, there is no active stream. * In this case just set channels to 2 */ - if (substream) - channels = substream->runtime->channels; - else - channels = 2; - cfg_val.cfg_regx_v2.num_ch = channels - 2; - - data = data | cfg_val.cfg_regval; - mask = mask | AUD_CONFIG_CH_MASK_V2; + channels = substream ? substream->runtime->channels : 2; + cfg_val.regx.num_ch = channels - 2; - pr_debug("%s : data = %x, mask =%x\n", __func__, data, mask); + data = cfg_val.regval; + if (enable) + data |= 1; + mask = AUD_CONFIG_CH_MASK | 1; - return had_read_modify(AUD_CONFIG, data, mask); -} + dev_dbg(intelhaddata->dev, "%s : data = %x, mask =%x\n", + __func__, data, mask); -static void snd_intelhad_enable_audio_v1(struct snd_pcm_substream *substream, - u8 enable) -{ - had_read_modify(AUD_CONFIG, enable, BIT(0)); + had_read_modify(intelhaddata, AUD_CONFIG, data, mask); } -static void snd_intelhad_enable_audio_v2(struct snd_pcm_substream *substream, - u8 enable) +/* enable / disable the audio interface */ +static void snd_intelhad_enable_audio_int(struct snd_intelhad *ctx, bool enable) { - had_read_modify_aud_config_v2(substream, enable, BIT(0)); -} + u32 status_reg; -static void snd_intelhad_reset_audio_v1(u8 reset) -{ - had_write_register(AUD_HDMI_STATUS, reset); + if (enable) { + mid_hdmi_audio_read(ctx, AUD_HDMI_STATUS, &status_reg); + status_reg |= HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; + mid_hdmi_audio_write(ctx, AUD_HDMI_STATUS, status_reg); + mid_hdmi_audio_read(ctx, AUD_HDMI_STATUS, &status_reg); + } } -static void snd_intelhad_reset_audio_v2(u8 reset) +static void snd_intelhad_reset_audio(struct snd_intelhad *intelhaddata, + u8 reset) { - had_write_register(AUD_HDMI_STATUS_v2, reset); + had_write_register(intelhaddata, AUD_HDMI_STATUS, reset); } -/** +/* * initialize audio channel status registers * This function is called in the prepare callback */ static int had_prog_status_reg(struct snd_pcm_substream *substream, struct snd_intelhad *intelhaddata) { - union aud_cfg cfg_val = {.cfg_regval = 0}; - union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0}; - union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0}; + union aud_cfg cfg_val = {.regval = 0}; + union aud_ch_status_0 ch_stat0 = {.regval = 0}; + union aud_ch_status_1 ch_stat1 = {.regval = 0}; int format; - pr_debug("Entry %s\n", __func__); - - ch_stat0.status_0_regx.lpcm_id = (intelhaddata->aes_bits & - IEC958_AES0_NONAUDIO)>>1; - ch_stat0.status_0_regx.clk_acc = (intelhaddata->aes_bits & - IEC958_AES3_CON_CLOCK)>>4; - cfg_val.cfg_regx.val_bit = ch_stat0.status_0_regx.lpcm_id; + ch_stat0.regx.lpcm_id = (intelhaddata->aes_bits & + IEC958_AES0_NONAUDIO) >> 1; + ch_stat0.regx.clk_acc = (intelhaddata->aes_bits & + IEC958_AES3_CON_CLOCK) >> 4; + cfg_val.regx.val_bit = ch_stat0.regx.lpcm_id; switch (substream->runtime->rate) { case AUD_SAMPLE_RATE_32: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_32KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_32KHZ; break; case AUD_SAMPLE_RATE_44_1: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_44KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_44KHZ; break; case AUD_SAMPLE_RATE_48: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_48KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_48KHZ; break; case AUD_SAMPLE_RATE_88_2: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_88KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_88KHZ; break; case AUD_SAMPLE_RATE_96: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_96KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_96KHZ; break; case AUD_SAMPLE_RATE_176_4: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_176KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_176KHZ; break; case AUD_SAMPLE_RATE_192: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_192KHZ; + ch_stat0.regx.samp_freq = CH_STATUS_MAP_192KHZ; break; default: /* control should never come here */ return -EINVAL; - break; - } - had_write_register(AUD_CH_STATUS_0, ch_stat0.status_0_regval); + + had_write_register(intelhaddata, + AUD_CH_STATUS_0, ch_stat0.regval); format = substream->runtime->format; if (format == SNDRV_PCM_FORMAT_S16_LE) { - ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_20; - ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_16BITS; + ch_stat1.regx.max_wrd_len = MAX_SMPL_WIDTH_20; + ch_stat1.regx.wrd_len = SMPL_WIDTH_16BITS; } else if (format == SNDRV_PCM_FORMAT_S24_LE) { - ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_24; - ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_24BITS; + ch_stat1.regx.max_wrd_len = MAX_SMPL_WIDTH_24; + ch_stat1.regx.wrd_len = SMPL_WIDTH_24BITS; } else { - ch_stat1.status_1_regx.max_wrd_len = 0; - ch_stat1.status_1_regx.wrd_len = 0; + ch_stat1.regx.max_wrd_len = 0; + ch_stat1.regx.wrd_len = 0; } - had_write_register(AUD_CH_STATUS_1, ch_stat1.status_1_regval); + + had_write_register(intelhaddata, + AUD_CH_STATUS_1, ch_stat1.regval); return 0; } -/** +/* * function to initialize audio * registers and buffer confgiuration registers * This function is called in the prepare callback */ -static int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata) +static int snd_intelhad_audio_ctrl(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) { - union aud_cfg cfg_val = {.cfg_regval = 0}; - union aud_buf_config buf_cfg = {.buf_cfgval = 0}; + union aud_cfg cfg_val = {.regval = 0}; + union aud_buf_config buf_cfg = {.regval = 0}; u8 channels; had_prog_status_reg(substream, intelhaddata); - buf_cfg.buf_cfg_regx_v2.audio_fifo_watermark = FIFO_THRESHOLD; - buf_cfg.buf_cfg_regx_v2.dma_fifo_watermark = DMA_FIFO_THRESHOLD; - buf_cfg.buf_cfg_regx_v2.aud_delay = 0; - had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval); + buf_cfg.regx.audio_fifo_watermark = FIFO_THRESHOLD; + buf_cfg.regx.dma_fifo_watermark = DMA_FIFO_THRESHOLD; + buf_cfg.regx.aud_delay = 0; + had_write_register(intelhaddata, AUD_BUF_CONFIG, buf_cfg.regval); channels = substream->runtime->channels; - cfg_val.cfg_regx_v2.num_ch = channels - 2; + cfg_val.regx.num_ch = channels - 2; if (channels <= 2) - cfg_val.cfg_regx_v2.layout = LAYOUT0; + cfg_val.regx.layout = LAYOUT0; else - cfg_val.cfg_regx_v2.layout = LAYOUT1; + cfg_val.regx.layout = LAYOUT1; - cfg_val.cfg_regx_v2.val_bit = 1; - had_write_register(AUD_CONFIG, cfg_val.cfg_regval); - return 0; -} - -/** - * function to initialize audio - * registers and buffer confgiuration registers - * This function is called in the prepare callback - */ -static int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata) -{ - union aud_cfg cfg_val = {.cfg_regval = 0}; - union aud_buf_config buf_cfg = {.buf_cfgval = 0}; - u8 channels; - - had_prog_status_reg(substream, intelhaddata); - - buf_cfg.buf_cfg_regx.fifo_width = FIFO_THRESHOLD; - buf_cfg.buf_cfg_regx.aud_delay = 0; - had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval); - - channels = substream->runtime->channels; - - switch (channels) { - case 1: - case 2: - cfg_val.cfg_regx.num_ch = CH_STEREO; - cfg_val.cfg_regx.layout = LAYOUT0; - break; - - case 3: - case 4: - cfg_val.cfg_regx.num_ch = CH_THREE_FOUR; - cfg_val.cfg_regx.layout = LAYOUT1; - break; - - case 5: - case 6: - cfg_val.cfg_regx.num_ch = CH_FIVE_SIX; - cfg_val.cfg_regx.layout = LAYOUT1; - break; - - case 7: - case 8: - cfg_val.cfg_regx.num_ch = CH_SEVEN_EIGHT; - cfg_val.cfg_regx.layout = LAYOUT1; - break; - - } - - cfg_val.cfg_regx.val_bit = 1; - had_write_register(AUD_CONFIG, cfg_val.cfg_regval); + cfg_val.regx.val_bit = 1; + had_write_register(intelhaddata, AUD_CONFIG, cfg_val.regval); return 0; } @@ -461,8 +412,6 @@ static void init_channel_allocations(void) int i, j; struct cea_channel_speaker_allocation *p; - pr_debug("%s: Enter\n", __func__); - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { p = channel_allocations + i; p->channels = 0; @@ -504,7 +453,7 @@ static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata, */ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (intelhaddata->eeld.speaker_allocation_block & (1 << i)) + if (intelhaddata->eld[DRM_ELD_SPEAKER] & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -518,7 +467,7 @@ static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata, } } - pr_debug("HDMI: select CA 0x%x for %d\n", ca, channels); + dev_dbg(intelhaddata->dev, "select CA 0x%x for %d\n", ca, channels); return ca; } @@ -526,7 +475,7 @@ static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata, /* from speaker bit mask to ALSA API channel position */ static int spk_to_chmap(int spk) { - struct channel_map_table *t = map_tables; + const struct channel_map_table *t = map_tables; for (; t->map; t++) { if (t->spk_mask == spk) @@ -535,25 +484,22 @@ static int spk_to_chmap(int spk) return 0; } -void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) +static void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) { - int i = 0, c = 0; + int i, c; int spk_mask = 0; struct snd_pcm_chmap_elem *chmap; u8 eld_high, eld_high_mask = 0xF0; u8 high_msb; chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); - if (chmap == NULL) { + if (!chmap) { intelhaddata->chmap->chmap = NULL; return; } - had_get_caps(HAD_GET_ELD, &intelhaddata->eeld); - had_get_caps(HAD_GET_DP_OUTPUT, &intelhaddata->dp_output); - - pr_debug("eeld.speaker_allocation_block = %x\n", - intelhaddata->eeld.speaker_allocation_block); + dev_dbg(intelhaddata->dev, "eld speaker = %x\n", + intelhaddata->eld[DRM_ELD_SPEAKER]); /* WA: Fix the max channel supported to 8 */ @@ -564,14 +510,14 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) */ /* if 0x2F < eld < 0x4F fall back to 0x2f, else fall back to 0x4F */ - eld_high = intelhaddata->eeld.speaker_allocation_block & eld_high_mask; + eld_high = intelhaddata->eld[DRM_ELD_SPEAKER] & eld_high_mask; if ((eld_high & (eld_high-1)) && (eld_high > 0x1F)) { /* eld_high & (eld_high-1): if more than 1 bit set */ /* 0x1F: 7 channels */ for (i = 1; i < 4; i++) { high_msb = eld_high & (0x80 >> i); if (high_msb) { - intelhaddata->eeld.speaker_allocation_block &= + intelhaddata->eld[DRM_ELD_SPEAKER] &= high_msb | 0xF; break; } @@ -579,7 +525,7 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) } for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (intelhaddata->eeld.speaker_allocation_block & (1 << i)) + if (intelhaddata->eld[DRM_ELD_SPEAKER] & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -588,7 +534,7 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) for (c = 0; c < channel_allocations[i].channels; c++) { chmap->map[c] = spk_to_chmap( channel_allocations[i].speakers[ - (MAX_SPEAKERS - 1)-c]); + (MAX_SPEAKERS - 1) - c]); } chmap->channels = channel_allocations[i].channels; intelhaddata->chmap->chmap = chmap; @@ -610,7 +556,7 @@ static int had_chmap_ctl_info(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_intelhad *intelhaddata = info->private_data; - if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) + if (!intelhaddata->connected) return -ENODEV; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = HAD_MAX_CHANNEL; @@ -624,18 +570,22 @@ static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol, { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_intelhad *intelhaddata = info->private_data; - int i = 0; + int i; const struct snd_pcm_chmap_elem *chmap; - if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) + if (!intelhaddata->connected) return -ENODEV; - if (intelhaddata->chmap->chmap == NULL) + + mutex_lock(&intelhaddata->mutex); + if (!intelhaddata->chmap->chmap) { + mutex_unlock(&intelhaddata->mutex); return -ENODATA; + } + chmap = intelhaddata->chmap->chmap; - for (i = 0; i < chmap->channels; i++) { + for (i = 0; i < chmap->channels; i++) ucontrol->value.integer.value[i] = chmap->map[i]; - pr_debug("chmap->map[%d] = %d\n", i, chmap->map[i]); - } + mutex_unlock(&intelhaddata->mutex); return 0; } @@ -643,7 +593,7 @@ static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol, static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata, struct snd_pcm *pcm) { - int err = 0; + int err; err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, NULL, 0, (unsigned long)intelhaddata, @@ -652,142 +602,75 @@ static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata, return err; intelhaddata->chmap->private_data = intelhaddata; - intelhaddata->kctl = intelhaddata->chmap->kctl; - intelhaddata->kctl->info = had_chmap_ctl_info; - intelhaddata->kctl->get = had_chmap_ctl_get; + intelhaddata->chmap->kctl->info = had_chmap_ctl_info; + intelhaddata->chmap->kctl->get = had_chmap_ctl_get; intelhaddata->chmap->chmap = NULL; return 0; } -/** - * snd_intelhad_prog_dip_v1 - to initialize Data Island Packets registers - * - * @substream:substream for which the prepare function is called - * @intelhaddata:substream private data - * - * This function is called in the prepare callback - */ -static void snd_intelhad_prog_dip_v1(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata) -{ - int i; - union aud_ctrl_st ctrl_state = {.ctrl_val = 0}; - union aud_info_frame2 frame2 = {.fr2_val = 0}; - union aud_info_frame3 frame3 = {.fr3_val = 0}; - u8 checksum = 0; - int channels; - - channels = substream->runtime->channels; - - had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); - - frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1; - - frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation( - intelhaddata, channels); - - /*Calculte the byte wide checksum for all valid DIP words*/ - for (i = 0; i < BYTES_PER_WORD; i++) - checksum += (HDMI_INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & MASK_BYTE0; - for (i = 0; i < BYTES_PER_WORD; i++) - checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0; - for (i = 0; i < BYTES_PER_WORD; i++) - checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0; - - frame2.fr2_regx.chksum = -(checksum); - - had_write_register(AUD_HDMIW_INFOFR, HDMI_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++) - had_write_register(AUD_HDMIW_INFOFR, 0x0); - - ctrl_state.ctrl_regx.dip_freq = 1; - ctrl_state.ctrl_regx.dip_en_sta = 1; - had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -} - -/** - * snd_intelhad_prog_dip_v2 - to initialize Data Island Packets registers - * - * @substream:substream for which the prepare function is called - * @intelhaddata:substream private data - * +/* + * Initialize Data Island Packets registers * This function is called in the prepare callback */ -static void snd_intelhad_prog_dip_v2(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata) +static void snd_intelhad_prog_dip(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) { int i; - union aud_ctrl_st ctrl_state = {.ctrl_val = 0}; - union aud_info_frame2 frame2 = {.fr2_val = 0}; - union aud_info_frame3 frame3 = {.fr3_val = 0}; + union aud_ctrl_st ctrl_state = {.regval = 0}; + union aud_info_frame2 frame2 = {.regval = 0}; + union aud_info_frame3 frame3 = {.regval = 0}; u8 checksum = 0; u32 info_frame; int channels; + int ca; channels = substream->runtime->channels; - had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); + had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval); + ca = snd_intelhad_channel_allocation(intelhaddata, channels); if (intelhaddata->dp_output) { info_frame = DP_INFO_FRAME_WORD1; - frame2.fr2_val = 1; + frame2.regval = (substream->runtime->channels - 1) | (ca << 24); } else { info_frame = HDMI_INFO_FRAME_WORD1; - frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1; - - frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation( - intelhaddata, channels); + frame2.regx.chnl_cnt = substream->runtime->channels - 1; + frame3.regx.chnl_alloc = ca; - /*Calculte the byte wide checksum for all valid DIP words*/ + /* Calculte the byte wide checksum for all valid DIP words */ for (i = 0; i < BYTES_PER_WORD; i++) - checksum += (info_frame >> i*BITS_PER_BYTE) & MASK_BYTE0; + checksum += (info_frame >> (i * 8)) & 0xff; for (i = 0; i < BYTES_PER_WORD; i++) - checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0; + checksum += (frame2.regval >> (i * 8)) & 0xff; for (i = 0; i < BYTES_PER_WORD; i++) - checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0; + checksum += (frame3.regval >> (i * 8)) & 0xff; - frame2.fr2_regx.chksum = -(checksum); + frame2.regx.chksum = -(checksum); } - had_write_register(AUD_HDMIW_INFOFR_v2, info_frame); - had_write_register(AUD_HDMIW_INFOFR_v2, frame2.fr2_val); - had_write_register(AUD_HDMIW_INFOFR_v2, frame3.fr3_val); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, info_frame); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, frame2.regval); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, frame3.regval); /* program remaining DIP words with zero */ for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) - had_write_register(AUD_HDMIW_INFOFR_v2, 0x0); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, 0x0); - ctrl_state.ctrl_regx.dip_freq = 1; - ctrl_state.ctrl_regx.dip_en_sta = 1; - had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); + ctrl_state.regx.dip_freq = 1; + ctrl_state.regx.dip_en_sta = 1; + had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval); } -/** - * snd_intelhad_prog_buffer - programs buffer - * address and length registers - * - * @substream:substream for which the prepare function is called - * @intelhaddata:substream private data - * +/* + * Programs buffer address and length registers * This function programs ring buffer address and length into registers. */ -int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, - int start, int end) +static int snd_intelhad_prog_buffer(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata, + int start, int end) { u32 ring_buf_addr, ring_buf_size, period_bytes; u8 i, num_periods; - struct snd_pcm_substream *substream; - - substream = intelhaddata->stream_info.had_substream; - if (!substream) { - pr_err("substream is NULL\n"); - dump_stack(); - return 0; - } ring_buf_addr = substream->runtime->dma_addr; ring_buf_size = snd_pcm_lib_buffer_bytes(substream); @@ -814,36 +697,41 @@ int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, intelhaddata->buf_info[i].buf_size = period_bytes; else intelhaddata->buf_info[i].buf_size = ring_buf_size - - (period_bytes*i); + (i * period_bytes); - had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH), + had_write_register(intelhaddata, + AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH), intelhaddata->buf_info[i].buf_addr | BIT(0) | BIT(1)); - had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), + had_write_register(intelhaddata, + AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), period_bytes); intelhaddata->buf_info[i].is_valid = true; } - 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); + dev_dbg(intelhaddata->dev, "%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; } -int snd_intelhad_read_len(struct snd_intelhad *intelhaddata) +static 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]); + had_read_register(intelhaddata, + 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]); + dev_dbg(intelhaddata->dev, "buf[%d] size=%d\n", + i, len[i]); } return retval; @@ -853,7 +741,7 @@ static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate) { u32 maud_val; - /* Select maud according to DP 1.2 spec*/ + /* Select maud according to DP 1.2 spec */ if (link_rate == DP_2_7_GHZ) { switch (aud_samp_freq) { case AUD_SAMPLE_RATE_32: @@ -928,34 +816,8 @@ static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate) return maud_val; } -/** - * snd_intelhad_prog_cts_v1 - Program HDMI audio CTS value - * - * @aud_samp_freq: sampling frequency of audio data - * @tmds: sampling frequency of the display data - * @n_param: N value, depends on aud_samp_freq - * @intelhaddata:substream private data - * - * Program CTS register based on the audio and display sampling frequency - */ -static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds, - u32 link_rate, u32 n_param, - struct snd_intelhad *intelhaddata) -{ - u32 cts_val; - u64 dividend, divisor; - - /* Calculate CTS according to HDMI 1.3a spec*/ - dividend = (u64)tmds * n_param*1000; - divisor = 128 * aud_samp_freq; - cts_val = div64_u64(dividend, divisor); - 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)); -} - -/** - * snd_intelhad_prog_cts_v2 - Program HDMI audio CTS value +/* + * Program HDMI audio CTS value * * @aud_samp_freq: sampling frequency of audio data * @tmds: sampling frequency of the display data @@ -964,9 +826,9 @@ static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds, * * Program CTS register based on the audio and display sampling frequency */ -static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds, - u32 link_rate, u32 n_param, - struct snd_intelhad *intelhaddata) +static void snd_intelhad_prog_cts(u32 aud_samp_freq, u32 tmds, + u32 link_rate, u32 n_param, + struct snd_intelhad *intelhaddata) { u32 cts_val; u64 dividend, divisor; @@ -980,79 +842,54 @@ static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds, divisor = 128 * aud_samp_freq; cts_val = div64_u64(dividend, divisor); } - pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n", + dev_dbg(intelhaddata->dev, "TMDS value=%d, N value=%d, CTS Value=%d\n", tmds, n_param, cts_val); - had_write_register(AUD_HDMI_CTS, (BIT(24) | cts_val)); + had_write_register(intelhaddata, AUD_HDMI_CTS, (BIT(24) | cts_val)); } static int had_calculate_n_value(u32 aud_samp_freq) { - s32 n_val; + int n_val; /* Select N according to HDMI 1.3a spec*/ switch (aud_samp_freq) { case AUD_SAMPLE_RATE_32: n_val = 4096; - break; + break; case AUD_SAMPLE_RATE_44_1: n_val = 6272; - break; + break; case AUD_SAMPLE_RATE_48: n_val = 6144; - break; + break; case AUD_SAMPLE_RATE_88_2: n_val = 12544; - break; + break; case AUD_SAMPLE_RATE_96: n_val = 12288; - break; + break; case AUD_SAMPLE_RATE_176_4: n_val = 25088; - break; + break; case HAD_MAX_RATE: n_val = 24576; - break; + break; default: n_val = -EINVAL; - break; + break; } return n_val; } -/** - * snd_intelhad_prog_n_v1 - Program HDMI audio N value - * - * @aud_samp_freq: sampling frequency of audio data - * @n_param: N value, depends on aud_samp_freq - * @intelhaddata:substream private data - * - * This function is called in the prepare callback. - * It programs based on the audio and display sampling frequency - */ -static int snd_intelhad_prog_n_v1(u32 aud_samp_freq, u32 *n_param, - struct snd_intelhad *intelhaddata) -{ - s32 n_val; - - n_val = had_calculate_n_value(aud_samp_freq); - - if (n_val < 0) - return n_val; - - had_write_register(AUD_N_ENABLE, (BIT(20) | n_val)); - *n_param = n_val; - return 0; -} - -/** - * snd_intelhad_prog_n_v2 - Program HDMI audio N value +/* + * Program HDMI audio N value * * @aud_samp_freq: sampling frequency of audio data * @n_param: N value, depends on aud_samp_freq @@ -1061,10 +898,10 @@ static int snd_intelhad_prog_n_v1(u32 aud_samp_freq, u32 *n_param, * This function is called in the prepare callback. * It programs based on the audio and display sampling frequency */ -static int snd_intelhad_prog_n_v2(u32 aud_samp_freq, u32 *n_param, - struct snd_intelhad *intelhaddata) +static int snd_intelhad_prog_n(u32 aud_samp_freq, u32 *n_param, + struct snd_intelhad *intelhaddata) { - s32 n_val; + int n_val; if (intelhaddata->dp_output) { /* @@ -1082,249 +919,143 @@ static int snd_intelhad_prog_n_v2(u32 aud_samp_freq, u32 *n_param, if (n_val < 0) return n_val; - had_write_register(AUD_N_ENABLE, (BIT(24) | n_val)); + had_write_register(intelhaddata, AUD_N_ENABLE, (BIT(24) | n_val)); *n_param = n_val; return 0; } -static void had_clear_underrun_intr_v1(struct snd_intelhad *intelhaddata) -{ - u32 hdmi_status, i = 0; +#define MAX_CNT 0xFF - /* Handle Underrun interrupt within Audio Unit */ - had_write_register(AUD_CONFIG, 0); - /* Reset buffer pointers */ - 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 */ - 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; - 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"); -} - -static void had_clear_underrun_intr_v2(struct snd_intelhad *intelhaddata) +static void snd_intelhad_handle_underrun(struct snd_intelhad *intelhaddata) { - u32 hdmi_status, i = 0; + u32 hdmi_status = 0, i = 0; /* Handle Underrun interrupt within Audio Unit */ - had_write_register(AUD_CONFIG, 0); + had_write_register(intelhaddata, AUD_CONFIG, 0); /* Reset buffer pointers */ - had_write_register(AUD_HDMI_STATUS_v2, 1); - had_write_register(AUD_HDMI_STATUS_v2, 0); - /** + had_write_register(intelhaddata, AUD_HDMI_STATUS, 1); + had_write_register(intelhaddata, 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 */ - had_read_register(AUD_HDMI_STATUS_v2, &hdmi_status); - pr_debug("HDMI status =0x%x\n", hdmi_status); + had_read_register(intelhaddata, AUD_HDMI_STATUS, + &hdmi_status); + dev_dbg(intelhaddata->dev, "HDMI status =0x%x\n", hdmi_status); if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { i++; - had_write_register(AUD_HDMI_STATUS_v2, hdmi_status); + had_write_register(intelhaddata, + AUD_HDMI_STATUS, hdmi_status); } else break; } while (i < MAX_CNT); if (i >= MAX_CNT) - pr_err("Unable to clear UNDERRUN bits\n"); + dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n"); } -/** - * 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 +/* + * ALSA PCM open callback */ static int snd_intelhad_open(struct snd_pcm_substream *substream) { 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; runtime = substream->runtime; - underrun_count = 0; - pm_runtime_get(intelhaddata->dev); + pm_runtime_get_sync(intelhaddata->dev); - if (had_get_hwstate(intelhaddata)) { - pr_err("%s: HDMI cable plugged-out\n", __func__); + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "%s: HDMI cable plugged-out\n", + __func__); retval = -ENODEV; - goto exit_put_handle; - } - - /* Check, if device already in use */ - if (runtime->private_data) { - pr_err("Device already in use\n"); - retval = -EBUSY; - goto exit_put_handle; + goto error; } /* 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_put_handle; - } - stream->stream_status = STREAM_INIT; - runtime->private_data = stream; - retval = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (retval < 0) - goto exit_err; + goto error; /* Make sure, that the period size is always aligned * 64byte boundary */ retval = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); - if (retval < 0) { - pr_err("%s:step_size=64 failed,err=%d\n", __func__, retval); - goto exit_err; - } + if (retval < 0) + goto error; + + /* expose PCM substream */ + spin_lock_irq(&intelhaddata->had_spinlock); + intelhaddata->stream_info.substream = substream; + intelhaddata->stream_info.substream_refcount++; + spin_unlock_irq(&intelhaddata->had_spinlock); + + /* these are cleared in prepare callback, but just to be sure */ + intelhaddata->curr_buf = 0; + intelhaddata->underrun_count = 0; + intelhaddata->stream_info.buffer_rendered = 0; return retval; -exit_err: - kfree(stream); -exit_put_handle: + error: pm_runtime_put(intelhaddata->dev); - runtime->private_data = NULL; 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; - - /* pr_debug("had_period_elapsed called\n"); */ - - 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); -} - -/** - * 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("snd_intelhad_init_stream called\n"); - - 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 +/* + * ALSA PCM close callback */ 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); - runtime = substream->runtime; - if (!runtime->private_data) { - pr_debug("close() might have called after failed open"); - return 0; + /* unreference and sync with the pending PCM accesses */ + spin_lock_irq(&intelhaddata->had_spinlock); + intelhaddata->stream_info.substream = NULL; + intelhaddata->stream_info.substream_refcount--; + while (intelhaddata->stream_info.substream_refcount > 0) { + spin_unlock_irq(&intelhaddata->had_spinlock); + cpu_relax(); + spin_lock_irq(&intelhaddata->had_spinlock); } + spin_unlock_irq(&intelhaddata->had_spinlock); - intelhaddata->stream_info.buffer_rendered = 0; - intelhaddata->stream_info.buffer_ptr = 0; - intelhaddata->stream_info.str_id = 0; - intelhaddata->stream_info.had_substream = NULL; - - /* Check if following drv_status modification is required - VA */ - if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) { - intelhaddata->drv_status = HAD_DRV_CONNECTED; - pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", - __func__, __LINE__); - } - kfree(runtime->private_data); - runtime->private_data = NULL; pm_runtime_put(intelhaddata->dev); 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 +/* + * ALSA PCM hw_params callback */ static int snd_intelhad_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_intelhad *intelhaddata; unsigned long addr; int pages, buf_size, retval; - pr_debug("snd_intelhad_hw_params called\n"); - - if (!hw_params) - return -EINVAL; - + intelhaddata = snd_pcm_substream_chip(substream); buf_size = params_buffer_bytes(hw_params); retval = snd_pcm_lib_malloc_pages(substream, buf_size); if (retval < 0) return retval; - pr_debug("%s:allocated memory = %d\n", __func__, buf_size); + dev_dbg(intelhaddata->dev, "%s:allocated memory = %d\n", + __func__, buf_size); /* mark the pages as uncached region */ addr = (unsigned long) substream->runtime->dma_area; pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / PAGE_SIZE; retval = set_memory_uc(addr, pages); if (retval) { - pr_err("set_memory_uc failed.Error:%d\n", retval); + dev_err(intelhaddata->dev, "set_memory_uc failed.Error:%d\n", + retval); return retval; } memset(substream->runtime->dma_area, 0, buf_size); @@ -1332,22 +1063,14 @@ static int snd_intelhad_hw_params(struct snd_pcm_substream *substream, 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. - * +/* + * ALSA PCM hw_free callback */ static int snd_intelhad_hw_free(struct snd_pcm_substream *substream) { unsigned long addr; u32 pages; - pr_debug("snd_intelhad_hw_free called\n"); - /* mark back the pages as cached/writeback region before the free */ if (substream->runtime->dma_area != NULL) { addr = (unsigned long) substream->runtime->dma_area; @@ -1359,78 +1082,52 @@ static int snd_intelhad_hw_free(struct snd_pcm_substream *substream) return 0; } -/** - * 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 +/* + * ALSA PCM trigger callback */ static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - int caps, retval = 0; - unsigned long flag_irq; + int retval = 0; struct snd_intelhad *intelhaddata; - struct had_stream_pvt *stream; - struct had_pvt_data *had_stream; - - pr_debug("snd_intelhad_pcm_trigger called\n"); intelhaddata = snd_pcm_substream_chip(substream); - stream = substream->runtime->private_data; - had_stream = intelhaddata->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - pr_debug("Trigger Start\n"); - + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: /* Disable local INTRs till register prgmng is done */ - if (had_get_hwstate(intelhaddata)) { - pr_err("_START: HDMI cable plugged-out\n"); + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, + "_START: HDMI cable plugged-out\n"); retval = -ENODEV; break; } - stream->stream_status = STREAM_RUNNING; - had_stream->stream_type = HAD_RUNNING_STREAM; + intelhaddata->stream_info.running = true; /* Enable Audio */ - /* - * ToDo: Need to enable UNDERRUN interrupts as well - * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; - */ - caps = HDMI_AUDIO_BUFFER_DONE; - retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps); - retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL); - intelhaddata->ops->enable_audio(substream, 1); - - pr_debug("Processed _Start\n"); - + snd_intelhad_enable_audio_int(intelhaddata, true); + snd_intelhad_enable_audio(substream, intelhaddata, true); break; case SNDRV_PCM_TRIGGER_STOP: - pr_debug("Trigger Stop\n"); - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq); - intelhaddata->stream_info.str_id = 0; - intelhaddata->curr_buf = 0; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + spin_lock(&intelhaddata->had_spinlock); - /* Stop reporting BUFFER_DONE/UNDERRUN to above layers*/ + /* Stop reporting BUFFER_DONE/UNDERRUN to above layers */ - had_stream->stream_type = HAD_INIT; - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq); + intelhaddata->stream_info.running = false; + spin_unlock(&intelhaddata->had_spinlock); /* Disable Audio */ - /* - * ToDo: Need to disable UNDERRUN interrupts as well - * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; - */ - caps = HDMI_AUDIO_BUFFER_DONE; - had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); - intelhaddata->ops->enable_audio(substream, 0); + snd_intelhad_enable_audio_int(intelhaddata, false); + snd_intelhad_enable_audio(substream, intelhaddata, false); /* Reset buffer pointers */ - intelhaddata->ops->reset_audio(1); - intelhaddata->ops->reset_audio(0); - stream->stream_status = STREAM_DROPPED; - had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); + snd_intelhad_reset_audio(intelhaddata, 1); + snd_intelhad_reset_audio(intelhaddata, 0); + snd_intelhad_enable_audio_int(intelhaddata, false); break; default: @@ -1439,12 +1136,8 @@ static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, 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. +/* + * ALSA PCM prepare callback */ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) { @@ -1453,71 +1146,53 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) u32 link_rate = 0; struct snd_intelhad *intelhaddata; struct snd_pcm_runtime *runtime; - struct had_pvt_data *had_stream; - - pr_debug("snd_intelhad_pcm_prepare called\n"); intelhaddata = snd_pcm_substream_chip(substream); runtime = substream->runtime; - had_stream = intelhaddata->private_data; - if (had_get_hwstate(intelhaddata)) { - pr_err("%s: HDMI cable plugged-out\n", __func__); + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "%s: HDMI cable plugged-out\n", + __func__); retval = -ENODEV; goto prep_end; } - pr_debug("period_size=%d\n", + dev_dbg(intelhaddata->dev, "period_size=%d\n", (int)frames_to_bytes(runtime, runtime->period_size)); - pr_debug("periods=%d\n", runtime->periods); - pr_debug("buffer_size=%d\n", (int)snd_pcm_lib_buffer_bytes(substream)); - pr_debug("rate=%d\n", runtime->rate); - pr_debug("channels=%d\n", runtime->channels); - - 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; - } - - retval = snd_intelhad_init_stream(substream); - if (retval) - goto prep_end; - + dev_dbg(intelhaddata->dev, "periods=%d\n", runtime->periods); + dev_dbg(intelhaddata->dev, "buffer_size=%d\n", + (int)snd_pcm_lib_buffer_bytes(substream)); + dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate); + dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels); + + intelhaddata->curr_buf = 0; + intelhaddata->underrun_count = 0; + intelhaddata->stream_info.buffer_rendered = 0; /* Get N value in KHz */ - retval = had_get_caps(HAD_GET_DISPLAY_RATE, &disp_samp_freq); - if (retval) { - pr_err("querying display sampling freq failed %#x\n", retval); - goto prep_end; - } + disp_samp_freq = intelhaddata->tmds_clock_speed; - had_get_caps(HAD_GET_ELD, &intelhaddata->eeld); - had_get_caps(HAD_GET_DP_OUTPUT, &intelhaddata->dp_output); - - retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param, - intelhaddata); + retval = snd_intelhad_prog_n(substream->runtime->rate, &n_param, + intelhaddata); if (retval) { - pr_err("programming N value failed %#x\n", retval); + dev_err(intelhaddata->dev, + "programming N value failed %#x\n", retval); goto prep_end; } if (intelhaddata->dp_output) - had_get_caps(HAD_GET_LINK_RATE, &link_rate); - + link_rate = intelhaddata->link_rate; - intelhaddata->ops->prog_cts(substream->runtime->rate, - disp_samp_freq, link_rate, - n_param, intelhaddata); + snd_intelhad_prog_cts(substream->runtime->rate, + disp_samp_freq, link_rate, + n_param, intelhaddata); - intelhaddata->ops->prog_dip(substream, intelhaddata); + snd_intelhad_prog_dip(substream, intelhaddata); - retval = intelhaddata->ops->audio_ctrl(substream, intelhaddata); + retval = snd_intelhad_audio_ctrl(substream, intelhaddata); /* Prog buffer address */ - retval = snd_intelhad_prog_buffer(intelhaddata, + retval = snd_intelhad_prog_buffer(substream, intelhaddata, HAD_BUF_TYPE_A, HAD_BUF_TYPE_D); /* @@ -1525,58 +1200,51 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) * FL, FR, C, LFE, RL, RR */ - had_write_register(AUD_BUF_CH_SWAP, SWAP_LFE_CENTER); + had_write_register(intelhaddata, AUD_BUF_CH_SWAP, SWAP_LFE_CENTER); 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 +/* + * ALSA PCM pointer callback */ -static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - struct snd_pcm_substream *substream) +static snd_pcm_uframes_t +snd_intelhad_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_intelhad *intelhaddata; u32 bytes_rendered = 0; u32 t; int buf_id; - /* pr_debug("snd_intelhad_pcm_pointer called\n"); */ - intelhaddata = snd_pcm_substream_chip(substream); - if (intelhaddata->flag_underrun) { - intelhaddata->flag_underrun = 0; + if (!intelhaddata->connected) return SNDRV_PCM_POS_XRUN; - } /* Use a hw register to calculate sub-period position reports. * This makes PulseAudio happier. */ buf_id = intelhaddata->curr_buf % 4; - had_read_register(AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), &t); + had_read_register(intelhaddata, + AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), &t); if ((t == 0) || (t == ((u32)-1L))) { - underrun_count++; - pr_debug("discovered buffer done for buf %d, count = %d\n", - buf_id, underrun_count); - - if (underrun_count > (HAD_MIN_PERIODS/2)) { - pr_debug("assume audio_codec_reset, underrun = %d - do xrun\n", - underrun_count); - underrun_count = 0; + intelhaddata->underrun_count++; + dev_dbg(intelhaddata->dev, + "discovered buffer done for buf %d, count = %d\n", + buf_id, intelhaddata->underrun_count); + + if (intelhaddata->underrun_count > (HAD_MIN_PERIODS/2)) { + dev_dbg(intelhaddata->dev, + "assume audio_codec_reset, underrun = %d - do xrun\n", + intelhaddata->underrun_count); return SNDRV_PCM_POS_XRUN; } } else { /* Reset Counter */ - underrun_count = 0; + intelhaddata->underrun_count = 0; } t = intelhaddata->buf_info[buf_id].buf_size - t; @@ -1586,124 +1254,327 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( intelhaddata->stream_info.ring_buf_size, &(bytes_rendered)); - intelhaddata->stream_info.buffer_ptr = bytes_to_frames( - substream->runtime, - bytes_rendered + t); - return intelhaddata->stream_info.buffer_ptr; + return bytes_to_frames(substream->runtime, bytes_rendered + t); } -/** - * 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 +/* + * ALSA PCM mmap callback */ static int snd_intelhad_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { - - pr_debug("snd_intelhad_pcm_mmap called\n"); - - 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) +/* + * ALSA PCM ops + */ +static const 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, + .mmap = snd_intelhad_pcm_mmap, +}; + +/* process mode change of the running stream; called in mutex */ +static int hdmi_audio_mode_change(struct snd_intelhad *intelhaddata) { + struct snd_pcm_substream *substream; int retval = 0; u32 disp_samp_freq, n_param; u32 link_rate = 0; - struct snd_intelhad *intelhaddata; - intelhaddata = snd_pcm_substream_chip(substream); + substream = had_substream_get(intelhaddata); + if (!substream) + return 0; /* Disable Audio */ - intelhaddata->ops->enable_audio(substream, 0); + snd_intelhad_enable_audio(substream, intelhaddata, false); /* Update CTS value */ - retval = had_get_caps(HAD_GET_DISPLAY_RATE, &disp_samp_freq); - if (retval) { - pr_err("querying display sampling freq failed %#x\n", retval); - goto out; - } + disp_samp_freq = intelhaddata->tmds_clock_speed; - retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param, - intelhaddata); + retval = snd_intelhad_prog_n(substream->runtime->rate, &n_param, + intelhaddata); if (retval) { - pr_err("programming N value failed %#x\n", retval); + dev_err(intelhaddata->dev, + "programming N value failed %#x\n", retval); goto out; } if (intelhaddata->dp_output) - had_get_caps(HAD_GET_LINK_RATE, &link_rate); + link_rate = intelhaddata->link_rate; - intelhaddata->ops->prog_cts(substream->runtime->rate, - disp_samp_freq, link_rate, - n_param, intelhaddata); + snd_intelhad_prog_cts(substream->runtime->rate, + disp_samp_freq, link_rate, + n_param, intelhaddata); /* Enable Audio */ - intelhaddata->ops->enable_audio(substream, 1); + snd_intelhad_enable_audio(substream, intelhaddata, true); out: + had_substream_put(intelhaddata); return retval; } -/*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, - .mmap = snd_intelhad_pcm_mmap, -}; +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; + + buff_done = buf_id; + + intr_count = snd_intelhad_read_len(intelhaddata); + if (intr_count > 1) { + /* In case of active playback */ + dev_err(intelhaddata->dev, + "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++) { + int j = i % 4; + + buf_size = intelhaddata->buf_info[j].buf_size; + buf_addr = intelhaddata->buf_info[j].buf_addr; + had_write_register(intelhaddata, + AUD_BUF_A_LENGTH + + (j * HAD_REG_WIDTH), buf_size); + had_write_register(intelhaddata, + AUD_BUF_A_ADDR+(j * HAD_REG_WIDTH), + (buf_addr | BIT(0) | BIT(1))); + } + buf_id = buf_id % 4; + intelhaddata->buff_done = buf_id; + } -/** - * 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 snd_intelhad_create( - struct snd_intelhad *intelhaddata, - struct snd_card *card) + return intr_count; +} + +/* called from irq handler */ +static int had_process_buffer_done(struct snd_intelhad *intelhaddata) { - int retval; - static struct snd_device_ops ops = { - }; + u32 len = 1; + enum intel_had_aud_buf_type buf_id; + enum intel_had_aud_buf_type buff_done; + struct pcm_stream_info *stream; + struct snd_pcm_substream *substream; + u32 buf_size; + int intr_count; + unsigned long flags; + + stream = &intelhaddata->stream_info; + intr_count = 1; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + if (!intelhaddata->connected) { + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + dev_dbg(intelhaddata->dev, + "%s:Device already disconnected\n", __func__); + return 0; + } + buf_id = intelhaddata->curr_buf; + intelhaddata->buff_done = buf_id; + buff_done = intelhaddata->buff_done; + 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. + */ - pr_debug("snd_intelhad_create called\n"); + /* Check for any intr_miss in case of active playback */ + if (stream->running) { + intr_count = had_chk_intrmiss(intelhaddata, buf_id); + if (!intr_count || (intr_count > 3)) { + spin_unlock_irqrestore(&intelhaddata->had_spinlock, + flags); + dev_err(intelhaddata->dev, + "HAD SW state in non-recoverable mode\n"); + return 0; + } + buf_id += (intr_count - 1); + buf_id = buf_id % 4; + } - if (!intelhaddata) - return -EINVAL; + intelhaddata->buf_info[buf_id].is_valid = true; + if (intelhaddata->valid_buf_cnt-1 == buf_id) { + if (stream->running) + intelhaddata->curr_buf = HAD_BUF_TYPE_A; + } else + intelhaddata->curr_buf = buf_id + 1; - /* ALSA api to register the device */ - retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelhaddata, &ops); - return retval; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "HDMI cable plugged-out\n"); + return 0; + } + + /* Reprogram the registers with addr and length */ + had_write_register(intelhaddata, + AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), + buf_size); + had_write_register(intelhaddata, + AUD_BUF_A_ADDR + (buf_id * HAD_REG_WIDTH), + intelhaddata->buf_info[buf_id].buf_addr | + BIT(0) | BIT(1)); + + had_read_register(intelhaddata, + AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), + &len); + dev_dbg(intelhaddata->dev, "%s:Enabled buf[%d]\n", __func__, buf_id); + + /* In case of actual data, + * report buffer_done to above ALSA layer + */ + substream = had_substream_get(intelhaddata); + if (substream) { + buf_size = intelhaddata->buf_info[buf_id].buf_size; + intelhaddata->stream_info.buffer_rendered += + (intr_count * buf_size); + snd_pcm_period_elapsed(substream); + had_substream_put(intelhaddata); + } + + return 0; } -/** - * 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) + +/* called from irq handler */ +static int had_process_buffer_underrun(struct snd_intelhad *intelhaddata) +{ + enum intel_had_aud_buf_type buf_id; + struct pcm_stream_info *stream; + struct snd_pcm_substream *substream; + unsigned long flags; + int connected; + + stream = &intelhaddata->stream_info; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + buf_id = intelhaddata->curr_buf; + intelhaddata->buff_done = buf_id; + connected = intelhaddata->connected; + if (stream->running) + intelhaddata->curr_buf = HAD_BUF_TYPE_A; + + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + + dev_dbg(intelhaddata->dev, "Enter:%s buf_id=%d, stream_running=%d\n", + __func__, buf_id, stream->running); + + snd_intelhad_handle_underrun(intelhaddata); + + if (!connected) { + dev_dbg(intelhaddata->dev, + "%s:Device already disconnected\n", __func__); + return 0; + } + + /* Report UNDERRUN error to above layers */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + + return 0; +} + +/* process hot plug, called from wq with mutex locked */ +static void had_process_hot_plug(struct snd_intelhad *intelhaddata) { - pr_debug("Freeing PCM preallocated pages\n"); - snd_pcm_lib_preallocate_free_for_all(pcm); + enum intel_had_aud_buf_type buf_id; + struct snd_pcm_substream *substream; + + spin_lock_irq(&intelhaddata->had_spinlock); + if (intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "Device already connected\n"); + spin_unlock_irq(&intelhaddata->had_spinlock); + return; + } + + buf_id = intelhaddata->curr_buf; + intelhaddata->buff_done = buf_id; + intelhaddata->connected = true; + dev_dbg(intelhaddata->dev, + "%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", + __func__, __LINE__); + spin_unlock_irq(&intelhaddata->had_spinlock); + + dev_dbg(intelhaddata->dev, "Processing HOT_PLUG, buf_id = %d\n", + buf_id); + + /* Safety check */ + substream = had_substream_get(intelhaddata); + if (substream) { + dev_dbg(intelhaddata->dev, + "Force to stop the active stream by disconnection\n"); + /* Set runtime->state to hw_params done */ + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + had_substream_put(intelhaddata); + } + + had_build_channel_allocation_map(intelhaddata); } +/* process hot unplug, called from wq with mutex locked */ +static void had_process_hot_unplug(struct snd_intelhad *intelhaddata) +{ + enum intel_had_aud_buf_type buf_id; + struct snd_pcm_substream *substream; + + buf_id = intelhaddata->curr_buf; + + substream = had_substream_get(intelhaddata); + + spin_lock_irq(&intelhaddata->had_spinlock); + + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "Device already disconnected\n"); + spin_unlock_irq(&intelhaddata->had_spinlock); + goto out; + + } + + /* Disable Audio */ + snd_intelhad_enable_audio_int(intelhaddata, false); + snd_intelhad_enable_audio(substream, intelhaddata, false); + + intelhaddata->connected = false; + dev_dbg(intelhaddata->dev, + "%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", + __func__, __LINE__); + spin_unlock_irq(&intelhaddata->had_spinlock); + + /* Report to above ALSA layer */ + if (substream) + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + + out: + if (substream) + had_substream_put(intelhaddata); + kfree(intelhaddata->chmap->chmap); + intelhaddata->chmap->chmap = NULL; +} + +/* + * ALSA iec958 and ELD controls + */ + static int had_iec958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1717,14 +1588,17 @@ static int had_iec958_get(struct snd_kcontrol *kcontrol, { struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); + mutex_lock(&intelhaddata->mutex); 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->mutex); return 0; } + static int had_iec958_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1734,254 +1608,381 @@ static int had_iec958_mask_get(struct snd_kcontrol *kcontrol, ucontrol->value.iec958.status[3] = 0xff; return 0; } + static int had_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned int val; struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); + int changed = 0; - pr_debug("entered had_iec958_put\n"); val = (ucontrol->value.iec958.status[0] << 0) | (ucontrol->value.iec958.status[1] << 8) | (ucontrol->value.iec958.status[2] << 16) | (ucontrol->value.iec958.status[3] << 24); + mutex_lock(&intelhaddata->mutex); if (intelhaddata->aes_bits != val) { intelhaddata->aes_bits = val; - return 1; + changed = 1; } - return 1; + mutex_unlock(&intelhaddata->mutex); + return changed; } -static struct snd_kcontrol_new had_control_iec958_mask = { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), - .info = had_iec958_info, /* shared */ - .get = had_iec958_mask_get, -}; +static int had_ctl_eld_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = HDMI_MAX_ELD_BYTES; + return 0; +} -static struct snd_kcontrol_new had_control_iec958 = { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), - .info = had_iec958_info, - .get = had_iec958_get, - .put = had_iec958_put -}; +static int had_ctl_eld_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); -static struct snd_intel_had_interface had_interface = { - .name = "hdmi-audio", - .query = hdmi_audio_query, - .suspend = hdmi_audio_suspend, - .resume = hdmi_audio_resume, -}; + mutex_lock(&intelhaddata->mutex); + memcpy(ucontrol->value.bytes.data, intelhaddata->eld, + HDMI_MAX_ELD_BYTES); + mutex_unlock(&intelhaddata->mutex); + return 0; +} -static struct had_ops had_ops_v1 = { - .enable_audio = snd_intelhad_enable_audio_v1, - .reset_audio = snd_intelhad_reset_audio_v1, - .prog_n = snd_intelhad_prog_n_v1, - .prog_cts = snd_intelhad_prog_cts_v1, - .audio_ctrl = snd_intelhad_prog_audio_ctrl_v1, - .prog_dip = snd_intelhad_prog_dip_v1, - .handle_underrun = had_clear_underrun_intr_v1, +static const struct snd_kcontrol_new had_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = had_iec958_info, /* shared */ + .get = had_iec958_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = had_iec958_info, + .get = had_iec958_get, + .put = had_iec958_put, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = had_ctl_eld_info, + .get = had_ctl_eld_get, + }, }; -static struct had_ops had_ops_v2 = { - .enable_audio = snd_intelhad_enable_audio_v2, - .reset_audio = snd_intelhad_reset_audio_v2, - .prog_n = snd_intelhad_prog_n_v2, - .prog_cts = snd_intelhad_prog_cts_v2, - .audio_ctrl = snd_intelhad_prog_audio_ctrl_v2, - .prog_dip = snd_intelhad_prog_dip_v2, - .handle_underrun = had_clear_underrun_intr_v2, -}; -/** - * 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 +/* + * audio interrupt handler */ -int hdmi_audio_probe(void *deviceptr) +static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) { - int retval; - struct snd_pcm *pcm; - struct snd_card *card; - struct had_callback_ops ops_cb; - struct snd_intelhad *intelhaddata; - struct had_pvt_data *had_stream; - struct platform_device *devptr = deviceptr; + struct snd_intelhad *ctx = dev_id; + u32 audio_stat, audio_reg; + + audio_reg = AUD_HDMI_STATUS; + mid_hdmi_audio_read(ctx, audio_reg, &audio_stat); + + if (audio_stat & HDMI_AUDIO_UNDERRUN) { + mid_hdmi_audio_write(ctx, audio_reg, HDMI_AUDIO_UNDERRUN); + had_process_buffer_underrun(ctx); + } + + if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { + mid_hdmi_audio_write(ctx, audio_reg, HDMI_AUDIO_BUFFER_DONE); + had_process_buffer_done(ctx); + } + + return IRQ_HANDLED; +} + +/* + * monitor plug/unplug notification from i915; just kick off the work + */ +static void notify_audio_lpe(struct platform_device *pdev) +{ + struct snd_intelhad *ctx = platform_get_drvdata(pdev); + + schedule_work(&ctx->hdmi_audio_wq); +} + +/* the work to handle monitor hot plug/unplug */ +static void had_audio_wq(struct work_struct *work) +{ + struct snd_intelhad *ctx = + container_of(work, struct snd_intelhad, hdmi_audio_wq); + struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data; + + pm_runtime_get_sync(ctx->dev); + mutex_lock(&ctx->mutex); + if (!pdata->hdmi_connected) { + dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n", + __func__); + memset(ctx->eld, 0, sizeof(ctx->eld)); /* clear the old ELD */ + had_process_hot_unplug(ctx); + } else { + struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld; + + dev_dbg(ctx->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", + __func__, eld->port_id, pdata->tmds_clock_speed); - pr_debug("Enter %s\n", __func__); + switch (eld->pipe_id) { + case 0: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; + break; + case 1: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_B; + break; + case 2: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; + break; + default: + dev_dbg(ctx->dev, "Invalid pipe %d\n", + eld->pipe_id); + break; + } - pr_debug("hdmi_audio_probe dma_mask: %p\n", devptr->dev.dma_mask); + memcpy(ctx->eld, eld->eld_data, sizeof(ctx->eld)); - /* allocate memory for saving internal context and working */ - intelhaddata = kzalloc(sizeof(*intelhaddata), GFP_KERNEL); - if (!intelhaddata) - return -ENOMEM; + ctx->dp_output = pdata->dp_output; + ctx->tmds_clock_speed = pdata->tmds_clock_speed; + ctx->link_rate = pdata->link_rate; - had_stream = kzalloc(sizeof(*had_stream), GFP_KERNEL); - if (!had_stream) { - retval = -ENOMEM; - goto free_haddata; + had_process_hot_plug(ctx); + + /* Process mode change if stream is active */ + hdmi_audio_mode_change(ctx); } + mutex_unlock(&ctx->mutex); + pm_runtime_put(ctx->dev); +} - had_data = intelhaddata; - ops_cb.intel_had_event_call_back = had_event_handler; +/* + * PM callbacks + */ - /* registering with display driver to get access to display APIs */ +static int hdmi_lpe_audio_runtime_suspend(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + struct snd_pcm_substream *substream; - retval = mid_hdmi_audio_setup( - 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_hadstream; + substream = had_substream_get(ctx); + if (substream) { + snd_pcm_suspend(substream); + had_substream_put(ctx); + } + + return 0; +} + +static int hdmi_lpe_audio_suspend(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + int err; + + err = hdmi_lpe_audio_runtime_suspend(dev); + if (!err) + snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D3hot); + return err; +} + +static int hdmi_lpe_audio_resume(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + + snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D0); + return 0; +} + +/* release resources */ +static void hdmi_lpe_audio_free(struct snd_card *card) +{ + struct snd_intelhad *ctx = card->private_data; + + cancel_work_sync(&ctx->hdmi_audio_wq); + + if (ctx->mmio_start) + iounmap(ctx->mmio_start); + if (ctx->irq >= 0) + free_irq(ctx->irq, ctx); +} + +/* + * hdmi_lpe_audio_probe - start bridge with i915 + * + * This function is called when the i915 driver creates the + * hdmi-lpe-audio platform device. + */ +static int hdmi_lpe_audio_probe(struct platform_device *pdev) +{ + struct snd_card *card; + struct snd_intelhad *ctx; + struct snd_pcm *pcm; + struct intel_hdmi_lpe_audio_pdata *pdata; + int irq; + struct resource *res_mmio; + int i, ret; + + dev_dbg(&pdev->dev, "dma_mask: %p\n", pdev->dev.dma_mask); + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: quit: pdata not allocated by i915!!\n", __func__); + return -EINVAL; + } + + /* get resources */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Could not get irq resource\n"); + return -ENODEV; + } + + res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mmio) { + dev_err(&pdev->dev, "Could not get IO_MEM resources\n"); + return -ENXIO; } - mutex_lock(&had_mutex); - spin_lock_init(&intelhaddata->had_spinlock); - intelhaddata->drv_status = HAD_DRV_DISCONNECTED; - pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", - __func__, __LINE__); /* create a card instance with ALSA framework */ - retval = snd_card_new(&devptr->dev, hdmi_card_index, hdmi_card_id, - THIS_MODULE, 0, &card); - - if (retval) - goto unlock_mutex; - intelhaddata->card = card; - intelhaddata->card_id = hdmi_card_id; - intelhaddata->card_index = card->number; - 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)); - 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) + ret = snd_card_new(&pdev->dev, hdmi_card_index, hdmi_card_id, + THIS_MODULE, sizeof(*ctx), &card); + if (ret) + return ret; + + ctx = card->private_data; + spin_lock_init(&ctx->had_spinlock); + mutex_init(&ctx->mutex); + ctx->connected = false; + ctx->dev = &pdev->dev; + ctx->card = card; + ctx->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + strcpy(card->driver, INTEL_HAD); + strcpy(card->shortname, INTEL_HAD); + + ctx->irq = -1; + ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; + INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq); + + card->private_free = hdmi_lpe_audio_free; + + /* assume pipe A as default */ + ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; + + platform_set_drvdata(pdev, ctx); + + dev_dbg(&pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n", + __func__, (unsigned int)res_mmio->start, + (unsigned int)res_mmio->end); + + ctx->mmio_start = ioremap_nocache(res_mmio->start, + (size_t)(resource_size(res_mmio))); + if (!ctx->mmio_start) { + dev_err(&pdev->dev, "Could not get ioremap\n"); + ret = -EACCES; + goto err; + } + + /* setup interrupt handler */ + ret = request_irq(irq, display_pipe_interrupt_handler, 0, + pdev->name, ctx); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err; + } + + ctx->irq = irq; + + ret = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS, + MAX_CAP_STREAMS, &pcm); + if (ret) goto err; /* setup private data which can be retrieved when required */ - pcm->private_data = intelhaddata; - pcm->private_free = snd_intelhad_pcm_free; + pcm->private_data = ctx; pcm->info_flags = 0; strncpy(pcm->name, card->shortname, strlen(card->shortname)); - /* setup the ops for palyabck */ + /* setup the ops for playabck */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intelhad_playback_ops); /* allocate dma pages for ALSA stream operations * memory allocated is based on size, not max value * thus using same argument for max & size */ - retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, NULL, HAD_MAX_BUFFER, HAD_MAX_BUFFER); - if (card->dev == NULL) - pr_debug("card->dev is NULL!!!!! Should not be this case\n"); - else if (card->dev->dma_mask == NULL) - pr_debug("hdmi_audio_probe dma_mask is NULL!!!!!\n"); - else - pr_debug("hdmi_audio_probe dma_mask is : %p\n", - card->dev->dma_mask); - - if (retval) - goto err; + /* create controls */ + for (i = 0; i < ARRAY_SIZE(had_controls); i++) { + ret = snd_ctl_add(card, snd_ctl_new1(&had_controls[i], ctx)); + if (ret < 0) + goto err; + } - /* internal function call to register device with ALSA */ - retval = snd_intelhad_create(intelhaddata, card); - if (retval) - goto err; + init_channel_allocations(); - card->private_data = &intelhaddata; - retval = snd_card_register(card); - if (retval) + /* Register channel map controls */ + ret = had_register_chmap_ctls(ctx, pcm); + if (ret < 0) 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) + ret = snd_card_register(card); + if (ret) goto err; - init_channel_allocations(); + spin_lock_irq(&pdata->lpe_audio_slock); + pdata->notify_audio_lpe = notify_audio_lpe; + pdata->notify_pending = false; + spin_unlock_irq(&pdata->lpe_audio_slock); - /* Register channel map controls */ - retval = had_register_chmap_ctls(intelhaddata, pcm); - if (retval < 0) - goto err; + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); - intelhaddata->dev = &devptr->dev; - pm_runtime_set_active(intelhaddata->dev); - pm_runtime_enable(intelhaddata->dev); + dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); + schedule_work(&ctx->hdmi_audio_wq); - mutex_unlock(&had_mutex); - retval = mid_hdmi_audio_register(&had_interface, intelhaddata); - if (retval) { - pr_err("registering with display driver failed %#x\n", retval); - snd_card_free(card); - goto free_hadstream; - } - - intelhaddata->hw_silence = 1; - had_ops_v1 = had_ops_v1; /* unused */ - intelhaddata->ops = &had_ops_v2; + return 0; - return retval; err: snd_card_free(card); -unlock_mutex: - mutex_unlock(&had_mutex); -free_hadstream: - kfree(had_stream); - pm_runtime_disable(intelhaddata->dev); - intelhaddata->dev = NULL; -free_haddata: - kfree(intelhaddata); - intelhaddata = NULL; - pr_err("Error returned from %s api %#x\n", __func__, retval); - return retval; + return ret; } -/** - * hdmi_audio_remove - removes the alsa card - * - *@haddata: pointer to HAD private data +/* + * hdmi_lpe_audio_remove - stop bridge with i915 * - * This function is called when the hdmi cable is un-plugged. This function - * free the sound card. + * This function is called when the platform device is destroyed. */ -int hdmi_audio_remove(void *pdevptr) +static int hdmi_lpe_audio_remove(struct platform_device *pdev) { - struct snd_intelhad *intelhaddata = had_data; - int caps; + struct snd_intelhad *ctx = platform_get_drvdata(pdev); - pr_debug("Enter %s\n", __func__); - - if (!intelhaddata) - return 0; - - if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) { - caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; - had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); - had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); - } - snd_card_free(intelhaddata->card); - kfree(intelhaddata->private_data); - kfree(intelhaddata); + if (ctx->connected) + snd_intelhad_enable_audio_int(ctx, false); + snd_card_free(ctx->card); return 0; } +static const struct dev_pm_ops hdmi_lpe_audio_pm = { + SET_SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) + SET_RUNTIME_PM_OPS(hdmi_lpe_audio_runtime_suspend, NULL, NULL) +}; + +static struct platform_driver hdmi_lpe_audio_driver = { + .driver = { + .name = "hdmi-lpe-audio", + .pm = &hdmi_lpe_audio_pm, + }, + .probe = hdmi_lpe_audio_probe, + .remove = hdmi_lpe_audio_remove, +}; + +module_platform_driver(hdmi_lpe_audio_driver); +MODULE_ALIAS("platform:hdmi_lpe_audio"); + MODULE_AUTHOR("Sailaja Bandarupalli <sailaja.bandarupalli@intel.com>"); MODULE_AUTHOR("Ramesh Babu K V <ramesh.babu@intel.com>"); MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@intel.com>"); diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h index 034b3873ffa1..9f713a8a88bc 100644 --- a/sound/x86/intel_hdmi_audio.h +++ b/sound/x86/intel_hdmi_audio.h @@ -30,19 +30,11 @@ #ifndef _INTEL_HDMI_AUDIO_H_ #define _INTEL_HDMI_AUDIO_H_ -#include <linux/types.h> -#include <sound/initval.h> -#include <linux/version.h> -#include <linux/pm_runtime.h> -#include <sound/asoundef.h> -#include <sound/control.h> -#include <sound/pcm.h> #include "intel_hdmi_lpe_audio.h" #define PCM_INDEX 0 #define MAX_PB_STREAMS 1 #define MAX_CAP_STREAMS 0 -#define HDMI_AUDIO_DRIVER "hdmi-audio" #define HDMI_INFO_FRAME_WORD1 0x000a0184 #define DP_INFO_FRAME_WORD1 0x00441b84 @@ -64,21 +56,18 @@ #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 #define SWAP_LFE_CENTER 0x00fac4c8 -#define AUD_CONFIG_CH_MASK_V2 0x70 +#define AUD_CONFIG_CH_MASK 0x70 struct pcm_stream_info { - int str_id; - void *had_substream; - void (*period_elapsed)(void *had_substream); - u32 buffer_ptr; + struct snd_pcm_substream *substream; u64 buffer_rendered; u32 ring_buf_size; - int sfreq; + int substream_refcount; + bool running; }; struct ring_buf_info { @@ -87,113 +76,47 @@ struct ring_buf_info { u8 is_valid; }; -struct had_stream_pvt { - enum had_stream_status stream_status; - int stream_ops; - ssize_t dbg_cum_bytes; -}; - -struct had_pvt_data { - enum had_status_stream stream_type; -}; - -struct had_callback_ops { - had_event_call_back intel_had_event_call_back; -}; - -/** +/* * struct snd_intelhad - intelhad driver structure * * @card: ptr to hold card details - * @card_index: sound card index - * @card_id: detected sound card id - * @reg_ops: register operations to program registers - * @query_ops: caps call backs for get/set operations - * @drv_status: driver status + * @connected: the monitor connection status * @buf_info: ring buffer info * @stream_info: stream information - * @eeld: holds EELD info + * @eld: holds ELD info * @curr_buf: pointer to hold current active ring buf * @valid_buf_cnt: ring buffer count for stream * @had_spinlock: driver lock * @aes_bits: IEC958 status bits * @buff_done: id of current buffer done intr * @dev: platoform device handle - * @kctl: holds kctl ptrs used for channel map * @chmap: holds channel map info - * @audio_reg_base: hdmi audio register base offset - * @hw_silence: flag indicates SoC support for HW silence/Keep alive - * @ops: holds ops functions based on platform + * @underrun_count: PCM stream underrun counter */ struct snd_intelhad { struct snd_card *card; - int card_index; - char *card_id; - struct hdmi_audio_registers_ops reg_ops; - struct hdmi_audio_query_set_ops query_ops; - enum had_drv_status drv_status; + bool connected; struct ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS]; struct pcm_stream_info stream_info; - union otm_hdmi_eld_t eeld; + unsigned char eld[HDMI_MAX_ELD_BYTES]; bool dp_output; enum intel_had_aud_buf_type curr_buf; int valid_buf_cnt; unsigned int aes_bits; - int flag_underrun; - struct had_pvt_data *private_data; spinlock_t had_spinlock; enum intel_had_aud_buf_type buff_done; struct device *dev; - struct snd_kcontrol *kctl; struct snd_pcm_chmap *chmap; - unsigned int *audio_reg_base; - unsigned int audio_cfg_offset; - bool hw_silence; - struct had_ops *ops; + int underrun_count; + int tmds_clock_speed; + int link_rate; + + /* internal stuff */ + int irq; + void __iomem *mmio_start; + unsigned int had_config_offset; + struct work_struct hdmi_audio_wq; + struct mutex mutex; /* for protecting chmap and eld */ }; -struct had_ops { - void (*enable_audio)(struct snd_pcm_substream *substream, - u8 enable); - void (*reset_audio)(u8 reset); - int (*prog_n)(u32 aud_samp_freq, u32 *n_param, - struct snd_intelhad *intelhaddata); - void (*prog_cts)(u32 aud_samp_freq, u32 tmds, u32 link_rate, - u32 n_param, struct snd_intelhad *intelhaddata); - int (*audio_ctrl)(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata); - void (*prog_dip)(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata); - void (*handle_underrun)(struct snd_intelhad *intelhaddata); -}; - - -int had_event_handler(enum had_event_type event_type, void *data); - -int hdmi_audio_query(void *drv_data, struct hdmi_audio_event event); -int hdmi_audio_suspend(void *drv_data, struct hdmi_audio_event event); -int hdmi_audio_resume(void *drv_data); -int hdmi_audio_mode_change(struct snd_pcm_substream *substream); -extern struct snd_pcm_ops snd_intelhad_playback_ops; - -int snd_intelhad_init_audio_ctrl(struct snd_pcm_substream *substream, - struct snd_intelhad *intelhaddata, - int flag_silence); -int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, - int start, int end); -int snd_intelhad_invd_buffer(int start, int end); -int snd_intelhad_read_len(struct snd_intelhad *intelhaddata); -void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata); - -/* Register access functions */ -int had_get_hwstate(struct snd_intelhad *intelhaddata); -int had_get_caps(enum had_caps_list query_element, void *capabilties); -int had_set_caps(enum had_caps_list set_element, void *capabilties); -int had_read_register(u32 reg_addr, u32 *data); -int had_write_register(u32 reg_addr, u32 data); -int had_read_modify(u32 reg_addr, u32 data, u32 mask); - -int hdmi_audio_probe(void *devptr); -int hdmi_audio_remove(void *pdev); - #endif /* _INTEL_HDMI_AUDIO_ */ diff --git a/sound/x86/intel_hdmi_audio_if.c b/sound/x86/intel_hdmi_audio_if.c deleted file mode 100644 index 9ae242d62eb2..000000000000 --- a/sound/x86/intel_hdmi_audio_if.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * intel_hdmi_audio_if.c - Intel HDMI audio driver for MID - * - * Copyright (C) 2016 Intel Corp - * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> - * Ramesh Babu K V <ramesh.babu@intel.com> - * Vaibhav Agarwal <vaibhav.agarwal@intel.com> - * Jerome Anand <jerome.anand@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * 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 <linux/io.h> -#include <linux/jiffies.h> -#include <linux/slab.h> -#include <sound/pcm.h> -#include <sound/core.h> -#include "intel_hdmi_audio.h" -#include "intel_hdmi_lpe_audio.h" - -/** - * hdmi_audio_query - hdmi audio query function - * - *@haddata: pointer to HAD private data - *@event: audio event for which this method is invoked - * - * This function is called by client driver to query the - * hdmi audio. - */ -int hdmi_audio_query(void *haddata, struct hdmi_audio_event event) -{ - struct snd_pcm_substream *substream = NULL; - struct had_pvt_data *had_stream; - unsigned long flag_irqs; - struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; - - if (intelhaddata->stream_info.had_substream) - substream = intelhaddata->stream_info.had_substream; - had_stream = intelhaddata->private_data; - switch (event.type) { - case HAD_EVENT_QUERY_IS_AUDIO_BUSY: - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); - - if ((had_stream->stream_type == HAD_RUNNING_STREAM) || - substream) { - spin_unlock_irqrestore(&intelhaddata->had_spinlock, - flag_irqs); - pr_debug("Audio stream active\n"); - return -EBUSY; - } - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - break; - - case HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED: - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); - if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) { - spin_unlock_irqrestore(&intelhaddata->had_spinlock, - flag_irqs); - pr_debug("Audio is suspended\n"); - return 1; - } - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - break; - - default: - pr_debug("error un-handled event !!\n"); - return -EINVAL; - break; - - } - - return 0; -} - -/** - * 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, struct hdmi_audio_event event) -{ - int caps, retval = 0; - struct had_pvt_data *had_stream; - unsigned long flag_irqs; - struct snd_pcm_substream *substream; - struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; - - pr_debug("Enter:%s\n", __func__); - - had_stream = intelhaddata->private_data; - substream = intelhaddata->stream_info.had_substream; - - if (intelhaddata->dev->power.runtime_status != RPM_SUSPENDED) { - pr_err("audio stream is active\n"); - return -EAGAIN; - } - - - 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"); - 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; - pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_SUSPENDED\n", - __func__, __LINE__); - - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - /* - * ToDo: Need to disable UNDERRUN interrupts as well - * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; - */ - caps = HDMI_AUDIO_BUFFER_DONE; - had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); - had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); - pr_debug("Exit:%s", __func__); - 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) -{ - int caps, retval = 0; - struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; - unsigned long flag_irqs; - - pr_debug("Enter:%s\n", __func__); - - 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"); - return 0; - } - - if (intelhaddata->drv_status != HAD_DRV_SUSPENDED) { - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - pr_err("had is not in suspended state\n"); - return 0; - } - - 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; - pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", - __func__, __LINE__); - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - /* - * ToDo: Need to enable UNDERRUN interrupts as well - * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; - */ - caps = HDMI_AUDIO_BUFFER_DONE; - retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps); - retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL); - 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++) { - int j = i % 4; - - buf_size = intelhaddata->buf_info[j].buf_size; - buf_addr = intelhaddata->buf_info[j].buf_addr; - had_write_register(AUD_BUF_A_LENGTH + - (j * HAD_REG_WIDTH), buf_size); - had_write_register( - AUD_BUF_A_ADDR+(j * 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; -} - -int had_process_buffer_done(struct snd_intelhad *intelhaddata) -{ - 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 had_pvt_data *had_stream; - int intr_count; - enum had_status_stream stream_type; - unsigned long flag_irqs; - - had_stream = intelhaddata->private_data; - stream = &intelhaddata->stream_info; - intr_count = 1; - - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); - if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - pr_err("%s:Device already disconnected\n", __func__); - return 0; - } - buf_id = intelhaddata->curr_buf; - intelhaddata->buff_done = buf_id; - buff_done = intelhaddata->buff_done; - buf_size = intelhaddata->buf_info[buf_id].buf_size; - stream_type = had_stream->stream_type; - - pr_debug("Enter:%s buf_id=%d\n", __func__, buf_id); - - /* 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 (had_stream->stream_type == HAD_RUNNING_STREAM) { - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - 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 0; - } - buf_id += (intr_count - 1); - buf_id = buf_id % 4; - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); - } - - intelhaddata->buf_info[buf_id].is_valid = true; - if (intelhaddata->valid_buf_cnt-1 == buf_id) { - if (had_stream->stream_type >= HAD_RUNNING_STREAM) - intelhaddata->curr_buf = HAD_BUF_TYPE_A; - } else - intelhaddata->curr_buf = buf_id + 1; - - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - - if (had_get_hwstate(intelhaddata)) { - pr_err("HDMI cable plugged-out\n"); - return 0; - } - - /*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); - - /* In case of actual data, - * report buffer_done to above ALSA layer - */ - buf_size = intelhaddata->buf_info[buf_id].buf_size; - if (stream_type >= HAD_RUNNING_STREAM) { - intelhaddata->stream_info.buffer_rendered += - (intr_count * buf_size); - stream->period_elapsed(stream->had_substream); - } - - return 0; -} - -int had_process_buffer_underrun(struct snd_intelhad *intelhaddata) -{ - enum intel_had_aud_buf_type buf_id; - struct pcm_stream_info *stream; - struct had_pvt_data *had_stream; - enum had_status_stream stream_type; - unsigned long flag_irqs; - int drv_status; - - had_stream = intelhaddata->private_data; - stream = &intelhaddata->stream_info; - - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); - buf_id = intelhaddata->curr_buf; - stream_type = had_stream->stream_type; - intelhaddata->buff_done = buf_id; - drv_status = intelhaddata->drv_status; - if (stream_type == HAD_RUNNING_STREAM) - intelhaddata->curr_buf = HAD_BUF_TYPE_A; - - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - - pr_debug("Enter:%s buf_id=%d, stream_type=%d\n", - __func__, buf_id, stream_type); - - intelhaddata->ops->handle_underrun(intelhaddata); - - if (drv_status == HAD_DRV_DISCONNECTED) { - pr_err("%s:Device already disconnected\n", __func__); - return 0; - } - - if (stream_type == HAD_RUNNING_STREAM) { - /* Report UNDERRUN error to above layers */ - intelhaddata->flag_underrun = 1; - stream->period_elapsed(stream->had_substream); - } - - return 0; -} - -int had_process_hot_plug(struct snd_intelhad *intelhaddata) -{ - enum intel_had_aud_buf_type buf_id; - struct snd_pcm_substream *substream; - struct had_pvt_data *had_stream; - unsigned long flag_irqs; - - pr_debug("Enter:%s\n", __func__); - - substream = intelhaddata->stream_info.had_substream; - had_stream = intelhaddata->private_data; - - 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 0; - } - buf_id = intelhaddata->curr_buf; - intelhaddata->buff_done = buf_id; - intelhaddata->drv_status = HAD_DRV_CONNECTED; - pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", - __func__, __LINE__); - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - - pr_debug("Processing HOT_PLUG, buf_id = %d\n", buf_id); - - /* Query display driver for audio register base */ - if (intelhaddata->reg_ops.hdmi_audio_get_register_base( - &intelhaddata->audio_reg_base, - &intelhaddata->audio_cfg_offset)) { - pr_err("Unable to get audio reg base from Display driver\n"); - goto err; - } - - if (intelhaddata->audio_reg_base == NULL) { - pr_err("audio reg base value is NULL\n"); - goto err; - } - - pr_debug("%s audio_reg_base = 0x%p\n", __func__, - intelhaddata->audio_reg_base); - - /* Safety check */ - if (substream) { - pr_debug("There should not be active PB from ALSA\n"); - pr_debug("Signifies, cable is plugged-in even before\n"); - pr_debug("processing snd_pcm_disconnect\n"); - /* Set runtime->state to hw_params done */ - snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); - } - - had_build_channel_allocation_map(intelhaddata); - - return 0; - -err: - pm_runtime_disable(intelhaddata->dev); - intelhaddata->dev = NULL; - return 0; -} - -int had_process_hot_unplug(struct snd_intelhad *intelhaddata) -{ - int caps, retval = 0; - enum intel_had_aud_buf_type buf_id; - struct had_pvt_data *had_stream; - unsigned long flag_irqs; - - pr_debug("Enter:%s\n", __func__); - - had_stream = intelhaddata->private_data; - buf_id = intelhaddata->curr_buf; - - 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_BUFFER_DONE; - retval = had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); - retval = had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); - intelhaddata->ops->enable_audio( - intelhaddata->stream_info.had_substream, 0); - } - - intelhaddata->drv_status = HAD_DRV_DISCONNECTED; - pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", - __func__, __LINE__); - - /* Report to above ALSA layer */ - if (intelhaddata->stream_info.had_substream != NULL) { - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - pr_debug("%s: unlock -> sending pcm_stop -> lock\n", __func__); - snd_pcm_stop(intelhaddata->stream_info.had_substream, - SNDRV_PCM_STATE_DISCONNECTED); - spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); - } - - had_stream->stream_type = HAD_INIT; - spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - kfree(intelhaddata->chmap->chmap); - intelhaddata->chmap->chmap = NULL; - intelhaddata->audio_reg_base = NULL; - pr_debug("%s: unlocked -> returned\n", __func__); - - return retval; -} - -/** - * 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 snd_pcm_substream *substream; - struct had_pvt_data *had_stream; - unsigned long flag_irqs; - - buf_id = intelhaddata->curr_buf; - 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 separately to avoid real contention - * of data(struct intelhaddata) between IRQ/SOFT_IRQ/PROCESS context. - */ - substream = intelhaddata->stream_info.had_substream; - switch (event_type) { - case HAD_EVENT_AUDIO_BUFFER_DONE: - retval = had_process_buffer_done(intelhaddata); - break; - - case HAD_EVENT_AUDIO_BUFFER_UNDERRUN: - retval = had_process_buffer_underrun(intelhaddata); - break; - - case HAD_EVENT_HOT_PLUG: - retval = had_process_hot_plug(intelhaddata); - break; - - case HAD_EVENT_HOT_UNPLUG: - retval = had_process_hot_unplug(intelhaddata); - break; - - case HAD_EVENT_MODE_CHANGING: - pr_debug(" called _event_handler with _MODE_CHANGE event\n"); - /* 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_type == HAD_RUNNING_STREAM) - && substream) - retval = hdmi_audio_mode_change(substream); - break; - - default: - pr_debug("error un-handled event !!\n"); - retval = -EINVAL; - break; - - } - return retval; -} diff --git a/sound/x86/intel_hdmi_lpe_audio.c b/sound/x86/intel_hdmi_lpe_audio.c deleted file mode 100644 index 3cb0f642575c..000000000000 --- a/sound/x86/intel_hdmi_lpe_audio.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * intel_hdmi_lpe_audio.c - Intel HDMI LPE audio driver for Atom platforms - * - * Copyright (C) 2016 Intel Corp - * Authors: - * Jerome Anand <jerome.anand@intel.com> - * Aravind Siddappaji <aravindx.siddappaji@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include <linux/platform_device.h> -#include <linux/irqreturn.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> -#include <sound/control.h> -#include <sound/initval.h> -#include <drm/intel_lpe_audio.h> -#include "intel_hdmi_lpe_audio.h" -#include "intel_hdmi_audio.h" - -/* globals*/ -static struct platform_device *hlpe_pdev; -static int hlpe_state; -static union otm_hdmi_eld_t hlpe_eld; - -struct hdmi_lpe_audio_ctx { - int irq; - void __iomem *mmio_start; - had_event_call_back had_event_callbacks; - struct snd_intel_had_interface *had_interface; - void *had_pvt_data; - int tmds_clock_speed; - bool dp_output; - int link_rate; - unsigned int had_config_offset; - int hdmi_audio_interrupt_mask; - struct work_struct hdmi_audio_wq; -}; - -static void hdmi_set_eld(void *eld) -{ - int size; - - BUILD_BUG_ON(sizeof(hlpe_eld) > HDMI_MAX_ELD_BYTES); - - size = sizeof(hlpe_eld); - memcpy((void *)&hlpe_eld, eld, size); -} - -static int hdmi_get_eld(void *eld) -{ - u8 *eld_data = (u8 *)&hlpe_eld; - - memcpy(eld, (void *)&hlpe_eld, sizeof(hlpe_eld)); - - print_hex_dump_bytes("eld: ", DUMP_PREFIX_NONE, eld_data, - sizeof(hlpe_eld)); - return 0; -} - - -static struct hdmi_lpe_audio_ctx *get_hdmi_context(void) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - return ctx; -} - -/* - * return whether HDMI audio device is busy. - */ -bool mid_hdmi_audio_is_busy(void *ddev) -{ - struct hdmi_lpe_audio_ctx *ctx; - int hdmi_audio_busy = 0; - struct hdmi_audio_event hdmi_audio_event; - - dev_dbg(&hlpe_pdev->dev, "%s: Enter", __func__); - - ctx = platform_get_drvdata(hlpe_pdev); - - if (hlpe_state == hdmi_connector_status_disconnected) { - /* HDMI is not connected, assuming audio device is idle. */ - return false; - } - - if (ctx->had_interface) { - hdmi_audio_event.type = HAD_EVENT_QUERY_IS_AUDIO_BUSY; - hdmi_audio_busy = ctx->had_interface->query( - ctx->had_pvt_data, - hdmi_audio_event); - return hdmi_audio_busy != 0; - } - return false; -} - -/* - * return true if HDMI audio device is suspended/ disconnected - */ -bool mid_hdmi_audio_suspend(void *ddev) -{ - struct hdmi_lpe_audio_ctx *ctx; - struct hdmi_audio_event hdmi_audio_event; - int ret = 0; - - ctx = platform_get_drvdata(hlpe_pdev); - - if (hlpe_state == hdmi_connector_status_disconnected) { - /* HDMI is not connected, assuming audio device - * is suspended already. - */ - return true; - } - - dev_dbg(&hlpe_pdev->dev, "%s: hlpe_state %d", __func__, - hlpe_state); - - if (ctx->had_interface) { - hdmi_audio_event.type = 0; - ret = ctx->had_interface->suspend(ctx->had_pvt_data, - hdmi_audio_event); - return (ret == 0) ? true : false; - } - return true; -} - -void mid_hdmi_audio_resume(void *ddev) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - - if (hlpe_state == hdmi_connector_status_disconnected) { - /* HDMI is not connected, there is no need - * to resume audio device. - */ - return; - } - - dev_dbg(&hlpe_pdev->dev, "%s: hlpe_state %d", __func__, hlpe_state); - - if (ctx->had_interface) - ctx->had_interface->resume(ctx->had_pvt_data); -} - -void mid_hdmi_audio_signal_event(enum had_event_type event) -{ - struct hdmi_lpe_audio_ctx *ctx; - - dev_dbg(&hlpe_pdev->dev, "%s: Enter\n", __func__); - - ctx = platform_get_drvdata(hlpe_pdev); - - /* The handler is protected in the respective - * event handlers to avoid races - */ - if (ctx->had_event_callbacks) - (*ctx->had_event_callbacks)(event, - ctx->had_pvt_data); -} - -/** - * used to write into display controller HDMI audio registers. - */ -static int hdmi_audio_write(u32 reg, u32 val) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - - dev_dbg(&hlpe_pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, reg, val); - - if (ctx->dp_output) { - if ((reg == AUDIO_HDMI_CONFIG_A) || - (reg == AUDIO_HDMI_CONFIG_B) || - (reg == AUDIO_HDMI_CONFIG_C)) { - if (val & AUD_CONFIG_VALID_BIT) - val = val | AUD_CONFIG_DP_MODE | - AUD_CONFIG_BLOCK_BIT; - } - } - iowrite32(val, (ctx->mmio_start+reg)); - - return 0; -} - -/** - * used to get the register value read from - * display controller HDMI audio registers. - */ -static int hdmi_audio_read(u32 reg, u32 *val) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - *val = ioread32(ctx->mmio_start+reg); - dev_dbg(&hlpe_pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, reg, *val); - return 0; -} - -/** - * used to update the masked bits in display controller HDMI - * audio registers. - */ -static int hdmi_audio_rmw(u32 reg, u32 val, u32 mask) -{ - struct hdmi_lpe_audio_ctx *ctx; - u32 val_tmp = 0; - - ctx = platform_get_drvdata(hlpe_pdev); - - val_tmp = (val & mask) | - ((ioread32(ctx->mmio_start + reg)) & ~mask); - - if (ctx->dp_output) { - if ((reg == AUDIO_HDMI_CONFIG_A) || - (reg == AUDIO_HDMI_CONFIG_B) || - (reg == AUDIO_HDMI_CONFIG_C)) { - if (val_tmp & AUD_CONFIG_VALID_BIT) - val_tmp = val_tmp | AUD_CONFIG_DP_MODE | - AUD_CONFIG_BLOCK_BIT; - } - } - - iowrite32(val_tmp, (ctx->mmio_start+reg)); - dev_dbg(&hlpe_pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, - reg, val_tmp); - - return 0; -} - -/** - * used to return the HDMI audio capabilities. - * e.g. resolution, frame rate. - */ -static int hdmi_audio_get_caps(enum had_caps_list get_element, - void *capabilities) -{ - struct hdmi_lpe_audio_ctx *ctx; - int ret = 0; - - ctx = get_hdmi_context(); - - dev_dbg(&hlpe_pdev->dev, "%s: Enter\n", __func__); - - switch (get_element) { - case HAD_GET_ELD: - ret = hdmi_get_eld(capabilities); - break; - case HAD_GET_DISPLAY_RATE: - /* ToDo: Verify if sampling freq logic is correct */ - *(u32 *)capabilities = ctx->tmds_clock_speed; - dev_dbg(&hlpe_pdev->dev, "%s: tmds_clock_speed = 0x%x\n", - __func__, ctx->tmds_clock_speed); - break; - case HAD_GET_LINK_RATE: - /* ToDo: Verify if sampling freq logic is correct */ - *(u32 *)capabilities = ctx->link_rate; - dev_dbg(&hlpe_pdev->dev, "%s: link rate = 0x%x\n", - __func__, ctx->link_rate); - break; - case HAD_GET_DP_OUTPUT: - *(u32 *)capabilities = ctx->dp_output; - dev_dbg(&hlpe_pdev->dev, "%s: dp_output = %d\n", - __func__, ctx->dp_output); - break; - default: - break; - } - - return ret; -} - -/** - * used to get the current hdmi base address - */ -int hdmi_audio_get_register_base(u32 **reg_base, - u32 *config_offset) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - *reg_base = (u32 *)(ctx->mmio_start); - *config_offset = ctx->had_config_offset; - dev_dbg(&hlpe_pdev->dev, "%s: reg_base = 0x%p, cfg_off = 0x%x\n", - __func__, *reg_base, *config_offset); - return 0; -} - -/** - * used to set the HDMI audio capabilities. - * e.g. Audio INT. - */ -int hdmi_audio_set_caps(enum had_caps_list set_element, - void *capabilties) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - - dev_dbg(&hlpe_pdev->dev, "%s: cap_id = 0x%x\n", __func__, set_element); - - switch (set_element) { - case HAD_SET_ENABLE_AUDIO_INT: - { - u32 status_reg; - - hdmi_audio_read(AUD_HDMI_STATUS_v2 + - ctx->had_config_offset, &status_reg); - status_reg |= - HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; - hdmi_audio_write(AUD_HDMI_STATUS_v2 + - ctx->had_config_offset, status_reg); - hdmi_audio_read(AUD_HDMI_STATUS_v2 + - ctx->had_config_offset, &status_reg); - - } - break; - default: - break; - } - - return 0; -} - -static struct hdmi_audio_registers_ops hdmi_audio_reg_ops = { - .hdmi_audio_get_register_base = hdmi_audio_get_register_base, - .hdmi_audio_read_register = hdmi_audio_read, - .hdmi_audio_write_register = hdmi_audio_write, - .hdmi_audio_read_modify = hdmi_audio_rmw, -}; - -static struct hdmi_audio_query_set_ops hdmi_audio_get_set_ops = { - .hdmi_audio_get_caps = hdmi_audio_get_caps, - .hdmi_audio_set_caps = hdmi_audio_set_caps, -}; - -int mid_hdmi_audio_setup( - had_event_call_back audio_callbacks, - struct hdmi_audio_registers_ops *reg_ops, - struct hdmi_audio_query_set_ops *query_ops) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - - dev_dbg(&hlpe_pdev->dev, "%s: called\n", __func__); - - reg_ops->hdmi_audio_get_register_base = - (hdmi_audio_reg_ops.hdmi_audio_get_register_base); - reg_ops->hdmi_audio_read_register = - (hdmi_audio_reg_ops.hdmi_audio_read_register); - reg_ops->hdmi_audio_write_register = - (hdmi_audio_reg_ops.hdmi_audio_write_register); - reg_ops->hdmi_audio_read_modify = - (hdmi_audio_reg_ops.hdmi_audio_read_modify); - query_ops->hdmi_audio_get_caps = - hdmi_audio_get_set_ops.hdmi_audio_get_caps; - query_ops->hdmi_audio_set_caps = - hdmi_audio_get_set_ops.hdmi_audio_set_caps; - - ctx->had_event_callbacks = audio_callbacks; - - return 0; -} - -static void _had_wq(struct work_struct *work) -{ - mid_hdmi_audio_signal_event(HAD_EVENT_HOT_PLUG); -} - -int mid_hdmi_audio_register(struct snd_intel_had_interface *driver, - void *had_data) -{ - struct hdmi_lpe_audio_ctx *ctx; - - ctx = platform_get_drvdata(hlpe_pdev); - - dev_dbg(&hlpe_pdev->dev, "%s: called\n", __func__); - - ctx->had_pvt_data = had_data; - ctx->had_interface = driver; - - /* The Audio driver is loading now and we need to notify - * it if there is an HDMI device attached - */ - INIT_WORK(&ctx->hdmi_audio_wq, _had_wq); - dev_dbg(&hlpe_pdev->dev, "%s: Scheduling HDMI audio work queue\n", - __func__); - schedule_work(&ctx->hdmi_audio_wq); - - return 0; -} - -static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) -{ - u32 audio_stat, audio_reg; - - struct hdmi_lpe_audio_ctx *ctx; - - dev_dbg(&hlpe_pdev->dev, "%s: Enter\n", __func__); - - ctx = platform_get_drvdata(hlpe_pdev); - - audio_reg = ctx->had_config_offset + AUD_HDMI_STATUS_v2; - hdmi_audio_read(audio_reg, &audio_stat); - - if (audio_stat & HDMI_AUDIO_UNDERRUN) { - hdmi_audio_write(audio_reg, HDMI_AUDIO_UNDERRUN); - mid_hdmi_audio_signal_event( - HAD_EVENT_AUDIO_BUFFER_UNDERRUN); - } - - if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { - hdmi_audio_write(audio_reg, HDMI_AUDIO_BUFFER_DONE); - mid_hdmi_audio_signal_event( - HAD_EVENT_AUDIO_BUFFER_DONE); - } - - return IRQ_HANDLED; -} - -static void notify_audio_lpe(struct platform_device *pdev) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - struct intel_hdmi_lpe_audio_pdata *pdata = pdev->dev.platform_data; - - if (pdata->hdmi_connected != true) { - - dev_dbg(&pdev->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n", - __func__); - - if (hlpe_state == hdmi_connector_status_connected) { - - hlpe_state = - hdmi_connector_status_disconnected; - - mid_hdmi_audio_signal_event( - HAD_EVENT_HOT_UNPLUG); - } else - dev_dbg(&pdev->dev, "%s: Already Unplugged!\n", - __func__); - - } else { - struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld; - - switch (eld->pipe_id) { - case 0: - ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; - break; - case 1: - ctx->had_config_offset = AUDIO_HDMI_CONFIG_B; - break; - case 2: - ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; - break; - default: - dev_dbg(&pdev->dev, "Invalid pipe %d\n", - eld->pipe_id); - break; - } - - hdmi_set_eld(eld->eld_data); - - mid_hdmi_audio_signal_event(HAD_EVENT_HOT_PLUG); - - hlpe_state = hdmi_connector_status_connected; - - dev_dbg(&pdev->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", - __func__, eld->port_id, pdata->tmds_clock_speed); - - if (pdata->tmds_clock_speed) { - ctx->tmds_clock_speed = pdata->tmds_clock_speed; - ctx->dp_output = pdata->dp_output; - ctx->link_rate = pdata->link_rate; - mid_hdmi_audio_signal_event(HAD_EVENT_MODE_CHANGING); - } - } -} - -/** - * hdmi_lpe_audio_probe - start bridge with i915 - * - * This function is called when the i915 driver creates the - * hdmi-lpe-audio platform device. Card creation is deferred until a - * hot plug event is received - */ -static int hdmi_lpe_audio_probe(struct platform_device *pdev) -{ - struct hdmi_lpe_audio_ctx *ctx; - struct intel_hdmi_lpe_audio_pdata *pdata; - int irq; - struct resource *res_mmio; - void __iomem *mmio_start; - int ret = 0; - unsigned long flag_irq; - static const struct pci_device_id cherryview_ids[] = { - {PCI_DEVICE(0x8086, 0x22b0)}, - {PCI_DEVICE(0x8086, 0x22b1)}, - {PCI_DEVICE(0x8086, 0x22b2)}, - {PCI_DEVICE(0x8086, 0x22b3)}, - {} - }; - - dev_dbg(&hlpe_pdev->dev, "Enter %s\n", __func__); - - /*TBD:remove globals*/ - hlpe_pdev = pdev; - hlpe_state = hdmi_connector_status_disconnected; - - /* get resources */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&hlpe_pdev->dev, "Could not get irq resource\n"); - return -ENODEV; - } - - res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res_mmio) { - dev_err(&hlpe_pdev->dev, "Could not get IO_MEM resources\n"); - return -ENXIO; - } - - dev_dbg(&hlpe_pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n", - __func__, (unsigned int)res_mmio->start, - (unsigned int)res_mmio->end); - - mmio_start = ioremap_nocache(res_mmio->start, - (size_t)(resource_size(res_mmio))); - if (!mmio_start) { - dev_err(&hlpe_pdev->dev, "Could not get ioremap\n"); - return -EACCES; - } - - /* setup interrupt handler */ - ret = request_irq(irq, display_pipe_interrupt_handler, - 0, - pdev->name, - NULL); - if (ret < 0) { - dev_err(&hlpe_pdev->dev, "request_irq failed\n"); - iounmap(mmio_start); - return -ENODEV; - } - - /* alloc and save context */ - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (ctx == NULL) { - free_irq(irq, NULL); - iounmap(mmio_start); - return -ENOMEM; - } - - ctx->irq = irq; - dev_dbg(&hlpe_pdev->dev, "hdmi lpe audio: irq num = %d\n", irq); - ctx->mmio_start = mmio_start; - ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; - - if (pci_dev_present(cherryview_ids)) - dev_dbg(&hlpe_pdev->dev, "%s: Cherrytrail LPE - Detected\n", - __func__); - else - dev_dbg(&hlpe_pdev->dev, "%s: Baytrail LPE - Assume\n", - __func__); - - /* assume pipe A as default */ - ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; - - pdata = pdev->dev.platform_data; - - if (pdata == NULL) { - dev_err(&hlpe_pdev->dev, "%s: quit: pdata not allocated by i915!!\n", __func__); - kfree(ctx); - free_irq(irq, NULL); - iounmap(mmio_start); - return -ENOMEM; - } - - platform_set_drvdata(pdev, ctx); - - ret = hdmi_audio_probe((void *)pdev); - dev_dbg(&hlpe_pdev->dev, "hdmi lpe audio: setting pin eld notify callback\n"); - - spin_lock_irqsave(&pdata->lpe_audio_slock, flag_irq); - pdata->notify_audio_lpe = notify_audio_lpe; - if (pdata->notify_pending) { - - dev_dbg(&hlpe_pdev->dev, "%s: handle pending notification\n", __func__); - notify_audio_lpe(pdev); - pdata->notify_pending = false; - } - spin_unlock_irqrestore(&pdata->lpe_audio_slock, flag_irq); - - return ret; -} - -/** - * hdmi_lpe_audio_remove - stop bridge with i915 - * - * This function is called when the platform device is destroyed. The sound - * card should have been removed on hot plug event. - */ -static int hdmi_lpe_audio_remove(struct platform_device *pdev) -{ - struct hdmi_lpe_audio_ctx *ctx; - - dev_dbg(&hlpe_pdev->dev, "Enter %s\n", __func__); - - hdmi_audio_remove(pdev); - - /* get context, release resources */ - ctx = platform_get_drvdata(pdev); - iounmap(ctx->mmio_start); - free_irq(ctx->irq, NULL); - kfree(ctx); - return 0; -} - -static int hdmi_lpe_audio_suspend(struct platform_device *pt_dev, - pm_message_t state) -{ - dev_dbg(&hlpe_pdev->dev, "Enter %s\n", __func__); - mid_hdmi_audio_suspend(NULL); - return 0; -} - -static int hdmi_lpe_audio_resume(struct platform_device *pt_dev) -{ - dev_dbg(&hlpe_pdev->dev, "Enter %s\n", __func__); - mid_hdmi_audio_resume(NULL); - return 0; -} - -static struct platform_driver hdmi_lpe_audio_driver = { - .driver = { - .name = "hdmi-lpe-audio", - }, - .probe = hdmi_lpe_audio_probe, - .remove = hdmi_lpe_audio_remove, - .suspend = hdmi_lpe_audio_suspend, - .resume = hdmi_lpe_audio_resume -}; - -module_platform_driver(hdmi_lpe_audio_driver); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:hdmi_lpe_audio"); diff --git a/sound/x86/intel_hdmi_lpe_audio.h b/sound/x86/intel_hdmi_lpe_audio.h index 3aed89af5b45..be9783910a3a 100644 --- a/sound/x86/intel_hdmi_lpe_audio.h +++ b/sound/x86/intel_hdmi_lpe_audio.h @@ -23,19 +23,6 @@ #ifndef __INTEL_HDMI_LPE_AUDIO_H #define __INTEL_HDMI_LPE_AUDIO_H -#include <linux/types.h> -#include <sound/initval.h> -#include <linux/version.h> -#include <linux/pm_runtime.h> -#include <sound/asoundef.h> -#include <sound/control.h> -#include <sound/pcm.h> - -#define AUD_CONFIG_VALID_BIT (1<<9) -#define AUD_CONFIG_DP_MODE (1<<15) -#define AUD_CONFIG_BLOCK_BIT (1<<7) - -#define HMDI_LPE_AUDIO_DRIVER_NAME "intel-hdmi-lpe-audio" #define HAD_MAX_DEVICES 1 #define HAD_MIN_CHANNEL 2 #define HAD_MAX_CHANNEL 8 @@ -95,164 +82,6 @@ /* Naud Value */ #define DP_NAUD_VAL 32768 -/* _AUD_CONFIG register MASK */ -#define AUD_CONFIG_MASK_UNDERRUN 0xC0000000 -#define AUD_CONFIG_MASK_SRDBG 0x00000002 -#define AUD_CONFIG_MASK_FUNCRST 0x00000001 - -#define MAX_CNT 0xFF -#define HAD_SUSPEND_DELAY 1000 - -#define OTM_HDMI_ELD_SIZE 128 - -union otm_hdmi_eld_t { - unsigned char eld_data[OTM_HDMI_ELD_SIZE]; - struct { - /* Byte[0] = ELD Version Number */ - union { - unsigned char byte0; - struct { - unsigned char reserved:3; /* Reserf */ - unsigned char eld_ver:5; /* ELD Version Number */ - /* 00000b - reserved - * 00001b - first rev, obsoleted - * 00010b - version 2, supporting CEA version - * 861D or below - * 00011b:11111b - reserved - * for future - */ - }; - }; - - /* Byte[1] = Vendor Version Field */ - union { - unsigned char vendor_version; - struct { - unsigned char reserved1:3; - unsigned char veld_ver:5; /* Version number of the ELD - * extension. This value is - * provisioned and unique to - * each vendor. - */ - }; - }; - - /* Byte[2] = Baseline Length field */ - unsigned char baseline_eld_length; /* Length of the Baseline structure - * divided by Four. - */ - - /* Byte [3] = Reserved for future use */ - unsigned char byte3; - - /* Starting of the BaseLine EELD structure - * Byte[4] = Monitor Name Length - */ - union { - unsigned char byte4; - struct { - unsigned char mnl:5; - unsigned char cea_edid_rev_id:3; - }; - }; - - /* Byte[5] = Capabilities */ - union { - unsigned char capabilities; - struct { - unsigned char hdcp:1; /* HDCP support */ - unsigned char ai_support:1; /* AI support */ - unsigned char connection_type:2; /* Connection type - * 00 - HDMI - * 01 - DP - * 10 -11 Reserved - * for future - * connection types - */ - unsigned char sadc:4; /* Indicates number of 3 bytes - * Short Audio Descriptors. - */ - }; - }; - - /* Byte[6] = Audio Synch Delay */ - unsigned char audio_synch_delay; /* Amount of time reported by the - * sink that the video trails audio - * in milliseconds. - */ - - /* Byte[7] = Speaker Allocation Block */ - union { - unsigned char speaker_allocation_block; - struct { - unsigned char flr:1; /*Front Left and Right channels*/ - unsigned char lfe:1; /*Low Frequency Effect channel*/ - unsigned char fc:1; /*Center transmission channel*/ - unsigned char rlr:1; /*Rear Left and Right channels*/ - unsigned char rc:1; /*Rear Center channel*/ - unsigned char flrc:1; /*Front left and Right of Center - *transmission channels - */ - unsigned char rlrc:1; /*Rear left and Right of Center - *transmission channels - */ - unsigned char reserved3:1; /* Reserved */ - }; - }; - - /* Byte[8 - 15] - 8 Byte port identification value */ - unsigned char port_id_value[8]; - - /* Byte[16 - 17] - 2 Byte Manufacturer ID */ - unsigned char manufacturer_id[2]; - - /* Byte[18 - 19] - 2 Byte Product ID */ - unsigned char product_id[2]; - - /* Byte [20-83] - 64 Bytes of BaseLine Data */ - unsigned char mn_sand_sads[64]; /* This will include - * - ASCII string of Monitor name - * - List of 3 byte SADs - * - Zero padding - */ - - /* Vendor ELD Block should continue here! - * No Vendor ELD block defined as of now. - */ - } __packed; -}; - -/** - * enum had_status - Audio stream states - * - * @STREAM_INIT: Stream initialized - * @STREAM_RUNNING: Stream running - * @STREAM_PAUSED: Stream paused - * @STREAM_DROPPED: Stream dropped - */ -enum had_stream_status { - STREAM_INIT = 0, - STREAM_RUNNING = 1, - STREAM_PAUSED = 2, - STREAM_DROPPED = 3 -}; - -/** - * enum had_status_stream - HAD stream states - */ -enum had_status_stream { - HAD_INIT = 0, - HAD_RUNNING_STREAM, -}; - -enum had_drv_status { - HAD_DRV_CONNECTED, - HAD_DRV_RUNNING, - HAD_DRV_DISCONNECTED, - HAD_DRV_SUSPENDED, - HAD_DRV_ERR, -}; - /* enum intel_had_aud_buf_type - HDMI controller ring buffer types */ enum intel_had_aud_buf_type { HAD_BUF_TYPE_A = 0, @@ -261,22 +90,15 @@ enum intel_had_aud_buf_type { HAD_BUF_TYPE_D = 3, }; -enum num_aud_ch { - CH_STEREO = 0, - CH_THREE_FOUR = 1, - CH_FIVE_SIX = 2, - CH_SEVEN_EIGHT = 3 -}; - /* HDMI Controller register offsets - audio domain common */ /* Base address for below regs = 0x65000 */ enum hdmi_ctrl_reg_offset_common { - AUDIO_HDMI_CONFIG_A = 0x000, + AUDIO_HDMI_CONFIG_A = 0x000, AUDIO_HDMI_CONFIG_B = 0x800, AUDIO_HDMI_CONFIG_C = 0x900, }; /* HDMI controller register offsets */ -enum hdmi_ctrl_reg_offset_v1 { +enum hdmi_ctrl_reg_offset { AUD_CONFIG = 0x0, AUD_CH_STATUS_0 = 0x08, AUD_CH_STATUS_1 = 0x0C, @@ -294,18 +116,8 @@ enum hdmi_ctrl_reg_offset_v1 { AUD_BUF_D_ADDR = 0x58, AUD_BUF_D_LENGTH = 0x5c, AUD_CNTL_ST = 0x60, - AUD_HDMI_STATUS = 0x68, - AUD_HDMIW_INFOFR = 0x114, -}; - -/* - * Delta changes in HDMI controller register offsets - * compare to v1 version - */ - -enum hdmi_ctrl_reg_offset_v2 { - AUD_HDMI_STATUS_v2 = 0x64, - AUD_HDMIW_INFOFR_v2 = 0x68, + AUD_HDMI_STATUS = 0x64, /* v2 */ + AUD_HDMIW_INFOFR = 0x68, /* v2 */ }; /* @@ -350,31 +162,12 @@ struct channel_map_table { int spk_mask; /* speaker position bit mask */ }; -/** - * union aud_cfg - Audio configuration - * - * @cfg_regx: individual register bits - * @cfg_regval: full register value - * - */ +/* Audio configuration */ union aud_cfg { struct { u32 aud_en:1; u32 layout:1; u32 fmt:2; - u32 num_ch:2; - u32 rsvd0:1; - u32 set:1; - u32 flat:1; - u32 val_bit:1; - u32 user_bit:1; - u32 underrun:1; - u32 rsvd1:20; - } cfg_regx; - struct { - u32 aud_en:1; - u32 layout:1; - u32 fmt:2; u32 num_ch:3; u32 set:1; u32 flat:1; @@ -386,17 +179,15 @@ union aud_cfg { u32 bogus_sample:1; u32 dp_modei:1; u32 rsvd:16; - } cfg_regx_v2; - u32 cfg_regval; + } regx; + u32 regval; }; -/** - * union aud_ch_status_0 - Audio Channel Status 0 Attributes - * - * @status_0_regx:individual register bits - * @status_0_regval:full register value - * - */ +#define AUD_CONFIG_BLOCK_BIT (1 << 7) +#define AUD_CONFIG_VALID_BIT (1 << 9) +#define AUD_CONFIG_DP_MODE (1 << 15) + +/* Audio Channel Status 0 Attributes */ union aud_ch_status_0 { struct { u32 ch_status:1; @@ -410,99 +201,53 @@ union aud_ch_status_0 { u32 samp_freq:4; u32 clk_acc:2; u32 rsvd:2; - } status_0_regx; - u32 status_0_regval; + } regx; + u32 regval; }; -/** - * union aud_ch_status_1 - Audio Channel Status 1 Attributes - * - * @status_1_regx: individual register bits - * @status_1_regval: full register value - * - */ +/* Audio Channel Status 1 Attributes */ union aud_ch_status_1 { struct { u32 max_wrd_len:1; u32 wrd_len:3; u32 rsvd:28; - } status_1_regx; - u32 status_1_regval; + } regx; + u32 regval; }; -/** - * union aud_hdmi_cts - CTS register - * - * @cts_regx: individual register bits - * @cts_regval: full register value - * - */ +/* CTS register */ union aud_hdmi_cts { struct { - u32 cts_val:20; - u32 en_cts_prog:1; - u32 rsvd:11; - } cts_regx; - struct { u32 cts_val:24; u32 en_cts_prog:1; u32 rsvd:7; - } cts_regx_v2; - u32 cts_regval; + } regx; + u32 regval; }; -/** - * union aud_hdmi_n_enable - N register - * - * @n_regx: individual register bits - * @n_regval: full register value - * - */ +/* N register */ union aud_hdmi_n_enable { struct { - u32 n_val:20; - u32 en_n_prog:1; - u32 rsvd:11; - } n_regx; - struct { u32 n_val:24; u32 en_n_prog:1; u32 rsvd:7; - } n_regx_v2; - u32 n_regval; + } regx; + u32 regval; }; -/** - * union aud_buf_config - Audio Buffer configurations - * - * @buf_cfg_regx: individual register bits - * @buf_cfgval: full register value - * - */ +/* Audio Buffer configurations */ union aud_buf_config { struct { - u32 fifo_width:8; - u32 rsvd0:8; - u32 aud_delay:8; - u32 rsvd1:8; - } buf_cfg_regx; - struct { u32 audio_fifo_watermark:8; u32 dma_fifo_watermark:3; u32 rsvd0:5; u32 aud_delay:8; u32 rsvd1:8; - } buf_cfg_regx_v2; - u32 buf_cfgval; + } regx; + u32 regval; }; -/** - * union aud_buf_ch_swap - Audio Sample Swapping offset - * - * @buf_ch_swap_regx: individual register bits - * @buf_ch_swap_val: full register value - * - */ +/* Audio Sample Swapping offset */ union aud_buf_ch_swap { struct { u32 first_0:3; @@ -514,49 +259,31 @@ union aud_buf_ch_swap { u32 first_3:3; u32 second_3:3; u32 rsvd:8; - } buf_ch_swap_regx; - u32 buf_ch_swap_val; + } regx; + u32 regval; }; -/** - * union aud_buf_addr - Address for Audio Buffer - * - * @buf_addr_regx: individual register bits - * @buf_addr_val: full register value - * - */ +/* Address for Audio Buffer */ union aud_buf_addr { struct { u32 valid:1; u32 intr_en:1; u32 rsvd:4; u32 addr:26; - } buf_addr_regx; - u32 buf_addr_val; + } regx; + u32 regval; }; -/** - * union aud_buf_len - Length of Audio Buffer - * - * @buf_len_regx: individual register bits - * @buf_len_val: full register value - * - */ +/* Length of Audio Buffer */ union aud_buf_len { struct { u32 buf_len:20; u32 rsvd:12; - } buf_len_regx; - u32 buf_len_val; + } regx; + u32 regval; }; -/** - * union aud_ctrl_st - Audio Control State Register offset - * - * @ctrl_regx: individual register bits - * @ctrl_val: full register value - * - */ +/* Audio Control State Register offset */ union aud_ctrl_st { struct { u32 ram_addr:4; @@ -569,34 +296,22 @@ union aud_ctrl_st { u32 dip_idx:3; u32 dip_en_sta:4; u32 rsvd:7; - } ctrl_regx; - u32 ctrl_val; + } regx; + u32 regval; }; -/** - * union aud_info_frame1 - Audio HDMI Widget Data Island Packet offset - * - * @fr1_regx: individual register bits - * @fr1_val: full register value - * - */ +/* Audio HDMI Widget Data Island Packet offset */ union aud_info_frame1 { struct { u32 pkt_type:8; u32 ver_num:8; u32 len:5; u32 rsvd:11; - } fr1_regx; - u32 fr1_val; + } regx; + u32 regval; }; -/** - * union aud_info_frame2 - DIP frame 2 - * - * @fr2_regx: individual register bits - * @fr2_val: full register value - * - */ +/* DIP frame 2 */ union aud_info_frame2 { struct { u32 chksum:8; @@ -607,17 +322,11 @@ union aud_info_frame2 { u32 smpl_freq:3; u32 rsvd1:3; u32 format:8; - } fr2_regx; - u32 fr2_val; + } regx; + u32 regval; }; -/** - * union aud_info_frame3 - DIP frame 3 - * - * @fr3_regx: individual register bits - * @fr3_val: full register value - * - */ +/* DIP frame 3 */ union aud_info_frame3 { struct { u32 chnl_alloc:8; @@ -625,88 +334,17 @@ union aud_info_frame3 { u32 lsv:4; u32 dm_inh:1; u32 rsvd1:16; - } fr3_regx; - u32 fr3_val; -}; - -enum hdmi_connector_status { - hdmi_connector_status_connected = 1, - hdmi_connector_status_disconnected = 2, - hdmi_connector_status_unknown = 3, -}; - -#define HDMI_AUDIO_UNDERRUN (1UL<<31) -#define HDMI_AUDIO_BUFFER_DONE (1UL<<29) - - -#define PORT_ENABLE (1 << 31) -#define SDVO_AUDIO_ENABLE (1 << 6) - -enum had_caps_list { - HAD_GET_ELD = 1, - HAD_GET_DISPLAY_RATE, - HAD_GET_DP_OUTPUT, - HAD_GET_LINK_RATE, - HAD_SET_ENABLE_AUDIO, - HAD_SET_DISABLE_AUDIO, - HAD_SET_ENABLE_AUDIO_INT, - HAD_SET_DISABLE_AUDIO_INT, -}; - -enum had_event_type { - HAD_EVENT_HOT_PLUG = 1, - HAD_EVENT_HOT_UNPLUG, - HAD_EVENT_MODE_CHANGING, - HAD_EVENT_AUDIO_BUFFER_DONE, - HAD_EVENT_AUDIO_BUFFER_UNDERRUN, - HAD_EVENT_QUERY_IS_AUDIO_BUSY, - HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED, -}; - -/* - * HDMI Display Controller Audio Interface - * - */ -typedef int (*had_event_call_back) (enum had_event_type event_type, - void *ctxt_info); - -struct hdmi_audio_registers_ops { - int (*hdmi_audio_get_register_base)(u32 **reg_base, - u32 *config_offset); - int (*hdmi_audio_read_register)(u32 reg_addr, u32 *data); - int (*hdmi_audio_write_register)(u32 reg_addr, u32 data); - int (*hdmi_audio_read_modify)(u32 reg_addr, u32 data, - u32 mask); + } regx; + u32 regval; }; -struct hdmi_audio_query_set_ops { - int (*hdmi_audio_get_caps)(enum had_caps_list query_element, - void *capabilties); - int (*hdmi_audio_set_caps)(enum had_caps_list set_element, - void *capabilties); -}; +/* AUD_HDMI_STATUS bits */ +#define HDMI_AUDIO_UNDERRUN (1U << 31) +#define HDMI_AUDIO_BUFFER_DONE (1U << 29) -struct hdmi_audio_event { - int type; -}; - -struct snd_intel_had_interface { - const char *name; - int (*query)(void *had_data, struct hdmi_audio_event event); - int (*suspend)(void *had_data, struct hdmi_audio_event event); - int (*resume)(void *had_data); -}; - -bool mid_hdmi_audio_is_busy(void *dev); -bool mid_hdmi_audio_suspend(void *dev); -void mid_hdmi_audio_resume(void *dev); -void mid_hdmi_audio_signal_event(enum had_event_type event); -int mid_hdmi_audio_setup( - had_event_call_back audio_callbacks, - struct hdmi_audio_registers_ops *reg_ops, - struct hdmi_audio_query_set_ops *query_ops); -int mid_hdmi_audio_register( - struct snd_intel_had_interface *driver, - void *had_data); +/* AUD_HDMI_STATUS register mask */ +#define AUD_CONFIG_MASK_UNDERRUN 0xC0000000 +#define AUD_CONFIG_MASK_SRDBG 0x00000002 +#define AUD_CONFIG_MASK_FUNCRST 0x00000001 #endif |