Skip to content

Commit

Permalink
ALSA: x86: Split snd_intelhad into card and PCM specific structures
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
vsyrjala committed May 3, 2017
1 parent bb4ac5a commit b4eb0d5
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 100 deletions.
227 changes: 130 additions & 97 deletions sound/x86/intel_hdmi_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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_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;
Expand All @@ -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;

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 */
Expand Down Expand Up @@ -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;
Expand All @@ -1632,26 +1646,31 @@ 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;
}

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;
}

Expand All @@ -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];

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);
}

/*
Expand All @@ -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) {
Expand All @@ -1725,105 +1749,110 @@ 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;
}

/* 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;
Expand All @@ -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;

Expand All @@ -1853,9 +1886,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;
}

Expand Down
Loading

0 comments on commit b4eb0d5

Please sign in to comment.