| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793 |
- // SPDX-License-Identifier: GPL-2.0-only OR MIT
- /* Copyright (c) 2023 Imagination Technologies Ltd. */
- #include "pvr_context.h"
- #include "pvr_device.h"
- #include "pvr_drv.h"
- #include "pvr_gem.h"
- #include "pvr_hwrt.h"
- #include "pvr_job.h"
- #include "pvr_mmu.h"
- #include "pvr_power.h"
- #include "pvr_rogue_fwif.h"
- #include "pvr_rogue_fwif_client.h"
- #include "pvr_stream.h"
- #include "pvr_stream_defs.h"
- #include "pvr_sync.h"
- #include <drm/drm_exec.h>
- #include <drm/drm_gem.h>
- #include <linux/types.h>
- #include <uapi/drm/pvr_drm.h>
- static void pvr_job_release(struct kref *kref)
- {
- struct pvr_job *job = container_of(kref, struct pvr_job, ref_count);
- xa_erase(&job->pvr_dev->job_ids, job->id);
- pvr_hwrt_data_put(job->hwrt);
- pvr_context_put(job->ctx);
- WARN_ON(job->paired_job);
- pvr_queue_job_cleanup(job);
- pvr_job_release_pm_ref(job);
- kfree(job->cmd);
- kfree(job);
- }
- /**
- * pvr_job_put() - Release reference on job
- * @job: Target job.
- */
- void
- pvr_job_put(struct pvr_job *job)
- {
- if (job)
- kref_put(&job->ref_count, pvr_job_release);
- }
- /**
- * pvr_job_process_stream() - Build job FW structure from stream
- * @pvr_dev: Device pointer.
- * @cmd_defs: Stream definition.
- * @stream: Pointer to command stream.
- * @stream_size: Size of command stream, in bytes.
- * @job: Pointer to job.
- *
- * Caller is responsible for freeing the output structure.
- *
- * Returns:
- * * 0 on success,
- * * -%ENOMEM on out of memory, or
- * * -%EINVAL on malformed stream.
- */
- static int
- pvr_job_process_stream(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
- void *stream, u32 stream_size, struct pvr_job *job)
- {
- int err;
- job->cmd = kzalloc(cmd_defs->dest_size, GFP_KERNEL);
- if (!job->cmd)
- return -ENOMEM;
- job->cmd_len = cmd_defs->dest_size;
- err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, job->cmd);
- if (err)
- kfree(job->cmd);
- return err;
- }
- static int pvr_fw_cmd_init(struct pvr_device *pvr_dev, struct pvr_job *job,
- const struct pvr_stream_cmd_defs *stream_def,
- u64 stream_userptr, u32 stream_len)
- {
- void *stream;
- int err;
- stream = kzalloc(stream_len, GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
- if (copy_from_user(stream, u64_to_user_ptr(stream_userptr), stream_len)) {
- err = -EFAULT;
- goto err_free_stream;
- }
- err = pvr_job_process_stream(pvr_dev, stream_def, stream, stream_len, job);
- err_free_stream:
- kfree(stream);
- return err;
- }
- static u32
- convert_geom_flags(u32 in_flags)
- {
- u32 out_flags = 0;
- if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_FIRST)
- out_flags |= ROGUE_GEOM_FLAGS_FIRSTKICK;
- if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_LAST)
- out_flags |= ROGUE_GEOM_FLAGS_LASTKICK;
- if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_SINGLE_CORE)
- out_flags |= ROGUE_GEOM_FLAGS_SINGLE_CORE;
- return out_flags;
- }
- static u32
- convert_frag_flags(u32 in_flags)
- {
- u32 out_flags = 0;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_SINGLE_CORE)
- out_flags |= ROGUE_FRAG_FLAGS_SINGLE_CORE;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_DEPTHBUFFER)
- out_flags |= ROGUE_FRAG_FLAGS_DEPTHBUFFER;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_STENCILBUFFER)
- out_flags |= ROGUE_FRAG_FLAGS_STENCILBUFFER;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_PREVENT_CDM_OVERLAP)
- out_flags |= ROGUE_FRAG_FLAGS_PREVENT_CDM_OVERLAP;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_SCRATCHBUFFER)
- out_flags |= ROGUE_FRAG_FLAGS_SCRATCHBUFFER;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_GET_VIS_RESULTS)
- out_flags |= ROGUE_FRAG_FLAGS_GET_VIS_RESULTS;
- if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE)
- out_flags |= ROGUE_FRAG_FLAGS_DISABLE_PIXELMERGE;
- return out_flags;
- }
- static int
- pvr_geom_job_fw_cmd_init(struct pvr_job *job,
- struct drm_pvr_job *args)
- {
- struct rogue_fwif_cmd_geom *cmd;
- int err;
- if (args->flags & ~DRM_PVR_SUBMIT_JOB_GEOM_CMD_FLAGS_MASK)
- return -EINVAL;
- if (job->ctx->type != DRM_PVR_CTX_TYPE_RENDER)
- return -EINVAL;
- if (!job->hwrt)
- return -EINVAL;
- job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_GEOM;
- err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_geom_stream,
- args->cmd_stream, args->cmd_stream_len);
- if (err)
- return err;
- cmd = job->cmd;
- cmd->cmd_shared.cmn.frame_num = 0;
- cmd->flags = convert_geom_flags(args->flags);
- pvr_fw_object_get_fw_addr(job->hwrt->fw_obj, &cmd->cmd_shared.hwrt_data_fw_addr);
- return 0;
- }
- static int
- pvr_frag_job_fw_cmd_init(struct pvr_job *job,
- struct drm_pvr_job *args)
- {
- struct rogue_fwif_cmd_frag *cmd;
- int err;
- if (args->flags & ~DRM_PVR_SUBMIT_JOB_FRAG_CMD_FLAGS_MASK)
- return -EINVAL;
- if (job->ctx->type != DRM_PVR_CTX_TYPE_RENDER)
- return -EINVAL;
- if (!job->hwrt)
- return -EINVAL;
- job->fw_ccb_cmd_type = (args->flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_PARTIAL_RENDER) ?
- ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR :
- ROGUE_FWIF_CCB_CMD_TYPE_FRAG;
- err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_frag_stream,
- args->cmd_stream, args->cmd_stream_len);
- if (err)
- return err;
- cmd = job->cmd;
- cmd->cmd_shared.cmn.frame_num = 0;
- cmd->flags = convert_frag_flags(args->flags);
- pvr_fw_object_get_fw_addr(job->hwrt->fw_obj, &cmd->cmd_shared.hwrt_data_fw_addr);
- return 0;
- }
- static u32
- convert_compute_flags(u32 in_flags)
- {
- u32 out_flags = 0;
- if (in_flags & DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_PREVENT_ALL_OVERLAP)
- out_flags |= ROGUE_COMPUTE_FLAG_PREVENT_ALL_OVERLAP;
- if (in_flags & DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_SINGLE_CORE)
- out_flags |= ROGUE_COMPUTE_FLAG_SINGLE_CORE;
- return out_flags;
- }
- static int
- pvr_compute_job_fw_cmd_init(struct pvr_job *job,
- struct drm_pvr_job *args)
- {
- struct rogue_fwif_cmd_compute *cmd;
- int err;
- if (args->flags & ~DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_FLAGS_MASK)
- return -EINVAL;
- if (job->ctx->type != DRM_PVR_CTX_TYPE_COMPUTE)
- return -EINVAL;
- job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_CDM;
- err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_compute_stream,
- args->cmd_stream, args->cmd_stream_len);
- if (err)
- return err;
- cmd = job->cmd;
- cmd->common.frame_num = 0;
- cmd->flags = convert_compute_flags(args->flags);
- return 0;
- }
- static u32
- convert_transfer_flags(u32 in_flags)
- {
- u32 out_flags = 0;
- if (in_flags & DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_SINGLE_CORE)
- out_flags |= ROGUE_TRANSFER_FLAGS_SINGLE_CORE;
- return out_flags;
- }
- static int
- pvr_transfer_job_fw_cmd_init(struct pvr_job *job,
- struct drm_pvr_job *args)
- {
- struct rogue_fwif_cmd_transfer *cmd;
- int err;
- if (args->flags & ~DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_FLAGS_MASK)
- return -EINVAL;
- if (job->ctx->type != DRM_PVR_CTX_TYPE_TRANSFER_FRAG)
- return -EINVAL;
- job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_TQ_3D;
- err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_transfer_stream,
- args->cmd_stream, args->cmd_stream_len);
- if (err)
- return err;
- cmd = job->cmd;
- cmd->common.frame_num = 0;
- cmd->flags = convert_transfer_flags(args->flags);
- return 0;
- }
- static int
- pvr_job_fw_cmd_init(struct pvr_job *job,
- struct drm_pvr_job *args)
- {
- switch (args->type) {
- case DRM_PVR_JOB_TYPE_GEOMETRY:
- return pvr_geom_job_fw_cmd_init(job, args);
- case DRM_PVR_JOB_TYPE_FRAGMENT:
- return pvr_frag_job_fw_cmd_init(job, args);
- case DRM_PVR_JOB_TYPE_COMPUTE:
- return pvr_compute_job_fw_cmd_init(job, args);
- case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
- return pvr_transfer_job_fw_cmd_init(job, args);
- default:
- return -EINVAL;
- }
- }
- /**
- * struct pvr_job_data - Helper container for pairing jobs with the
- * sync_ops supplied for them by the user.
- */
- struct pvr_job_data {
- /** @job: Pointer to the job. */
- struct pvr_job *job;
- /** @sync_ops: Pointer to the sync_ops associated with @job. */
- struct drm_pvr_sync_op *sync_ops;
- /** @sync_op_count: Number of members of @sync_ops. */
- u32 sync_op_count;
- };
- /**
- * prepare_job_syncs() - Prepare all sync objects for a single job.
- * @pvr_file: PowerVR file.
- * @job_data: Precreated job and sync_ops array.
- * @signal_array: xarray to receive signal sync objects.
- *
- * Returns:
- * * 0 on success, or
- * * Any error code returned by pvr_sync_signal_array_collect_ops(),
- * pvr_sync_add_deps_to_job(), drm_sched_job_add_resv_dependencies() or
- * pvr_sync_signal_array_update_fences().
- */
- static int
- prepare_job_syncs(struct pvr_file *pvr_file,
- struct pvr_job_data *job_data,
- struct xarray *signal_array)
- {
- struct dma_fence *done_fence;
- int err = pvr_sync_signal_array_collect_ops(signal_array,
- from_pvr_file(pvr_file),
- job_data->sync_op_count,
- job_data->sync_ops);
- if (err)
- return err;
- err = pvr_sync_add_deps_to_job(pvr_file, &job_data->job->base,
- job_data->sync_op_count,
- job_data->sync_ops, signal_array);
- if (err)
- return err;
- if (job_data->job->hwrt) {
- /* The geometry job writes the HWRT region headers, which are
- * then read by the fragment job.
- */
- struct drm_gem_object *obj =
- gem_from_pvr_gem(job_data->job->hwrt->fw_obj->gem);
- enum dma_resv_usage usage =
- dma_resv_usage_rw(job_data->job->type ==
- DRM_PVR_JOB_TYPE_GEOMETRY);
- dma_resv_lock(obj->resv, NULL);
- err = drm_sched_job_add_resv_dependencies(&job_data->job->base,
- obj->resv, usage);
- dma_resv_unlock(obj->resv);
- if (err)
- return err;
- }
- /* We need to arm the job to get the job done fence. */
- done_fence = pvr_queue_job_arm(job_data->job);
- err = pvr_sync_signal_array_update_fences(signal_array,
- job_data->sync_op_count,
- job_data->sync_ops,
- done_fence);
- return err;
- }
- /**
- * prepare_job_syncs_for_each() - Prepare all sync objects for an array of jobs.
- * @pvr_file: PowerVR file.
- * @job_data: Array of precreated jobs and their sync_ops.
- * @job_count: Number of jobs.
- * @signal_array: xarray to receive signal sync objects.
- *
- * Returns:
- * * 0 on success, or
- * * Any error code returned by pvr_vm_bind_job_prepare_syncs().
- */
- static int
- prepare_job_syncs_for_each(struct pvr_file *pvr_file,
- struct pvr_job_data *job_data,
- u32 *job_count,
- struct xarray *signal_array)
- {
- for (u32 i = 0; i < *job_count; i++) {
- int err = prepare_job_syncs(pvr_file, &job_data[i],
- signal_array);
- if (err) {
- *job_count = i;
- return err;
- }
- }
- return 0;
- }
- static struct pvr_job *
- create_job(struct pvr_device *pvr_dev,
- struct pvr_file *pvr_file,
- struct drm_pvr_job *args)
- {
- struct pvr_job *job = NULL;
- int err;
- if (!args->cmd_stream || !args->cmd_stream_len)
- return ERR_PTR(-EINVAL);
- if (args->type != DRM_PVR_JOB_TYPE_GEOMETRY &&
- args->type != DRM_PVR_JOB_TYPE_FRAGMENT &&
- (args->hwrt.set_handle || args->hwrt.data_index))
- return ERR_PTR(-EINVAL);
- job = kzalloc(sizeof(*job), GFP_KERNEL);
- if (!job)
- return ERR_PTR(-ENOMEM);
- kref_init(&job->ref_count);
- job->type = args->type;
- job->pvr_dev = pvr_dev;
- err = xa_alloc(&pvr_dev->job_ids, &job->id, job, xa_limit_32b, GFP_KERNEL);
- if (err)
- goto err_put_job;
- job->ctx = pvr_context_lookup(pvr_file, args->context_handle);
- if (!job->ctx) {
- err = -EINVAL;
- goto err_put_job;
- }
- if (args->hwrt.set_handle) {
- job->hwrt = pvr_hwrt_data_lookup(pvr_file, args->hwrt.set_handle,
- args->hwrt.data_index);
- if (!job->hwrt) {
- err = -EINVAL;
- goto err_put_job;
- }
- }
- err = pvr_job_fw_cmd_init(job, args);
- if (err)
- goto err_put_job;
- err = pvr_queue_job_init(job);
- if (err)
- goto err_put_job;
- return job;
- err_put_job:
- pvr_job_put(job);
- return ERR_PTR(err);
- }
- /**
- * pvr_job_data_fini() - Cleanup all allocs used to set up job submission.
- * @job_data: Job data array.
- * @job_count: Number of members of @job_data.
- */
- static void
- pvr_job_data_fini(struct pvr_job_data *job_data, u32 job_count)
- {
- for (u32 i = 0; i < job_count; i++) {
- pvr_job_put(job_data[i].job);
- kvfree(job_data[i].sync_ops);
- }
- }
- /**
- * pvr_job_data_init() - Init an array of created jobs, associating them with
- * the appropriate sync_ops args, which will be copied in.
- * @pvr_dev: Target PowerVR device.
- * @pvr_file: Pointer to PowerVR file structure.
- * @job_args: Job args array copied from user.
- * @job_count: Number of members of @job_args.
- * @job_data_out: Job data array.
- */
- static int pvr_job_data_init(struct pvr_device *pvr_dev,
- struct pvr_file *pvr_file,
- struct drm_pvr_job *job_args,
- u32 *job_count,
- struct pvr_job_data *job_data_out)
- {
- int err = 0, i = 0;
- for (; i < *job_count; i++) {
- job_data_out[i].job =
- create_job(pvr_dev, pvr_file, &job_args[i]);
- err = PTR_ERR_OR_ZERO(job_data_out[i].job);
- if (err) {
- *job_count = i;
- job_data_out[i].job = NULL;
- goto err_cleanup;
- }
- err = PVR_UOBJ_GET_ARRAY(job_data_out[i].sync_ops,
- &job_args[i].sync_ops);
- if (err) {
- *job_count = i;
- /* Ensure the job created above is also cleaned up. */
- i++;
- goto err_cleanup;
- }
- job_data_out[i].sync_op_count = job_args[i].sync_ops.count;
- }
- return 0;
- err_cleanup:
- pvr_job_data_fini(job_data_out, i);
- return err;
- }
- static void
- push_jobs(struct pvr_job_data *job_data, u32 job_count)
- {
- for (u32 i = 0; i < job_count; i++)
- pvr_queue_job_push(job_data[i].job);
- }
- static int
- prepare_fw_obj_resv(struct drm_exec *exec, struct pvr_fw_object *fw_obj)
- {
- return drm_exec_prepare_obj(exec, gem_from_pvr_gem(fw_obj->gem), 1);
- }
- static int
- jobs_lock_all_objs(struct drm_exec *exec, struct pvr_job_data *job_data,
- u32 job_count)
- {
- for (u32 i = 0; i < job_count; i++) {
- struct pvr_job *job = job_data[i].job;
- /* Grab a lock on a the context, to guard against
- * concurrent submission to the same queue.
- */
- int err = drm_exec_lock_obj(exec,
- gem_from_pvr_gem(job->ctx->fw_obj->gem));
- if (err)
- return err;
- if (job->hwrt) {
- err = prepare_fw_obj_resv(exec,
- job->hwrt->fw_obj);
- if (err)
- return err;
- }
- }
- return 0;
- }
- static int
- prepare_job_resvs_for_each(struct drm_exec *exec, struct pvr_job_data *job_data,
- u32 job_count)
- {
- drm_exec_until_all_locked(exec) {
- int err = jobs_lock_all_objs(exec, job_data, job_count);
- drm_exec_retry_on_contention(exec);
- if (err)
- return err;
- }
- return 0;
- }
- static void
- update_job_resvs(struct pvr_job *job)
- {
- if (job->hwrt) {
- enum dma_resv_usage usage = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ?
- DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
- struct drm_gem_object *obj = gem_from_pvr_gem(job->hwrt->fw_obj->gem);
- dma_resv_add_fence(obj->resv, &job->base.s_fence->finished, usage);
- }
- }
- static void
- update_job_resvs_for_each(struct pvr_job_data *job_data, u32 job_count)
- {
- for (u32 i = 0; i < job_count; i++)
- update_job_resvs(job_data[i].job);
- }
- static bool can_combine_jobs(struct pvr_job *a, struct pvr_job *b)
- {
- struct pvr_job *geom_job = a, *frag_job = b;
- struct dma_fence *fence;
- unsigned long index;
- /* Geometry and fragment jobs can be combined if they are queued to the
- * same context and targeting the same HWRT.
- */
- if (a->type != DRM_PVR_JOB_TYPE_GEOMETRY ||
- b->type != DRM_PVR_JOB_TYPE_FRAGMENT ||
- a->ctx != b->ctx ||
- a->hwrt != b->hwrt)
- return false;
- xa_for_each(&frag_job->base.dependencies, index, fence) {
- /* We combine when we see an explicit geom -> frag dep. */
- if (&geom_job->base.s_fence->scheduled == fence)
- return true;
- }
- return false;
- }
- static struct dma_fence *
- get_last_queued_job_scheduled_fence(struct pvr_queue *queue,
- struct pvr_job_data *job_data,
- u32 cur_job_pos)
- {
- /* We iterate over the current job array in reverse order to grab the
- * last to-be-queued job targeting the same queue.
- */
- for (u32 i = cur_job_pos; i > 0; i--) {
- struct pvr_job *job = job_data[i - 1].job;
- if (job->ctx == queue->ctx && job->type == queue->type)
- return dma_fence_get(&job->base.s_fence->scheduled);
- }
- /* If we didn't find any, we just return the last queued job scheduled
- * fence attached to the queue.
- */
- return dma_fence_get(queue->last_queued_job_scheduled_fence);
- }
- static int
- pvr_jobs_link_geom_frag(struct pvr_job_data *job_data, u32 *job_count)
- {
- for (u32 i = 0; i < *job_count - 1; i++) {
- struct pvr_job *geom_job = job_data[i].job;
- struct pvr_job *frag_job = job_data[i + 1].job;
- struct pvr_queue *frag_queue;
- struct dma_fence *f;
- if (!can_combine_jobs(job_data[i].job, job_data[i + 1].job))
- continue;
- /* The fragment job will be submitted by the geometry queue. We
- * need to make sure it comes after all the other fragment jobs
- * queued before it.
- */
- frag_queue = pvr_context_get_queue_for_job(frag_job->ctx,
- frag_job->type);
- f = get_last_queued_job_scheduled_fence(frag_queue, job_data,
- i);
- if (f) {
- int err = drm_sched_job_add_dependency(&geom_job->base,
- f);
- if (err) {
- *job_count = i;
- return err;
- }
- }
- /* The KCCB slot will be reserved by the geometry job, so we can
- * drop the KCCB fence on the fragment job.
- */
- pvr_kccb_fence_put(frag_job->kccb_fence);
- frag_job->kccb_fence = NULL;
- geom_job->paired_job = frag_job;
- frag_job->paired_job = geom_job;
- /* The geometry job pvr_job structure is used when the fragment
- * job is being prepared by the GPU scheduler. Have the fragment
- * job hold a reference on the geometry job to prevent it being
- * freed until the fragment job has finished with it.
- */
- pvr_job_get(geom_job);
- /* Skip the fragment job we just paired to the geometry job. */
- i++;
- }
- return 0;
- }
- /**
- * pvr_submit_jobs() - Submit jobs to the GPU
- * @pvr_dev: Target PowerVR device.
- * @pvr_file: Pointer to PowerVR file structure.
- * @args: Ioctl args.
- *
- * This initial implementation is entirely synchronous; on return the GPU will
- * be idle. This will not be the case for future implementations.
- *
- * Returns:
- * * 0 on success,
- * * -%EFAULT if arguments can not be copied from user space, or
- * * -%EINVAL on invalid arguments, or
- * * Any other error.
- */
- int
- pvr_submit_jobs(struct pvr_device *pvr_dev, struct pvr_file *pvr_file,
- struct drm_pvr_ioctl_submit_jobs_args *args)
- {
- struct pvr_job_data *job_data = NULL;
- struct drm_pvr_job *job_args;
- struct xarray signal_array;
- u32 jobs_alloced = 0;
- struct drm_exec exec;
- int err;
- if (!args->jobs.count)
- return -EINVAL;
- err = PVR_UOBJ_GET_ARRAY(job_args, &args->jobs);
- if (err)
- return err;
- job_data = kvmalloc_array(args->jobs.count, sizeof(*job_data),
- GFP_KERNEL | __GFP_ZERO);
- if (!job_data) {
- err = -ENOMEM;
- goto out_free;
- }
- err = pvr_job_data_init(pvr_dev, pvr_file, job_args, &args->jobs.count,
- job_data);
- if (err)
- goto out_free;
- jobs_alloced = args->jobs.count;
- /*
- * Flush MMU if needed - this has been deferred until now to avoid
- * overuse of this expensive operation.
- */
- err = pvr_mmu_flush_exec(pvr_dev, false);
- if (err)
- goto out_job_data_cleanup;
- drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, 0);
- xa_init_flags(&signal_array, XA_FLAGS_ALLOC);
- err = prepare_job_syncs_for_each(pvr_file, job_data, &args->jobs.count,
- &signal_array);
- if (err)
- goto out_exec_fini;
- err = prepare_job_resvs_for_each(&exec, job_data, args->jobs.count);
- if (err)
- goto out_exec_fini;
- err = pvr_jobs_link_geom_frag(job_data, &args->jobs.count);
- if (err)
- goto out_exec_fini;
- /* Anything after that point must succeed because we start exposing job
- * finished fences to the outside world.
- */
- update_job_resvs_for_each(job_data, args->jobs.count);
- push_jobs(job_data, args->jobs.count);
- pvr_sync_signal_array_push_fences(&signal_array);
- err = 0;
- out_exec_fini:
- drm_exec_fini(&exec);
- pvr_sync_signal_array_cleanup(&signal_array);
- out_job_data_cleanup:
- pvr_job_data_fini(job_data, jobs_alloced);
- out_free:
- kvfree(job_data);
- kvfree(job_args);
- return err;
- }
|