Commit 99b2ab9d authored by Takashi Iwai's avatar Takashi Iwai

ALSA: x86: Fix sleep-in-atomic via i915 notification

i915 notification is executed in a spinlock, thus it must not sleep;
i.e. we can't use kmalloc with GFP_KERNEL or such.

For making it working properly, move the notification handler in a
work, and handle it gracefully.  We have already such a work, and it
was used just at the start.  This can be re-used in a more generic
hotplug handling.

Also, the patch adds the proper call of cancel_work_sync() to the
destructor.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent f6a82a0c
...@@ -1857,14 +1857,6 @@ static struct snd_kcontrol_new had_control_iec958 = { ...@@ -1857,14 +1857,6 @@ static struct snd_kcontrol_new had_control_iec958 = {
.put = had_iec958_put .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) static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id)
{ {
struct snd_intelhad *ctx = dev_id; struct snd_intelhad *ctx = dev_id;
...@@ -1889,21 +1881,28 @@ static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) ...@@ -1889,21 +1881,28 @@ 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 *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", schedule_work(&ctx->hdmi_audio_wq);
__func__); }
if (ctx->state == hdmi_connector_status_connected) { static void had_audio_wq(struct work_struct *work)
{
struct snd_intelhad *ctx =
container_of(work, struct snd_intelhad, hdmi_audio_wq);
struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data;
ctx->state = hdmi_connector_status_disconnected; if (!pdata->hdmi_connected) {
dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n",
__func__);
had_process_hot_unplug(ctx); if (ctx->state != hdmi_connector_status_connected) {
} else dev_dbg(ctx->dev, "%s: Already Unplugged!\n",
dev_dbg(&pdev->dev, "%s: Already Unplugged!\n",
__func__); __func__);
return;
}
ctx->state = hdmi_connector_status_disconnected;
had_process_hot_unplug(ctx);
} else { } else {
struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld; struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld;
...@@ -1919,7 +1918,7 @@ static void notify_audio_lpe(struct platform_device *pdev) ...@@ -1919,7 +1918,7 @@ static void notify_audio_lpe(struct platform_device *pdev)
ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; ctx->had_config_offset = AUDIO_HDMI_CONFIG_C;
break; break;
default: default:
dev_dbg(&pdev->dev, "Invalid pipe %d\n", dev_dbg(ctx->dev, "Invalid pipe %d\n",
eld->pipe_id); eld->pipe_id);
break; break;
} }
...@@ -1930,7 +1929,7 @@ static void notify_audio_lpe(struct platform_device *pdev) ...@@ -1930,7 +1929,7 @@ static void notify_audio_lpe(struct platform_device *pdev)
ctx->state = hdmi_connector_status_connected; ctx->state = hdmi_connector_status_connected;
dev_dbg(&pdev->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", dev_dbg(ctx->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n",
__func__, eld->port_id, pdata->tmds_clock_speed); __func__, eld->port_id, pdata->tmds_clock_speed);
if (pdata->tmds_clock_speed) { if (pdata->tmds_clock_speed) {
...@@ -1950,6 +1949,8 @@ static void hdmi_lpe_audio_free(struct snd_card *card) ...@@ -1950,6 +1949,8 @@ static void hdmi_lpe_audio_free(struct snd_card *card)
{ {
struct snd_intelhad *ctx = card->private_data; struct snd_intelhad *ctx = card->private_data;
cancel_work_sync(&ctx->hdmi_audio_wq);
if (ctx->mmio_start) if (ctx->mmio_start)
iounmap(ctx->mmio_start); iounmap(ctx->mmio_start);
if (ctx->irq >= 0) if (ctx->irq >= 0)
...@@ -2013,7 +2014,7 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) ...@@ -2013,7 +2014,7 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
ctx->irq = -1; ctx->irq = -1;
ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5;
INIT_WORK(&ctx->hdmi_audio_wq, _had_wq); INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq);
ctx->state = hdmi_connector_status_disconnected; ctx->state = hdmi_connector_status_disconnected;
card->private_free = hdmi_lpe_audio_free; card->private_free = hdmi_lpe_audio_free;
...@@ -2086,17 +2087,13 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) ...@@ -2086,17 +2087,13 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev)
spin_lock_irqsave(&pdata->lpe_audio_slock, flags); spin_lock_irqsave(&pdata->lpe_audio_slock, flags);
pdata->notify_audio_lpe = notify_audio_lpe; pdata->notify_audio_lpe = notify_audio_lpe;
if (pdata->notify_pending) { pdata->notify_pending = false;
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); spin_unlock_irqrestore(&pdata->lpe_audio_slock, flags);
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__);
schedule_work(&ctx->hdmi_audio_wq); schedule_work(&ctx->hdmi_audio_wq);
return 0; return 0;
......
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