/** * pvr_ccb_init() - Initialise a CCB * @pvr_dev: Device pointer. * @pvr_ccb: Pointer to CCB structure to initialise. * @num_cmds_log2: Log2 of number of commands in this CCB. * @cmd_size: Command size for this CCB. * * Return: * * Zero on success, or * * Any error code returned by pvr_fw_object_create_and_map().
*/ staticint
pvr_ccb_init(struct pvr_device *pvr_dev, struct pvr_ccb *pvr_ccb,
u32 num_cmds_log2, size_t cmd_size)
{
u32 num_cmds = 1 << num_cmds_log2;
u32 ccb_size = num_cmds * cmd_size; int err;
err = drmm_mutex_init(from_pvr_device(pvr_dev), &pvr_ccb->lock); if (err) return err;
/* * Map CCB and control structure as uncached, so we don't have to flush * CPU cache repeatedly when polling for space.
*/
pvr_ccb->ctrl = pvr_fw_object_create_and_map(pvr_dev, sizeof(*pvr_ccb->ctrl),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
ccb_ctrl_init, pvr_ccb, &pvr_ccb->ctrl_obj); if (IS_ERR(pvr_ccb->ctrl)) return PTR_ERR(pvr_ccb->ctrl);
/** * pvr_ccb_slot_available_locked() - Test whether any slots are available in CCB * @pvr_ccb: CCB to test. * @write_offset: Address to store number of next available slot. May be %NULL. * * Caller must hold @pvr_ccb->lock. * * Return: * * %true if a slot is available, or * * %false if no slot is available.
*/ static __always_inline bool
pvr_ccb_slot_available_locked(struct pvr_ccb *pvr_ccb, u32 *write_offset)
{ struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl;
u32 next_write_offset = (READ_ONCE(ctrl->write_offset) + 1) & READ_ONCE(ctrl->wrap_mask);
lockdep_assert_held(&pvr_ccb->lock);
if (READ_ONCE(ctrl->read_offset) != next_write_offset) { if (write_offset)
*write_offset = next_write_offset; returntrue;
}
case ROGUE_FWIF_FWCCB_CMD_FREELISTS_RECONSTRUCTION:
pvr_free_list_process_reconstruct_req(pvr_dev,
&cmd->cmd_data.cmd_freelists_reconstruction); break;
case ROGUE_FWIF_FWCCB_CMD_FREELIST_GROW:
pvr_free_list_process_grow_req(pvr_dev, &cmd->cmd_data.cmd_free_list_gs); break;
/* Drop FWCCB lock while we process command. */
mutex_unlock(&pvr_dev->fwccb.lock);
process_fwccb_command(pvr_dev, &cmd);
mutex_lock(&pvr_dev->fwccb.lock);
}
mutex_unlock(&pvr_dev->fwccb.lock);
}
/** * pvr_kccb_capacity() - Returns the maximum number of usable KCCB slots. * @pvr_dev: Target PowerVR device * * Return: * * The maximum number of active slots.
*/ static u32 pvr_kccb_capacity(struct pvr_device *pvr_dev)
{ /* Capacity is the number of slot minus one to cope with the wrapping * mechanisms. If we were to use all slots, we might end up with * read_offset == write_offset, which the FW considers as a KCCB-is-empty * condition.
*/ return pvr_dev->kccb.slot_count - 1;
}
/** * pvr_kccb_used_slot_count_locked() - Get the number of used slots * @pvr_dev: Device pointer. * * KCCB lock must be held. * * Return: * * The number of slots currently used.
*/ static u32
pvr_kccb_used_slot_count_locked(struct pvr_device *pvr_dev)
{ struct pvr_ccb *pvr_ccb = &pvr_dev->kccb.ccb; struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl;
u32 wr_offset = READ_ONCE(ctrl->write_offset);
u32 rd_offset = READ_ONCE(ctrl->read_offset);
u32 used_count;
/** * pvr_kccb_send_cmd_reserved_powered() - Send command to the KCCB, with the PM ref * held and a slot pre-reserved * @pvr_dev: Device pointer. * @cmd: Command to sent. * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*/ void
pvr_kccb_send_cmd_reserved_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot)
{ struct pvr_ccb *pvr_ccb = &pvr_dev->kccb.ccb; struct rogue_fwif_kccb_cmd *kccb = pvr_ccb->ccb; struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl;
u32 old_write_offset;
u32 new_write_offset;
WARN_ON(pvr_dev->lost);
mutex_lock(&pvr_ccb->lock);
if (WARN_ON(!pvr_dev->kccb.reserved_count)) goto out_unlock;
old_write_offset = READ_ONCE(ctrl->write_offset);
/* We reserved the slot, we should have one available. */ if (WARN_ON(!pvr_ccb_slot_available_locked(pvr_ccb, &new_write_offset))) goto out_unlock;
memcpy(&kccb[old_write_offset], cmd, sizeof(struct rogue_fwif_kccb_cmd)); if (kccb_slot) {
*kccb_slot = old_write_offset; /* Clear return status for this slot. */
WRITE_ONCE(pvr_dev->kccb.rtn[old_write_offset],
ROGUE_FWIF_KCCB_RTN_SLOT_NO_RESPONSE);
}
mb(); /* memory barrier */
WRITE_ONCE(ctrl->write_offset, new_write_offset);
pvr_dev->kccb.reserved_count--;
/** * pvr_kccb_reserve_slot_sync() - Try to reserve a slot synchronously * @pvr_dev: Device pointer. * * Return: * * 0 on success, or * * -EBUSY if no slots were reserved after %RESERVE_SLOT_TIMEOUT, with a minimum of * %RESERVE_SLOT_MIN_RETRIES retries.
*/ staticint pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev)
{ unsignedlong start_timestamp = jiffies; bool reserved = false;
u32 retries = 0;
while (time_before(jiffies, start_timestamp + RESERVE_SLOT_TIMEOUT) ||
retries < RESERVE_SLOT_MIN_RETRIES) {
reserved = pvr_kccb_try_reserve_slot(pvr_dev); if (reserved) break;
usleep_range(1, 50);
if (retries < U32_MAX)
retries++;
}
return reserved ? 0 : -EBUSY;
}
/** * pvr_kccb_send_cmd_powered() - Send command to the KCCB, with a PM ref held * @pvr_dev: Device pointer. * @cmd: Command to sent. * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. * * Returns: * * Zero on success, or * * -EBUSY if timeout while waiting for a free KCCB slot.
*/ int
pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot)
{ int err;
err = pvr_kccb_reserve_slot_sync(pvr_dev); if (err) return err;
/** * pvr_kccb_send_cmd() - Send command to the KCCB * @pvr_dev: Device pointer. * @cmd: Command to sent. * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. * * Returns: * * Zero on success, or * * -EBUSY if timeout while waiting for a free KCCB slot.
*/ int
pvr_kccb_send_cmd(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot)
{ int err;
err = pvr_power_get(pvr_dev); if (err) return err;
/** * pvr_kccb_wait_for_completion() - Wait for a KCCB command to complete * @pvr_dev: Device pointer. * @slot_nr: KCCB slot to wait on. * @timeout: Timeout length (in jiffies). * @rtn_out: Location to store KCCB command result. May be %NULL. * * Returns: * * Zero on success, or * * -ETIMEDOUT on timeout.
*/ int
pvr_kccb_wait_for_completion(struct pvr_device *pvr_dev, u32 slot_nr,
u32 timeout, u32 *rtn_out)
{ int ret = wait_event_timeout(pvr_dev->kccb.rtn_q, READ_ONCE(pvr_dev->kccb.rtn[slot_nr]) &
ROGUE_FWIF_KCCB_RTN_SLOT_CMD_EXECUTED, timeout);
if (ret && rtn_out)
*rtn_out = READ_ONCE(pvr_dev->kccb.rtn[slot_nr]);
return ret ? 0 : -ETIMEDOUT;
}
/** * pvr_kccb_is_idle() - Returns whether the device's KCCB is idle * @pvr_dev: Device pointer * * Returns: * * %true if the KCCB is idle (contains no commands), or * * %false if the KCCB contains pending commands.
*/ bool
pvr_kccb_is_idle(struct pvr_device *pvr_dev)
{ struct rogue_fwif_ccb_ctl *ctrl = pvr_dev->kccb.ccb.ctrl; bool idle;
/** * struct pvr_kccb_fence - Fence object used to wait for a KCCB slot
*/ struct pvr_kccb_fence { /** @base: Base dma_fence object. */ struct dma_fence base;
/** @node: Node used to insert the fence in the pvr_device::kccb::waiters list. */ struct list_head node;
};
/** * pvr_kccb_wake_up_waiters() - Check the KCCB waiters * @pvr_dev: Target PowerVR device * * Signal as many KCCB fences as we have slots available.
*/ void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev)
{ struct pvr_kccb_fence *fence, *tmp_fence;
u32 used_count, available_count;
/* Wake up those waiting for KCCB slot execution. */
wake_up_all(&pvr_dev->kccb.rtn_q);
/* Then iterate over all KCCB fences and signal as many as we can. */
mutex_lock(&pvr_dev->kccb.ccb.lock);
used_count = pvr_kccb_used_slot_count_locked(pvr_dev);
if (WARN_ON(used_count + pvr_dev->kccb.reserved_count > pvr_kccb_capacity(pvr_dev))) goto out_unlock;
/** * pvr_kccb_fence_alloc() - Allocate a pvr_kccb_fence object * * Return: * * NULL if the allocation fails, or * * A valid dma_fence pointer otherwise.
*/ struct dma_fence *pvr_kccb_fence_alloc(void)
{ struct pvr_kccb_fence *kccb_fence;
kccb_fence = kzalloc(sizeof(*kccb_fence), GFP_KERNEL); if (!kccb_fence) return NULL;
return &kccb_fence->base;
}
/** * pvr_kccb_fence_put() - Drop a KCCB fence reference * @fence: The fence to drop the reference on. * * If the fence hasn't been initialized yet, dma_fence_free() is called. This * way we have a single function taking care of both cases.
*/ void pvr_kccb_fence_put(struct dma_fence *fence)
{ if (!fence) return;
/** * pvr_kccb_reserve_slot() - Reserve a KCCB slot for later use * @pvr_dev: Target PowerVR device * @f: KCCB fence object previously allocated with pvr_kccb_fence_alloc() * * Try to reserve a KCCB slot, and if there's no slot available, * initializes the fence object and queue it to the waiters list. * * If NULL is returned, that means the slot is reserved. In that case, * the @f is freed and shouldn't be accessed after that point. * * Return: * * NULL if a slot was available directly, or * * A valid dma_fence object to wait on if no slot was available.
*/ struct dma_fence *
pvr_kccb_reserve_slot(struct pvr_device *pvr_dev, struct dma_fence *f)
{ struct pvr_kccb_fence *fence = container_of(f, struct pvr_kccb_fence, base); struct dma_fence *out_fence = NULL;
u32 used_count;
/** * pvr_kccb_release_slot() - Release a KCCB slot reserved with * pvr_kccb_reserve_slot() * @pvr_dev: Target PowerVR device * * Should only be called if something failed after the * pvr_kccb_reserve_slot() call and you know you won't call * pvr_kccb_send_cmd_reserved().
*/ void pvr_kccb_release_slot(struct pvr_device *pvr_dev)
{
mutex_lock(&pvr_dev->kccb.ccb.lock); if (!WARN_ON(!pvr_dev->kccb.reserved_count))
pvr_dev->kccb.reserved_count--;
mutex_unlock(&pvr_dev->kccb.ccb.lock);
}
/** * pvr_fwccb_init() - Initialise device FWCCB * @pvr_dev: Target PowerVR device * * Returns: * * 0 on success, or * * Any error returned by pvr_ccb_init().
*/ int
pvr_fwccb_init(struct pvr_device *pvr_dev)
{ return pvr_ccb_init(pvr_dev, &pvr_dev->fwccb,
ROGUE_FWIF_FWCCB_NUMCMDS_LOG2, sizeof(struct rogue_fwif_fwccb_cmd));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.