diff options
-rw-r--r-- | sound/x86/Makefile | 3 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_audio.c | 476 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_audio.h | 37 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_audio_if.c | 128 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_lpe_audio.c | 462 | ||||
-rw-r--r-- | sound/x86/intel_hdmi_lpe_audio.h | 23 |
6 files changed, 358 insertions, 771 deletions
diff --git a/sound/x86/Makefile b/sound/x86/Makefile index 85ea22a2cf28..3c0bf63333e6 100644 --- a/sound/x86/Makefile +++ b/sound/x86/Makefile @@ -1,6 +1,5 @@ snd-hdmi-lpe-audio-objs += \ intel_hdmi_audio.o \ - intel_hdmi_audio_if.o \ - intel_hdmi_lpe_audio.o + intel_hdmi_audio_if.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 82f42a6c363c..1594f826cf31 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -27,6 +27,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/interrupt.h> #include <linux/acpi.h> #include <asm/cacheflush.h> #include <sound/pcm.h> @@ -35,6 +36,7 @@ #include <sound/initval.h> #include <sound/control.h> #include <sound/initval.h> +#include <drm/intel_lpe_audio.h> #include "intel_hdmi_audio.h" /*standard module options for ALSA. This module supports only one card*/ @@ -168,72 +170,75 @@ int had_get_hwstate(struct snd_intelhad *intelhaddata) return 0; } -int had_get_caps(struct snd_intelhad *intelhaddata, - enum had_caps_list query, void *caps) +static inline void +mid_hdmi_audio_read(struct snd_intelhad *ctx, u32 reg, u32 *val) { - struct platform_device *pdev = to_platform_device(intelhaddata->dev); - int retval; - - retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = mid_hdmi_audio_get_caps(pdev, query, caps); - - return retval; + *val = ioread32(ctx->mmio_start + ctx->had_config_offset + reg); } -int had_set_caps(struct snd_intelhad *intelhaddata, - enum had_caps_list set_element, void *caps) +static inline void +mid_hdmi_audio_write(struct snd_intelhad *ctx, u32 reg, u32 val) { - struct platform_device *pdev = to_platform_device(intelhaddata->dev); - int retval; - - retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = mid_hdmi_audio_set_caps(pdev, set_element, caps); - - return retval; + iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg); } int had_read_register(struct snd_intelhad *intelhaddata, u32 offset, u32 *data) { - struct platform_device *pdev = to_platform_device(intelhaddata->dev); int retval; retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = mid_hdmi_audio_read(pdev, offset, data); + if (retval) + return retval; - return retval; + mid_hdmi_audio_read(intelhaddata, offset, data); + return 0; +} + +static void fixup_dp_config(struct snd_intelhad *intelhaddata, + u32 offset, u32 *data) +{ + if (intelhaddata->dp_output) { + if (offset == AUD_CONFIG && (*data & AUD_CONFIG_VALID_BIT)) + *data |= AUD_CONFIG_DP_MODE | AUD_CONFIG_BLOCK_BIT; + } } int had_write_register(struct snd_intelhad *intelhaddata, u32 offset, u32 data) { - struct platform_device *pdev = to_platform_device(intelhaddata->dev); int retval; retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = mid_hdmi_audio_write(pdev, offset, data); + if (retval) + return retval; - return retval; + fixup_dp_config(intelhaddata, offset, &data); + mid_hdmi_audio_write(intelhaddata, offset, data); + return 0; } int had_read_modify(struct snd_intelhad *intelhaddata, u32 offset, u32 data, u32 mask) { - struct platform_device *pdev = to_platform_device(intelhaddata->dev); + u32 val_tmp; int retval; retval = had_get_hwstate(intelhaddata); - if (!retval) - retval = mid_hdmi_audio_rmw(pdev, offset, data, mask); + if (retval) + return retval; - return retval; + mid_hdmi_audio_read(intelhaddata, offset, &val_tmp); + val_tmp &= ~mask; + val_tmp |= (data & mask); + + 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. + +/* + * 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. * 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 @@ -249,10 +254,10 @@ int had_read_modify(struct snd_intelhad *intelhaddata, u32 offset, * @mask : mask * */ -static int had_read_modify_aud_config_v2(struct snd_pcm_substream *substream, +static int had_read_modify_aud_config_v2(struct snd_intelhad *intelhaddata, u32 data, u32 mask) { - struct snd_intelhad *intelhaddata = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *substream; union aud_cfg cfg_val = {.cfg_regval = 0}; u8 channels; @@ -260,7 +265,8 @@ static int had_read_modify_aud_config_v2(struct snd_pcm_substream *substream, * If substream is NULL, there is no active stream. * In this case just set channels to 2 */ - if (substream) + substream = intelhaddata->stream_info.had_substream; + if (substream && substream->runtime) channels = substream->runtime->channels; else channels = 2; @@ -274,9 +280,23 @@ static int had_read_modify_aud_config_v2(struct snd_pcm_substream *substream, return had_read_modify(intelhaddata, AUD_CONFIG, data, mask); } -void snd_intelhad_enable_audio(struct snd_pcm_substream *substream, u8 enable) +void snd_intelhad_enable_audio_int(struct snd_intelhad *ctx, bool enable) +{ + u32 status_reg; + + if (enable) { + mid_hdmi_audio_read(ctx, AUD_HDMI_STATUS_v2, &status_reg); + status_reg |= HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; + mid_hdmi_audio_write(ctx, AUD_HDMI_STATUS_v2, status_reg); + mid_hdmi_audio_read(ctx, AUD_HDMI_STATUS_v2, &status_reg); + } +} + +void snd_intelhad_enable_audio(struct snd_intelhad *intelhaddata, + bool enable) { - had_read_modify_aud_config_v2(substream, enable, BIT(0)); + had_read_modify_aud_config_v2(intelhaddata, enable ? BIT(0) : 0, + BIT(0)); } static void snd_intelhad_reset_audio(struct snd_intelhad *intelhaddata, @@ -437,7 +457,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.speaker_allocation_block & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -482,11 +502,8 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) return; } - had_get_caps(intelhaddata, HAD_GET_ELD, &intelhaddata->eeld); - had_get_caps(intelhaddata, HAD_GET_DP_OUTPUT, &intelhaddata->dp_output); - - pr_debug("eeld.speaker_allocation_block = %x\n", - intelhaddata->eeld.speaker_allocation_block); + pr_debug("eld.speaker_allocation_block = %x\n", + intelhaddata->eld.speaker_allocation_block); /* WA: Fix the max channel supported to 8 */ @@ -497,14 +514,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.speaker_allocation_block & 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.speaker_allocation_block &= high_msb | 0xF; break; } @@ -512,7 +529,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.speaker_allocation_block & (1 << i)) spk_mask |= eld_speaker_allocation_bits[i]; } @@ -1176,7 +1193,7 @@ static int snd_intelhad_hw_free(struct snd_pcm_substream *substream) static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - int caps, retval = 0; + int retval = 0; unsigned long flag_irq; struct snd_intelhad *intelhaddata; struct had_stream_pvt *stream; @@ -1203,15 +1220,8 @@ static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, had_stream->stream_type = HAD_RUNNING_STREAM; /* 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(intelhaddata, HAD_SET_ENABLE_AUDIO_INT, - &caps); - retval = had_set_caps(intelhaddata, HAD_SET_ENABLE_AUDIO, NULL); - snd_intelhad_enable_audio(substream, 1); + snd_intelhad_enable_audio_int(intelhaddata, true); + snd_intelhad_enable_audio(intelhaddata, true); pr_debug("Processed _Start\n"); @@ -1228,18 +1238,13 @@ static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, had_stream->stream_type = HAD_INIT; spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq); /* 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(intelhaddata, HAD_SET_DISABLE_AUDIO_INT, &caps); - snd_intelhad_enable_audio(substream, 0); + snd_intelhad_enable_audio_int(intelhaddata, false); + snd_intelhad_enable_audio(intelhaddata, false); /* Reset buffer pointers */ snd_intelhad_reset_audio(intelhaddata, 1); snd_intelhad_reset_audio(intelhaddata, 0); stream->stream_status = STREAM_DROPPED; - had_set_caps(intelhaddata, HAD_SET_DISABLE_AUDIO, NULL); + snd_intelhad_enable_audio_int(intelhaddata, false); break; default: @@ -1297,15 +1302,7 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) /* Get N value in KHz */ - retval = had_get_caps(intelhaddata, HAD_GET_DISPLAY_RATE, - &disp_samp_freq); - if (retval) { - pr_err("querying display sampling freq failed %#x\n", retval); - goto prep_end; - } - - had_get_caps(intelhaddata, HAD_GET_ELD, &intelhaddata->eeld); - had_get_caps(intelhaddata, HAD_GET_DP_OUTPUT, &intelhaddata->dp_output); + disp_samp_freq = intelhaddata->tmds_clock_speed; retval = snd_intelhad_prog_n(substream->runtime->rate, &n_param, intelhaddata); @@ -1315,8 +1312,7 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) } if (intelhaddata->dp_output) - had_get_caps(intelhaddata, HAD_GET_LINK_RATE, &link_rate); - + link_rate = intelhaddata->link_rate; snd_intelhad_prog_cts(substream->runtime->rate, disp_samp_freq, link_rate, @@ -1425,25 +1421,22 @@ static int snd_intelhad_pcm_mmap(struct snd_pcm_substream *substream, vma->vm_end - vma->vm_start, vma->vm_page_prot); } -int hdmi_audio_mode_change(struct snd_pcm_substream *substream) +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 = intelhaddata->stream_info.had_substream; + if (!substream || !substream->runtime) + return 0; /* Disable Audio */ - snd_intelhad_enable_audio(substream, 0); + snd_intelhad_enable_audio(intelhaddata, false); /* Update CTS value */ - retval = had_get_caps(intelhaddata, 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 = snd_intelhad_prog_n(substream->runtime->rate, &n_param, intelhaddata); @@ -1453,14 +1446,14 @@ int hdmi_audio_mode_change(struct snd_pcm_substream *substream) } if (intelhaddata->dp_output) - had_get_caps(intelhaddata, HAD_GET_LINK_RATE, &link_rate); + link_rate = intelhaddata->link_rate; snd_intelhad_prog_cts(substream->runtime->rate, disp_samp_freq, link_rate, n_param, intelhaddata); /* Enable Audio */ - snd_intelhad_enable_audio(substream, 1); + snd_intelhad_enable_audio(intelhaddata, true); out: return retval; @@ -1555,129 +1548,290 @@ static struct snd_kcontrol_new had_control_iec958 = { .put = had_iec958_put }; +static void _had_wq(struct work_struct *work) +{ + struct snd_intelhad *ctx = + container_of(work, struct snd_intelhad, hdmi_audio_wq); + + had_process_hot_plug(ctx); +} + +static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) +{ + struct snd_intelhad *ctx = dev_id; + u32 audio_stat, audio_reg; + + audio_reg = AUD_HDMI_STATUS_v2; + 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; +} + +static void notify_audio_lpe(struct platform_device *pdev) +{ + struct snd_intelhad *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 (ctx->state == hdmi_connector_status_connected) { + + ctx->state = hdmi_connector_status_disconnected; + + had_process_hot_unplug(ctx); + } 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; + } + + memcpy(&ctx->eld, eld->eld_data, sizeof(ctx->eld)); + + had_process_hot_plug(ctx); + + ctx->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; + + /* Process mode change if stream is active */ + if (ctx->stream_data.stream_type == HAD_RUNNING_STREAM) + hdmi_audio_mode_change(ctx); + } + } +} + +/* release resources */ +static void hdmi_lpe_audio_free(struct snd_card *card) +{ + struct snd_intelhad *ctx = card->private_data; + + if (ctx->mmio_start) + iounmap(ctx->mmio_start); + if (ctx->irq >= 0) + free_irq(ctx->irq, ctx); +} + /* - * hdmi_audio_probe - to create sound card instance for HDMI audio playabck - * - * @devptr: platform device - * @had_ret: pointer to store the created snd_intelhad object + * hdmi_lpe_audio_probe - start bridge with i915 * - * This function is called when the platform device is probed. This function - * creates and registers the sound card with ALSA + * 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 */ -int hdmi_audio_probe(struct platform_device *devptr, - struct snd_intelhad **had_ret) +static int hdmi_lpe_audio_probe(struct platform_device *pdev) { - int retval; - struct snd_pcm *pcm; struct snd_card *card; - struct snd_intelhad *intelhaddata; + struct snd_intelhad *ctx; + struct snd_pcm *pcm; + struct intel_hdmi_lpe_audio_pdata *pdata; + int irq; + struct resource *res_mmio; + int ret; + unsigned long flags; + + dev_dbg(&pdev->dev, "Enter %s\n", __func__); + 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; + } - pr_debug("Enter %s\n", __func__); + /* 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; + } /* create a card instance with ALSA framework */ - retval = snd_card_new(&devptr->dev, hdmi_card_index, hdmi_card_id, - THIS_MODULE, sizeof(*intelhaddata), &card); - if (retval) - return 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); + ctx->drv_status = HAD_DRV_DISCONNECTED; + ctx->dev = &pdev->dev; + ctx->card = card; + ctx->card_id = hdmi_card_id; + ctx->card_index = card->number; + ctx->flag_underrun = 0; + 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_wq); + ctx->state = hdmi_connector_status_disconnected; + + 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; + } - intelhaddata = card->private_data; - 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__); + /* 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; + } - intelhaddata->dev = &devptr->dev; - intelhaddata->card = card; - intelhaddata->card_id = hdmi_card_id; - intelhaddata->card_index = card->number; - 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) + 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_data = ctx; pcm->private_free = snd_intelhad_pcm_free; 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 (retval) - goto err; /* IEC958 controls */ - retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958_mask, - intelhaddata)); - if (retval < 0) + ret = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958_mask, ctx)); + if (ret < 0) goto err; - retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958, - intelhaddata)); - if (retval < 0) + ret = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958, ctx)); + if (ret < 0) goto err; init_channel_allocations(); /* Register channel map controls */ - retval = had_register_chmap_ctls(intelhaddata, pcm); - if (retval < 0) + ret = had_register_chmap_ctls(ctx, pcm); + if (ret < 0) goto err; - retval = snd_card_register(card); - if (retval) + ret = snd_card_register(card); + if (ret) goto err; - pm_runtime_set_active(intelhaddata->dev); - pm_runtime_enable(intelhaddata->dev); + spin_lock_irqsave(&pdata->lpe_audio_slock, flags); + pdata->notify_audio_lpe = notify_audio_lpe; + if (pdata->notify_pending) { - *had_ret = intelhaddata; + dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); + notify_audio_lpe(pdev); + pdata->notify_pending = false; + } + spin_unlock_irqrestore(&pdata->lpe_audio_slock, flags); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + schedule_work(&ctx->hdmi_audio_wq); return 0; err: snd_card_free(card); - pr_err("Error returned from %s api %#x\n", __func__, retval); - return retval; + return ret; } /* - * hdmi_audio_remove - removes the alsa card + * hdmi_lpe_audio_remove - stop bridge with i915 * - *@haddata: pointer to HAD private data - * - * 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. The sound + * card should have been removed on hot plug event. */ -int hdmi_audio_remove(struct snd_intelhad *intelhaddata) +static int hdmi_lpe_audio_remove(struct platform_device *pdev) { - int caps; - - pr_debug("Enter %s\n", __func__); + struct snd_intelhad *ctx = platform_get_drvdata(pdev); - if (!intelhaddata) - return 0; + dev_dbg(&pdev->dev, "Enter %s\n", __func__); - if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) { - caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; - had_set_caps(intelhaddata, HAD_SET_DISABLE_AUDIO_INT, &caps); - had_set_caps(intelhaddata, HAD_SET_DISABLE_AUDIO, NULL); - } - snd_card_free(intelhaddata->card); + if (ctx->drv_status != HAD_DRV_DISCONNECTED) + snd_intelhad_enable_audio_int(ctx, false); + snd_card_free(ctx->card); 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_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 8415f93e40dc..6efa846f98c9 100644 --- a/sound/x86/intel_hdmi_audio.h +++ b/sound/x86/intel_hdmi_audio.h @@ -108,7 +108,7 @@ struct had_stream_data { * @drv_status: driver 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 @@ -127,7 +127,7 @@ struct snd_intelhad { enum had_drv_status drv_status; struct ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS]; struct pcm_stream_info stream_info; - union otm_hdmi_eld_t eeld; + union otm_hdmi_eld_t eld; bool dp_output; enum intel_had_aud_buf_type curr_buf; int valid_buf_cnt; @@ -142,15 +142,27 @@ struct snd_intelhad { unsigned int *audio_reg_base; unsigned int audio_cfg_offset; int underrun_count; + enum hdmi_connector_status state; + int tmds_clock_speed; + int link_rate; + + /* internal stuff */ + int irq; + void __iomem *mmio_start; + unsigned int had_config_offset; + int hdmi_audio_interrupt_mask; + struct work_struct hdmi_audio_wq; }; -int had_event_handler(enum had_event_type event_type, void *data); - -int hdmi_audio_suspend(void *drv_data); -int hdmi_audio_resume(void *drv_data); -int hdmi_audio_mode_change(struct snd_pcm_substream *substream); +int hdmi_lpe_audio_suspend(struct platform_device *pdev, pm_message_t state); +int hdmi_lpe_audio_resume(struct platform_device *pdev); extern struct snd_pcm_ops snd_intelhad_playback_ops; +int had_process_buffer_done(struct snd_intelhad *intelhaddata); +int had_process_buffer_underrun(struct snd_intelhad *intelhaddata); +int had_process_hot_plug(struct snd_intelhad *intelhaddata); +int had_process_hot_unplug(struct snd_intelhad *intelhaddata); + int snd_intelhad_init_audio_ctrl(struct snd_pcm_substream *substream, struct snd_intelhad *intelhaddata, int flag_silence); @@ -160,15 +172,12 @@ 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); -void snd_intelhad_enable_audio(struct snd_pcm_substream *substream, u8 enable); +void snd_intelhad_enable_audio_int(struct snd_intelhad *ctx, bool enable); +void snd_intelhad_enable_audio(struct snd_intelhad *ctx, bool enable); void snd_intelhad_handle_underrun(struct snd_intelhad *intelhaddata); /* Register access functions */ int had_get_hwstate(struct snd_intelhad *intelhaddata); -int had_get_caps(struct snd_intelhad *intelhaddata, - enum had_caps_list query_element, void *capabilties); -int had_set_caps(struct snd_intelhad *intelhaddata, - enum had_caps_list set_element, void *capabilties); int had_read_register(struct snd_intelhad *intelhaddata, u32 reg_addr, u32 *data); int had_write_register(struct snd_intelhad *intelhaddata, @@ -176,8 +185,4 @@ int had_write_register(struct snd_intelhad *intelhaddata, int had_read_modify(struct snd_intelhad *intelhaddata, u32 reg_addr, u32 data, u32 mask); -int hdmi_audio_probe(struct platform_device *devptr, - struct snd_intelhad **had_ret); -int hdmi_audio_remove(struct snd_intelhad *intelhaddata); - #endif /* _INTEL_HDMI_AUDIO_ */ diff --git a/sound/x86/intel_hdmi_audio_if.c b/sound/x86/intel_hdmi_audio_if.c index 57acefaf930e..327650dd1723 100644 --- a/sound/x86/intel_hdmi_audio_if.c +++ b/sound/x86/intel_hdmi_audio_if.c @@ -33,21 +33,20 @@ #include "intel_hdmi_audio.h" #include "intel_hdmi_lpe_audio.h" -/** - * hdmi_audio_suspend - power management suspend function +/* + * hdmi_lpe_audio_suspend - power management suspend function * - *@haddata: pointer to HAD private data + * @pdev: platform device * * This function is called by client driver to suspend the * hdmi audio. */ -int hdmi_audio_suspend(void *haddata) +int hdmi_lpe_audio_suspend(struct platform_device *pdev, pm_message_t state) { - int caps, retval = 0; struct had_stream_data *had_stream; unsigned long flag_irqs; struct snd_pcm_substream *substream; - struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; + struct snd_intelhad *intelhaddata = platform_get_drvdata(pdev); pr_debug("Enter:%s\n", __func__); @@ -64,13 +63,13 @@ int hdmi_audio_suspend(void *haddata) if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); pr_debug("had not connected\n"); - return retval; + return 0; } if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) { spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); pr_debug("had already suspended\n"); - return retval; + return 0; } intelhaddata->drv_status = HAD_DRV_SUSPENDED; @@ -78,29 +77,22 @@ int hdmi_audio_suspend(void *haddata) __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(intelhaddata, HAD_SET_DISABLE_AUDIO_INT, &caps); - had_set_caps(intelhaddata, HAD_SET_DISABLE_AUDIO, NULL); + snd_intelhad_enable_audio_int(intelhaddata, false); pr_debug("Exit:%s", __func__); - return retval; + return 0; } -/** - * hdmi_audio_resume - power management resume function +/* + * hdmi_lpe_audio_resume - power management resume function * - *@haddata: pointer to HAD private data + *@pdev: platform device * * This function is called by client driver to resume the * hdmi audio. */ -int hdmi_audio_resume(void *haddata) +int hdmi_lpe_audio_resume(struct platform_device *pdev) { - int caps, retval = 0; - struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; + struct snd_intelhad *intelhaddata = platform_get_drvdata(pdev); unsigned long flag_irqs; pr_debug("Enter:%s\n", __func__); @@ -128,15 +120,9 @@ int hdmi_audio_resume(void *haddata) 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(intelhaddata, HAD_SET_ENABLE_AUDIO_INT, &caps); - retval = had_set_caps(intelhaddata, HAD_SET_ENABLE_AUDIO, NULL); + snd_intelhad_enable_audio_int(intelhaddata, true); pr_debug("Exit:%s", __func__); - return retval; + return 0; } static inline int had_chk_intrmiss(struct snd_intelhad *intelhaddata, @@ -357,7 +343,6 @@ int had_process_hot_plug(struct snd_intelhad *intelhaddata) int had_process_hot_unplug(struct snd_intelhad *intelhaddata) { - int caps, retval = 0; enum intel_had_aud_buf_type buf_id; struct had_stream_data *had_stream; unsigned long flag_irqs; @@ -372,17 +357,12 @@ int had_process_hot_unplug(struct snd_intelhad *intelhaddata) if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { pr_debug("Device already disconnected\n"); spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); - return retval; + return 0; } else { /* Disable Audio */ - caps = HDMI_AUDIO_BUFFER_DONE; - retval = had_set_caps(intelhaddata, HAD_SET_DISABLE_AUDIO_INT, - &caps); - retval = had_set_caps(intelhaddata, HAD_SET_DISABLE_AUDIO, - NULL); - snd_intelhad_enable_audio( - intelhaddata->stream_info.had_substream, 0); + snd_intelhad_enable_audio_int(intelhaddata, false); + snd_intelhad_enable_audio(intelhaddata, false); } intelhaddata->drv_status = HAD_DRV_DISCONNECTED; @@ -405,74 +385,6 @@ int had_process_hot_unplug(struct snd_intelhad *intelhaddata) intelhaddata->audio_reg_base = NULL; pr_debug("%s: unlocked -> returned\n", __func__); - return retval; + return 0; } -/** - * had_event_handler - Call back function to handle events - * - * @event_type: Event type to handle - * @data: data related to the event_type - * - * This function is invoked to handle HDMI events from client driver. - */ -int had_event_handler(enum had_event_type event_type, void *data) -{ - int retval = 0; - struct snd_intelhad *intelhaddata = data; - enum intel_had_aud_buf_type buf_id; - struct snd_pcm_substream *substream; - struct had_stream_data *had_stream; - unsigned long flag_irqs; - - buf_id = intelhaddata->curr_buf; - had_stream = &intelhaddata->stream_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 8789f55724da..000000000000 --- a/sound/x86/intel_hdmi_lpe_audio.c +++ /dev/null @@ -1,462 +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" - -struct hdmi_lpe_audio_ctx { - struct platform_device *pdev; - int irq; - void __iomem *mmio_start; - struct snd_intelhad *had; - 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; - int state; /* connection state */ - union otm_hdmi_eld_t eld; /* ELD copy */ -}; - -static void mid_hdmi_audio_signal_event(struct platform_device *pdev, - enum had_event_type event) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "%s: Enter\n", __func__); - - /* The handler is protected in the respective - * event handlers to avoid races - */ - had_event_handler(event, ctx->had); -} - -/* - * used to write into display controller HDMI audio registers. - */ -int mid_hdmi_audio_write(struct platform_device *pdev, u32 reg, u32 val) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, reg, val); - - if (ctx->dp_output) { - if (reg == AUD_CONFIG && (val & AUD_CONFIG_VALID_BIT)) - val |= AUD_CONFIG_DP_MODE | AUD_CONFIG_BLOCK_BIT; - } - iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg); - - return 0; -} - -/* - * used to get the register value read from - * display controller HDMI audio registers. - */ -int mid_hdmi_audio_read(struct platform_device *pdev, u32 reg, u32 *val) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - - *val = ioread32(ctx->mmio_start + ctx->had_config_offset + reg); - dev_dbg(&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. - */ -int mid_hdmi_audio_rmw(struct platform_device *pdev, - u32 reg, u32 val, u32 mask) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - u32 val_tmp = 0; - - val_tmp = ioread32(ctx->mmio_start + ctx->had_config_offset + reg); - val_tmp &= ~mask; - val_tmp |= (val & mask); - - if (ctx->dp_output) { - if (reg == AUD_CONFIG && (val_tmp & AUD_CONFIG_VALID_BIT)) - val_tmp |= AUD_CONFIG_DP_MODE | AUD_CONFIG_BLOCK_BIT; - } - - iowrite32(val_tmp, ctx->mmio_start + ctx->had_config_offset + reg); - dev_dbg(&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. - */ -int mid_hdmi_audio_get_caps(struct platform_device *pdev, - enum had_caps_list get_element, - void *capabilities) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - int ret = 0; - - dev_dbg(&pdev->dev, "%s: Enter\n", __func__); - - switch (get_element) { - case HAD_GET_ELD: - memcpy(capabilities, &ctx->eld, sizeof(ctx->eld)); - print_hex_dump_bytes("eld: ", DUMP_PREFIX_NONE, - (u8 *)&ctx->eld, sizeof(ctx->eld)); - break; - case HAD_GET_DISPLAY_RATE: - /* ToDo: Verify if sampling freq logic is correct */ - *(u32 *)capabilities = ctx->tmds_clock_speed; - dev_dbg(&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(&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(&pdev->dev, "%s: dp_output = %d\n", - __func__, ctx->dp_output); - break; - default: - break; - } - - return ret; -} - -/* - * used to set the HDMI audio capabilities. - * e.g. Audio INT. - */ -int mid_hdmi_audio_set_caps(struct platform_device *pdev, - enum had_caps_list set_element, - void *capabilties) -{ - dev_dbg(&pdev->dev, "%s: cap_id = 0x%x\n", __func__, set_element); - - switch (set_element) { - case HAD_SET_ENABLE_AUDIO_INT: - { - u32 status_reg; - - mid_hdmi_audio_read(pdev, AUD_HDMI_STATUS_v2, - &status_reg); - status_reg |= - HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; - mid_hdmi_audio_write(pdev, AUD_HDMI_STATUS_v2, - status_reg); - mid_hdmi_audio_read(pdev, AUD_HDMI_STATUS_v2, - &status_reg); - } - break; - default: - break; - } - - return 0; -} - -static void _had_wq(struct work_struct *work) -{ - struct hdmi_lpe_audio_ctx *ctx = - container_of(work, struct hdmi_lpe_audio_ctx, hdmi_audio_wq); - - mid_hdmi_audio_signal_event(ctx->pdev, HAD_EVENT_HOT_PLUG); -} - -static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) -{ - struct platform_device *pdev = dev_id; - u32 audio_stat, audio_reg; - struct hdmi_lpe_audio_ctx *ctx; - - dev_dbg(&pdev->dev, "%s: Enter\n", __func__); - - ctx = platform_get_drvdata(pdev); - - audio_reg = AUD_HDMI_STATUS_v2; - mid_hdmi_audio_read(pdev, audio_reg, &audio_stat); - - if (audio_stat & HDMI_AUDIO_UNDERRUN) { - mid_hdmi_audio_write(pdev, audio_reg, HDMI_AUDIO_UNDERRUN); - mid_hdmi_audio_signal_event(pdev, - HAD_EVENT_AUDIO_BUFFER_UNDERRUN); - } - - if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { - mid_hdmi_audio_write(pdev, audio_reg, HDMI_AUDIO_BUFFER_DONE); - mid_hdmi_audio_signal_event(pdev, - 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 (ctx->state == hdmi_connector_status_connected) { - - ctx->state = hdmi_connector_status_disconnected; - - mid_hdmi_audio_signal_event(pdev, - 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; - } - - memcpy(&ctx->eld, eld->eld_data, sizeof(ctx->eld)); - - mid_hdmi_audio_signal_event(pdev, HAD_EVENT_HOT_PLUG); - - ctx->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(pdev, - 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; - - dev_dbg(&pdev->dev, "Enter %s\n", __func__); - 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; - } - - 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); - - mmio_start = ioremap_nocache(res_mmio->start, - (size_t)(resource_size(res_mmio))); - if (!mmio_start) { - dev_err(&pdev->dev, "Could not get ioremap\n"); - return -EACCES; - } - - /* alloc and save context */ - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (ctx == NULL) { - ret = -ENOMEM; - goto error_ctx; - } - - ctx->pdev = pdev; - ctx->irq = irq; - dev_dbg(&pdev->dev, "hdmi lpe audio: irq num = %d\n", irq); - ctx->mmio_start = mmio_start; - ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; - INIT_WORK(&ctx->hdmi_audio_wq, _had_wq); - ctx->state = hdmi_connector_status_disconnected; - - /* assume pipe A as default */ - ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; - - platform_set_drvdata(pdev, ctx); - - /* setup interrupt handler */ - ret = request_irq(irq, display_pipe_interrupt_handler, 0, - pdev->name, pdev); - - if (ret < 0) { - dev_err(&pdev->dev, "request_irq failed\n"); - goto error_irq; - } - - ret = hdmi_audio_probe(pdev, &ctx->had); - if (ret < 0) - goto error_probe; - - dev_dbg(&pdev->dev, "hdmi lpe audio: setting pin eld notify callback\n"); - - /* The Audio driver is loading now and we need to notify - * it if there is an HDMI device attached - */ - dev_dbg(&pdev->dev, "%s: Scheduling HDMI audio work queue\n", - __func__); - schedule_work(&ctx->hdmi_audio_wq); - - spin_lock_irqsave(&pdata->lpe_audio_slock, flag_irq); - pdata->notify_audio_lpe = notify_audio_lpe; - if (pdata->notify_pending) { - - dev_dbg(&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; - - error_probe: - free_irq(irq, pdev); - error_irq: - kfree(ctx); - error_ctx: - iounmap(mmio_start); - 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 = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "Enter %s\n", __func__); - - hdmi_audio_remove(ctx->had); - - /* release resources */ - iounmap(ctx->mmio_start); - free_irq(ctx->irq, pdev); - kfree(ctx); - return 0; -} - -static int hdmi_lpe_audio_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "%s: state %d", __func__, ctx->state); - /* HDMI is not connected, assuming audio device is suspended already */ - if (ctx->state != hdmi_connector_status_disconnected) - hdmi_audio_suspend(ctx->had); - return 0; -} - -static int hdmi_lpe_audio_resume(struct platform_device *pdev) -{ - struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "%s: state %d", __func__, ctx->state); - /* HDMI is not connected, there is no need to resume audio device */ - if (ctx->state != hdmi_connector_status_disconnected) - hdmi_audio_resume(ctx->had); - 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 a9d51b7c5bae..8f320b4aa3b7 100644 --- a/sound/x86/intel_hdmi_lpe_audio.h +++ b/sound/x86/intel_hdmi_lpe_audio.h @@ -27,12 +27,11 @@ #include <sound/initval.h> #include <linux/version.h> #include <linux/pm_runtime.h> +#include <linux/platform_device.h> #include <sound/asoundef.h> #include <sound/control.h> #include <sound/pcm.h> -struct platform_device; - #define AUD_CONFIG_VALID_BIT (1<<9) #define AUD_CONFIG_DP_MODE (1<<15) #define AUD_CONFIG_BLOCK_BIT (1<<7) @@ -636,24 +635,4 @@ enum had_event_type { HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED, }; -/* - * HDMI Display Controller Audio Interface - * - */ -struct hdmi_audio_event { - int type; -}; - -int mid_hdmi_audio_read(struct platform_device *pdev, u32 reg, u32 *val); -int mid_hdmi_audio_write(struct platform_device *pdev, u32 reg, u32 val); -int mid_hdmi_audio_rmw(struct platform_device *pdev, - u32 reg, u32 val, u32 mask); - -int mid_hdmi_audio_get_caps(struct platform_device *pdev, - enum had_caps_list get_element, - void *capabilities); -int mid_hdmi_audio_set_caps(struct platform_device *pdev, - enum had_caps_list set_element, - void *capabilties); - #endif |