diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index ff1f0f57afd9f..1e25586426c18 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -1190,7 +1190,7 @@ int afk_service_call_timeout(struct apple_epic_service *service, u16 group, if (!service) return -ENODEV; - bfr = kzalloc(bfr_len, GFP_KERNEL); + bfr = kvzalloc(bfr_len, GFP_KERNEL); if (!bfr) return -ENOMEM; @@ -1249,7 +1249,7 @@ int afk_service_call_timeout(struct apple_epic_service *service, u16 group, } out: - kfree(bfr); + kvfree(bfr); return ret; } diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index ac55f1f30f6ac..406e6c8bca1ee 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,7 +32,11 @@ #include "audio.h" #include "parser.h" -#define DCPAUD_ELEMENTS_MAXSIZE 16384 +/* + * As macOS 13 asks for. Elements that do not fit come back cut off at the + * buffer's end, with a used size of 0. + */ +#define DCPAUD_ELEMENTS_MAXSIZE SZ_256K #define DCPAUD_PRODUCTATTRS_MAXSIZE 1024 struct dcp_audio { @@ -52,6 +58,8 @@ struct dcp_audio { struct snd_pcm_chmap_elem selected_chmap; struct dcp_sound_cookie selected_cookie; + /* the stream holds a runtime PM reference, put in sync_stop */ + bool pm_held; void *elements; void *productattrs; @@ -418,7 +426,10 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (!dcpaud_connection_up(dcpaud)) return -ENXIO; - WARN_ON(pm_runtime_get_sync(dcpaud->dev) < 0); + if (!dcpaud->pm_held) { + WARN_ON(pm_runtime_get_sync(dcpaud->dev) < 0); + dcpaud->pm_held = true; + } ret = dcp_audiosrv_startlink(dcpaud->dcp_dev, &dcpaud->selected_cookie); if (ret < 0) @@ -444,9 +455,8 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: + /* the runtime PM reference goes in sync_stop */ ret = dcp_audiosrv_stoplink(dcpaud->dcp_dev); - pm_runtime_mark_last_busy(dcpaud->dev); - __pm_runtime_put_autosuspend(dcpaud->dev); if (ret < 0) return ret; break; @@ -455,6 +465,36 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } +/* + * ALSA calls this once a stream has stopped (STOP, SUSPEND, or the STOP + * that undoes a failed START), before the next hw_params, prepare or + * hw_free, at close, at system suspend and at disconnect, outside the + * stream lock. The DMA terminate is asynchronous: putting the runtime PM + * reference in trigger STOP powered the DPA's domain (ps_dpa*) down while + * SIO still ran DMA towards it, and SIO crashed ("Shim DMA unavailable", + * the TERMINATE timing out after it). Wait for the terminate first. + */ +static int dcp_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + struct dcp_audio *dcpaud = substream->pcm->private_data; + bool held; + + snd_dmaengine_pcm_sync_stop(substream); + + /* START sets it under the stream lock */ + snd_pcm_stream_lock_irq(substream); + held = dcpaud->pm_held; + dcpaud->pm_held = false; + snd_pcm_stream_unlock_irq(substream); + + if (held) { + pm_runtime_mark_last_busy(dcpaud->dev); + __pm_runtime_put_autosuspend(dcpaud->dev); + } + + return 0; +} + struct snd_pcm_ops dcp_playback_ops = { .open = dcp_pcm_open, .close = dcp_pcm_close, @@ -462,6 +502,7 @@ struct snd_pcm_ops dcp_playback_ops = { .hw_free = dcp_pcm_hw_free, .prepare = dcp_pcm_prepare, .trigger = dcp_pcm_trigger, + .sync_stop = dcp_pcm_sync_stop, .pointer = snd_dmaengine_pcm_pointer, }; @@ -736,18 +777,27 @@ static const struct component_ops dcpaud_comp_ops = { .unbind = dcpaud_comp_unbind, }; +static void dcpaud_free_elements(void *elements) +{ + kvfree(elements); +} + static int dcpaud_probe(struct platform_device *pdev) { struct dcp_audio *dcpaud; + int ret; dcpaud = devm_kzalloc(&pdev->dev, sizeof(*dcpaud), GFP_KERNEL); if (!dcpaud) return -ENOMEM; - dcpaud->elements = devm_kzalloc(&pdev->dev, DCPAUD_ELEMENTS_MAXSIZE, - GFP_KERNEL); + dcpaud->elements = kvzalloc(DCPAUD_ELEMENTS_MAXSIZE, GFP_KERNEL); if (!dcpaud->elements) return -ENOMEM; + ret = devm_add_action_or_reset(&pdev->dev, dcpaud_free_elements, + dcpaud->elements); + if (ret) + return ret; dcpaud->productattrs = devm_kzalloc(&pdev->dev, DCPAUD_PRODUCTATTRS_MAXSIZE, GFP_KERNEL); diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index 563a02bb4e551..9708b2b0235a6 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -197,10 +197,10 @@ dcp_audiosrv_osobject_call(struct apple_epic_service *service, u16 group, } __attribute__((packed)) *hdr; static_assert(sizeof(*hdr) == 48); size_t bfr_len = output_maxsize + sizeof(*hdr); - void *bfr __free(kfree) = NULL; + void *bfr __free(kvfree) = NULL; int ret; - bfr = kzalloc(bfr_len, GFP_KERNEL); + bfr = kvzalloc(bfr_len, GFP_KERNEL); if (!bfr) return -ENOMEM;