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: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170427160231.13337-11-ville.syrjala@linux.intel.com
Reviewed-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Ville Syrjälä 2017-04-27 19:02:29 +03:00
parent bb4ac5a0ec
commit b4eb0d522f
2 changed files with 142 additions and 100 deletions

View File

@ -42,6 +42,9 @@
#include <drm/intel_lpe_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*/
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 port;
/* use raw register access to ack IRQs even while disconnected */
audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS);
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
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_BUFFER_DONE) {
had_write_register_raw(ctx, AUD_HDMI_STATUS,
HDMI_AUDIO_BUFFER_DONE);
had_process_buffer_done(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) {
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 port;
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);
}
}
/* 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 port;
substream = had_substream_get(ctx);
if (substream) {
snd_pcm_suspend(substream);
had_substream_put(ctx);
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
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 port;
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_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
if (ctx->mmio_start)
iounmap(ctx->mmio_start);
if (ctx->irq >= 0)
free_irq(ctx->irq, ctx);
cancel_work_sync(&ctx->hdmi_audio_wq);
}
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 port, 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,79 @@ 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_ports = 1;
kctl = snd_ctl_new1(&had_controls[i], ctx);
if (!kctl) {
ret = -ENOMEM;
for_each_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
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;
/* 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 +1866,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_port(card_ctx, port) {
struct snd_intelhad *ctx = &card_ctx->pcm_ctx[port];
schedule_work(&ctx->hdmi_audio_wq);
}
return 0;
@ -1853,9 +1886,9 @@ err:
*/
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;
}

View File

@ -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];
@ -123,8 +123,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 +131,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_ports;
struct snd_intelhad pcm_ctx[3];
};
#endif /* _INTEL_HDMI_AUDIO_ */