diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 0296cb9b41bba..1a631596a7e68 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -3186,7 +3186,7 @@ int afk_service_call_timeout_len(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; @@ -3256,7 +3256,7 @@ int afk_service_call_timeout_len(struct apple_epic_service *service, u16 group, } out: - kfree(bfr); + kvfree(bfr); return ret; } @@ -3294,7 +3294,7 @@ int afk_service_call_with_reply_len(struct apple_epic_service *service, &bfr_len) || bfr_len > U32_MAX) return -EOVERFLOW; - bfr = kzalloc(bfr_len, GFP_KERNEL); + bfr = kvzalloc(bfr_len, GFP_KERNEL); if (!bfr) return -ENOMEM; @@ -3339,7 +3339,7 @@ int afk_service_call_with_reply_len(struct apple_epic_service *service, } out: - kfree(bfr); + kvfree(bfr); return ret; } diff --git a/drivers/gpu/drm/apple/audio-legacy-test.c b/drivers/gpu/drm/apple/audio-legacy-test.c new file mode 100644 index 0000000000000..fb082c244ee2b --- /dev/null +++ b/drivers/gpu/drm/apple/audio-legacy-test.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Included by audio.c: on the legacy (pre-14.7) audio service a stopped + * stream keeps its runtime PM reference until sync_stop has waited for the + * DMA terminate, a failed START leaves exactly one reference to put, and the + * element catalog is asked for with the full 256 KiB capacity. + * The production trigger and sync_stop run; the audio service's link calls + * and the DMA channel are stubbed. + */ +#include +#include +#include + +struct dcpaud_legacy_test { + struct dma_device dma; + struct dma_chan channel; + struct dma_async_tx_descriptor desc; + char sequence[32]; + unsigned int nsequence; + /* The runtime PM usage count when the terminate was waited for */ + int usage_at_drain; + int startlink_err; + size_t elements_asked; + size_t catalog; + struct device *dev; +}; + +static struct dcpaud_legacy_test *dcpaud_legacy_current; + +static void dcpaud_legacy_record(char step) +{ + struct dcpaud_legacy_test *t = dcpaud_legacy_current; + + if (t->nsequence < sizeof(t->sequence) - 1) + t->sequence[t->nsequence++] = step; +} + +static int dcpaud_legacy_terminate(struct dma_chan *chan) +{ + dcpaud_legacy_record('X'); + return 0; +} + +static void dcpaud_legacy_drain(struct dma_chan *chan) +{ + struct dcpaud_legacy_test *t = dcpaud_legacy_current; + + t->usage_at_drain = atomic_read(&t->dev->power.usage_count); + dcpaud_legacy_record('D'); +} + +static dma_cookie_t dcpaud_legacy_submit(struct dma_async_tx_descriptor *tx) +{ + return 1; +} + +static struct dma_async_tx_descriptor * +dcpaud_legacy_prep(struct dma_chan *chan, dma_addr_t buf, size_t len, size_t period, + enum dma_transfer_direction dir, unsigned long flags) +{ + struct dcpaud_legacy_test *t = dcpaud_legacy_current; + + dcpaud_legacy_record('Q'); + t->desc.chan = chan; + t->desc.tx_submit = dcpaud_legacy_submit; + return &t->desc; +} + +static void dcpaud_legacy_issue(struct dma_chan *chan) +{ +} + +static enum dma_status dcpaud_legacy_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *state) +{ + return DMA_COMPLETE; +} + +static int dcpaud_legacy_startlink(struct device *dev, struct dcp_sound_cookie *cookie) +{ + dcpaud_legacy_record('T'); + return dcpaud_legacy_current->startlink_err; +} + +static int dcpaud_legacy_stoplink(struct device *dev) +{ + dcpaud_legacy_record('S'); + return 0; +} + +static void dcpaud_legacy_rpm_disable(void *data) +{ + pm_runtime_disable(data); +} + +static void dcpaud_legacy_close(void *data) +{ + snd_dmaengine_pcm_close(data); +} + +/* A legacy service instance with a sink, and a configured stream on it */ +static struct snd_pcm_substream *dcpaud_legacy_setup(struct kunit *test, + struct dcp_audio **audio) +{ + struct dcpaud_legacy_test *t = kunit_kzalloc(test, sizeof(*t), GFP_KERNEL); + struct dcp_audio *dcpaud = kunit_kzalloc(test, sizeof(*dcpaud), GFP_KERNEL); + struct snd_pcm_substream *sub = kunit_kzalloc(test, sizeof(*sub), GFP_KERNEL); + struct snd_pcm_runtime *runtime = kunit_kzalloc(test, sizeof(*runtime), GFP_KERNEL); + struct snd_pcm *pcm = kunit_kzalloc(test, sizeof(*pcm), GFP_KERNEL); + struct platform_device *pdev; + + KUNIT_ASSERT_NOT_NULL(test, t); + KUNIT_ASSERT_NOT_NULL(test, dcpaud); + KUNIT_ASSERT_NOT_NULL(test, sub); + KUNIT_ASSERT_NOT_NULL(test, runtime); + KUNIT_ASSERT_NOT_NULL(test, pcm); + pdev = kunit_platform_device_alloc(test, "dcpaud-legacy", PLATFORM_DEVID_AUTO); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + platform_set_drvdata(pdev, dcpaud); + dcpaud->dev = &pdev->dev; + dcpaud->dcp_dev = &pdev->dev; + mutex_init(&dcpaud->data_lock); + INIT_DELAYED_WORK(&dcpaud->void_work, dcpaud_void_work); + dcpaud->connected = true; + dcpaud->substream = sub; + + /* Runtime PM on, without callbacks: only the usage count matters */ + pm_runtime_no_callbacks(dcpaud->dev); + pm_runtime_enable(dcpaud->dev); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dcpaud_legacy_rpm_disable, + dcpaud->dev), 0); + + dcpaud_legacy_current = t; + t->dev = dcpaud->dev; + t->usage_at_drain = -1; + t->channel.device = &t->dma; + t->dma.device_terminate_all = dcpaud_legacy_terminate; + t->dma.device_synchronize = dcpaud_legacy_drain; + t->dma.device_tx_status = dcpaud_legacy_status; + t->dma.device_prep_dma_cyclic = dcpaud_legacy_prep; + t->dma.device_issue_pending = dcpaud_legacy_issue; + kunit_activate_static_stub(test, dcp_audiosrv_startlink, dcpaud_legacy_startlink); + kunit_activate_static_stub(test, dcp_audiosrv_stoplink, dcpaud_legacy_stoplink); + + pcm->private_data = dcpaud; + pcm->nonatomic = true; + sub->pcm = pcm; + sub->runtime = runtime; + mutex_init(&sub->self_group.mutex); + spin_lock_init(&sub->self_group.lock); + sub->group = &sub->self_group; + runtime->frame_bits = 64; + runtime->rate = 48000; + runtime->buffer_size = 8192; + runtime->period_size = 512; + KUNIT_ASSERT_EQ(test, snd_dmaengine_pcm_open(sub, &t->channel), 0); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dcpaud_legacy_close, sub), 0); + + *audio = dcpaud; + return sub; +} + +static int dcpaud_legacy_usage(struct dcp_audio *dcpaud) +{ + return atomic_read(&dcpaud->dev->power.usage_count); +} + +static void dcpaud_legacy_stop_waits_for_dma(struct kunit *test) +{ + struct dcp_audio *audio; + struct snd_pcm_substream *sub = dcpaud_legacy_setup(test, &audio); + + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_START), 0); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 1); + + /* The terminate is only started: the DPA stays powered */ + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_STOP), 0); + KUNIT_EXPECT_STREQ(test, dcpaud_legacy_current->sequence, "TQXS"); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 1); + + /* The reference goes once the terminate was waited for */ + KUNIT_ASSERT_EQ(test, dcp_pcm_sync_stop(sub), 0); + KUNIT_EXPECT_STREQ(test, dcpaud_legacy_current->sequence, "TQXSD"); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_current->usage_at_drain, 1); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 0); + KUNIT_EXPECT_FALSE(test, audio->pm_held); + + /* A second sync_stop (close after prepare) has nothing to put */ + KUNIT_ASSERT_EQ(test, dcp_pcm_sync_stop(sub), 0); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 0); +} + +static void dcpaud_legacy_suspend_waits_for_dma(struct kunit *test) +{ + struct dcp_audio *audio; + struct snd_pcm_substream *sub = dcpaud_legacy_setup(test, &audio); + + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_START), 0); + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_SUSPEND), 0); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 1); + KUNIT_ASSERT_EQ(test, dcp_pcm_sync_stop(sub), 0); + KUNIT_EXPECT_STREQ(test, dcpaud_legacy_current->sequence, "TQXSD"); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_current->usage_at_drain, 1); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 0); +} + +static void dcpaud_legacy_failed_start(struct kunit *test) +{ + struct dcp_audio *audio; + struct snd_pcm_substream *sub = dcpaud_legacy_setup(test, &audio); + + /* START fails at the link; ALSA undoes it with a STOP */ + dcpaud_legacy_current->startlink_err = -EIO; + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_START), -EIO); + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_STOP), 0); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 1); + + /* Retried before sync_stop ran: still one reference */ + dcpaud_legacy_current->startlink_err = 0; + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_START), 0); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 1); + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_STOP), 0); + KUNIT_ASSERT_EQ(test, dcp_pcm_sync_stop(sub), 0); + KUNIT_EXPECT_STREQ(test, dcpaud_legacy_current->sequence, "TXSTQXSD"); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 0); +} + +static void dcpaud_legacy_void_takes_no_reference(struct kunit *test) +{ + struct dcp_audio *audio; + struct snd_pcm_substream *sub = dcpaud_legacy_setup(test, &audio); + + /* No sink: the stream plays into the void; a 10 s period never elapses here */ + audio->open_unplugged = true; + sub->runtime->period_size = 480000; + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_START), 0); + KUNIT_EXPECT_TRUE(test, audio->void_running); + KUNIT_ASSERT_EQ(test, dcp_pcm_trigger(sub, SNDRV_PCM_TRIGGER_STOP), 0); + KUNIT_ASSERT_EQ(test, dcp_pcm_sync_stop(sub), 0); + KUNIT_EXPECT_STREQ(test, dcpaud_legacy_current->sequence, "D"); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_usage(audio), 0); +} + +static int dcpaud_legacy_get_elements(struct device *dev, void *elements, size_t maxsize) +{ + struct dcpaud_legacy_test *t = dcpaud_legacy_current; + + t->elements_asked = maxsize; + /* What the service returns: cut off at the buffer's end */ + memset(elements, 0xa5, min(t->catalog, maxsize)); + return 0; +} + +static int dcpaud_legacy_get_attrs(struct device *dev, void *attrs, size_t maxsize) +{ + return 0; +} + +static void dcpaud_legacy_free(void *data) +{ + kvfree(data); +} + +static void dcpaud_legacy_elements_capacity(struct kunit *test) +{ + struct dcp_audio *audio; + u8 *elements; + + dcpaud_legacy_setup(test, &audio); + elements = kvzalloc(DCPAUD_ELEMENTS_MAXSIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, elements); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dcpaud_legacy_free, elements), 0); + audio->elements = elements; + audio->productattrs = kunit_kzalloc(test, DCPAUD_PRODUCTATTRS_MAXSIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, audio->productattrs); + kunit_activate_static_stub(test, dcp_audiosrv_get_elements, dcpaud_legacy_get_elements); + kunit_activate_static_stub(test, dcp_audiosrv_get_product_attrs, dcpaud_legacy_get_attrs); + + /* A TV behind a USB-C to HDMI adapter: 17312 bytes of elements */ + dcpaud_legacy_current->catalog = 17312; + KUNIT_ASSERT_FALSE(test, dcpaud_native_link(audio)); + KUNIT_ASSERT_EQ(test, dcpaud_read_remote_info(audio), 0); + KUNIT_EXPECT_EQ(test, dcpaud_legacy_current->elements_asked, (size_t)SZ_256K); + KUNIT_EXPECT_EQ(test, elements[17311], 0xa5); + KUNIT_EXPECT_EQ(test, elements[17312], 0); +} + +static struct kunit_case dcpaud_legacy_cases[] = { + KUNIT_CASE(dcpaud_legacy_stop_waits_for_dma), + KUNIT_CASE(dcpaud_legacy_suspend_waits_for_dma), + KUNIT_CASE(dcpaud_legacy_failed_start), + KUNIT_CASE(dcpaud_legacy_void_takes_no_reference), + KUNIT_CASE(dcpaud_legacy_elements_capacity), + {} +}; + +static struct kunit_suite dcpaud_legacy_suite = { + .name = "apple-dcpaud-legacy", + .test_cases = dcpaud_legacy_cases, +}; + +kunit_test_suite(dcpaud_legacy_suite); diff --git a/drivers/gpu/drm/apple/audio.c b/drivers/gpu/drm/apple/audio.c index 9088a022ba9f3..bd25c0358ef2e 100644 --- a/drivers/gpu/drm/apple/audio.c +++ b/drivers/gpu/drm/apple/audio.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,11 +39,15 @@ #include "audio.h" #include "parser.h" -#define DCPAUD_ELEMENTS_MAXSIZE 16384 +/* + * The element catalog capacity, on every firmware. Elements that do not + * fit come back cut off at the buffer's end with a used size of 0: a TV + * behind a USB-C to HDMI adapter has 17312 bytes of them. macOS 13 asks + * for up to 256 KiB. + */ +#define DCPAUD_ELEMENTS_MAXSIZE SZ_256K #define DCPAUD_PRODUCTATTRS_MAXSIZE 1024 #define DCPAUD_ELD_SIZE MAX_ELD_BYTES -/* The native element catalog capacity (14.7 external role). */ -#define DCPAUD_ELEMENTS_V14_7 SZ_256K /* * SIO takes one of its 64 descriptor slots per period, shared by all its @@ -97,6 +103,8 @@ struct dcp_audio { ktime_t void_start; u64 void_periods; struct delayed_work void_work; + /* The stream holds a runtime PM reference, put in sync_stop; stream lock */ + bool pm_held; void *elements; void *productattrs; @@ -140,11 +148,6 @@ static inline bool dcpaud_native_link(struct dcp_audio *dcpaud) #endif } -static inline size_t dcpaud_elements_size(struct dcp_audio *dcpaud) -{ - return dcpaud_native_link(dcpaud) ? DCPAUD_ELEMENTS_V14_7 : DCPAUD_ELEMENTS_MAXSIZE; -} - static const struct snd_pcm_hardware dcp_pcm_hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED, @@ -193,7 +196,7 @@ static int dcpaud_read_remote_info(struct dcp_audio *dcpaud) int ret; ret = dcp_audiosrv_get_elements(dcpaud->dcp_dev, dcpaud->elements, - dcpaud_elements_size(dcpaud)); + DCPAUD_ELEMENTS_MAXSIZE); if (ret < 0) return ret; @@ -250,7 +253,7 @@ static void dcpaud_consult_elements(struct dcp_audio *dcpaud, struct dcp_parse_ctx elements = { .dcp = dev_get_drvdata(dcpaud->dcp_dev), .blob = dcpaud->elements + 4, - .len = dcpaud_elements_size(dcpaud) - 4, + .len = DCPAUD_ELEMENTS_MAXSIZE - 4, .pos = 0, #if IS_ENABLED(CONFIG_DRM_APPLE_DCP_T6030_AUDIO) .v14_7_audio = dcpaud->native_link, @@ -270,7 +273,7 @@ static int dcpaud_select_cookie(struct dcp_audio *dcpaud, struct dcp_parse_ctx elements = { .dcp = dev_get_drvdata(dcpaud->dcp_dev), .blob = dcpaud->elements + 4, - .len = dcpaud_elements_size(dcpaud) - 4, + .len = DCPAUD_ELEMENTS_MAXSIZE - 4, .pos = 0, #if IS_ENABLED(CONFIG_DRM_APPLE_DCP_T6030_AUDIO) .v14_7_audio = dcpaud->native_link, @@ -647,7 +650,11 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (dcpaud->open_unplugged || !dcpaud_connection_up(dcpaud)) return dcpaud_void_start(substream); - WARN_ON(pm_runtime_get_sync(dcpaud->dev) < 0); + /* A START can follow a failed one before sync_stop ran */ + 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) @@ -680,9 +687,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; @@ -692,14 +698,32 @@ static int dcp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } /* - * ALSA calls this once a stream has stopped, outside the stream lock, before - * the next hw_params, prepare or hw_free and at close. + * ALSA calls this once a stream has stopped (STOP, SUSPEND, or the STOP that + * undoes a failed START), outside the stream lock, before the next + * hw_params, prepare or hw_free, at close, at system suspend and at + * disconnect. The DMA terminate STOP started is asynchronous: putting the + * runtime PM reference in 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), taking DP audio down on every output until + * a reboot. 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; cancel_delayed_work_sync(&dcpaud->void_work); + snd_dmaengine_pcm_sync_stop(substream); + + 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; } @@ -779,7 +803,7 @@ static int dcpaud_pin_rate_format(struct dcp_audio *dcpaud, struct dcp_parse_ctx elements = { .dcp = dev_get_drvdata(dcpaud->dcp_dev), .blob = dcpaud->elements + 4, - .len = dcpaud_elements_size(dcpaud) - 4, + .len = DCPAUD_ELEMENTS_MAXSIZE - 4, .pos = 0, .v14_7_audio = dcpaud->native_link, }; @@ -1292,7 +1316,7 @@ static int dcpaud_select_default_cookie(struct dcp_audio *dcpaud) struct dcp_parse_ctx elements = { .dcp = dev_get_drvdata(dcpaud->dcp_dev), .blob = dcpaud->elements + 4, - .len = dcpaud_elements_size(dcpaud) - 4, + .len = DCPAUD_ELEMENTS_MAXSIZE - 4, .pos = 0, .v14_7_audio = dcpaud->native_link, }; @@ -1838,16 +1862,9 @@ static int dcpaud_comp_bind(struct device *dev, struct device *main, void *data) dcpaud->dcp_dev = &dcp_pdev->dev; #if IS_ENABLED(CONFIG_DRM_APPLE_DCP_T6030_AUDIO) - /* Native external roles share the link lifecycle and element catalog. */ - if (dcp_audiosrv_native_link(dcpaud->dcp_dev)) { - void *elements = devm_kzalloc(dev, DCPAUD_ELEMENTS_V14_7, GFP_KERNEL); - - if (!elements) - goto rpm_put; - devm_kfree(dev, dcpaud->elements); - dcpaud->elements = elements; + /* Native external roles share the link lifecycle. */ + if (dcp_audiosrv_native_link(dcpaud->dcp_dev)) dcpaud->native_link = true; - } #endif dma_pdev = of_find_device_by_node(dma_spec.np); @@ -1870,7 +1887,7 @@ static int dcpaud_comp_bind(struct device *dev, struct device *main, void *data) dcpaud_expose_debugfs_blob(dcpaud, "selected_cookie", &dcpaud->selected_cookie, sizeof(dcpaud->selected_cookie)); dcpaud_expose_debugfs_blob(dcpaud, "elements", dcpaud->elements, - dcpaud_elements_size(dcpaud)); + DCPAUD_ELEMENTS_MAXSIZE); dcpaud_expose_debugfs_blob(dcpaud, "product_attrs", dcpaud->productattrs, DCPAUD_PRODUCTATTRS_MAXSIZE); } @@ -1922,18 +1939,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); @@ -2048,6 +2074,7 @@ void dcp_audio_unregister(void) #if IS_ENABLED(CONFIG_DRM_APPLE_DISPLAY_KUNIT_TEST) #include "audio-no-sio-test.c" +#include "audio-legacy-test.c" #if IS_ENABLED(CONFIG_DRM_APPLE_DCP_T6030_AUDIO) #include "audio-link-test.c" #endif diff --git a/drivers/gpu/drm/apple/av.c b/drivers/gpu/drm/apple/av.c index 6b350cb0b9414..7f1a45587457b 100644 --- a/drivers/gpu/drm/apple/av.c +++ b/drivers/gpu/drm/apple/av.c @@ -229,10 +229,13 @@ int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie) int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie) { - struct apple_dcp *dcp = dev_get_drvdata(dev); - struct audiosrv_data *asrv = dcp->audiosrv; + struct apple_dcp *dcp; + struct audiosrv_data *asrv; int ret; + KUNIT_STATIC_STUB_REDIRECT(dcp_audiosrv_startlink, dev, cookie); + dcp = dev_get_drvdata(dev); + asrv = dcp->audiosrv; down_write(&asrv->srv_rwsem); ret = afk_service_call(asrv->srv, 0, asrv->cmds.start_link, cookie, sizeof(*cookie), 64 - sizeof(*cookie), NULL, 0, @@ -244,10 +247,13 @@ int dcp_audiosrv_startlink(struct device *dev, struct dcp_sound_cookie *cookie) int dcp_audiosrv_stoplink(struct device *dev) { - struct apple_dcp *dcp = dev_get_drvdata(dev); - struct audiosrv_data *asrv = dcp->audiosrv; + struct apple_dcp *dcp; + struct audiosrv_data *asrv; int ret; + KUNIT_STATIC_STUB_REDIRECT(dcp_audiosrv_stoplink, dev); + dcp = dev_get_drvdata(dev); + asrv = dcp->audiosrv; down_write(&asrv->srv_rwsem); ret = afk_service_call(asrv->srv, 0, asrv->cmds.stop_link, NULL, 0, 64, NULL, 0, 64); @@ -564,12 +570,12 @@ dcp_audiosrv_osobject_call(struct apple_epic_service *service, u16 group, static_assert(sizeof(*hdr) == 48); size_t received = 0, used; size_t bfr_len = output_maxsize + sizeof(*hdr); - void *bfr __free(kfree) = NULL; + void *bfr __free(kvfree) = NULL; int ret; KUNIT_STATIC_STUB_REDIRECT(dcp_audiosrv_osobject_call, service, group, command, output, output_maxsize, output_size); - bfr = kzalloc(bfr_len, GFP_KERNEL); + bfr = kvzalloc(bfr_len, GFP_KERNEL); if (!bfr) return -ENOMEM; @@ -633,13 +639,16 @@ static bool av_elements_need_more(int ret, size_t size, size_t cap, bool empty) int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize) { - struct apple_dcp *dcp = dev_get_drvdata(dev); - struct audiosrv_data *asrv = dcp->audiosrv; + struct apple_dcp *dcp; + struct audiosrv_data *asrv; struct apple_epic_service *srv; size_t size = 0, cap; bool compact, empty; int ret; + KUNIT_STATIC_STUB_REDIRECT(dcp_audiosrv_get_elements, dev, elements, maxsize); + dcp = dev_get_drvdata(dev); + asrv = dcp->audiosrv; if (!asrv) return -ENODEV; scoped_guard(rwsem_read, &asrv->srv_rwsem) @@ -687,12 +696,15 @@ int dcp_audiosrv_get_elements(struct device *dev, void *elements, size_t maxsize int dcp_audiosrv_get_product_attrs(struct device *dev, void *attrs, size_t maxsize) { - struct apple_dcp *dcp = dev_get_drvdata(dev); - struct audiosrv_data *asrv = dcp->audiosrv; + struct apple_dcp *dcp; + struct audiosrv_data *asrv; struct apple_epic_service *srv; size_t size; int ret; + KUNIT_STATIC_STUB_REDIRECT(dcp_audiosrv_get_product_attrs, dev, attrs, maxsize); + dcp = dev_get_drvdata(dev); + asrv = dcp->audiosrv; if (!asrv) return -ENODEV; scoped_guard(rwsem_read, &asrv->srv_rwsem) diff --git a/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-edid.py b/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-edid.py index bcced2f684cfa..5448555012263 100644 --- a/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-edid.py +++ b/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-edid.py @@ -27,6 +27,8 @@ def function(name): #include "epic/dpavserv-edid.h" #define GFP_KERNEL 0 #define kzalloc(n,f) calloc(1,n) +#define kvzalloc(n,f) calloc(1,n) +#define kvfree(p) free(p) #define cpu_to_le64(x) (x) #define le64_to_cpu(x) (x) #define READ_ONCE(x) (x) diff --git a/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-hpd.py b/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-hpd.py index b21cba6c7665c..7a9881784a816 100644 --- a/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-hpd.py +++ b/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-hpd.py @@ -36,6 +36,8 @@ def function(source, name): typedef u32 __le32; #define GFP_KERNEL 0 #define kzalloc(n,f) calloc(1,n) +#define kvzalloc(n,f) calloc(1,n) +#define kvfree(p) free(p) #define kfree(p) free(p) #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) diff --git a/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-service.py b/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-service.py index 19381f919d7e8..4c0164dce1aef 100644 --- a/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-service.py +++ b/tools/testing/selftests/drivers/gpu/apple-afkv2/extract-service.py @@ -24,6 +24,8 @@ #define GFP_KERNEL 0 #define kzalloc(n,f) calloc(1,n) #define kfree(p) free(p) +#define kvzalloc(n,f) calloc(1,n) +#define kvfree(p) free(p) #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define cpu_to_le16(x) (x) diff --git a/tools/testing/selftests/drivers/gpu/apple-dcp/audio.py b/tools/testing/selftests/drivers/gpu/apple-dcp/audio.py index 44e2bd9768089..8b0af0f2558e5 100644 --- a/tools/testing/selftests/drivers/gpu/apple-dcp/audio.py +++ b/tools/testing/selftests/drivers/gpu/apple-dcp/audio.py @@ -24,7 +24,7 @@ def reply_bounds(root): #define GFP_KERNEL 0 static void free_buffer(void *p) {free(*(void **)p);} #define __free(x) __attribute__((cleanup(free_buffer))) -static void *kzalloc(size_t n, int flags) {(void)flags;return calloc(1,n);} +static void *kvzalloc(size_t n, int flags) {(void)flags;return calloc(1,n);} #define cpu_to_le64(x) (x) #define le64_to_cpu(x) (x) struct apple_dcp_afkep {bool compact;};