Commit b4eb0d52 authored by Ville Syrjälä's avatar Ville Syrjälä

ALSA: x86: Split snd_intelhad into card and PCM specific structures

To allow multiple PCM devices to be registered for the LPE audio card,
split the private data into card and PCM specific chunks. For now we'll
stick to just one PCM device as before.

v2: Rework to do a pcm device per port instead of per pipe

Cc: Takashi Iwai <tiwai@suse.de>
Cc: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170427160231.13337-11-ville.syrjala@linux.intel.comReviewed-by: default avatarTakashi Iwai <tiwai@suse.de>
parent bb4ac5a0
...@@ -42,6 +42,9 @@ ...@@ -42,6 +42,9 @@
#include <drm/intel_lpe_audio.h> #include <drm/intel_lpe_audio.h>
#include "intel_hdmi_audio.h" #include "intel_hdmi_audio.h"
#define for_each_port(card_ctx, port) \
for ((port) = 0; (port) < (card_ctx)->num_ports; (port)++)
/*standard module options for ALSA. This module supports only one card*/ /*standard module options for ALSA. This module supports only one card*/
static int hdmi_card_index = SNDRV_DEFAULT_IDX1; static int hdmi_card_index = SNDRV_DEFAULT_IDX1;
static char *hdmi_card_id = SNDRV_DEFAULT_STR1; static char *hdmi_card_id = SNDRV_DEFAULT_STR1;
...@@ -192,12 +195,12 @@ static void had_substream_put(struct snd_intelhad *intelhaddata) ...@@ -192,12 +195,12 @@ static void had_substream_put(struct snd_intelhad *intelhaddata)
/* Register access functions */ /* Register access functions */
static u32 had_read_register_raw(struct snd_intelhad *ctx, u32 reg) 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) 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) 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[] = { ...@@ -1519,22 +1522,27 @@ static const struct snd_kcontrol_new had_controls[] = {
*/ */
static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id)
{ {
struct snd_intelhad *ctx = dev_id; struct snd_intelhad_card *card_ctx = dev_id;
u32 audio_stat; int port;
/* use raw register access to ack IRQs even while disconnected */ for_each_port(card_ctx, port) {
audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS); struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
u32 audio_stat;
if (audio_stat & HDMI_AUDIO_UNDERRUN) { /* use raw register access to ack IRQs even while disconnected */
had_write_register_raw(ctx, AUD_HDMI_STATUS, audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS);
HDMI_AUDIO_UNDERRUN);
had_process_buffer_underrun(ctx); 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) { if (audio_stat & HDMI_AUDIO_BUFFER_DONE) {
had_write_register_raw(ctx, AUD_HDMI_STATUS, had_write_register_raw(ctx, AUD_HDMI_STATUS,
HDMI_AUDIO_BUFFER_DONE); HDMI_AUDIO_BUFFER_DONE);
had_process_buffer_done(ctx); had_process_buffer_done(ctx);
}
} }
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -1545,9 +1553,14 @@ static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) ...@@ -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) 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 port;
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
schedule_work(&ctx->hdmi_audio_wq); schedule_work(&ctx->hdmi_audio_wq);
}
} }
/* the work to handle monitor hot plug/unplug */ /* the work to handle monitor hot plug/unplug */
...@@ -1618,7 +1631,8 @@ static int had_create_jack(struct snd_intelhad *ctx, ...@@ -1618,7 +1631,8 @@ static int had_create_jack(struct snd_intelhad *ctx,
snprintf(hdmi_str, sizeof(hdmi_str), snprintf(hdmi_str, sizeof(hdmi_str),
"HDMI/DP,pcm=%d", pcm->device); "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); true, false);
if (err < 0) if (err < 0)
return err; return err;
...@@ -1632,13 +1646,18 @@ static int had_create_jack(struct snd_intelhad *ctx, ...@@ -1632,13 +1646,18 @@ static int had_create_jack(struct snd_intelhad *ctx,
static int hdmi_lpe_audio_runtime_suspend(struct device *dev) static int hdmi_lpe_audio_runtime_suspend(struct device *dev)
{ {
struct snd_intelhad *ctx = dev_get_drvdata(dev); struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev);
struct snd_pcm_substream *substream; int port;
substream = had_substream_get(ctx); for_each_port(card_ctx, port) {
if (substream) { struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
snd_pcm_suspend(substream); struct snd_pcm_substream *substream;
had_substream_put(ctx);
substream = had_substream_get(ctx);
if (substream) {
snd_pcm_suspend(substream);
had_substream_put(ctx);
}
} }
return 0; return 0;
...@@ -1646,12 +1665,12 @@ static int hdmi_lpe_audio_runtime_suspend(struct device *dev) ...@@ -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) 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; int err;
err = hdmi_lpe_audio_runtime_suspend(dev); err = hdmi_lpe_audio_runtime_suspend(dev);
if (!err) 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; return err;
} }
...@@ -1663,29 +1682,34 @@ static int hdmi_lpe_audio_runtime_resume(struct device *dev) ...@@ -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) 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); 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; return 0;
} }
/* release resources */ /* release resources */
static void hdmi_lpe_audio_free(struct snd_card *card) static void hdmi_lpe_audio_free(struct snd_card *card)
{ {
struct snd_intelhad *ctx = card->private_data; struct snd_intelhad_card *card_ctx = card->private_data;
struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data; struct intel_hdmi_lpe_audio_pdata *pdata = card_ctx->dev->platform_data;
int port;
spin_lock_irq(&pdata->lpe_audio_slock); spin_lock_irq(&pdata->lpe_audio_slock);
pdata->notify_audio_lpe = NULL; pdata->notify_audio_lpe = NULL;
spin_unlock_irq(&pdata->lpe_audio_slock); spin_unlock_irq(&pdata->lpe_audio_slock);
cancel_work_sync(&ctx->hdmi_audio_wq); for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
cancel_work_sync(&ctx->hdmi_audio_wq);
}
if (ctx->mmio_start) if (card_ctx->mmio_start)
iounmap(ctx->mmio_start); iounmap(card_ctx->mmio_start);
if (ctx->irq >= 0) if (card_ctx->irq >= 0)
free_irq(ctx->irq, ctx); free_irq(card_ctx->irq, card_ctx);
} }
/* /*
...@@ -1697,12 +1721,12 @@ static void hdmi_lpe_audio_free(struct snd_card *card) ...@@ -1697,12 +1721,12 @@ static void hdmi_lpe_audio_free(struct snd_card *card)
static int hdmi_lpe_audio_probe(struct platform_device *pdev) static int hdmi_lpe_audio_probe(struct platform_device *pdev)
{ {
struct snd_card *card; struct snd_card *card;
struct snd_intelhad *ctx; struct snd_intelhad_card *card_ctx;
struct snd_pcm *pcm; struct snd_pcm *pcm;
struct intel_hdmi_lpe_audio_pdata *pdata; struct intel_hdmi_lpe_audio_pdata *pdata;
int irq; int irq;
struct resource *res_mmio; struct resource *res_mmio;
int i, ret; int port, ret;
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
if (!pdata) { if (!pdata) {
...@@ -1725,39 +1749,30 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) ...@@ -1725,39 +1749,30 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
/* create a card instance with ALSA framework */ /* create a card instance with ALSA framework */
ret = snd_card_new(&pdev->dev, hdmi_card_index, hdmi_card_id, 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) if (ret)
return ret; return ret;
ctx = card->private_data; card_ctx = card->private_data;
spin_lock_init(&ctx->had_spinlock); card_ctx->dev = &pdev->dev;
mutex_init(&ctx->mutex); card_ctx->card = card;
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->driver, INTEL_HAD);
strcpy(card->shortname, "Intel HDMI/DP LPE Audio"); strcpy(card->shortname, "Intel HDMI/DP LPE Audio");
strcpy(card->longname, "Intel HDMI/DP LPE Audio"); strcpy(card->longname, "Intel HDMI/DP LPE Audio");
ctx->irq = -1; card_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; card->private_free = hdmi_lpe_audio_free;
/* assume pipe A as default */ platform_set_drvdata(pdev, card_ctx);
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", dev_dbg(&pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n",
__func__, (unsigned int)res_mmio->start, __func__, (unsigned int)res_mmio->start,
(unsigned int)res_mmio->end); (unsigned int)res_mmio->end);
ctx->mmio_start = ioremap_nocache(res_mmio->start, card_ctx->mmio_start = ioremap_nocache(res_mmio->start,
(size_t)(resource_size(res_mmio))); (size_t)(resource_size(res_mmio)));
if (!ctx->mmio_start) { if (!card_ctx->mmio_start) {
dev_err(&pdev->dev, "Could not get ioremap\n"); dev_err(&pdev->dev, "Could not get ioremap\n");
ret = -EACCES; ret = -EACCES;
goto err; goto err;
...@@ -1765,65 +1780,79 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) ...@@ -1765,65 +1780,79 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
/* setup interrupt handler */ /* setup interrupt handler */
ret = request_irq(irq, display_pipe_interrupt_handler, 0, ret = request_irq(irq, display_pipe_interrupt_handler, 0,
pdev->name, ctx); pdev->name, card_ctx);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "request_irq failed\n"); dev_err(&pdev->dev, "request_irq failed\n");
goto err; goto err;
} }
ctx->irq = irq; card_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);
/* only 32bit addressable */ /* only 32bit addressable */
dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
/* allocate dma pages; init_channel_allocations();
* 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 */ card_ctx->num_ports = 1;
for (i = 0; i < ARRAY_SIZE(had_controls); i++) {
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&had_controls[i], ctx); for_each_port(card_ctx, port) {
if (!kctl) { struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
ret = -ENOMEM; int i;
ctx->card_ctx = card_ctx;
ctx->dev = card_ctx->dev;
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; 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) if (ret < 0)
goto err; 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); ret = snd_card_register(card);
if (ret) if (ret)
goto err; goto err;
...@@ -1837,7 +1866,11 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) ...@@ -1837,7 +1866,11 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__);
schedule_work(&ctx->hdmi_audio_wq); for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
schedule_work(&ctx->hdmi_audio_wq);
}
return 0; return 0;
...@@ -1853,9 +1886,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) ...@@ -1853,9 +1886,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
*/ */
static int hdmi_lpe_audio_remove(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; return 0;
} }
......
...@@ -101,7 +101,7 @@ struct pcm_stream_info { ...@@ -101,7 +101,7 @@ struct pcm_stream_info {
* @chmap: holds channel map info * @chmap: holds channel map info
*/ */
struct snd_intelhad { struct snd_intelhad {
struct snd_card *card; struct snd_intelhad_card *card_ctx;
bool connected; bool connected;
struct pcm_stream_info stream_info; struct pcm_stream_info stream_info;
unsigned char eld[HDMI_MAX_ELD_BYTES]; unsigned char eld[HDMI_MAX_ELD_BYTES];
...@@ -123,8 +123,6 @@ struct snd_intelhad { ...@@ -123,8 +123,6 @@ struct snd_intelhad {
unsigned int period_bytes; /* PCM period size in bytes */ unsigned int period_bytes; /* PCM period size in bytes */
/* internal stuff */ /* internal stuff */
int irq;
void __iomem *mmio_start;
unsigned int had_config_offset; unsigned int had_config_offset;
union aud_cfg aud_config; /* AUD_CONFIG reg value cache */ union aud_cfg aud_config; /* AUD_CONFIG reg value cache */
struct work_struct hdmi_audio_wq; struct work_struct hdmi_audio_wq;
...@@ -133,4 +131,15 @@ struct snd_intelhad { ...@@ -133,4 +131,15 @@ struct snd_intelhad {
struct snd_jack *jack; 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_ports;
struct snd_intelhad pcm_ctx[3];
};
#endif /* _INTEL_HDMI_AUDIO_ */ #endif /* _INTEL_HDMI_AUDIO_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment