@@ -42,6 +42,9 @@
#include <drm/intel_lpe_audio.h>
#include "intel_hdmi_audio.h"
+#define for_each_pipe(card_ctx, pipe) \
+ for ((pipe) = 0; (pipe) < (card_ctx)->num_pipes; (pipe)++)
+
/*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;
@@ -192,12 +195,12 @@ static void had_substream_put(struct snd_intelhad *intelhaddata)
/* Register access functions */
static u32 had_read_register_raw(struct snd_intelhad *ctx, u32 reg)
{
- return ioread32(ctx->mmio_start + ctx->had_config_offset + reg);
+ return ioread32(ctx->card_ctx->mmio_start + ctx->had_config_offset + reg);
}
static void had_write_register_raw(struct snd_intelhad *ctx, u32 reg, u32 val)
{
- iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg);
+ iowrite32(val, ctx->card_ctx->mmio_start + ctx->had_config_offset + reg);
}
static void had_read_register(struct snd_intelhad *ctx, u32 reg, u32 *val)
@@ -1519,22 +1522,27 @@ static const struct snd_kcontrol_new had_controls[] = {
*/
static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id)
{
- struct snd_intelhad *ctx = dev_id;
- u32 audio_stat;
+ struct snd_intelhad_card *card_ctx = dev_id;
+ int pipe;
- /* use raw register access to ack IRQs even while disconnected */
- audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS);
+ for_each_pipe(card_ctx, pipe) {
+ struct snd_intelhad *ctx = &card_ctx->pcm_ctx[pipe];
+ u32 audio_stat;
- if (audio_stat & HDMI_AUDIO_UNDERRUN) {
- had_write_register_raw(ctx, AUD_HDMI_STATUS,
- HDMI_AUDIO_UNDERRUN);
- had_process_buffer_underrun(ctx);
- }
+ /* use raw register access to ack IRQs even while disconnected */
+ audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS);
+
+ if (audio_stat & HDMI_AUDIO_UNDERRUN) {
+ had_write_register_raw(ctx, AUD_HDMI_STATUS,
+ HDMI_AUDIO_UNDERRUN);
+ had_process_buffer_underrun(ctx);
+ }
- if (audio_stat & HDMI_AUDIO_BUFFER_DONE) {
- had_write_register_raw(ctx, AUD_HDMI_STATUS,
- HDMI_AUDIO_BUFFER_DONE);
- had_process_buffer_done(ctx);
+ if (audio_stat & HDMI_AUDIO_BUFFER_DONE) {
+ had_write_register_raw(ctx, AUD_HDMI_STATUS,
+ HDMI_AUDIO_BUFFER_DONE);
+ had_process_buffer_done(ctx);
+ }
}
return IRQ_HANDLED;
@@ -1545,9 +1553,14 @@ static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id)
*/
static void notify_audio_lpe(struct platform_device *pdev)
{
- struct snd_intelhad *ctx = platform_get_drvdata(pdev);
+ struct snd_intelhad_card *card_ctx = platform_get_drvdata(pdev);
+ int pipe;
+
+ for_each_pipe(card_ctx, pipe) {
+ struct snd_intelhad *ctx = &card_ctx->pcm_ctx[pipe];
- schedule_work(&ctx->hdmi_audio_wq);
+ schedule_work(&ctx->hdmi_audio_wq);
+ }
}
/* the work to handle monitor hot plug/unplug */
@@ -1618,7 +1631,8 @@ static int had_create_jack(struct snd_intelhad *ctx,
snprintf(hdmi_str, sizeof(hdmi_str),
"HDMI/DP,pcm=%d", pcm->device);
- err = snd_jack_new(ctx->card, hdmi_str, SND_JACK_AVOUT, &ctx->jack,
+ err = snd_jack_new(ctx->card_ctx->card, hdmi_str,
+ SND_JACK_AVOUT, &ctx->jack,
true, false);
if (err < 0)
return err;
@@ -1632,13 +1646,18 @@ static int had_create_jack(struct snd_intelhad *ctx,
static int hdmi_lpe_audio_runtime_suspend(struct device *dev)
{
- struct snd_intelhad *ctx = dev_get_drvdata(dev);
- struct snd_pcm_substream *substream;
+ struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev);
+ int pipe;
- substream = had_substream_get(ctx);
- if (substream) {
- snd_pcm_suspend(substream);
- had_substream_put(ctx);
+ for_each_pipe(card_ctx, pipe) {
+ struct snd_intelhad *ctx = &card_ctx->pcm_ctx[pipe];
+ struct snd_pcm_substream *substream;
+
+ substream = had_substream_get(ctx);
+ if (substream) {
+ snd_pcm_suspend(substream);
+ had_substream_put(ctx);
+ }
}
return 0;
@@ -1646,12 +1665,12 @@ static int hdmi_lpe_audio_runtime_suspend(struct device *dev)
static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev)
{
- struct snd_intelhad *ctx = dev_get_drvdata(dev);
+ struct snd_intelhad_card *card_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);
+ snd_power_change_state(card_ctx->card, SNDRV_CTL_POWER_D3hot);
return err;
}
@@ -1663,29 +1682,34 @@ static int hdmi_lpe_audio_runtime_resume(struct device *dev)
static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev)
{
- struct snd_intelhad *ctx = dev_get_drvdata(dev);
+ struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev);
hdmi_lpe_audio_runtime_resume(dev);
- snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D0);
+ snd_power_change_state(card_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;
- struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data;
+ struct snd_intelhad_card *card_ctx = card->private_data;
+ struct intel_hdmi_lpe_audio_pdata *pdata = card_ctx->dev->platform_data;
+ int pipe;
spin_lock_irq(&pdata->lpe_audio_slock);
pdata->notify_audio_lpe = NULL;
spin_unlock_irq(&pdata->lpe_audio_slock);
- cancel_work_sync(&ctx->hdmi_audio_wq);
+ for_each_pipe(card_ctx, pipe) {
+ struct snd_intelhad *ctx = &card_ctx->pcm_ctx[pipe];
+
+ cancel_work_sync(&ctx->hdmi_audio_wq);
+ }
- if (ctx->mmio_start)
- iounmap(ctx->mmio_start);
- if (ctx->irq >= 0)
- free_irq(ctx->irq, ctx);
+ if (card_ctx->mmio_start)
+ iounmap(card_ctx->mmio_start);
+ if (card_ctx->irq >= 0)
+ free_irq(card_ctx->irq, card_ctx);
}
/*
@@ -1697,12 +1721,12 @@ static void hdmi_lpe_audio_free(struct snd_card *card)
static int hdmi_lpe_audio_probe(struct platform_device *pdev)
{
struct snd_card *card;
- struct snd_intelhad *ctx;
+ struct snd_intelhad_card *card_ctx;
struct snd_pcm *pcm;
struct intel_hdmi_lpe_audio_pdata *pdata;
int irq;
struct resource *res_mmio;
- int i, ret;
+ int pipe, ret;
pdata = pdev->dev.platform_data;
if (!pdata) {
@@ -1725,39 +1749,30 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
/* create a card instance with ALSA framework */
ret = snd_card_new(&pdev->dev, hdmi_card_index, hdmi_card_id,
- THIS_MODULE, sizeof(*ctx), &card);
+ THIS_MODULE, sizeof(*card_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;
+ card_ctx = card->private_data;
+ card_ctx->dev = &pdev->dev;
+ card_ctx->card = card;
strcpy(card->driver, INTEL_HAD);
strcpy(card->shortname, "Intel HDMI/DP LPE Audio");
strcpy(card->longname, "Intel HDMI/DP LPE Audio");
- ctx->irq = -1;
- ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5;
- INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq);
+ card_ctx->irq = -1;
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);
+ platform_set_drvdata(pdev, card_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) {
+ card_ctx->mmio_start = ioremap_nocache(res_mmio->start,
+ (size_t)(resource_size(res_mmio)));
+ if (!card_ctx->mmio_start) {
dev_err(&pdev->dev, "Could not get ioremap\n");
ret = -EACCES;
goto err;
@@ -1765,65 +1780,80 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
/* setup interrupt handler */
ret = request_irq(irq, display_pipe_interrupt_handler, 0,
- pdev->name, ctx);
+ pdev->name, card_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 = ctx;
- pcm->info_flags = 0;
- strncpy(pcm->name, card->shortname, strlen(card->shortname));
- /* setup the ops for playabck */
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops);
+ card_ctx->irq = irq;
/* only 32bit addressable */
dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- /* allocate dma pages;
- * try to allocate 600k buffer as default which is large enough
- */
- snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV, NULL,
- HAD_DEFAULT_BUFFER, HAD_MAX_BUFFER);
+ init_channel_allocations();
- /* create controls */
- for (i = 0; i < ARRAY_SIZE(had_controls); i++) {
- struct snd_kcontrol *kctl;
+ card_ctx->num_pipes = 1;
- kctl = snd_ctl_new1(&had_controls[i], ctx);
- if (!kctl) {
- ret = -ENOMEM;
+ for_each_pipe(card_ctx, pipe) {
+ struct snd_intelhad *ctx = &card_ctx->pcm_ctx[pipe];
+ int i;
+
+ ctx->card_ctx = card_ctx;
+ ctx->dev = card_ctx->dev;
+ ctx->pipe = pipe;
+
+ INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq);
+
+ ctx->had_config_offset = AUDIO_HDMI_CONFIG_A;
+
+ 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 = ctx;
+ pcm->info_flags = 0;
+ strncpy(pcm->name, card->shortname, strlen(card->shortname));
+ /* setup the ops for playabck */
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops);
+
+ /* allocate dma pages;
+ * try to allocate 600k buffer as default which is large enough
+ */
+ snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV, NULL,
+ HAD_DEFAULT_BUFFER, HAD_MAX_BUFFER);
+
+ /* create controls */
+ for (i = 0; i < ARRAY_SIZE(had_controls); i++) {
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_ctl_new1(&had_controls[i], ctx);
+ if (!kctl) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ kctl->id.device = pcm->device;
+
+ ret = snd_ctl_add(card, kctl);
+ if (ret < 0)
+ goto err;
}
- kctl->id.device = pcm->device;
+ /* Register channel map controls */
+ ret = had_register_chmap_ctls(ctx, pcm);
+ if (ret < 0)
+ goto err;
- ret = snd_ctl_add(card, kctl);
+ ret = had_create_jack(ctx, pcm);
if (ret < 0)
goto err;
}
- init_channel_allocations();
-
- /* Register channel map controls */
- ret = had_register_chmap_ctls(ctx, pcm);
- if (ret < 0)
- goto err;
-
- ret = had_create_jack(ctx, pcm);
- if (ret < 0)
- goto err;
-
ret = snd_card_register(card);
if (ret)
goto err;
@@ -1837,7 +1867,11 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
pm_runtime_set_active(&pdev->dev);
dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__);
- schedule_work(&ctx->hdmi_audio_wq);
+ for_each_pipe(card_ctx, pipe) {
+ struct snd_intelhad *ctx = &card_ctx->pcm_ctx[pipe];
+
+ schedule_work(&ctx->hdmi_audio_wq);
+ }
return 0;
@@ -1853,9 +1887,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
*/
static int hdmi_lpe_audio_remove(struct platform_device *pdev)
{
- struct snd_intelhad *ctx = platform_get_drvdata(pdev);
+ struct snd_intelhad_card *card_ctx = platform_get_drvdata(pdev);
- snd_card_free(ctx->card);
+ snd_card_free(card_ctx->card);
return 0;
}
@@ -101,7 +101,7 @@ struct pcm_stream_info {
* @chmap: holds channel map info
*/
struct snd_intelhad {
- struct snd_card *card;
+ struct snd_intelhad_card *card_ctx;
bool connected;
struct pcm_stream_info stream_info;
unsigned char eld[HDMI_MAX_ELD_BYTES];
@@ -112,6 +112,7 @@ struct snd_intelhad {
struct snd_pcm_chmap *chmap;
int tmds_clock_speed;
int link_rate;
+ int pipe;
/* ring buffer (BD) position index */
unsigned int bd_head;
@@ -123,8 +124,6 @@ struct snd_intelhad {
unsigned int period_bytes; /* PCM period size in bytes */
/* internal stuff */
- int irq;
- void __iomem *mmio_start;
unsigned int had_config_offset;
union aud_cfg aud_config; /* AUD_CONFIG reg value cache */
struct work_struct hdmi_audio_wq;
@@ -133,4 +132,15 @@ struct snd_intelhad {
struct snd_jack *jack;
};
+struct snd_intelhad_card {
+ struct snd_card *card;
+ struct device *dev;
+
+ /* internal stuff */
+ int irq;
+ void __iomem *mmio_start;
+ int num_pipes;
+ struct snd_intelhad pcm_ctx[3];
+};
+
#endif /* _INTEL_HDMI_AUDIO_ */