Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions drivers/gpu/drm/apple/afk-service-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ static void afk_service_completed_reader_test(struct kunit *test)
KUNIT_EXPECT_TRUE(test, afk_service_reinit(service, NULL, &reusable_ops, 2));
}

static void afk_service_close_pin_test(struct kunit *test)
{
struct apple_epic_service *service = test->priv;

KUNIT_EXPECT_PTR_EQ(test, afk_service_pin(NULL), NULL);
KUNIT_ASSERT_TRUE(test, afk_service_reinit(service, NULL, &reusable_ops, 1));
KUNIT_ASSERT_PTR_EQ(test, afk_service_get(service), service); /* owner */
afk_test_retire(service);
/* A close issued after teardown still pins the owner's service. */
KUNIT_ASSERT_PTR_EQ(test, afk_service_pin(service), service);
afk_service_put(service); /* owner detaches */
KUNIT_EXPECT_FALSE(test, afk_service_reinit(service, NULL, &reusable_ops, 2));
KUNIT_EXPECT_EQ(test, service->channel, 1U);
afk_service_put(service);
KUNIT_EXPECT_TRUE(test, afk_service_reinit(service, NULL, &reusable_ops, 2));
}

static void afk_service_tag_sequence_test(struct kunit *test)
{
struct apple_epic_service *service = test->priv;
Expand Down Expand Up @@ -226,6 +243,7 @@ static struct kunit_case afk_service_cases[] = {
KUNIT_CASE(afk_service_reader_pin_test),
KUNIT_CASE(afk_service_pending_reply_test),
KUNIT_CASE(afk_service_completed_reader_test),
KUNIT_CASE(afk_service_close_pin_test),
KUNIT_CASE(afk_service_tag_sequence_test),
KUNIT_CASE(afk_service_unconverted_owner_test),
{ }
Expand Down
19 changes: 19 additions & 0 deletions drivers/gpu/drm/apple/afk.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ static inline void afk_service_put(struct apple_epic_service *service)
afk_service_retry(service->ep);
}

/*
* Take another pin on a service the caller already keeps pinned, e.g. an
* owner pointer read under the owner's lock. Unlike afk_service_get() this
* also succeeds after teardown, so a close can still be issued.
*/
static inline struct apple_epic_service *afk_service_pin(struct apple_epic_service *service)
{
unsigned long flags;

if (!service)
return NULL;

spin_lock_irqsave(&service->lock, flags);
service->users++;
spin_unlock_irqrestore(&service->lock, flags);

return service;
}

static inline void afk_service_disable(struct apple_epic_service *service)
{
unsigned long flags;
Expand Down
46 changes: 35 additions & 11 deletions drivers/gpu/drm/apple/av.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,21 @@ static void av_audiosrv_init(struct apple_epic_service *service, const char *nam
{
struct apple_dcp *dcp = service->ep->dcp;
struct audiosrv_data *asrv = dcp->audiosrv;
struct apple_epic_service *old;

/* The owner pin keeps the slot from being recycled while published. */
service = afk_service_get(service);
if (!service)
return;

mutex_lock(&asrv->plug_lock);

down_write(&asrv->srv_rwsem);
old = asrv->srv;
asrv->srv = service;
up_write(&asrv->srv_rwsem);
if (old)
afk_service_put(old);

asrv->plugged = true;
mutex_unlock(&asrv->plug_lock);
Expand All @@ -112,18 +121,27 @@ static void av_audiosrv_teardown(struct apple_epic_service *service)
{
struct apple_dcp *dcp = service->ep->dcp;
struct audiosrv_data *asrv = dcp->audiosrv;
bool owned;

mutex_lock(&asrv->plug_lock);

down_write(&asrv->srv_rwsem);
asrv->srv = NULL;
owned = asrv->srv == service;
if (owned)
asrv->srv = NULL;
up_write(&asrv->srv_rwsem);

asrv->plugged = false;
if (asrv->audio_dev)
dcpaud_disconnect(asrv->audio_dev);
/* A stale generation must not unplug its successor. */
if (owned) {
asrv->plugged = false;
if (asrv->audio_dev)
dcpaud_disconnect(asrv->audio_dev);
}

mutex_unlock(&asrv->plug_lock);

if (owned)
afk_service_put(service);
}

int dcp_audiosrv_prepare(struct device *dev, struct dcp_sound_cookie *cookie)
Expand Down Expand Up @@ -279,11 +297,13 @@ static int av_audiosrv_report(struct apple_epic_service *service, u32 idx,
static const struct apple_epic_service_ops avep_ops[] = {
{
.name = "DCPAVSimpleVideoInterface",
.reusable = true,
.init = av_interface_init,
.teardown = av_interface_teardown,
},
{
.name = "DCPAVAudioInterface",
.reusable = true,
.init = av_audiosrv_init,
.report = av_audiosrv_report,
.teardown = av_audiosrv_teardown,
Expand All @@ -298,28 +318,30 @@ void av_service_connect(struct apple_dcp *dcp)
int ret;

scoped_guard(rwsem_write, &asrv->srv_rwsem) {
if (!asrv->srv)
service = afk_service_pin(asrv->srv);
if (!service)
return;
service = asrv->srv;
}

/* open AV audio service */
dev_info(dcp->dev, "%s: starting audio service, plugged:%d\n", __func__, asrv->plugged);
if (asrv->is_open)
return;
goto out;

ret = afk_service_call(service, 0, asrv->cmds.open, NULL, 0, 32,
NULL, 0, 32);
if (ret) {
dev_err(dcp->dev, "error opening audio service: %d\n", ret);
return;
goto out;
}
mutex_lock(&asrv->plug_lock);
asrv->is_open = true;

if (asrv->audio_dev)
dcpaud_connect(asrv->audio_dev, asrv->plugged);
mutex_unlock(&asrv->plug_lock);
out:
afk_service_put(service);
}

void av_service_disconnect(struct apple_dcp *dcp)
Expand All @@ -329,15 +351,15 @@ void av_service_disconnect(struct apple_dcp *dcp)
int ret;

scoped_guard(rwsem_write, &asrv->srv_rwsem) {
if (!asrv->srv)
service = afk_service_pin(asrv->srv);
if (!service)
return;
service = asrv->srv;
}

/* close AV audio service */
dev_info(dcp->dev, "%s: stopping audio service\n", __func__);
if (!asrv->is_open)
return;
goto out;

mutex_lock(&asrv->plug_lock);

Expand All @@ -354,6 +376,8 @@ void av_service_disconnect(struct apple_dcp *dcp)
if (service->torndown)
afk_service_disable(service);
asrv->is_open = false;
out:
afk_service_put(service);
}

static void av_work_service_start(struct work_struct *work)
Expand Down