diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 51803d98b175..6242852b3880 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -609,11 +609,11 @@ int kgsl_context_init(struct kgsl_device_private *dev_priv, if (id == -ENOSPC) { /* * Before declaring that there are no contexts left try - * flushing the event workqueue just in case there are + * flushing the event worker just in case there are * detached contexts waiting to finish */ - flush_workqueue(device->events_wq); + kthread_flush_worker(device->events_worker); id = _kgsl_get_context_id(device); } @@ -5334,6 +5334,16 @@ int kgsl_device_platform_probe(struct kgsl_device *device) if (status) goto error; + device->events_worker = kthread_create_worker(0, "kgsl-events"); + + if (IS_ERR(device->events_worker)) { + status = PTR_ERR(device->events_worker); + dev_err(device->dev, "Failed to create events worker ret=%d\n", status); + goto error_pwrctrl_close; + } + + sched_set_fifo(device->events_worker->task); + if (!devm_request_mem_region(device->dev, device->reg_phys, device->reg_len, device->name)) { dev_err(device->dev, "request_mem_region failed\n"); @@ -5422,9 +5432,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device) PM_QOS_DEFAULT_VALUE); } - device->events_wq = alloc_workqueue("kgsl-events", - WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS | WQ_HIGHPRI, 0); - /* Initialize the snapshot engine */ kgsl_device_snapshot_init(device); @@ -5436,6 +5443,9 @@ int kgsl_device_platform_probe(struct kgsl_device *device) error_close_mmu: kgsl_mmu_close(device); error_pwrctrl_close: + if (!IS_ERR(device->events_worker)) + kthread_destroy_worker(device->events_worker); + kgsl_pwrctrl_close(device); error: kgsl_device_debugfs_close(device); @@ -5446,7 +5456,7 @@ EXPORT_SYMBOL(kgsl_device_platform_probe); void kgsl_device_platform_remove(struct kgsl_device *device) { - destroy_workqueue(device->events_wq); + kthread_destroy_worker(device->events_worker); kfree(device->dev->dma_parms); device->dev->dma_parms = NULL; @@ -5481,6 +5491,16 @@ _flush_mem_workqueue(struct work_struct *work) static void kgsl_core_exit(void) { + if (kgsl_driver.workqueue) { + destroy_workqueue(kgsl_driver.workqueue); + kgsl_driver.workqueue = NULL; + } + + if (kgsl_driver.mem_workqueue) { + destroy_workqueue(kgsl_driver.mem_workqueue); + kgsl_driver.mem_workqueue = NULL; + } + kgsl_events_exit(); kgsl_core_debugfs_close(); @@ -5579,9 +5599,21 @@ static int __init kgsl_core_init(void) kgsl_driver.workqueue = alloc_workqueue("kgsl-workqueue", WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0); + if (!kgsl_driver.workqueue) { + pr_err("kgsl: Failed to allocate kgsl workqueue\n"); + result = -ENOMEM; + goto err; + } + kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + if (!kgsl_driver.mem_workqueue) { + pr_err("kgsl: Failed to allocate mem workqueue\n"); + result = -ENOMEM; + goto err; + } + INIT_WORK(&kgsl_driver.mem_work, _flush_mem_workqueue); kthread_init_worker(&kgsl_driver.worker); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 0dc906aa3d9e..95703d31a659 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -316,7 +316,7 @@ typedef void (*kgsl_event_func)(struct kgsl_device *, struct kgsl_event_group *, * @priv: Private data passed to the callback function * @node: List node for the kgsl_event_group list * @created: Jiffies when the event was created - * @work: Work struct for dispatching the callback + * @work: kthread_work struct for dispatching the callback * @result: KGSL event result type to pass to the callback * group: The event group this event belongs to */ @@ -328,7 +328,7 @@ struct kgsl_event { void *priv; struct list_head node; unsigned int created; - struct work_struct work; + struct kthread_work work; int result; struct kgsl_event_group *group; }; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 55008e288e72..9137fc6138ed 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __KGSL_DEVICE_H #define __KGSL_DEVICE_H @@ -311,7 +311,7 @@ struct kgsl_device { struct kgsl_pwrscale pwrscale; int reset_counter; /* Track how many GPU core resets have occurred */ - struct workqueue_struct *events_wq; + struct kthread_worker *events_worker; struct device *busmondev; /* pseudo dev for GPU BW voting governor */ diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index b2c07c38e0d5..a6705d752299 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -21,17 +22,17 @@ static inline void signal_event(struct kgsl_device *device, { list_del(&event->node); event->result = result; - queue_work(device->events_wq, &event->work); + kthread_queue_work(device->events_worker, &event->work); } /** * _kgsl_event_worker() - Work handler for processing GPU event callbacks - * @work: Pointer to the work_struct for the event + * @work: Pointer to the kthread_work for the event * - * Each event callback has its own work struct and is run on a event specific - * workqeuue. This is the worker that queues up the event callback function. + * Each event callback has its own kthread_work struct and is run on a event specific + * worker thread. This is the worker that queues up the event callback function. */ -static void _kgsl_event_worker(struct work_struct *work) +static void _kgsl_event_worker(struct kthread_work *work) { struct kgsl_event *event = container_of(work, struct kgsl_event, work); int id = KGSL_CONTEXT_ID(event->context); @@ -273,7 +274,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group, event->created = jiffies; event->group = group; - INIT_WORK(&event->work, _kgsl_event_worker); + kthread_init_work(&event->work, _kgsl_event_worker); trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func); @@ -288,7 +289,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group, if (timestamp_cmp(retired, timestamp) >= 0) { event->result = KGSL_EVENT_RETIRED; - queue_work(device->events_wq, &event->work); + kthread_queue_work(device->events_worker, &event->work); spin_unlock(&group->lock); return 0; } diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 892e9e69f1d0..3ea7d28f39c1 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -1028,6 +1028,13 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) } else data->bus.num = 0; + pwrscale->devfreq_wq = create_freezable_workqueue("kgsl_devfreq_wq"); + if (!pwrscale->devfreq_wq) { + dev_err(device->dev, "Failed to allocate kgsl devfreq workqueue\n"); + device->pwrscale.enabled = false; + return -ENOMEM; + } + devfreq = devfreq_add_device(dev, &pwrscale->gpu_profile.profile, governor, pwrscale->gpu_profile.private_data); if (IS_ERR(devfreq)) { @@ -1074,7 +1081,6 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) ret = sysfs_create_link(&device->dev->kobj, &devfreq->dev.kobj, "devfreq"); - pwrscale->devfreq_wq = create_freezable_workqueue("kgsl_devfreq_wq"); INIT_WORK(&pwrscale->devfreq_suspend_ws, do_devfreq_suspend); INIT_WORK(&pwrscale->devfreq_resume_ws, do_devfreq_resume); INIT_WORK(&pwrscale->devfreq_notify_ws, do_devfreq_notify); @@ -1125,8 +1131,13 @@ void kgsl_pwrscale_close(struct kgsl_device *device) devfreq_cooling_unregister(pwrscale->cooling_dev); kgsl_pwrscale_midframe_timer_cancel(device); - flush_workqueue(pwrscale->devfreq_wq); - destroy_workqueue(pwrscale->devfreq_wq); + + if (pwrscale->devfreq_wq) { + flush_workqueue(pwrscale->devfreq_wq); + destroy_workqueue(pwrscale->devfreq_wq); + pwrscale->devfreq_wq = NULL; + } + devfreq_remove_device(device->pwrscale.devfreqptr); kfree(kgsl_midframe); kgsl_midframe = NULL; diff --git a/scripts/as-version.sh b/scripts/as-version.sh index 1a21495e9ff0..532270bd4b7e 100755 --- a/scripts/as-version.sh +++ b/scripts/as-version.sh @@ -51,12 +51,9 @@ set -- $(LC_ALL=C "$@" -Wa,--version -c -x assembler /dev/null -o /dev/null 2>/d IFS=' ' set -- $1 -min_tool_version=$(dirname $0)/min-tool-version.sh - if [ "$1" = GNU -a "$2" = assembler ]; then shift $(($# - 1)) version=$1 - min_version=$($min_tool_version binutils) name=GNU else echo "$orig_args: unknown assembler invoked" >&2 @@ -68,15 +65,5 @@ fi version=${version%-*} cversion=$(get_canonical_version $version) -min_cversion=$(get_canonical_version $min_version) - -if [ "$cversion" -lt "$min_cversion" ]; then - echo >&2 "***" - echo >&2 "*** Assembler is too old." - echo >&2 "*** Your $name assembler version: $version" - echo >&2 "*** Minimum $name assembler version: $min_version" - echo >&2 "***" - exit 1 -fi echo $name $cversion diff --git a/techpack/display/msm/sde/sde_crtc.c b/techpack/display/msm/sde/sde_crtc.c index c44e1644cf55..e887d17f9e54 100644 --- a/techpack/display/msm/sde/sde_crtc.c +++ b/techpack/display/msm/sde/sde_crtc.c @@ -216,6 +216,47 @@ static int _sde_debugfs_fps_status(struct inode *inode, struct file *file) } #endif +static ssize_t early_wakeup_store(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct drm_crtc *crtc; + struct sde_crtc *sde_crtc; + struct msm_drm_private *priv; + u32 crtc_id; + bool trigger; + + if (!device || !buf || !count) { + SDE_ERROR("invalid input param(s)\n"); + return -EINVAL; + } + + if (kstrtobool(buf, &trigger) < 0) + return -EINVAL; + + if (!trigger) + return count; + + crtc = dev_get_drvdata(device); + if (!crtc || !crtc->dev || !crtc->dev->dev_private) { + SDE_ERROR("invalid crtc\n"); + return -EINVAL; + } + + sde_crtc = to_sde_crtc(crtc); + priv = crtc->dev->dev_private; + + crtc_id = drm_crtc_index(crtc); + if (crtc_id >= ARRAY_SIZE(priv->disp_thread)) { + SDE_ERROR("invalid crtc index[%d]\n", crtc_id); + return -EINVAL; + } + + kthread_queue_work(&priv->disp_thread[crtc_id].worker, + &sde_crtc->early_wakeup_work); + + return count; +} + static ssize_t fps_periodicity_ms_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { @@ -403,12 +444,14 @@ static DEVICE_ATTR_RO(vsync_event); static DEVICE_ATTR_RO(measured_fps); static DEVICE_ATTR_RW(fps_periodicity_ms); static DEVICE_ATTR_RO(retire_frame_event); +static DEVICE_ATTR_WO(early_wakeup); static struct attribute *sde_crtc_dev_attrs[] = { &dev_attr_vsync_event.attr, &dev_attr_measured_fps.attr, &dev_attr_fps_periodicity_ms.attr, &dev_attr_retire_frame_event.attr, + &dev_attr_early_wakeup.attr, NULL }; @@ -6266,6 +6309,40 @@ static void __sde_crtc_idle_notify_work(struct kthread_work *work) } } +/* + * __sde_crtc_early_wakeup_work - trigger early wakeup from user space + */ +static void __sde_crtc_early_wakeup_work(struct kthread_work *work) +{ + struct sde_crtc *sde_crtc = container_of(work, struct sde_crtc, + early_wakeup_work); + struct drm_crtc *crtc; + struct drm_device *dev; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; + + if (!sde_crtc) { + SDE_ERROR("invalid sde crtc\n"); + return; + } + + if (!sde_crtc->enabled) { + SDE_INFO("sde crtc is not enabled\n"); + return; + } + + crtc = &sde_crtc->base; + dev = crtc->dev; + if (!dev) { + SDE_ERROR("invalid drm device\n"); + return; + } + + priv = dev->dev_private; + sde_kms = to_sde_kms(priv->kms); + sde_kms_trigger_early_wakeup(sde_kms, crtc); +} + /* initialize crtc */ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) { @@ -6357,6 +6434,8 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) kthread_init_delayed_work(&sde_crtc->idle_notify_work, __sde_crtc_idle_notify_work); + kthread_init_work(&sde_crtc->early_wakeup_work, + __sde_crtc_early_wakeup_work); SDE_DEBUG("crtc=%d new_llcc=%d, old_llcc=%d\n", crtc->base.id, diff --git a/techpack/display/msm/sde/sde_crtc.h b/techpack/display/msm/sde/sde_crtc.h index bd16cd3da6fe..9d0526888922 100644 --- a/techpack/display/msm/sde/sde_crtc.h +++ b/techpack/display/msm/sde/sde_crtc.h @@ -272,6 +272,7 @@ struct sde_crtc_misr_info { * @misr_frame_count : misr frame count provided by client * @misr_data : store misr data before turning off the clocks. * @idle_notify_work: delayed worker to notify idle timeout to user space + * @early_wakeup_work: work to trigger early wakeup * @power_event : registered power event handle * @cur_perf : current performance committed to clock/bandwidth driver * @plane_mask_old: keeps track of the planes used in the previous commit @@ -349,6 +350,7 @@ struct sde_crtc { bool misr_enable_debugfs; u32 misr_frame_count; struct kthread_delayed_work idle_notify_work; + struct kthread_work early_wakeup_work; struct sde_power_event *power_event; diff --git a/techpack/display/msm/sde/sde_encoder.c b/techpack/display/msm/sde/sde_encoder.c index cbb8b5b87bbc..c7b5cb0f6777 100644 --- a/techpack/display/msm/sde/sde_encoder.c +++ b/techpack/display/msm/sde/sde_encoder.c @@ -6320,3 +6320,27 @@ void sde_encoder_recovery_events_handler(struct drm_encoder *encoder, sde_enc = to_sde_encoder_virt(encoder); sde_enc->recovery_events_enabled = enabled; } + +void sde_encoder_trigger_early_wakeup(struct drm_encoder *drm_enc) +{ + struct sde_encoder_virt *sde_enc = NULL; + struct msm_drm_private *priv = NULL; + + priv = drm_enc->dev->dev_private; + sde_enc = to_sde_encoder_virt(drm_enc); + if (!sde_enc->crtc || (sde_enc->crtc->index + >= ARRAY_SIZE(priv->disp_thread))) { + SDE_DEBUG_ENC(sde_enc, + "invalid cached CRTC: %d or crtc index: %d\n", + sde_enc->crtc == NULL, + sde_enc->crtc ? sde_enc->crtc->index : -EINVAL); + return; + } + + if (sde_enc->rc_state == SDE_ENC_RC_STATE_IDLE) { + SDE_ATRACE_BEGIN("sde_encoder_resource_control"); + sde_encoder_resource_control(drm_enc, + SDE_ENC_RC_EVENT_EARLY_WAKEUP); + SDE_ATRACE_END("sde_encoder_resource_control"); + } +} diff --git a/techpack/display/msm/sde/sde_encoder.h b/techpack/display/msm/sde/sde_encoder.h index ebee140da628..b70cfd590956 100644 --- a/techpack/display/msm/sde/sde_encoder.h +++ b/techpack/display/msm/sde/sde_encoder.h @@ -399,4 +399,10 @@ static inline struct sde_kms *sde_encoder_get_kms(struct drm_encoder *drm_enc) return to_sde_kms(priv->kms); } +/** + * sde_encoder_trigger_early_wakeup - trigger early wake up + * @drm_enc: Pointer to drm encoder structure + */ +void sde_encoder_trigger_early_wakeup(struct drm_encoder *drm_enc); + #endif /* __SDE_ENCODER_H__ */ diff --git a/techpack/display/msm/sde/sde_kms.c b/techpack/display/msm/sde/sde_kms.c index dfaa9a750de6..7a7b9e2a2b46 100644 --- a/techpack/display/msm/sde/sde_kms.c +++ b/techpack/display/msm/sde/sde_kms.c @@ -3945,3 +3945,26 @@ int sde_kms_handle_recovery(struct drm_encoder *encoder) SDE_EVT32(DRMID(encoder), MSM_ENC_ACTIVE_REGION); return sde_encoder_wait_for_event(encoder, MSM_ENC_ACTIVE_REGION); } + +void sde_kms_trigger_early_wakeup(struct sde_kms *sde_kms, + struct drm_crtc *crtc) +{ + struct msm_drm_private *priv; + struct drm_encoder *drm_enc; + + if (!sde_kms || !crtc) { + SDE_ERROR("invalid argument sde_kms %pK crtc %pK\n", + sde_kms, crtc); + return; + } + + priv = sde_kms->dev->dev_private; + + SDE_ATRACE_BEGIN("sde_kms_trigger_early_wakeup"); + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc->state->encoder_mask) + sde_encoder_trigger_early_wakeup(drm_enc); + + if (sde_kms->first_kickoff) + sde_power_scale_reg_bus(&priv->phandle, VOTE_INDEX_HIGH, false); + SDE_ATRACE_END("sde_kms_trigger_early_wakeup"); +} diff --git a/techpack/display/msm/sde/sde_kms.h b/techpack/display/msm/sde/sde_kms.h index b9d9333f6a13..75d0a0eaa0ae 100644 --- a/techpack/display/msm/sde/sde_kms.h +++ b/techpack/display/msm/sde/sde_kms.h @@ -671,4 +671,13 @@ int sde_kms_handle_recovery(struct drm_encoder *encoder); */ void sde_kms_update_pm_qos_irq_request(struct sde_kms *sde_kms, bool enable, bool skip_lock); + +/** + * sde_kms_trigger_early_wakeup - trigger early wake up + * @sde_kms: pointer to sde_kms structure + * @crtc: pointer to drm_crtc structure + */ +void sde_kms_trigger_early_wakeup(struct sde_kms *sde_kms, + struct drm_crtc *crtc); + #endif /* __sde_kms_H__ */