mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 20:46:48 +02:00
Merge tag 'drm-misc-next-2025-11-14-1' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.19: UAPI Changes: - Add sysfs entries, coredump support and uevents to QAIC. - Add fdinfo memory statistics to ivpu. Cross-subsystem Changes: - Handle stub fence initialization during module init. - Stop using system_wq in scheduler and drivers. Core Changes: - Documentation updates to ttm, vblank. - Add EDID quirk for sharp panel. - Use drm_crtc_vblank_(crtc,waitqueue) more in core and drivers. Driver Changes: - Small updates and fixes to panfrost, amdxdna, vmwgfx, ast, ivpu. - Handle preemption in amdxdna. - Add PM support to qaic. - Huge refactor of sun4i's layer code to decouple plane code from output and improve support for DE33. - Add larger page and compression support to nouveau. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patch.msgid.link/1ad3ea69-d029-4a21-8b3d-6b264b1b2a30@linux.intel.com
This commit is contained in:
commit
490fd93366
19
Documentation/ABI/stable/sysfs-driver-qaic
Normal file
19
Documentation/ABI/stable/sysfs-driver-qaic
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
What: /sys/bus/pci/drivers/qaic/XXXX:XX:XX.X/accel/accel<minor_nr>/dbc<N>_state
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
|
||||
Description: Represents the current state of DMA Bridge channel (DBC). Below are the possible
|
||||
states:
|
||||
|
||||
=================== ==========================================================
|
||||
IDLE (0) DBC is free and can be activated
|
||||
ASSIGNED (1) DBC is activated and a workload is running on device
|
||||
BEFORE_SHUTDOWN (2) Sub-system associated with this workload has crashed and
|
||||
it will shutdown soon
|
||||
AFTER_SHUTDOWN (3) Sub-system associated with this workload has crashed and
|
||||
it has shutdown
|
||||
BEFORE_POWER_UP (4) Sub-system associated with this workload is shutdown and
|
||||
it will be powered up soon
|
||||
AFTER_POWER_UP (5) Sub-system associated with this workload is now powered up
|
||||
=================== ==========================================================
|
||||
Users: Any userspace application or clients interested in DBC state.
|
||||
|
|
@ -487,8 +487,8 @@ one user crashes, the fallout of that should be limited to that workload and not
|
|||
impact other workloads. SSR accomplishes this.
|
||||
|
||||
If a particular workload crashes, QSM notifies the host via the QAIC_SSR MHI
|
||||
channel. This notification identifies the workload by it's assigned DBC. A
|
||||
multi-stage recovery process is then used to cleanup both sides, and get the
|
||||
channel. This notification identifies the workload by its assigned DBC. A
|
||||
multi-stage recovery process is then used to cleanup both sides, and gets the
|
||||
DBC/NSPs into a working state.
|
||||
|
||||
When SSR occurs, any state in the workload is lost. Any inputs that were in
|
||||
|
|
@ -496,6 +496,27 @@ process, or queued by not yet serviced, are lost. The loaded artifacts will
|
|||
remain in on-card DDR, but the host will need to re-activate the workload if
|
||||
it desires to recover the workload.
|
||||
|
||||
When SSR occurs for a specific NSP, the assigned DBC goes through the
|
||||
following state transactions in order:
|
||||
|
||||
DBC_STATE_BEFORE_SHUTDOWN
|
||||
Indicates that the affected NSP was found in an unrecoverable error
|
||||
condition.
|
||||
DBC_STATE_AFTER_SHUTDOWN
|
||||
Indicates that the NSP is under reset.
|
||||
DBC_STATE_BEFORE_POWER_UP
|
||||
Indicates that the NSP's debug information has been collected, and is
|
||||
ready to be collected by the host (if desired). At that stage the NSP
|
||||
is restarted by QSM.
|
||||
DBC_STATE_AFTER_POWER_UP
|
||||
Indicates that the NSP has been restarted, fully operational and is
|
||||
in idle state.
|
||||
|
||||
SSR also has an optional crashdump collection feature. If enabled, the host can
|
||||
collect the memory dump for the crashed NSP and dump it to the user space via
|
||||
the dev_coredump subsystem. The host can also decline the crashdump collection
|
||||
request from the device.
|
||||
|
||||
Reliability, Accessibility, Serviceability (RAS)
|
||||
================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ properties:
|
|||
- enum:
|
||||
- renesas,r9a07g054-du # RZ/V2L
|
||||
- const: renesas,r9a07g044-du # RZ/G2L fallback
|
||||
- items:
|
||||
- const: renesas,r9a09g056-du # RZ/V2N
|
||||
- const: renesas,r9a09g057-du # RZ/V2H(P) fallback
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -189,7 +189,6 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
|
|||
|
||||
up(&job->hwctx->priv->job_sem);
|
||||
job->job_done = true;
|
||||
dma_fence_put(fence);
|
||||
mmput_async(job->mm);
|
||||
aie2_job_put(job);
|
||||
}
|
||||
|
|
@ -691,17 +690,19 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
|
|||
xdna = hwctx->client->xdna;
|
||||
|
||||
XDNA_DBG(xdna, "%s sequence number %lld", hwctx->name, hwctx->priv->seq);
|
||||
drm_sched_entity_destroy(&hwctx->priv->entity);
|
||||
|
||||
aie2_hwctx_wait_for_idle(hwctx);
|
||||
|
||||
/* Request fw to destroy hwctx and cancel the rest pending requests */
|
||||
aie2_release_resource(hwctx);
|
||||
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
drm_sched_entity_destroy(&hwctx->priv->entity);
|
||||
|
||||
/* Wait for all submitted jobs to be completed or canceled */
|
||||
wait_event(hwctx->priv->job_free_wq,
|
||||
atomic64_read(&hwctx->job_submit_cnt) ==
|
||||
atomic64_read(&hwctx->job_free_cnt));
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
|
||||
drm_sched_fini(&hwctx->priv->sched);
|
||||
aie2_ctx_syncobj_destroy(hwctx);
|
||||
|
|
|
|||
|
|
@ -210,6 +210,14 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
|
|||
hwctx->fw_ctx_id = resp.context_id;
|
||||
WARN_ONCE(hwctx->fw_ctx_id == -1, "Unexpected context id");
|
||||
|
||||
if (ndev->force_preempt_enabled) {
|
||||
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "failed to enable force preempt %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
cq_pair = &resp.cq_pair[0];
|
||||
x2i.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.head_addr);
|
||||
x2i.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.tail_addr);
|
||||
|
|
@ -601,6 +609,11 @@ aie2_cmdlist_fill_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_cmdlist_unsupp(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static u32 aie2_get_chain_msg_op(u32 cmd_op)
|
||||
{
|
||||
switch (cmd_op) {
|
||||
|
|
@ -621,6 +634,8 @@ static struct aie2_exec_msg_ops legacy_exec_message_ops = {
|
|||
.init_chain_req = aie2_init_exec_chain_req,
|
||||
.fill_cf_slot = aie2_cmdlist_fill_cf,
|
||||
.fill_dpu_slot = aie2_cmdlist_fill_dpu,
|
||||
.fill_preempt_slot = aie2_cmdlist_unsupp,
|
||||
.fill_elf_slot = aie2_cmdlist_unsupp,
|
||||
.get_chain_msg_op = aie2_get_chain_msg_op,
|
||||
};
|
||||
|
||||
|
|
@ -680,6 +695,74 @@ aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *si
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_npu *npu_slot = slot;
|
||||
struct amdxdna_cmd_preempt_data *pd;
|
||||
u32 cmd_len;
|
||||
u32 arg_sz;
|
||||
|
||||
pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
arg_sz = cmd_len - sizeof(*pd);
|
||||
if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (*size < sizeof(*npu_slot) + arg_sz)
|
||||
return -EINVAL;
|
||||
|
||||
npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (npu_slot->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
memset(npu_slot, 0, sizeof(*npu_slot));
|
||||
npu_slot->type = EXEC_NPU_TYPE_PREEMPT;
|
||||
npu_slot->inst_buf_addr = pd->inst_buf;
|
||||
npu_slot->save_buf_addr = pd->save_buf;
|
||||
npu_slot->restore_buf_addr = pd->restore_buf;
|
||||
npu_slot->inst_size = pd->inst_size;
|
||||
npu_slot->save_size = pd->save_size;
|
||||
npu_slot->restore_size = pd->restore_size;
|
||||
npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
|
||||
npu_slot->arg_cnt = arg_sz / sizeof(u32);
|
||||
memcpy(npu_slot->args, pd->prop_args, arg_sz);
|
||||
|
||||
*size = sizeof(*npu_slot) + arg_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_npu *npu_slot = slot;
|
||||
struct amdxdna_cmd_preempt_data *pd;
|
||||
u32 cmd_len;
|
||||
u32 arg_sz;
|
||||
|
||||
pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
arg_sz = cmd_len - sizeof(*pd);
|
||||
if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (*size < sizeof(*npu_slot) + arg_sz)
|
||||
return -EINVAL;
|
||||
|
||||
memset(npu_slot, 0, sizeof(*npu_slot));
|
||||
npu_slot->type = EXEC_NPU_TYPE_ELF;
|
||||
npu_slot->inst_buf_addr = pd->inst_buf;
|
||||
npu_slot->save_buf_addr = pd->save_buf;
|
||||
npu_slot->restore_buf_addr = pd->restore_buf;
|
||||
npu_slot->inst_size = pd->inst_size;
|
||||
npu_slot->save_size = pd->save_size;
|
||||
npu_slot->restore_size = pd->restore_size;
|
||||
npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
|
||||
npu_slot->arg_cnt = 1;
|
||||
npu_slot->args[0] = AIE2_EXEC_BUFFER_KERNEL_OP_TXN;
|
||||
|
||||
*size = struct_size(npu_slot, args, npu_slot->arg_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 aie2_get_npu_chain_msg_op(u32 cmd_op)
|
||||
{
|
||||
return MSG_OP_CHAIN_EXEC_NPU;
|
||||
|
|
@ -691,6 +774,8 @@ static struct aie2_exec_msg_ops npu_exec_message_ops = {
|
|||
.init_chain_req = aie2_init_npu_chain_req,
|
||||
.fill_cf_slot = aie2_cmdlist_fill_npu_cf,
|
||||
.fill_dpu_slot = aie2_cmdlist_fill_npu_dpu,
|
||||
.fill_preempt_slot = aie2_cmdlist_fill_npu_preempt,
|
||||
.fill_elf_slot = aie2_cmdlist_fill_npu_elf,
|
||||
.get_chain_msg_op = aie2_get_npu_chain_msg_op,
|
||||
};
|
||||
|
||||
|
|
@ -749,6 +834,16 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
|
|||
case ERT_START_NPU:
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
case ERT_START_NPU_PREEMPT:
|
||||
if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
|
||||
return -EOPNOTSUPP;
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
case ERT_START_NPU_PREEMPT_ELF:
|
||||
if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
|
||||
return -EOPNOTSUPP;
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
default:
|
||||
XDNA_INFO(xdna, "Unsupported op %d", op);
|
||||
ret = -EOPNOTSUPP;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,8 @@ struct exec_dpu_req {
|
|||
enum exec_npu_type {
|
||||
EXEC_NPU_TYPE_NON_ELF = 0x1,
|
||||
EXEC_NPU_TYPE_PARTIAL_ELF = 0x2,
|
||||
EXEC_NPU_TYPE_PREEMPT = 0x3,
|
||||
EXEC_NPU_TYPE_ELF = 0x4,
|
||||
};
|
||||
|
||||
union exec_req {
|
||||
|
|
@ -372,6 +374,7 @@ struct cmd_chain_slot_dpu {
|
|||
};
|
||||
|
||||
#define MAX_NPU_ARGS_SIZE (26 * sizeof(__u32))
|
||||
#define AIE2_EXEC_BUFFER_KERNEL_OP_TXN 3
|
||||
struct cmd_chain_slot_npu {
|
||||
enum exec_npu_type type;
|
||||
u64 inst_buf_addr;
|
||||
|
|
|
|||
|
|
@ -183,6 +183,10 @@ int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
|
|||
if (cfg->category != category)
|
||||
continue;
|
||||
|
||||
if (cfg->feature_mask &&
|
||||
bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX))
|
||||
continue;
|
||||
|
||||
value = val ? *val : cfg->value;
|
||||
ret = aie2_set_runtime_cfg(ndev, cfg->type, value);
|
||||
if (ret) {
|
||||
|
|
@ -932,6 +936,25 @@ static int aie2_get_telemetry(struct amdxdna_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_get_preempt_state(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_get_info *args)
|
||||
{
|
||||
struct amdxdna_drm_attribute_state state = {};
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_dev_hdl *ndev;
|
||||
|
||||
ndev = xdna->dev_handle;
|
||||
if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE)
|
||||
state.state = ndev->force_preempt_enabled;
|
||||
else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE)
|
||||
state.state = ndev->frame_boundary_preempt;
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args)
|
||||
{
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
|
|
@ -972,6 +995,10 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i
|
|||
case DRM_AMDXDNA_QUERY_RESOURCE_INFO:
|
||||
ret = aie2_query_resource_info(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE:
|
||||
case DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE:
|
||||
ret = aie2_get_preempt_state(client, args);
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
|
||||
ret = -EOPNOTSUPP;
|
||||
|
|
@ -1078,6 +1105,38 @@ static int aie2_set_power_mode(struct amdxdna_client *client,
|
|||
return aie2_pm_set_mode(xdna->dev_handle, power_mode);
|
||||
}
|
||||
|
||||
static int aie2_set_preempt_state(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_set_state *args)
|
||||
{
|
||||
struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle;
|
||||
struct amdxdna_drm_attribute_state state;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&state, u64_to_user_ptr(args->buffer), sizeof(state)))
|
||||
return -EFAULT;
|
||||
|
||||
if (state.state > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (XDNA_MBZ_DBG(client->xdna, state.pad, sizeof(state.pad)))
|
||||
return -EINVAL;
|
||||
|
||||
if (args->param == DRM_AMDXDNA_SET_FORCE_PREEMPT) {
|
||||
ndev->force_preempt_enabled = state.state;
|
||||
} else if (args->param == DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT) {
|
||||
val = state.state;
|
||||
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ndev->frame_boundary_preempt = state.state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_set_state(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_set_state *args)
|
||||
{
|
||||
|
|
@ -1095,6 +1154,10 @@ static int aie2_set_state(struct amdxdna_client *client,
|
|||
case DRM_AMDXDNA_SET_POWER_MODE:
|
||||
ret = aie2_set_power_mode(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_SET_FORCE_PREEMPT:
|
||||
case DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT:
|
||||
ret = aie2_set_preempt_state(client, args);
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
|
||||
ret = -EOPNOTSUPP;
|
||||
|
|
|
|||
|
|
@ -110,12 +110,15 @@ struct aie_metadata {
|
|||
enum rt_config_category {
|
||||
AIE2_RT_CFG_INIT,
|
||||
AIE2_RT_CFG_CLK_GATING,
|
||||
AIE2_RT_CFG_FORCE_PREEMPT,
|
||||
AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
|
||||
};
|
||||
|
||||
struct rt_config {
|
||||
u32 type;
|
||||
u32 value;
|
||||
u32 category;
|
||||
unsigned long feature_mask;
|
||||
};
|
||||
|
||||
struct dpm_clk_freq {
|
||||
|
|
@ -164,6 +167,8 @@ struct aie2_exec_msg_ops {
|
|||
void (*init_chain_req)(void *req, u64 slot_addr, size_t size, u32 cmd_cnt);
|
||||
int (*fill_cf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
int (*fill_dpu_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
int (*fill_preempt_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
int (*fill_elf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
u32 (*get_chain_msg_op)(u32 cmd_op);
|
||||
};
|
||||
|
||||
|
|
@ -197,6 +202,8 @@ struct amdxdna_dev_hdl {
|
|||
u32 hclk_freq;
|
||||
u32 max_tops;
|
||||
u32 curr_tops;
|
||||
u32 force_preempt_enabled;
|
||||
u32 frame_boundary_preempt;
|
||||
|
||||
/* Mailbox and the management channel */
|
||||
struct mailbox *mbox;
|
||||
|
|
@ -223,6 +230,7 @@ struct aie2_hw_ops {
|
|||
|
||||
enum aie2_fw_feature {
|
||||
AIE2_NPU_COMMAND,
|
||||
AIE2_PREEMPT,
|
||||
AIE2_FEATURE_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -147,6 +147,16 @@ int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Failing to set power off indicates an unrecoverable hardware or
|
||||
* firmware error.
|
||||
*/
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
|
|||
trace_amdxdna_debug_point(job->hwctx->name, job->seq, "job release");
|
||||
amdxdna_arg_bos_put(job);
|
||||
amdxdna_gem_put_obj(job->cmd_bo);
|
||||
dma_fence_put(job->fence);
|
||||
}
|
||||
|
||||
int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ enum ert_cmd_opcode {
|
|||
ERT_START_CU = 0,
|
||||
ERT_CMD_CHAIN = 19,
|
||||
ERT_START_NPU = 20,
|
||||
ERT_START_NPU_PREEMPT = 21,
|
||||
ERT_START_NPU_PREEMPT_ELF = 22,
|
||||
ERT_INVALID_CMD = ~0U,
|
||||
};
|
||||
|
||||
|
|
@ -55,6 +57,21 @@ struct amdxdna_cmd_chain {
|
|||
u64 data[] __counted_by(command_count);
|
||||
};
|
||||
|
||||
/*
|
||||
* Interpretation of the beginning of data payload for ERT_START_NPU_PREEMPT in
|
||||
* amdxdna_cmd. The rest of the payload in amdxdna_cmd is regular kernel args.
|
||||
*/
|
||||
struct amdxdna_cmd_preempt_data {
|
||||
u64 inst_buf; /* instruction buffer address */
|
||||
u64 save_buf; /* save buffer address */
|
||||
u64 restore_buf; /* restore buffer address */
|
||||
u32 inst_size; /* size of instruction buffer in bytes */
|
||||
u32 save_size; /* size of save buffer in bytes */
|
||||
u32 restore_size; /* size of restore buffer in bytes */
|
||||
u32 inst_prop_cnt; /* properties count */
|
||||
u32 prop_args[]; /* properties and regular kernel arguments */
|
||||
};
|
||||
|
||||
/* Exec buffer command header format */
|
||||
#define AMDXDNA_CMD_STATE GENMASK(3, 0)
|
||||
#define AMDXDNA_CMD_EXTRA_CU_MASK GENMASK(11, 10)
|
||||
|
|
|
|||
|
|
@ -516,6 +516,7 @@ xdna_mailbox_create_channel(struct mailbox *mb,
|
|||
}
|
||||
|
||||
mb_chann->bad_state = false;
|
||||
mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
|
||||
|
||||
MB_DBG(mb_chann, "Mailbox channel created (irq: %d)", mb_chann->msix_irq);
|
||||
return mb_chann;
|
||||
|
|
|
|||
|
|
@ -31,9 +31,10 @@ MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin");
|
|||
* 0.3: Support firmware debug buffer
|
||||
* 0.4: Support getting resource information
|
||||
* 0.5: Support getting telemetry data
|
||||
* 0.6: Support preemption
|
||||
*/
|
||||
#define AMDXDNA_DRIVER_MAJOR 0
|
||||
#define AMDXDNA_DRIVER_MINOR 5
|
||||
#define AMDXDNA_DRIVER_MINOR 6
|
||||
|
||||
/*
|
||||
* Bind the driver base on (vendor_id, device_id) pair and later use the
|
||||
|
|
|
|||
|
|
@ -64,10 +64,13 @@
|
|||
const struct rt_config npu4_default_rt_cfg[] = {
|
||||
{ 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
|
||||
{ 10, 1, AIE2_RT_CFG_INIT }, /* DEBUG BUF */
|
||||
{ 14, 0, AIE2_RT_CFG_INIT, BIT_U64(AIE2_PREEMPT) }, /* Frame boundary preemption */
|
||||
{ 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 2, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 3, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 4, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 13, 0, AIE2_RT_CFG_FORCE_PREEMPT },
|
||||
{ 14, 0, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
|
|
@ -85,6 +88,7 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = {
|
|||
|
||||
const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = {
|
||||
{ .feature = AIE2_NPU_COMMAND, .min_minor = 15 },
|
||||
{ .feature = AIE2_PREEMPT, .min_minor = 12 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -455,6 +455,9 @@ int ivpu_shutdown(struct ivpu_device *vdev)
|
|||
static const struct file_operations ivpu_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_ACCEL_FOPS,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_fdinfo,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct drm_driver driver = {
|
||||
|
|
@ -469,6 +472,9 @@ static const struct drm_driver driver = {
|
|||
.ioctls = ivpu_drm_ioctls,
|
||||
.num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls),
|
||||
.fops = &ivpu_fops,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_memory_stats,
|
||||
#endif
|
||||
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
|
|
|
|||
|
|
@ -333,6 +333,17 @@ static void ivpu_gem_bo_free(struct drm_gem_object *obj)
|
|||
drm_gem_shmem_free(&bo->base);
|
||||
}
|
||||
|
||||
static enum drm_gem_object_status ivpu_gem_status(struct drm_gem_object *obj)
|
||||
{
|
||||
struct ivpu_bo *bo = to_ivpu_bo(obj);
|
||||
enum drm_gem_object_status status = 0;
|
||||
|
||||
if (ivpu_bo_is_resident(bo))
|
||||
status |= DRM_GEM_OBJECT_RESIDENT;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs ivpu_gem_funcs = {
|
||||
.free = ivpu_gem_bo_free,
|
||||
.open = ivpu_gem_bo_open,
|
||||
|
|
@ -343,6 +354,7 @@ static const struct drm_gem_object_funcs ivpu_gem_funcs = {
|
|||
.vmap = drm_gem_shmem_object_vmap,
|
||||
.vunmap = drm_gem_shmem_object_vunmap,
|
||||
.mmap = drm_gem_shmem_object_mmap,
|
||||
.status = ivpu_gem_status,
|
||||
.vm_ops = &drm_gem_shmem_vm_ops,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,11 @@ static inline bool ivpu_bo_is_read_only(struct ivpu_bo *bo)
|
|||
return bo->flags & DRM_IVPU_BO_READ_ONLY;
|
||||
}
|
||||
|
||||
static inline bool ivpu_bo_is_resident(struct ivpu_bo *bo)
|
||||
{
|
||||
return !!bo->base.pages;
|
||||
}
|
||||
|
||||
static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr)
|
||||
{
|
||||
if (vpu_addr < bo->vpu_addr)
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ npu_memory_utilization_show(struct device *dev, struct device_attribute *attr, c
|
|||
|
||||
mutex_lock(&vdev->bo_list_lock);
|
||||
list_for_each_entry(bo, &vdev->bo_list, bo_list_node)
|
||||
total_npu_memory += bo->base.base.size;
|
||||
if (ivpu_bo_is_resident(bo))
|
||||
total_npu_memory += ivpu_bo_size(bo);
|
||||
mutex_unlock(&vdev->bo_list_lock);
|
||||
|
||||
return sysfs_emit(buf, "%lld\n", total_npu_memory);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ config DRM_ACCEL_QAIC
|
|||
depends on PCI && HAS_IOMEM
|
||||
depends on MHI_BUS
|
||||
select CRC32
|
||||
select WANT_DEV_COREDUMP
|
||||
help
|
||||
Enables driver for Qualcomm's Cloud AI accelerator PCIe cards that are
|
||||
designed to accelerate Deep Learning inference workloads.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ qaic-y := \
|
|||
qaic_data.o \
|
||||
qaic_drv.o \
|
||||
qaic_ras.o \
|
||||
qaic_ssr.o \
|
||||
qaic_sysfs.o \
|
||||
qaic_timesync.o \
|
||||
sahara.o
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#define QAIC_DBC_BASE SZ_128K
|
||||
#define QAIC_DBC_SIZE SZ_4K
|
||||
#define QAIC_SSR_DBC_SENTINEL U32_MAX /* No ongoing SSR sentinel */
|
||||
|
||||
#define QAIC_NO_PARTITION -1
|
||||
|
||||
|
|
@ -47,6 +48,22 @@ enum __packed dev_states {
|
|||
QAIC_ONLINE,
|
||||
};
|
||||
|
||||
enum dbc_states {
|
||||
/* DBC is free and can be activated */
|
||||
DBC_STATE_IDLE,
|
||||
/* DBC is activated and a workload is running on device */
|
||||
DBC_STATE_ASSIGNED,
|
||||
/* Sub-system associated with this workload has crashed and it will shutdown soon */
|
||||
DBC_STATE_BEFORE_SHUTDOWN,
|
||||
/* Sub-system associated with this workload has crashed and it has shutdown */
|
||||
DBC_STATE_AFTER_SHUTDOWN,
|
||||
/* Sub-system associated with this workload is shutdown and it will be powered up soon */
|
||||
DBC_STATE_BEFORE_POWER_UP,
|
||||
/* Sub-system associated with this workload is now powered up */
|
||||
DBC_STATE_AFTER_POWER_UP,
|
||||
DBC_STATE_MAX,
|
||||
};
|
||||
|
||||
extern bool datapath_polling;
|
||||
|
||||
struct qaic_user {
|
||||
|
|
@ -114,6 +131,8 @@ struct dma_bridge_chan {
|
|||
unsigned int irq;
|
||||
/* Polling work item to simulate interrupts */
|
||||
struct work_struct poll_work;
|
||||
/* Represents various states of this DBC from enum dbc_states */
|
||||
unsigned int state;
|
||||
};
|
||||
|
||||
struct qaic_device {
|
||||
|
|
@ -161,6 +180,8 @@ struct qaic_device {
|
|||
struct mhi_device *qts_ch;
|
||||
/* Work queue for tasks related to MHI "QAIC_TIMESYNC" channel */
|
||||
struct workqueue_struct *qts_wq;
|
||||
/* MHI "QAIC_TIMESYNC_PERIODIC" channel device */
|
||||
struct mhi_device *mqts_ch;
|
||||
/* Head of list of page allocated by MHI bootlog device */
|
||||
struct list_head bootlog;
|
||||
/* MHI bootlog channel device */
|
||||
|
|
@ -177,6 +198,14 @@ struct qaic_device {
|
|||
unsigned int ue_count;
|
||||
/* Un-correctable non-fatal error count */
|
||||
unsigned int ue_nf_count;
|
||||
/* MHI SSR channel device */
|
||||
struct mhi_device *ssr_ch;
|
||||
/* Work queue for tasks related to MHI SSR device */
|
||||
struct workqueue_struct *ssr_wq;
|
||||
/* Buffer to collect SSR crashdump via SSR MHI channel */
|
||||
void *ssr_mhi_buf;
|
||||
/* DBC which is under SSR. Sentinel U32_MAX would mean that no SSR in progress */
|
||||
u32 ssr_dbc;
|
||||
};
|
||||
|
||||
struct qaic_drm_device {
|
||||
|
|
@ -195,6 +224,8 @@ struct qaic_drm_device {
|
|||
struct list_head users;
|
||||
/* Synchronizes access to users list */
|
||||
struct mutex users_mutex;
|
||||
/* Pointer to array of DBC sysfs attributes */
|
||||
void *sysfs_attrs;
|
||||
};
|
||||
|
||||
struct qaic_bo {
|
||||
|
|
@ -317,6 +348,13 @@ int qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm
|
|||
int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
|
||||
int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
|
||||
int qaic_detach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
|
||||
void irq_polling_work(struct work_struct *work);
|
||||
void qaic_irq_polling_work(struct work_struct *work);
|
||||
void qaic_dbc_enter_ssr(struct qaic_device *qdev, u32 dbc_id);
|
||||
void qaic_dbc_exit_ssr(struct qaic_device *qdev);
|
||||
|
||||
/* qaic_sysfs.c */
|
||||
int qaic_sysfs_init(struct qaic_drm_device *qddev);
|
||||
void qaic_sysfs_remove(struct qaic_drm_device *qddev);
|
||||
void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state);
|
||||
|
||||
#endif /* _QAIC_H_ */
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ static void save_dbc_buf(struct qaic_device *qdev, struct ioctl_resources *resou
|
|||
enable_dbc(qdev, dbc_id, usr);
|
||||
qdev->dbc[dbc_id].in_use = true;
|
||||
resources->buf = NULL;
|
||||
set_dbc_state(qdev, dbc_id, DBC_STATE_ASSIGNED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -923,6 +924,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len
|
|||
}
|
||||
|
||||
release_dbc(qdev, dbc_id);
|
||||
set_dbc_state(qdev, dbc_id, DBC_STATE_IDLE);
|
||||
*msg_len += sizeof(*in_trans);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1047,6 +1047,11 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
|
|||
goto unlock_ch_srcu;
|
||||
}
|
||||
|
||||
if (dbc->id == qdev->ssr_dbc) {
|
||||
ret = -EPIPE;
|
||||
goto unlock_ch_srcu;
|
||||
}
|
||||
|
||||
ret = qaic_prepare_bo(qdev, bo, &args->hdr);
|
||||
if (ret)
|
||||
goto unlock_ch_srcu;
|
||||
|
|
@ -1370,6 +1375,11 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
|
|||
goto release_ch_rcu;
|
||||
}
|
||||
|
||||
if (dbc->id == qdev->ssr_dbc) {
|
||||
ret = -EPIPE;
|
||||
goto release_ch_rcu;
|
||||
}
|
||||
|
||||
ret = mutex_lock_interruptible(&dbc->req_lock);
|
||||
if (ret)
|
||||
goto release_ch_rcu;
|
||||
|
|
@ -1504,7 +1514,7 @@ irqreturn_t dbc_irq_handler(int irq, void *data)
|
|||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
void irq_polling_work(struct work_struct *work)
|
||||
void qaic_irq_polling_work(struct work_struct *work)
|
||||
{
|
||||
struct dma_bridge_chan *dbc = container_of(work, struct dma_bridge_chan, poll_work);
|
||||
unsigned long flags;
|
||||
|
|
@ -1722,6 +1732,11 @@ int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file
|
|||
goto unlock_ch_srcu;
|
||||
}
|
||||
|
||||
if (dbc->id == qdev->ssr_dbc) {
|
||||
ret = -EPIPE;
|
||||
goto unlock_ch_srcu;
|
||||
}
|
||||
|
||||
obj = drm_gem_object_lookup(file_priv, args->handle);
|
||||
if (!obj) {
|
||||
ret = -ENOENT;
|
||||
|
|
@ -1742,6 +1757,9 @@ int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file
|
|||
if (!dbc->usr)
|
||||
ret = -EPERM;
|
||||
|
||||
if (dbc->id == qdev->ssr_dbc)
|
||||
ret = -EPIPE;
|
||||
|
||||
put_obj:
|
||||
drm_gem_object_put(obj);
|
||||
unlock_ch_srcu:
|
||||
|
|
@ -1945,6 +1963,17 @@ static void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *db
|
|||
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
|
||||
}
|
||||
|
||||
static void sync_empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc)
|
||||
{
|
||||
empty_xfer_list(qdev, dbc);
|
||||
synchronize_srcu(&dbc->ch_lock);
|
||||
/*
|
||||
* Threads holding channel lock, may add more elements in the xfer_list.
|
||||
* Flush out these elements from xfer_list.
|
||||
*/
|
||||
empty_xfer_list(qdev, dbc);
|
||||
}
|
||||
|
||||
int disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
|
||||
{
|
||||
if (!qdev->dbc[dbc_id].usr || qdev->dbc[dbc_id].usr->handle != usr->handle)
|
||||
|
|
@ -1973,13 +2002,7 @@ void wakeup_dbc(struct qaic_device *qdev, u32 dbc_id)
|
|||
struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
|
||||
|
||||
dbc->usr = NULL;
|
||||
empty_xfer_list(qdev, dbc);
|
||||
synchronize_srcu(&dbc->ch_lock);
|
||||
/*
|
||||
* Threads holding channel lock, may add more elements in the xfer_list.
|
||||
* Flush out these elements from xfer_list.
|
||||
*/
|
||||
empty_xfer_list(qdev, dbc);
|
||||
sync_empty_xfer_list(qdev, dbc);
|
||||
}
|
||||
|
||||
void release_dbc(struct qaic_device *qdev, u32 dbc_id)
|
||||
|
|
@ -2020,3 +2043,30 @@ void qaic_data_get_fifo_info(struct dma_bridge_chan *dbc, u32 *head, u32 *tail)
|
|||
*head = readl(dbc->dbc_base + REQHP_OFF);
|
||||
*tail = readl(dbc->dbc_base + REQTP_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* qaic_dbc_enter_ssr - Prepare to enter in sub system reset(SSR) for given DBC ID.
|
||||
* @qdev: qaic device handle
|
||||
* @dbc_id: ID of the DBC which will enter SSR
|
||||
*
|
||||
* The device will automatically deactivate the workload as not
|
||||
* all errors can be silently recovered. The user will be
|
||||
* notified and will need to decide the required recovery
|
||||
* action to take.
|
||||
*/
|
||||
void qaic_dbc_enter_ssr(struct qaic_device *qdev, u32 dbc_id)
|
||||
{
|
||||
qdev->ssr_dbc = dbc_id;
|
||||
release_dbc(qdev, dbc_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* qaic_dbc_exit_ssr - Prepare to exit from sub system reset(SSR) for given DBC ID.
|
||||
* @qdev: qaic device handle
|
||||
*
|
||||
* The DBC returns to an operational state and begins accepting work after exiting SSR.
|
||||
*/
|
||||
void qaic_dbc_exit_ssr(struct qaic_device *qdev)
|
||||
{
|
||||
qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "qaic.h"
|
||||
#include "qaic_debugfs.h"
|
||||
#include "qaic_ras.h"
|
||||
#include "qaic_ssr.h"
|
||||
#include "qaic_timesync.h"
|
||||
#include "sahara.h"
|
||||
|
||||
|
|
@ -270,6 +271,13 @@ static int qaic_create_drm_device(struct qaic_device *qdev, s32 partition_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = qaic_sysfs_init(qddev);
|
||||
if (ret) {
|
||||
drm_dev_unregister(drm);
|
||||
pci_dbg(qdev->pdev, "qaic_sysfs_init failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qaic_debugfs_init(qddev);
|
||||
|
||||
return ret;
|
||||
|
|
@ -281,6 +289,7 @@ static void qaic_destroy_drm_device(struct qaic_device *qdev, s32 partition_id)
|
|||
struct drm_device *drm = to_drm(qddev);
|
||||
struct qaic_user *usr;
|
||||
|
||||
qaic_sysfs_remove(qddev);
|
||||
drm_dev_unregister(drm);
|
||||
qddev->partition_id = 0;
|
||||
/*
|
||||
|
|
@ -382,6 +391,7 @@ void qaic_dev_reset_clean_local_state(struct qaic_device *qdev)
|
|||
qaic_notify_reset(qdev);
|
||||
|
||||
/* start tearing things down */
|
||||
qaic_clean_up_ssr(qdev);
|
||||
for (i = 0; i < qdev->num_dbc; ++i)
|
||||
release_dbc(qdev, i);
|
||||
}
|
||||
|
|
@ -431,11 +441,18 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,
|
|||
qdev->qts_wq = qaicm_wq_init(drm, "qaic_ts");
|
||||
if (IS_ERR(qdev->qts_wq))
|
||||
return NULL;
|
||||
qdev->ssr_wq = qaicm_wq_init(drm, "qaic_ssr");
|
||||
if (IS_ERR(qdev->ssr_wq))
|
||||
return NULL;
|
||||
|
||||
ret = qaicm_srcu_init(drm, &qdev->dev_lock);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
ret = qaic_ssr_init(qdev, drm);
|
||||
if (ret)
|
||||
pci_info(pdev, "QAIC SSR crashdump collection not supported.\n");
|
||||
|
||||
qdev->qddev = qddev;
|
||||
qdev->pdev = pdev;
|
||||
qddev->qdev = qdev;
|
||||
|
|
@ -545,7 +562,7 @@ static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
|
|||
qdev->dbc[i].irq = pci_irq_vector(pdev, qdev->single_msi ? 0 : i + 1);
|
||||
if (!qdev->single_msi)
|
||||
disable_irq_nosync(qdev->dbc[i].irq);
|
||||
INIT_WORK(&qdev->dbc[i].poll_work, irq_polling_work);
|
||||
INIT_WORK(&qdev->dbc[i].poll_work, qaic_irq_polling_work);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -660,6 +677,92 @@ static const struct pci_error_handlers qaic_pci_err_handler = {
|
|||
.reset_done = qaic_pci_reset_done,
|
||||
};
|
||||
|
||||
static bool qaic_is_under_reset(struct qaic_device *qdev)
|
||||
{
|
||||
int rcu_id;
|
||||
bool ret;
|
||||
|
||||
rcu_id = srcu_read_lock(&qdev->dev_lock);
|
||||
ret = qdev->dev_state != QAIC_ONLINE;
|
||||
srcu_read_unlock(&qdev->dev_lock, rcu_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool qaic_data_path_busy(struct qaic_device *qdev)
|
||||
{
|
||||
bool ret = false;
|
||||
int dev_rcu_id;
|
||||
int i;
|
||||
|
||||
dev_rcu_id = srcu_read_lock(&qdev->dev_lock);
|
||||
if (qdev->dev_state != QAIC_ONLINE) {
|
||||
srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < qdev->num_dbc; i++) {
|
||||
struct dma_bridge_chan *dbc = &qdev->dbc[i];
|
||||
unsigned long flags;
|
||||
int ch_rcu_id;
|
||||
|
||||
ch_rcu_id = srcu_read_lock(&dbc->ch_lock);
|
||||
if (!dbc->usr || !dbc->in_use) {
|
||||
srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
|
||||
continue;
|
||||
}
|
||||
spin_lock_irqsave(&dbc->xfer_lock, flags);
|
||||
ret = !list_empty(&dbc->xfer_list);
|
||||
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
|
||||
srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qaic_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
dev_dbg(dev, "Suspending..\n");
|
||||
if (qaic_data_path_busy(qdev)) {
|
||||
dev_dbg(dev, "Device's datapath is busy. Aborting suspend..\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (qaic_is_under_reset(qdev)) {
|
||||
dev_dbg(dev, "Device is under reset. Aborting suspend..\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
qaic_mqts_ch_stop_timer(qdev->mqts_ch);
|
||||
qaic_pci_reset_prepare(qdev->pdev);
|
||||
pci_save_state(qdev->pdev);
|
||||
pci_disable_device(qdev->pdev);
|
||||
pci_set_power_state(qdev->pdev, PCI_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qaic_pm_resume(struct device *dev)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "Resuming..\n");
|
||||
pci_set_power_state(qdev->pdev, PCI_D0);
|
||||
pci_restore_state(qdev->pdev);
|
||||
ret = pci_enable_device(qdev->pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "pci_enable_device failed on resume %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
pci_set_master(qdev->pdev);
|
||||
qaic_pci_reset_done(qdev->pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops qaic_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(qaic_pm_suspend, qaic_pm_resume)
|
||||
};
|
||||
|
||||
static struct pci_driver qaic_pci_driver = {
|
||||
.name = QAIC_NAME,
|
||||
.id_table = qaic_ids,
|
||||
|
|
@ -667,6 +770,9 @@ static struct pci_driver qaic_pci_driver = {
|
|||
.remove = qaic_pci_remove,
|
||||
.shutdown = qaic_pci_shutdown,
|
||||
.err_handler = &qaic_pci_err_handler,
|
||||
.driver = {
|
||||
.pm = pm_sleep_ptr(&qaic_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init qaic_init(void)
|
||||
|
|
@ -702,9 +808,16 @@ static int __init qaic_init(void)
|
|||
ret = qaic_ras_register();
|
||||
if (ret)
|
||||
pr_debug("qaic: qaic_ras_register failed %d\n", ret);
|
||||
ret = qaic_ssr_register();
|
||||
if (ret) {
|
||||
pr_debug("qaic: qaic_ssr_register failed %d\n", ret);
|
||||
goto free_bootlog;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_bootlog:
|
||||
qaic_bootlog_unregister();
|
||||
free_mhi:
|
||||
mhi_driver_unregister(&qaic_mhi_driver);
|
||||
free_pci:
|
||||
|
|
@ -730,6 +843,7 @@ static void __exit qaic_exit(void)
|
|||
* reinitializing the link_up state after the cleanup is done.
|
||||
*/
|
||||
link_up = true;
|
||||
qaic_ssr_unregister();
|
||||
qaic_ras_unregister();
|
||||
qaic_bootlog_unregister();
|
||||
qaic_timesync_deinit();
|
||||
|
|
|
|||
815
drivers/accel/qaic/qaic_ssr.c
Normal file
815
drivers/accel/qaic/qaic_ssr.c
Normal file
|
|
@ -0,0 +1,815 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
|
||||
/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <linux/devcoredump.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mhi.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "qaic.h"
|
||||
#include "qaic_ssr.h"
|
||||
|
||||
#define SSR_RESP_MSG_SZ 32
|
||||
#define SSR_MHI_BUF_SIZE SZ_64K
|
||||
#define SSR_MEM_READ_DATA_SIZE ((u64)SSR_MHI_BUF_SIZE - sizeof(struct ssr_crashdump))
|
||||
#define SSR_MEM_READ_CHUNK_SIZE ((u64)SSR_MEM_READ_DATA_SIZE - sizeof(struct ssr_memory_read_rsp))
|
||||
|
||||
#define DEBUG_TRANSFER_INFO BIT(0)
|
||||
#define DEBUG_TRANSFER_INFO_RSP BIT(1)
|
||||
#define MEMORY_READ BIT(2)
|
||||
#define MEMORY_READ_RSP BIT(3)
|
||||
#define DEBUG_TRANSFER_DONE BIT(4)
|
||||
#define DEBUG_TRANSFER_DONE_RSP BIT(5)
|
||||
#define SSR_EVENT BIT(8)
|
||||
#define SSR_EVENT_RSP BIT(9)
|
||||
|
||||
#define SSR_EVENT_NACK BIT(0)
|
||||
#define BEFORE_SHUTDOWN BIT(1)
|
||||
#define AFTER_SHUTDOWN BIT(2)
|
||||
#define BEFORE_POWER_UP BIT(3)
|
||||
#define AFTER_POWER_UP BIT(4)
|
||||
|
||||
struct debug_info_table {
|
||||
/* Save preferences. Default is mandatory */
|
||||
u64 save_perf;
|
||||
/* Base address of the debug region */
|
||||
u64 mem_base;
|
||||
/* Size of debug region in bytes */
|
||||
u64 len;
|
||||
/* Description */
|
||||
char desc[20];
|
||||
/* Filename of debug region */
|
||||
char filename[20];
|
||||
};
|
||||
|
||||
struct _ssr_hdr {
|
||||
__le32 cmd;
|
||||
__le32 len;
|
||||
__le32 dbc_id;
|
||||
};
|
||||
|
||||
struct ssr_hdr {
|
||||
u32 cmd;
|
||||
u32 len;
|
||||
u32 dbc_id;
|
||||
};
|
||||
|
||||
struct ssr_debug_transfer_info {
|
||||
struct ssr_hdr hdr;
|
||||
u32 resv;
|
||||
u64 tbl_addr;
|
||||
u64 tbl_len;
|
||||
} __packed;
|
||||
|
||||
struct ssr_debug_transfer_info_rsp {
|
||||
struct _ssr_hdr hdr;
|
||||
__le32 ret;
|
||||
} __packed;
|
||||
|
||||
struct ssr_memory_read {
|
||||
struct _ssr_hdr hdr;
|
||||
__le32 resv;
|
||||
__le64 addr;
|
||||
__le64 len;
|
||||
} __packed;
|
||||
|
||||
struct ssr_memory_read_rsp {
|
||||
struct _ssr_hdr hdr;
|
||||
__le32 resv;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct ssr_debug_transfer_done {
|
||||
struct _ssr_hdr hdr;
|
||||
__le32 resv;
|
||||
} __packed;
|
||||
|
||||
struct ssr_debug_transfer_done_rsp {
|
||||
struct _ssr_hdr hdr;
|
||||
__le32 ret;
|
||||
} __packed;
|
||||
|
||||
struct ssr_event {
|
||||
struct ssr_hdr hdr;
|
||||
u32 event;
|
||||
} __packed;
|
||||
|
||||
struct ssr_event_rsp {
|
||||
struct _ssr_hdr hdr;
|
||||
__le32 event;
|
||||
} __packed;
|
||||
|
||||
struct ssr_resp {
|
||||
/* Work struct to schedule work coming on QAIC_SSR channel */
|
||||
struct work_struct work;
|
||||
/* Root struct of device, used to access device resources */
|
||||
struct qaic_device *qdev;
|
||||
/* Buffer used by MHI for transfer requests */
|
||||
u8 data[] __aligned(8);
|
||||
};
|
||||
|
||||
/* SSR crashdump book keeping structure */
|
||||
struct ssr_dump_info {
|
||||
/* DBC associated with this SSR crashdump */
|
||||
struct dma_bridge_chan *dbc;
|
||||
/*
|
||||
* It will be used when we complete the crashdump download and switch
|
||||
* to waiting on SSR events
|
||||
*/
|
||||
struct ssr_resp *resp;
|
||||
/* MEMORY READ request MHI buffer.*/
|
||||
struct ssr_memory_read *read_buf_req;
|
||||
/* TRUE: ->read_buf_req is queued for MHI transaction. FALSE: Otherwise */
|
||||
bool read_buf_req_queued;
|
||||
/* Address of table in host */
|
||||
void *tbl_addr;
|
||||
/* Total size of table */
|
||||
u64 tbl_len;
|
||||
/* Offset of table(->tbl_addr) where the new chunk will be dumped */
|
||||
u64 tbl_off;
|
||||
/* Address of table in device/target */
|
||||
u64 tbl_addr_dev;
|
||||
/* Ptr to the entire dump */
|
||||
void *dump_addr;
|
||||
/* Entire crashdump size */
|
||||
u64 dump_sz;
|
||||
/* Offset of crashdump(->dump_addr) where the new chunk will be dumped */
|
||||
u64 dump_off;
|
||||
/* Points to the table entry we are currently downloading */
|
||||
struct debug_info_table *tbl_ent;
|
||||
/* Offset in the current table entry(->tbl_ent) for next chuck */
|
||||
u64 tbl_ent_off;
|
||||
};
|
||||
|
||||
struct ssr_crashdump {
|
||||
/*
|
||||
* Points to a book keeping struct maintained by MHI SSR device while
|
||||
* downloading a SSR crashdump. It is NULL when crashdump downloading
|
||||
* not in progress.
|
||||
*/
|
||||
struct ssr_dump_info *dump_info;
|
||||
/* Work struct to schedule work coming on QAIC_SSR channel */
|
||||
struct work_struct work;
|
||||
/* Root struct of device, used to access device resources */
|
||||
struct qaic_device *qdev;
|
||||
/* Buffer used by MHI for transfer requests */
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
#define QAIC_SSR_DUMP_V1_MAGIC 0x1234567890abcdef
|
||||
#define QAIC_SSR_DUMP_V1_VER 1
|
||||
struct dump_file_meta {
|
||||
u64 magic;
|
||||
u64 version;
|
||||
u64 size; /* Total size of the entire dump */
|
||||
u64 tbl_len; /* Length of the table in byte */
|
||||
};
|
||||
|
||||
/*
|
||||
* Layout of crashdump
|
||||
* +------------------------------------------+
|
||||
* | Crashdump Meta structure |
|
||||
* | type: struct dump_file_meta |
|
||||
* +------------------------------------------+
|
||||
* | Crashdump Table |
|
||||
* | type: array of struct debug_info_table |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* +------------------------------------------+
|
||||
* | Crashdump |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* +------------------------------------------+
|
||||
*/
|
||||
|
||||
static void free_ssr_dump_info(struct ssr_crashdump *ssr_crash)
|
||||
{
|
||||
struct ssr_dump_info *dump_info = ssr_crash->dump_info;
|
||||
|
||||
ssr_crash->dump_info = NULL;
|
||||
if (!dump_info)
|
||||
return;
|
||||
if (!dump_info->read_buf_req_queued)
|
||||
kfree(dump_info->read_buf_req);
|
||||
vfree(dump_info->tbl_addr);
|
||||
vfree(dump_info->dump_addr);
|
||||
kfree(dump_info);
|
||||
}
|
||||
|
||||
void qaic_clean_up_ssr(struct qaic_device *qdev)
|
||||
{
|
||||
struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
|
||||
|
||||
if (!ssr_crash)
|
||||
return;
|
||||
|
||||
qaic_dbc_exit_ssr(qdev);
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
}
|
||||
|
||||
static int alloc_dump(struct ssr_dump_info *dump_info)
|
||||
{
|
||||
struct debug_info_table *tbl_ent = dump_info->tbl_addr;
|
||||
struct dump_file_meta *dump_meta;
|
||||
u64 tbl_sz_lp = 0;
|
||||
u64 dump_size = 0;
|
||||
|
||||
while (tbl_sz_lp < dump_info->tbl_len) {
|
||||
le64_to_cpus(&tbl_ent->save_perf);
|
||||
le64_to_cpus(&tbl_ent->mem_base);
|
||||
le64_to_cpus(&tbl_ent->len);
|
||||
|
||||
if (tbl_ent->len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
dump_size += tbl_ent->len;
|
||||
tbl_ent++;
|
||||
tbl_sz_lp += sizeof(*tbl_ent);
|
||||
}
|
||||
|
||||
dump_info->dump_sz = dump_size + dump_info->tbl_len + sizeof(*dump_meta);
|
||||
dump_info->dump_addr = vzalloc(dump_info->dump_sz);
|
||||
if (!dump_info->dump_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Copy crashdump meta and table */
|
||||
dump_meta = dump_info->dump_addr;
|
||||
dump_meta->magic = QAIC_SSR_DUMP_V1_MAGIC;
|
||||
dump_meta->version = QAIC_SSR_DUMP_V1_VER;
|
||||
dump_meta->size = dump_info->dump_sz;
|
||||
dump_meta->tbl_len = dump_info->tbl_len;
|
||||
memcpy(dump_info->dump_addr + sizeof(*dump_meta), dump_info->tbl_addr, dump_info->tbl_len);
|
||||
/* Offset by crashdump meta and table (copied above) */
|
||||
dump_info->dump_off = dump_info->tbl_len + sizeof(*dump_meta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_xfer_done(struct qaic_device *qdev, void *resp, u32 dbc_id)
|
||||
{
|
||||
struct ssr_debug_transfer_done *xfer_done;
|
||||
int ret;
|
||||
|
||||
xfer_done = kmalloc(sizeof(*xfer_done), GFP_KERNEL);
|
||||
if (!xfer_done) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp, SSR_RESP_MSG_SZ, MHI_EOT);
|
||||
if (ret)
|
||||
goto free_xfer_done;
|
||||
|
||||
xfer_done->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_DONE);
|
||||
xfer_done->hdr.len = cpu_to_le32(sizeof(*xfer_done));
|
||||
xfer_done->hdr.dbc_id = cpu_to_le32(dbc_id);
|
||||
|
||||
ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, xfer_done, sizeof(*xfer_done), MHI_EOT);
|
||||
if (ret)
|
||||
goto free_xfer_done;
|
||||
|
||||
return 0;
|
||||
|
||||
free_xfer_done:
|
||||
kfree(xfer_done);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mem_read_req(struct qaic_device *qdev, u64 dest_addr, u64 dest_len)
|
||||
{
|
||||
struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
|
||||
struct ssr_memory_read *read_buf_req;
|
||||
struct ssr_dump_info *dump_info;
|
||||
int ret;
|
||||
|
||||
dump_info = ssr_crash->dump_info;
|
||||
ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, ssr_crash->data, SSR_MEM_READ_DATA_SIZE,
|
||||
MHI_EOT);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
read_buf_req = dump_info->read_buf_req;
|
||||
read_buf_req->hdr.cmd = cpu_to_le32(MEMORY_READ);
|
||||
read_buf_req->hdr.len = cpu_to_le32(sizeof(*read_buf_req));
|
||||
read_buf_req->hdr.dbc_id = cpu_to_le32(qdev->ssr_dbc);
|
||||
read_buf_req->addr = cpu_to_le64(dest_addr);
|
||||
read_buf_req->len = cpu_to_le64(dest_len);
|
||||
|
||||
ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, read_buf_req, sizeof(*read_buf_req),
|
||||
MHI_EOT);
|
||||
if (!ret)
|
||||
dump_info->read_buf_req_queued = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ssr_copy_table(struct ssr_dump_info *dump_info, void *data, u64 len)
|
||||
{
|
||||
if (len > dump_info->tbl_len - dump_info->tbl_off)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(dump_info->tbl_addr + dump_info->tbl_off, data, len);
|
||||
dump_info->tbl_off += len;
|
||||
|
||||
/* Entire table has been downloaded, alloc dump memory */
|
||||
if (dump_info->tbl_off == dump_info->tbl_len) {
|
||||
dump_info->tbl_ent = dump_info->tbl_addr;
|
||||
return alloc_dump(dump_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssr_copy_dump(struct ssr_dump_info *dump_info, void *data, u64 len)
|
||||
{
|
||||
struct debug_info_table *tbl_ent;
|
||||
|
||||
tbl_ent = dump_info->tbl_ent;
|
||||
|
||||
if (len > tbl_ent->len - dump_info->tbl_ent_off)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(dump_info->dump_addr + dump_info->dump_off, data, len);
|
||||
dump_info->dump_off += len;
|
||||
dump_info->tbl_ent_off += len;
|
||||
|
||||
/*
|
||||
* Current segment (a entry in table) of the crashdump is complete,
|
||||
* move to next one
|
||||
*/
|
||||
if (tbl_ent->len == dump_info->tbl_ent_off) {
|
||||
dump_info->tbl_ent++;
|
||||
dump_info->tbl_ent_off = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssr_dump_worker(struct work_struct *work)
|
||||
{
|
||||
struct ssr_crashdump *ssr_crash = container_of(work, struct ssr_crashdump, work);
|
||||
struct qaic_device *qdev = ssr_crash->qdev;
|
||||
struct ssr_memory_read_rsp *mem_rd_resp;
|
||||
struct debug_info_table *tbl_ent;
|
||||
struct ssr_dump_info *dump_info;
|
||||
u64 dest_addr, dest_len;
|
||||
struct _ssr_hdr *_hdr;
|
||||
struct ssr_hdr hdr;
|
||||
u64 data_len;
|
||||
int ret;
|
||||
|
||||
mem_rd_resp = (struct ssr_memory_read_rsp *)ssr_crash->data;
|
||||
_hdr = &mem_rd_resp->hdr;
|
||||
hdr.cmd = le32_to_cpu(_hdr->cmd);
|
||||
hdr.len = le32_to_cpu(_hdr->len);
|
||||
hdr.dbc_id = le32_to_cpu(_hdr->dbc_id);
|
||||
|
||||
if (hdr.dbc_id != qdev->ssr_dbc)
|
||||
goto reset_device;
|
||||
|
||||
dump_info = ssr_crash->dump_info;
|
||||
if (!dump_info)
|
||||
goto reset_device;
|
||||
|
||||
if (hdr.cmd != MEMORY_READ_RSP)
|
||||
goto free_dump_info;
|
||||
|
||||
if (hdr.len > SSR_MEM_READ_DATA_SIZE)
|
||||
goto free_dump_info;
|
||||
|
||||
data_len = hdr.len - sizeof(*mem_rd_resp);
|
||||
|
||||
if (dump_info->tbl_off < dump_info->tbl_len) /* Chunk belongs to table */
|
||||
ret = ssr_copy_table(dump_info, mem_rd_resp->data, data_len);
|
||||
else /* Chunk belongs to crashdump */
|
||||
ret = ssr_copy_dump(dump_info, mem_rd_resp->data, data_len);
|
||||
|
||||
if (ret)
|
||||
goto free_dump_info;
|
||||
|
||||
if (dump_info->tbl_off < dump_info->tbl_len) {
|
||||
/* Continue downloading table */
|
||||
dest_addr = dump_info->tbl_addr_dev + dump_info->tbl_off;
|
||||
dest_len = min(SSR_MEM_READ_CHUNK_SIZE, dump_info->tbl_len - dump_info->tbl_off);
|
||||
ret = mem_read_req(qdev, dest_addr, dest_len);
|
||||
} else if (dump_info->dump_off < dump_info->dump_sz) {
|
||||
/* Continue downloading crashdump */
|
||||
tbl_ent = dump_info->tbl_ent;
|
||||
dest_addr = tbl_ent->mem_base + dump_info->tbl_ent_off;
|
||||
dest_len = min(SSR_MEM_READ_CHUNK_SIZE, tbl_ent->len - dump_info->tbl_ent_off);
|
||||
ret = mem_read_req(qdev, dest_addr, dest_len);
|
||||
} else {
|
||||
/* Crashdump download complete */
|
||||
ret = send_xfer_done(qdev, dump_info->resp->data, hdr.dbc_id);
|
||||
}
|
||||
|
||||
/* Most likely a MHI xfer has failed */
|
||||
if (ret)
|
||||
goto free_dump_info;
|
||||
|
||||
return;
|
||||
|
||||
free_dump_info:
|
||||
/* Free the allocated memory */
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
reset_device:
|
||||
/*
|
||||
* After subsystem crashes in device crashdump collection begins but
|
||||
* something went wrong while collecting crashdump, now instead of
|
||||
* handling this error we just reset the device as the best effort has
|
||||
* been made
|
||||
*/
|
||||
mhi_soc_reset(qdev->mhi_cntrl);
|
||||
}
|
||||
|
||||
static struct ssr_dump_info *alloc_dump_info(struct qaic_device *qdev,
|
||||
struct ssr_debug_transfer_info *debug_info)
|
||||
{
|
||||
struct ssr_dump_info *dump_info;
|
||||
int ret;
|
||||
|
||||
le64_to_cpus(&debug_info->tbl_len);
|
||||
le64_to_cpus(&debug_info->tbl_addr);
|
||||
|
||||
if (debug_info->tbl_len == 0 ||
|
||||
debug_info->tbl_len % sizeof(struct debug_info_table) != 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Allocate SSR crashdump book keeping structure */
|
||||
dump_info = kzalloc(sizeof(*dump_info), GFP_KERNEL);
|
||||
if (!dump_info) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Buffer used to send MEMORY READ request to device via MHI */
|
||||
dump_info->read_buf_req = kzalloc(sizeof(*dump_info->read_buf_req), GFP_KERNEL);
|
||||
if (!dump_info->read_buf_req) {
|
||||
ret = -ENOMEM;
|
||||
goto free_dump_info;
|
||||
}
|
||||
|
||||
/* Crashdump meta table buffer */
|
||||
dump_info->tbl_addr = vzalloc(debug_info->tbl_len);
|
||||
if (!dump_info->tbl_addr) {
|
||||
ret = -ENOMEM;
|
||||
goto free_read_buf_req;
|
||||
}
|
||||
|
||||
dump_info->tbl_addr_dev = debug_info->tbl_addr;
|
||||
dump_info->tbl_len = debug_info->tbl_len;
|
||||
|
||||
return dump_info;
|
||||
|
||||
free_read_buf_req:
|
||||
kfree(dump_info->read_buf_req);
|
||||
free_dump_info:
|
||||
kfree(dump_info);
|
||||
out:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int dbg_xfer_info_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
|
||||
struct ssr_debug_transfer_info *debug_info)
|
||||
{
|
||||
struct ssr_debug_transfer_info_rsp *debug_rsp;
|
||||
struct ssr_crashdump *ssr_crash = NULL;
|
||||
int ret = 0, ret2;
|
||||
|
||||
debug_rsp = kmalloc(sizeof(*debug_rsp), GFP_KERNEL);
|
||||
if (!debug_rsp)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!qdev->ssr_mhi_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto send_rsp;
|
||||
}
|
||||
|
||||
if (dbc->state != DBC_STATE_BEFORE_POWER_UP) {
|
||||
ret = -EINVAL;
|
||||
goto send_rsp;
|
||||
}
|
||||
|
||||
ssr_crash = qdev->ssr_mhi_buf;
|
||||
ssr_crash->dump_info = alloc_dump_info(qdev, debug_info);
|
||||
if (IS_ERR(ssr_crash->dump_info)) {
|
||||
ret = PTR_ERR(ssr_crash->dump_info);
|
||||
ssr_crash->dump_info = NULL;
|
||||
}
|
||||
|
||||
send_rsp:
|
||||
debug_rsp->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_INFO_RSP);
|
||||
debug_rsp->hdr.len = cpu_to_le32(sizeof(*debug_rsp));
|
||||
debug_rsp->hdr.dbc_id = cpu_to_le32(dbc->id);
|
||||
/*
|
||||
* 0 = Return an ACK confirming the host is ready to download crashdump
|
||||
* 1 = Return an NACK confirming the host is not ready to download crashdump
|
||||
*/
|
||||
debug_rsp->ret = cpu_to_le32(ret ? 1 : 0);
|
||||
|
||||
ret2 = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, debug_rsp, sizeof(*debug_rsp), MHI_EOT);
|
||||
if (ret2) {
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
kfree(debug_rsp);
|
||||
return ret2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dbg_xfer_done_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
|
||||
struct ssr_debug_transfer_done_rsp *xfer_rsp)
|
||||
{
|
||||
struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
|
||||
u32 status = le32_to_cpu(xfer_rsp->ret);
|
||||
struct device *dev = &qdev->pdev->dev;
|
||||
struct ssr_dump_info *dump_info;
|
||||
|
||||
dump_info = ssr_crash->dump_info;
|
||||
if (!dump_info)
|
||||
return;
|
||||
|
||||
if (status) {
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_coredumpv(dev, dump_info->dump_addr, dump_info->dump_sz, GFP_KERNEL);
|
||||
/* dev_coredumpv will free dump_info->dump_addr */
|
||||
dump_info->dump_addr = NULL;
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
}
|
||||
|
||||
static void ssr_worker(struct work_struct *work)
|
||||
{
|
||||
struct ssr_resp *resp = container_of(work, struct ssr_resp, work);
|
||||
struct ssr_hdr *hdr = (struct ssr_hdr *)resp->data;
|
||||
struct ssr_dump_info *dump_info = NULL;
|
||||
struct qaic_device *qdev = resp->qdev;
|
||||
struct ssr_crashdump *ssr_crash;
|
||||
struct ssr_event_rsp *event_rsp;
|
||||
struct dma_bridge_chan *dbc;
|
||||
struct ssr_event *event;
|
||||
u32 ssr_event_ack;
|
||||
int ret;
|
||||
|
||||
le32_to_cpus(&hdr->cmd);
|
||||
le32_to_cpus(&hdr->len);
|
||||
le32_to_cpus(&hdr->dbc_id);
|
||||
|
||||
if (hdr->len > SSR_RESP_MSG_SZ)
|
||||
goto out;
|
||||
|
||||
if (hdr->dbc_id >= qdev->num_dbc)
|
||||
goto out;
|
||||
|
||||
dbc = &qdev->dbc[hdr->dbc_id];
|
||||
|
||||
switch (hdr->cmd) {
|
||||
case DEBUG_TRANSFER_INFO:
|
||||
ret = dbg_xfer_info_rsp(qdev, dbc, (struct ssr_debug_transfer_info *)resp->data);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ssr_crash = qdev->ssr_mhi_buf;
|
||||
dump_info = ssr_crash->dump_info;
|
||||
dump_info->dbc = dbc;
|
||||
dump_info->resp = resp;
|
||||
|
||||
/* Start by downloading debug table */
|
||||
ret = mem_read_req(qdev, dump_info->tbl_addr_dev,
|
||||
min(dump_info->tbl_len, SSR_MEM_READ_CHUNK_SIZE));
|
||||
if (ret) {
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Till now everything went fine, which means that we will be
|
||||
* collecting crashdump chunk by chunk. Do not queue a response
|
||||
* buffer for SSR cmds till the crashdump is complete.
|
||||
*/
|
||||
return;
|
||||
case SSR_EVENT:
|
||||
event = (struct ssr_event *)hdr;
|
||||
le32_to_cpus(&event->event);
|
||||
ssr_event_ack = event->event;
|
||||
ssr_crash = qdev->ssr_mhi_buf;
|
||||
|
||||
switch (event->event) {
|
||||
case BEFORE_SHUTDOWN:
|
||||
set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_SHUTDOWN);
|
||||
qaic_dbc_enter_ssr(qdev, hdr->dbc_id);
|
||||
break;
|
||||
case AFTER_SHUTDOWN:
|
||||
set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_SHUTDOWN);
|
||||
break;
|
||||
case BEFORE_POWER_UP:
|
||||
set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_POWER_UP);
|
||||
break;
|
||||
case AFTER_POWER_UP:
|
||||
/*
|
||||
* If dump info is a non NULL value it means that we
|
||||
* have received this SSR event while downloading a
|
||||
* crashdump for this DBC is still in progress. NACK
|
||||
* the SSR event
|
||||
*/
|
||||
if (ssr_crash && ssr_crash->dump_info) {
|
||||
free_ssr_dump_info(ssr_crash);
|
||||
ssr_event_ack = SSR_EVENT_NACK;
|
||||
break;
|
||||
}
|
||||
|
||||
set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_POWER_UP);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
event_rsp = kmalloc(sizeof(*event_rsp), GFP_KERNEL);
|
||||
if (!event_rsp)
|
||||
break;
|
||||
|
||||
event_rsp->hdr.cmd = cpu_to_le32(SSR_EVENT_RSP);
|
||||
event_rsp->hdr.len = cpu_to_le32(sizeof(*event_rsp));
|
||||
event_rsp->hdr.dbc_id = cpu_to_le32(hdr->dbc_id);
|
||||
event_rsp->event = cpu_to_le32(ssr_event_ack);
|
||||
|
||||
ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, event_rsp, sizeof(*event_rsp),
|
||||
MHI_EOT);
|
||||
if (ret)
|
||||
kfree(event_rsp);
|
||||
|
||||
if (event->event == AFTER_POWER_UP && ssr_event_ack != SSR_EVENT_NACK) {
|
||||
qaic_dbc_exit_ssr(qdev);
|
||||
set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_IDLE);
|
||||
}
|
||||
|
||||
break;
|
||||
case DEBUG_TRANSFER_DONE_RSP:
|
||||
dbg_xfer_done_rsp(qdev, dbc, (struct ssr_debug_transfer_done_rsp *)hdr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
|
||||
if (ret)
|
||||
kfree(resp);
|
||||
}
|
||||
|
||||
static int qaic_ssr_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
|
||||
struct ssr_resp *resp;
|
||||
int ret;
|
||||
|
||||
ret = mhi_prepare_for_transfer(mhi_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
resp = kzalloc(sizeof(*resp) + SSR_RESP_MSG_SZ, GFP_KERNEL);
|
||||
if (!resp) {
|
||||
mhi_unprepare_from_transfer(mhi_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
resp->qdev = qdev;
|
||||
INIT_WORK(&resp->work, ssr_worker);
|
||||
|
||||
ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
|
||||
if (ret) {
|
||||
kfree(resp);
|
||||
mhi_unprepare_from_transfer(mhi_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&mhi_dev->dev, qdev);
|
||||
qdev->ssr_ch = mhi_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qaic_ssr_mhi_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct qaic_device *qdev;
|
||||
|
||||
qdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
mhi_unprepare_from_transfer(qdev->ssr_ch);
|
||||
qdev->ssr_ch = NULL;
|
||||
}
|
||||
|
||||
static void qaic_ssr_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
|
||||
{
|
||||
struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
|
||||
struct _ssr_hdr *hdr = mhi_result->buf_addr;
|
||||
struct ssr_dump_info *dump_info;
|
||||
|
||||
if (mhi_result->transaction_status) {
|
||||
kfree(mhi_result->buf_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* MEMORY READ is used to download crashdump. And crashdump is
|
||||
* downloaded chunk by chunk in a series of MEMORY READ SSR commands.
|
||||
* Hence to avoid too many kmalloc() and kfree() of the same MEMORY READ
|
||||
* request buffer, we allocate only one such buffer and free it only
|
||||
* once.
|
||||
*/
|
||||
if (le32_to_cpu(hdr->cmd) == MEMORY_READ) {
|
||||
dump_info = ssr_crash->dump_info;
|
||||
if (dump_info) {
|
||||
dump_info->read_buf_req_queued = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(mhi_result->buf_addr);
|
||||
}
|
||||
|
||||
static void qaic_ssr_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
|
||||
{
|
||||
struct ssr_resp *resp = container_of(mhi_result->buf_addr, struct ssr_resp, data);
|
||||
struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
|
||||
bool memory_read_rsp = false;
|
||||
|
||||
if (ssr_crash && ssr_crash->data == mhi_result->buf_addr)
|
||||
memory_read_rsp = true;
|
||||
|
||||
if (mhi_result->transaction_status) {
|
||||
/* Do not free SSR crashdump buffer as it allocated via managed APIs */
|
||||
if (!memory_read_rsp)
|
||||
kfree(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memory_read_rsp)
|
||||
queue_work(qdev->ssr_wq, &ssr_crash->work);
|
||||
else
|
||||
queue_work(qdev->ssr_wq, &resp->work);
|
||||
}
|
||||
|
||||
static const struct mhi_device_id qaic_ssr_mhi_match_table[] = {
|
||||
{ .chan = "QAIC_SSR", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct mhi_driver qaic_ssr_mhi_driver = {
|
||||
.id_table = qaic_ssr_mhi_match_table,
|
||||
.remove = qaic_ssr_mhi_remove,
|
||||
.probe = qaic_ssr_mhi_probe,
|
||||
.ul_xfer_cb = qaic_ssr_mhi_ul_xfer_cb,
|
||||
.dl_xfer_cb = qaic_ssr_mhi_dl_xfer_cb,
|
||||
.driver = {
|
||||
.name = "qaic_ssr",
|
||||
},
|
||||
};
|
||||
|
||||
int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm)
|
||||
{
|
||||
struct ssr_crashdump *ssr_crash;
|
||||
|
||||
qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
|
||||
|
||||
/*
|
||||
* Device requests only one SSR at a time. So allocating only one
|
||||
* buffer to download crashdump is good enough.
|
||||
*/
|
||||
ssr_crash = drmm_kzalloc(drm, SSR_MHI_BUF_SIZE, GFP_KERNEL);
|
||||
if (!ssr_crash)
|
||||
return -ENOMEM;
|
||||
|
||||
ssr_crash->qdev = qdev;
|
||||
INIT_WORK(&ssr_crash->work, ssr_dump_worker);
|
||||
qdev->ssr_mhi_buf = ssr_crash;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qaic_ssr_register(void)
|
||||
{
|
||||
return mhi_driver_register(&qaic_ssr_mhi_driver);
|
||||
}
|
||||
|
||||
void qaic_ssr_unregister(void)
|
||||
{
|
||||
mhi_driver_unregister(&qaic_ssr_mhi_driver);
|
||||
}
|
||||
17
drivers/accel/qaic/qaic_ssr.h
Normal file
17
drivers/accel/qaic/qaic_ssr.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __QAIC_SSR_H__
|
||||
#define __QAIC_SSR_H__
|
||||
|
||||
struct drm_device;
|
||||
struct qaic_device;
|
||||
|
||||
int qaic_ssr_register(void);
|
||||
void qaic_ssr_unregister(void);
|
||||
void qaic_clean_up_ssr(struct qaic_device *qdev);
|
||||
int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm);
|
||||
#endif /* __QAIC_SSR_H__ */
|
||||
109
drivers/accel/qaic/qaic_sysfs.c
Normal file
109
drivers/accel/qaic/qaic_sysfs.c
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* Copyright (c) 2020-2025, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "qaic.h"
|
||||
|
||||
#define NAME_LEN 14
|
||||
|
||||
struct dbc_attribute {
|
||||
struct device_attribute dev_attr;
|
||||
u32 dbc_id;
|
||||
char name[NAME_LEN];
|
||||
};
|
||||
|
||||
static ssize_t dbc_state_show(struct device *dev, struct device_attribute *a, char *buf)
|
||||
{
|
||||
struct dbc_attribute *dbc_attr = container_of(a, struct dbc_attribute, dev_attr);
|
||||
struct drm_minor *minor = dev_get_drvdata(dev);
|
||||
struct qaic_device *qdev;
|
||||
|
||||
qdev = to_qaic_device(minor->dev);
|
||||
return sysfs_emit(buf, "%d\n", qdev->dbc[dbc_attr->dbc_id].state);
|
||||
}
|
||||
|
||||
void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state)
|
||||
{
|
||||
struct device *kdev = to_accel_kdev(qdev->qddev);
|
||||
char *envp[3] = {};
|
||||
char state_str[16];
|
||||
char id_str[12];
|
||||
|
||||
envp[0] = id_str;
|
||||
envp[1] = state_str;
|
||||
|
||||
if (state >= DBC_STATE_MAX)
|
||||
return;
|
||||
if (dbc_id >= qdev->num_dbc)
|
||||
return;
|
||||
if (state == qdev->dbc[dbc_id].state)
|
||||
return;
|
||||
|
||||
scnprintf(id_str, ARRAY_SIZE(id_str), "DBC_ID=%d", dbc_id);
|
||||
scnprintf(state_str, ARRAY_SIZE(state_str), "DBC_STATE=%d", state);
|
||||
|
||||
qdev->dbc[dbc_id].state = state;
|
||||
kobject_uevent_env(&kdev->kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
int qaic_sysfs_init(struct qaic_drm_device *qddev)
|
||||
{
|
||||
struct device *kdev = to_accel_kdev(qddev);
|
||||
struct drm_device *drm = to_drm(qddev);
|
||||
u32 num_dbc = qddev->qdev->num_dbc;
|
||||
struct dbc_attribute *dbc_attrs;
|
||||
int i, ret;
|
||||
|
||||
dbc_attrs = drmm_kcalloc(drm, num_dbc, sizeof(*dbc_attrs), GFP_KERNEL);
|
||||
if (!dbc_attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_dbc; ++i) {
|
||||
struct dbc_attribute *dbc_attr = &dbc_attrs[i];
|
||||
|
||||
sysfs_attr_init(&dbc_attr->dev_attr.attr);
|
||||
dbc_attr->dbc_id = i;
|
||||
scnprintf(dbc_attr->name, NAME_LEN, "dbc%d_state", i);
|
||||
dbc_attr->dev_attr.attr.name = dbc_attr->name;
|
||||
dbc_attr->dev_attr.attr.mode = 0444;
|
||||
dbc_attr->dev_attr.show = dbc_state_show;
|
||||
ret = sysfs_create_file(&kdev->kobj, &dbc_attr->dev_attr.attr);
|
||||
if (ret) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
dbc_attr = &dbc_attrs[j];
|
||||
sysfs_remove_file(&kdev->kobj, &dbc_attr->dev_attr.attr);
|
||||
}
|
||||
drmm_kfree(drm, dbc_attrs);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
qddev->sysfs_attrs = dbc_attrs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qaic_sysfs_remove(struct qaic_drm_device *qddev)
|
||||
{
|
||||
struct dbc_attribute *dbc_attrs = qddev->sysfs_attrs;
|
||||
struct device *kdev = to_accel_kdev(qddev);
|
||||
u32 num_dbc = qddev->qdev->num_dbc;
|
||||
int i;
|
||||
|
||||
if (!dbc_attrs)
|
||||
return;
|
||||
|
||||
qddev->sysfs_attrs = NULL;
|
||||
for (i = 0; i < num_dbc; ++i)
|
||||
sysfs_remove_file(&kdev->kobj, &dbc_attrs[i].dev_attr.attr);
|
||||
drmm_kfree(to_drm(qddev), dbc_attrs);
|
||||
}
|
||||
|
|
@ -171,6 +171,13 @@ static void qaic_timesync_timer(struct timer_list *t)
|
|||
dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
timer_delete_sync(&mqtsdev->timer);
|
||||
}
|
||||
|
||||
static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
|
||||
|
|
@ -206,6 +213,7 @@ static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_devi
|
|||
timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
|
||||
add_timer(timer);
|
||||
dev_set_drvdata(&mhi_dev->dev, mqtsdev);
|
||||
qdev->mqts_ch = mhi_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -221,6 +229,7 @@ static void qaic_timesync_remove(struct mhi_device *mhi_dev)
|
|||
{
|
||||
struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
mqtsdev->qdev->mqts_ch = NULL;
|
||||
timer_delete_sync(&mqtsdev->timer);
|
||||
mhi_unprepare_from_transfer(mqtsdev->mhi_dev);
|
||||
kfree(mqtsdev->sync_msg);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
#ifndef __QAIC_TIMESYNC_H__
|
||||
#define __QAIC_TIMESYNC_H__
|
||||
|
||||
#include <linux/mhi.h>
|
||||
|
||||
int qaic_timesync_init(void);
|
||||
void qaic_timesync_deinit(void);
|
||||
void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev);
|
||||
#endif /* __QAIC_TIMESYNC_H__ */
|
||||
|
|
|
|||
|
|
@ -121,29 +121,27 @@ static const struct dma_fence_ops dma_fence_stub_ops = {
|
|||
.get_timeline_name = dma_fence_stub_get_name,
|
||||
};
|
||||
|
||||
static int __init dma_fence_init_stub(void)
|
||||
{
|
||||
dma_fence_init(&dma_fence_stub, &dma_fence_stub_ops,
|
||||
&dma_fence_stub_lock, 0, 0);
|
||||
|
||||
set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
|
||||
&dma_fence_stub.flags);
|
||||
|
||||
dma_fence_signal(&dma_fence_stub);
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(dma_fence_init_stub);
|
||||
|
||||
/**
|
||||
* dma_fence_get_stub - return a signaled fence
|
||||
*
|
||||
* Return a stub fence which is already signaled. The fence's
|
||||
* timestamp corresponds to the first time after boot this
|
||||
* function is called.
|
||||
* Return a stub fence which is already signaled. The fence's timestamp
|
||||
* corresponds to the initialisation time of the linux kernel.
|
||||
*/
|
||||
struct dma_fence *dma_fence_get_stub(void)
|
||||
{
|
||||
spin_lock(&dma_fence_stub_lock);
|
||||
if (!dma_fence_stub.ops) {
|
||||
dma_fence_init(&dma_fence_stub,
|
||||
&dma_fence_stub_ops,
|
||||
&dma_fence_stub_lock,
|
||||
0, 0);
|
||||
|
||||
set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
|
||||
&dma_fence_stub.flags);
|
||||
|
||||
dma_fence_signal_locked(&dma_fence_stub);
|
||||
}
|
||||
spin_unlock(&dma_fence_stub_lock);
|
||||
|
||||
return dma_fence_get(&dma_fence_stub);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_get_stub);
|
||||
|
|
|
|||
|
|
@ -557,9 +557,14 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
|
|||
ast_set_vbios_color_reg(ast, fb->format, ast_crtc_state->vmode);
|
||||
}
|
||||
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage);
|
||||
/* if the buffer comes from another device */
|
||||
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage);
|
||||
}
|
||||
|
||||
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1831,10 +1831,12 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
|
|||
}
|
||||
|
||||
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
|
||||
wait_queue_head_t *queue = drm_crtc_vblank_waitqueue(crtc);
|
||||
|
||||
if (!(crtc_mask & drm_crtc_mask(crtc)))
|
||||
continue;
|
||||
|
||||
ret = wait_event_timeout(dev->vblank[i].queue,
|
||||
ret = wait_event_timeout(*queue,
|
||||
state->crtcs[i].last_vblank_count !=
|
||||
drm_crtc_vblank_count(crtc),
|
||||
msecs_to_jiffies(100));
|
||||
|
|
|
|||
|
|
@ -250,6 +250,9 @@ static const struct edid_quirk {
|
|||
EDID_QUIRK('S', 'V', 'R', 0x1019, BIT(EDID_QUIRK_NON_DESKTOP)),
|
||||
EDID_QUIRK('A', 'U', 'O', 0x1111, BIT(EDID_QUIRK_NON_DESKTOP)),
|
||||
|
||||
/* LQ116M1JW10 displays noise when 8 bpc, but display fine as 6 bpc */
|
||||
EDID_QUIRK('S', 'H', 'P', 0x154c, BIT(EDID_QUIRK_FORCE_6BPC)),
|
||||
|
||||
/*
|
||||
* @drm_edid_internal_quirk entries end here, following with the
|
||||
* @drm_edid_quirk entries.
|
||||
|
|
|
|||
|
|
@ -1315,7 +1315,7 @@ void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
|
|||
|
||||
ret = wait_event_timeout(vblank->queue,
|
||||
last != drm_vblank_count(dev, pipe),
|
||||
msecs_to_jiffies(100));
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
drm_WARN(dev, ret == 0, "vblank wait timed out on crtc %i\n", pipe);
|
||||
|
||||
|
|
@ -2258,7 +2258,7 @@ int drm_crtc_vblank_start_timer(struct drm_crtc *crtc)
|
|||
EXPORT_SYMBOL(drm_crtc_vblank_start_timer);
|
||||
|
||||
/**
|
||||
* drm_crtc_vblank_start_timer - Cancels the given CRTC's vblank timer
|
||||
* drm_crtc_vblank_cancel_timer - Cancels the given CRTC's vblank timer
|
||||
* @crtc: the CRTC
|
||||
*
|
||||
* Drivers should call this function from their CRTC's disable_vblank
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ EXPORT_SYMBOL(drm_vblank_work_flush);
|
|||
void drm_vblank_work_flush_all(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(crtc)];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
spin_lock_irq(&dev->event_lock);
|
||||
wait_event_lock_irq(vblank->work_wait_queue,
|
||||
|
|
|
|||
|
|
@ -250,6 +250,7 @@ static irqreturn_t gma_irq_handler(int irq, void *arg)
|
|||
void gma_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
||||
struct drm_crtc *crtc;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
|
@ -260,10 +261,15 @@ void gma_irq_preinstall(struct drm_device *dev)
|
|||
PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE);
|
||||
PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE);
|
||||
|
||||
if (dev->vblank[0].enabled)
|
||||
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
|
||||
if (dev->vblank[1].enabled)
|
||||
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
|
||||
drm_for_each_crtc(crtc, dev) {
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
if (vblank->enabled) {
|
||||
u32 mask = drm_crtc_index(crtc) ? _PSB_VSYNC_PIPEB_FLAG :
|
||||
_PSB_VSYNC_PIPEA_FLAG;
|
||||
dev_priv->vdc_irq_mask |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Revisit this area - want per device masks ? */
|
||||
if (dev_priv->ops->hotplug)
|
||||
|
|
@ -278,8 +284,8 @@ void gma_irq_preinstall(struct drm_device *dev)
|
|||
void gma_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
||||
struct drm_crtc *crtc;
|
||||
unsigned long irqflags;
|
||||
unsigned int i;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
|
|
@ -292,11 +298,13 @@ void gma_irq_postinstall(struct drm_device *dev)
|
|||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
||||
|
||||
for (i = 0; i < dev->num_crtcs; ++i) {
|
||||
if (dev->vblank[i].enabled)
|
||||
gma_enable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
drm_for_each_crtc(crtc, dev) {
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
if (vblank->enabled)
|
||||
gma_enable_pipestat(dev_priv, drm_crtc_index(crtc), PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
else
|
||||
gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
gma_disable_pipestat(dev_priv, drm_crtc_index(crtc), PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
}
|
||||
|
||||
if (dev_priv->ops->hotplug_enable)
|
||||
|
|
@ -337,8 +345,8 @@ void gma_irq_uninstall(struct drm_device *dev)
|
|||
{
|
||||
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
|
||||
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
||||
struct drm_crtc *crtc;
|
||||
unsigned long irqflags;
|
||||
unsigned int i;
|
||||
|
||||
if (!dev_priv->irq_enabled)
|
||||
return;
|
||||
|
|
@ -350,9 +358,11 @@ void gma_irq_uninstall(struct drm_device *dev)
|
|||
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
||||
|
||||
for (i = 0; i < dev->num_crtcs; ++i) {
|
||||
if (dev->vblank[i].enabled)
|
||||
gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
drm_for_each_crtc(crtc, dev) {
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
if (vblank->enabled)
|
||||
gma_disable_pipestat(dev_priv, drm_crtc_index(crtc), PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
}
|
||||
|
||||
dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG |
|
||||
|
|
|
|||
|
|
@ -144,7 +144,6 @@ static int imx_drm_dumb_create(struct drm_file *file_priv,
|
|||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
u32 fourcc;
|
||||
const struct drm_format_info *info;
|
||||
u64 pitch_align;
|
||||
int ret;
|
||||
|
||||
|
|
@ -156,12 +155,15 @@ static int imx_drm_dumb_create(struct drm_file *file_priv,
|
|||
* the allocated buffer.
|
||||
*/
|
||||
fourcc = drm_driver_color_mode_format(drm, args->bpp);
|
||||
if (fourcc == DRM_FORMAT_INVALID)
|
||||
return -EINVAL;
|
||||
info = drm_format_info(fourcc);
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
pitch_align = drm_format_info_min_pitch(info, 0, SZ_8);
|
||||
if (fourcc != DRM_FORMAT_INVALID) {
|
||||
const struct drm_format_info *info = drm_format_info(fourcc);
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
pitch_align = drm_format_info_min_pitch(info, 0, 8);
|
||||
} else {
|
||||
pitch_align = DIV_ROUND_UP(args->bpp, SZ_8) * 8;
|
||||
}
|
||||
if (!pitch_align || pitch_align > U32_MAX)
|
||||
return -EINVAL;
|
||||
ret = drm_mode_size_dumb(drm, args, pitch_align, 0);
|
||||
|
|
|
|||
|
|
@ -527,13 +527,14 @@ static void mdp4_crtc_wait_for_flush_done(struct drm_crtc *crtc)
|
|||
struct drm_device *dev = crtc->dev;
|
||||
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
|
||||
struct mdp4_kms *mdp4_kms = get_kms(crtc);
|
||||
wait_queue_head_t *queue = drm_crtc_vblank_waitqueue(crtc);
|
||||
int ret;
|
||||
|
||||
ret = drm_crtc_vblank_get(crtc);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
|
||||
ret = wait_event_timeout(*queue,
|
||||
!(mdp4_read(mdp4_kms, REG_MDP4_OVERLAY_FLUSH) &
|
||||
mdp4_crtc->flushed_mask),
|
||||
msecs_to_jiffies(50));
|
||||
|
|
|
|||
|
|
@ -1234,6 +1234,7 @@ static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc)
|
|||
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
||||
struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
|
||||
struct mdp5_ctl *ctl = mdp5_cstate->ctl;
|
||||
wait_queue_head_t *queue = drm_crtc_vblank_waitqueue(crtc);
|
||||
int ret;
|
||||
|
||||
/* Should not call this function if crtc is disabled. */
|
||||
|
|
@ -1244,7 +1245,7 @@ static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc)
|
|||
if (ret)
|
||||
return;
|
||||
|
||||
ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
|
||||
ret = wait_event_timeout(*queue,
|
||||
((mdp5_ctl_get_commit_status(ctl) &
|
||||
mdp5_crtc->flushed_mask) == 0),
|
||||
msecs_to_jiffies(50));
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ struct nvfw_hs_load_header_v2 {
|
|||
struct {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
} app[];
|
||||
u32 data_offset;
|
||||
u32 data_size;
|
||||
} app[] __counted_by(num_apps);
|
||||
};
|
||||
|
||||
const struct nvfw_hs_load_header_v2 *nvfw_hs_load_header_v2(struct nvkm_subdev *, const void *);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 4
|
||||
#define DRIVER_PATCHLEVEL 0
|
||||
#define DRIVER_PATCHLEVEL 1
|
||||
|
||||
/*
|
||||
* 1.1.1:
|
||||
|
|
@ -35,6 +35,8 @@
|
|||
* programs that get directly linked with NVKM.
|
||||
* 1.3.1:
|
||||
* - implemented limited ABI16/NVIF interop
|
||||
* 1.4.1:
|
||||
* - add variable page sizes and compression for Turing+
|
||||
*/
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
|
|
|||
|
|
@ -107,34 +107,34 @@ nouveau_uvmm_vmm_sparse_unref(struct nouveau_uvmm *uvmm,
|
|||
|
||||
static int
|
||||
nouveau_uvmm_vmm_get(struct nouveau_uvmm *uvmm,
|
||||
u64 addr, u64 range)
|
||||
u64 addr, u64 range, u8 page_shift)
|
||||
{
|
||||
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
|
||||
|
||||
return nvif_vmm_raw_get(vmm, addr, range, PAGE_SHIFT);
|
||||
return nvif_vmm_raw_get(vmm, addr, range, page_shift);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_uvmm_vmm_put(struct nouveau_uvmm *uvmm,
|
||||
u64 addr, u64 range)
|
||||
u64 addr, u64 range, u8 page_shift)
|
||||
{
|
||||
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
|
||||
|
||||
return nvif_vmm_raw_put(vmm, addr, range, PAGE_SHIFT);
|
||||
return nvif_vmm_raw_put(vmm, addr, range, page_shift);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_uvmm_vmm_unmap(struct nouveau_uvmm *uvmm,
|
||||
u64 addr, u64 range, bool sparse)
|
||||
u64 addr, u64 range, u8 page_shift, bool sparse)
|
||||
{
|
||||
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
|
||||
|
||||
return nvif_vmm_raw_unmap(vmm, addr, range, PAGE_SHIFT, sparse);
|
||||
return nvif_vmm_raw_unmap(vmm, addr, range, page_shift, sparse);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_uvmm_vmm_map(struct nouveau_uvmm *uvmm,
|
||||
u64 addr, u64 range,
|
||||
u64 addr, u64 range, u8 page_shift,
|
||||
u64 bo_offset, u8 kind,
|
||||
struct nouveau_mem *mem)
|
||||
{
|
||||
|
|
@ -163,7 +163,7 @@ nouveau_uvmm_vmm_map(struct nouveau_uvmm *uvmm,
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return nvif_vmm_raw_map(vmm, addr, range, PAGE_SHIFT,
|
||||
return nvif_vmm_raw_map(vmm, addr, range, page_shift,
|
||||
&args, argc,
|
||||
&mem->mem, bo_offset);
|
||||
}
|
||||
|
|
@ -182,8 +182,9 @@ nouveau_uvma_vmm_put(struct nouveau_uvma *uvma)
|
|||
{
|
||||
u64 addr = uvma->va.va.addr;
|
||||
u64 range = uvma->va.va.range;
|
||||
u8 page_shift = uvma->page_shift;
|
||||
|
||||
return nouveau_uvmm_vmm_put(to_uvmm(uvma), addr, range);
|
||||
return nouveau_uvmm_vmm_put(to_uvmm(uvma), addr, range, page_shift);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -193,9 +194,11 @@ nouveau_uvma_map(struct nouveau_uvma *uvma,
|
|||
u64 addr = uvma->va.va.addr;
|
||||
u64 offset = uvma->va.gem.offset;
|
||||
u64 range = uvma->va.va.range;
|
||||
u8 page_shift = uvma->page_shift;
|
||||
|
||||
return nouveau_uvmm_vmm_map(to_uvmm(uvma), addr, range,
|
||||
offset, uvma->kind, mem);
|
||||
page_shift, offset, uvma->kind,
|
||||
mem);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -203,12 +206,13 @@ nouveau_uvma_unmap(struct nouveau_uvma *uvma)
|
|||
{
|
||||
u64 addr = uvma->va.va.addr;
|
||||
u64 range = uvma->va.va.range;
|
||||
u8 page_shift = uvma->page_shift;
|
||||
bool sparse = !!uvma->region;
|
||||
|
||||
if (drm_gpuva_invalidated(&uvma->va))
|
||||
return 0;
|
||||
|
||||
return nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, sparse);
|
||||
return nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, page_shift, sparse);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -450,6 +454,62 @@ op_unmap_prepare_unwind(struct drm_gpuva *va)
|
|||
drm_gpuva_insert(va->vm, va);
|
||||
}
|
||||
|
||||
static bool
|
||||
op_map_aligned_to_page_shift(const struct drm_gpuva_op_map *op, u8 page_shift)
|
||||
{
|
||||
u64 non_page_bits = (1ULL << page_shift) - 1;
|
||||
|
||||
return (op->va.addr & non_page_bits) == 0 &&
|
||||
(op->va.range & non_page_bits) == 0 &&
|
||||
(op->gem.offset & non_page_bits) == 0;
|
||||
}
|
||||
|
||||
static u8
|
||||
select_page_shift(struct nouveau_uvmm *uvmm, struct drm_gpuva_op_map *op)
|
||||
{
|
||||
struct nouveau_bo *nvbo = nouveau_gem_object(op->gem.obj);
|
||||
|
||||
/* nouveau_bo_fixup_align() guarantees that the page size will be aligned
|
||||
* for most cases, but it can't handle cases where userspace allocates with
|
||||
* a size and then binds with a smaller granularity. So in order to avoid
|
||||
* breaking old userspace, we need to ensure that the VA is actually
|
||||
* aligned before using it, and if it isn't, then we downgrade to the first
|
||||
* granularity that will fit, which is optimal from a correctness and
|
||||
* performance perspective.
|
||||
*/
|
||||
if (op_map_aligned_to_page_shift(op, nvbo->page))
|
||||
return nvbo->page;
|
||||
|
||||
struct nouveau_mem *mem = nouveau_mem(nvbo->bo.resource);
|
||||
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
|
||||
int i;
|
||||
|
||||
/* If the given granularity doesn't fit, let's find one that will fit. */
|
||||
for (i = 0; i < vmm->page_nr; i++) {
|
||||
/* Ignore anything that is bigger or identical to the BO preference. */
|
||||
if (vmm->page[i].shift >= nvbo->page)
|
||||
continue;
|
||||
|
||||
/* Skip incompatible domains. */
|
||||
if ((mem->mem.type & NVIF_MEM_VRAM) && !vmm->page[i].vram)
|
||||
continue;
|
||||
if ((mem->mem.type & NVIF_MEM_HOST) &&
|
||||
(!vmm->page[i].host || vmm->page[i].shift > PAGE_SHIFT))
|
||||
continue;
|
||||
|
||||
/* If it fits, return the proposed shift. */
|
||||
if (op_map_aligned_to_page_shift(op, vmm->page[i].shift))
|
||||
return vmm->page[i].shift;
|
||||
}
|
||||
|
||||
/* If we get here then nothing can reconcile the requirements. This should never
|
||||
* happen.
|
||||
*/
|
||||
drm_WARN_ONCE(op->gem.obj->dev, 1, "Could not find an appropriate page size.\n");
|
||||
|
||||
return PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
|
||||
struct nouveau_uvma_prealloc *new,
|
||||
|
|
@ -501,7 +561,8 @@ nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
|
|||
|
||||
if (vmm_get_range)
|
||||
nouveau_uvmm_vmm_put(uvmm, vmm_get_start,
|
||||
vmm_get_range);
|
||||
vmm_get_range,
|
||||
select_page_shift(uvmm, &op->map));
|
||||
break;
|
||||
}
|
||||
case DRM_GPUVA_OP_REMAP: {
|
||||
|
|
@ -528,6 +589,7 @@ nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
|
|||
u64 ustart = va->va.addr;
|
||||
u64 urange = va->va.range;
|
||||
u64 uend = ustart + urange;
|
||||
u8 page_shift = uvma_from_va(va)->page_shift;
|
||||
|
||||
/* Nothing to do for mappings we merge with. */
|
||||
if (uend == vmm_get_start ||
|
||||
|
|
@ -538,7 +600,8 @@ nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
|
|||
u64 vmm_get_range = ustart - vmm_get_start;
|
||||
|
||||
nouveau_uvmm_vmm_put(uvmm, vmm_get_start,
|
||||
vmm_get_range);
|
||||
vmm_get_range,
|
||||
page_shift);
|
||||
}
|
||||
vmm_get_start = uend;
|
||||
break;
|
||||
|
|
@ -592,6 +655,7 @@ op_map_prepare(struct nouveau_uvmm *uvmm,
|
|||
|
||||
uvma->region = args->region;
|
||||
uvma->kind = args->kind;
|
||||
uvma->page_shift = select_page_shift(uvmm, op);
|
||||
|
||||
drm_gpuva_map(&uvmm->base, &uvma->va, op);
|
||||
|
||||
|
|
@ -633,7 +697,8 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
|
|||
|
||||
if (vmm_get_range) {
|
||||
ret = nouveau_uvmm_vmm_get(uvmm, vmm_get_start,
|
||||
vmm_get_range);
|
||||
vmm_get_range,
|
||||
new->map->page_shift);
|
||||
if (ret) {
|
||||
op_map_prepare_unwind(new->map);
|
||||
goto unwind;
|
||||
|
|
@ -689,6 +754,7 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
|
|||
u64 ustart = va->va.addr;
|
||||
u64 urange = va->va.range;
|
||||
u64 uend = ustart + urange;
|
||||
u8 page_shift = uvma_from_va(va)->page_shift;
|
||||
|
||||
op_unmap_prepare(u);
|
||||
|
||||
|
|
@ -704,7 +770,7 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
|
|||
u64 vmm_get_range = ustart - vmm_get_start;
|
||||
|
||||
ret = nouveau_uvmm_vmm_get(uvmm, vmm_get_start,
|
||||
vmm_get_range);
|
||||
vmm_get_range, page_shift);
|
||||
if (ret) {
|
||||
op_unmap_prepare_unwind(va);
|
||||
goto unwind;
|
||||
|
|
@ -799,10 +865,11 @@ op_unmap_range(struct drm_gpuva_op_unmap *u,
|
|||
u64 addr, u64 range)
|
||||
{
|
||||
struct nouveau_uvma *uvma = uvma_from_va(u->va);
|
||||
u8 page_shift = uvma->page_shift;
|
||||
bool sparse = !!uvma->region;
|
||||
|
||||
if (!drm_gpuva_invalidated(u->va))
|
||||
nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, sparse);
|
||||
nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, page_shift, sparse);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -882,6 +949,7 @@ nouveau_uvmm_sm_cleanup(struct nouveau_uvmm *uvmm,
|
|||
struct drm_gpuva_op_map *n = r->next;
|
||||
struct drm_gpuva *va = r->unmap->va;
|
||||
struct nouveau_uvma *uvma = uvma_from_va(va);
|
||||
u8 page_shift = uvma->page_shift;
|
||||
|
||||
if (unmap) {
|
||||
u64 addr = va->va.addr;
|
||||
|
|
@ -893,7 +961,7 @@ nouveau_uvmm_sm_cleanup(struct nouveau_uvmm *uvmm,
|
|||
if (n)
|
||||
end = n->va.addr;
|
||||
|
||||
nouveau_uvmm_vmm_put(uvmm, addr, end - addr);
|
||||
nouveau_uvmm_vmm_put(uvmm, addr, end - addr, page_shift);
|
||||
}
|
||||
|
||||
nouveau_uvma_gem_put(uvma);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ struct nouveau_uvma {
|
|||
|
||||
struct nouveau_uvma_region *region;
|
||||
u8 kind;
|
||||
u8 page_shift;
|
||||
};
|
||||
|
||||
#define uvmm_from_gpuvm(x) container_of((x), struct nouveau_uvmm, base)
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@
|
|||
*/
|
||||
#include "vmm.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <subdev/fb.h>
|
||||
#include <subdev/ltc.h>
|
||||
#include <subdev/timer.h>
|
||||
#include <engine/gr.h>
|
||||
|
||||
|
|
@ -111,13 +109,33 @@ gp100_vmm_pgt_pfn(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
|
|||
nvkm_done(pt->memory);
|
||||
}
|
||||
|
||||
static inline u64
|
||||
gp100_vmm_comptag_nr(u64 size)
|
||||
{
|
||||
return size >> 16; /* One comptag per 64KiB VRAM. */
|
||||
}
|
||||
|
||||
static inline u64
|
||||
gp100_vmm_pte_comptagline_base(u64 addr)
|
||||
{
|
||||
/* RM allocates enough comptags for all of VRAM, so use a 1:1 mapping. */
|
||||
return (1 + gp100_vmm_comptag_nr(addr)) << 36; /* NV_MMU_VER2_PTE_COMPTAGLINE */
|
||||
}
|
||||
|
||||
static inline u64
|
||||
gp100_vmm_pte_comptagline_incr(u32 page_size)
|
||||
{
|
||||
return gp100_vmm_comptag_nr(page_size) << 36; /* NV_MMU_VER2_PTE_COMPTAGLINE */
|
||||
}
|
||||
|
||||
static inline void
|
||||
gp100_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
|
||||
u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr)
|
||||
{
|
||||
u64 data = (addr >> 4) | map->type;
|
||||
|
||||
map->type += ptes * map->ctag;
|
||||
if (map->ctag)
|
||||
data |= gp100_vmm_pte_comptagline_base(addr);
|
||||
|
||||
while (ptes--) {
|
||||
VMM_WO064(pt, vmm, ptei++ * 8, data);
|
||||
|
|
@ -142,7 +160,6 @@ gp100_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
|
|||
while (ptes--) {
|
||||
const u64 data = (*map->dma++ >> 4) | map->type;
|
||||
VMM_WO064(pt, vmm, ptei++ * 8, data);
|
||||
map->type += map->ctag;
|
||||
}
|
||||
nvkm_done(pt->memory);
|
||||
return;
|
||||
|
|
@ -200,7 +217,8 @@ gp100_vmm_pd0_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
|
|||
{
|
||||
u64 data = (addr >> 4) | map->type;
|
||||
|
||||
map->type += ptes * map->ctag;
|
||||
if (map->ctag)
|
||||
data |= gp100_vmm_pte_comptagline_base(addr);
|
||||
|
||||
while (ptes--) {
|
||||
VMM_WO128(pt, vmm, ptei++ * 0x10, data, 0ULL);
|
||||
|
|
@ -411,8 +429,6 @@ gp100_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc,
|
|||
struct gp100_vmm_map_vn vn;
|
||||
struct gp100_vmm_map_v0 v0;
|
||||
} *args = argv;
|
||||
struct nvkm_device *device = vmm->mmu->subdev.device;
|
||||
struct nvkm_memory *memory = map->memory;
|
||||
u8 kind, kind_inv, priv, ro, vol;
|
||||
int kindn, aper, ret = -ENOSYS;
|
||||
const u8 *kindm;
|
||||
|
|
@ -449,29 +465,24 @@ gp100_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Handle compression. */
|
||||
if (kindm[kind] != kind) {
|
||||
u64 tags = nvkm_memory_size(memory) >> 16;
|
||||
if (aper != 0 || !(page->type & NVKM_VMM_PAGE_COMP)) {
|
||||
VMM_DEBUG(vmm, "comp %d %02x", aper, page->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
struct nvkm_device *device = vmm->mmu->subdev.device;
|
||||
|
||||
if (!map->no_comp) {
|
||||
ret = nvkm_memory_tags_get(memory, device, tags,
|
||||
nvkm_ltc_tags_clear,
|
||||
&map->tags);
|
||||
if (ret) {
|
||||
VMM_DEBUG(vmm, "comp %d", ret);
|
||||
return ret;
|
||||
/* Compression is only supported when using GSP-RM, as
|
||||
* PMU firmware is required in order to initialise the
|
||||
* compbit backing store.
|
||||
*/
|
||||
if (nvkm_gsp_rm(device->gsp)) {
|
||||
/* Turing GPUs require PTE_COMPTAGLINE to be filled,
|
||||
* in addition to specifying a compressed kind.
|
||||
*/
|
||||
if (device->card_type < GA100) {
|
||||
map->ctag = gp100_vmm_pte_comptagline_incr(1 << map->page->shift);
|
||||
map->next |= map->ctag;
|
||||
}
|
||||
}
|
||||
|
||||
if (!map->no_comp && map->tags->mn) {
|
||||
tags = map->tags->mn->offset + (map->offset >> 16);
|
||||
map->ctag |= ((1ULL << page->shift) >> 16) << 36;
|
||||
map->type |= tags << 36;
|
||||
map->next |= map->ctag;
|
||||
} else {
|
||||
/* Revert to non-compressed kind. */
|
||||
kind = kindm[kind];
|
||||
}
|
||||
}
|
||||
|
|
@ -592,8 +603,8 @@ gp100_vmm = {
|
|||
{ 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx },
|
||||
{ 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx },
|
||||
{ 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx },
|
||||
{ 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxC },
|
||||
{ 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxC },
|
||||
{ 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxx },
|
||||
{ 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxx },
|
||||
{ 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SVHx },
|
||||
{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ gp10b_vmm = {
|
|||
{ 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx },
|
||||
{ 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx },
|
||||
{ 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx },
|
||||
{ 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SxHC },
|
||||
{ 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SxHC },
|
||||
{ 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SxHx },
|
||||
{ 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SxHx },
|
||||
{ 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SxHx },
|
||||
{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1147,6 +1147,20 @@ static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
panthor_vm_op_ctx_return_vma(struct panthor_vm_op_ctx *op_ctx,
|
||||
struct panthor_vma *vma)
|
||||
{
|
||||
for (u32 i = 0; i < ARRAY_SIZE(op_ctx->preallocated_vmas); i++) {
|
||||
if (!op_ctx->preallocated_vmas[i]) {
|
||||
op_ctx->preallocated_vmas[i] = vma;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
static struct panthor_vma *
|
||||
panthor_vm_op_ctx_get_vma(struct panthor_vm_op_ctx *op_ctx)
|
||||
{
|
||||
|
|
@ -2082,8 +2096,10 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
|
|||
ret = panthor_vm_map_pages(vm, op->map.va.addr, flags_to_prot(vma->flags),
|
||||
op_ctx->map.sgt, op->map.gem.offset,
|
||||
op->map.va.range);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
panthor_vm_op_ctx_return_vma(op_ctx, vma);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Ref owned by the mapping now, clear the obj field so we don't release the
|
||||
* pinning/obj ref behind GPUVA's back.
|
||||
|
|
|
|||
|
|
@ -1320,7 +1320,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
|
|||
sched->name = args->name;
|
||||
sched->timeout = args->timeout;
|
||||
sched->hang_limit = args->hang_limit;
|
||||
sched->timeout_wq = args->timeout_wq ? args->timeout_wq : system_wq;
|
||||
sched->timeout_wq = args->timeout_wq ? args->timeout_wq : system_percpu_wq;
|
||||
sched->score = args->score ? args->score : &sched->_score;
|
||||
sched->dev = args->dev;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,20 @@
|
|||
* Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
|
||||
*/
|
||||
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "sun8i_csc.h"
|
||||
#include "sun8i_mixer.h"
|
||||
|
||||
enum sun8i_csc_mode {
|
||||
SUN8I_CSC_MODE_OFF,
|
||||
SUN8I_CSC_MODE_YUV2RGB,
|
||||
SUN8I_CSC_MODE_YVU2RGB,
|
||||
};
|
||||
|
||||
static const u32 ccsc_base[][2] = {
|
||||
[CCSC_MIXER0_LAYOUT] = {CCSC00_OFFSET, CCSC01_OFFSET},
|
||||
[CCSC_MIXER1_LAYOUT] = {CCSC10_OFFSET, CCSC11_OFFSET},
|
||||
|
|
@ -107,23 +116,28 @@ static const u32 yuv2rgb_de3[2][3][12] = {
|
|||
},
|
||||
};
|
||||
|
||||
static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
|
||||
enum sun8i_csc_mode mode,
|
||||
enum drm_color_encoding encoding,
|
||||
enum drm_color_range range)
|
||||
static void sun8i_csc_setup(struct regmap *map, u32 base,
|
||||
enum sun8i_csc_mode mode,
|
||||
enum drm_color_encoding encoding,
|
||||
enum drm_color_range range)
|
||||
{
|
||||
u32 base_reg, val;
|
||||
const u32 *table;
|
||||
u32 base_reg;
|
||||
int i;
|
||||
|
||||
table = yuv2rgb[range][encoding];
|
||||
|
||||
switch (mode) {
|
||||
case SUN8I_CSC_MODE_OFF:
|
||||
val = 0;
|
||||
break;
|
||||
case SUN8I_CSC_MODE_YUV2RGB:
|
||||
val = SUN8I_CSC_CTRL_EN;
|
||||
base_reg = SUN8I_CSC_COEFF(base, 0);
|
||||
regmap_bulk_write(map, base_reg, table, 12);
|
||||
break;
|
||||
case SUN8I_CSC_MODE_YVU2RGB:
|
||||
val = SUN8I_CSC_CTRL_EN;
|
||||
for (i = 0; i < 12; i++) {
|
||||
if ((i & 3) == 1)
|
||||
base_reg = SUN8I_CSC_COEFF(base, i + 1);
|
||||
|
|
@ -135,28 +149,37 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
|
|||
}
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
DRM_WARN("Wrong CSC mode specified.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
regmap_write(map, SUN8I_CSC_CTRL(base), val);
|
||||
}
|
||||
|
||||
static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
|
||||
enum sun8i_csc_mode mode,
|
||||
enum drm_color_encoding encoding,
|
||||
enum drm_color_range range)
|
||||
static void sun8i_de3_ccsc_setup(struct regmap *map, int layer,
|
||||
enum sun8i_csc_mode mode,
|
||||
enum drm_color_encoding encoding,
|
||||
enum drm_color_range range)
|
||||
{
|
||||
u32 addr, val, mask;
|
||||
const u32 *table;
|
||||
u32 addr;
|
||||
int i;
|
||||
|
||||
mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
|
||||
table = yuv2rgb_de3[range][encoding];
|
||||
|
||||
switch (mode) {
|
||||
case SUN8I_CSC_MODE_OFF:
|
||||
val = 0;
|
||||
break;
|
||||
case SUN8I_CSC_MODE_YUV2RGB:
|
||||
val = mask;
|
||||
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
|
||||
regmap_bulk_write(map, addr, table, 12);
|
||||
break;
|
||||
case SUN8I_CSC_MODE_YVU2RGB:
|
||||
val = mask;
|
||||
for (i = 0; i < 12; i++) {
|
||||
if ((i & 3) == 1)
|
||||
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
|
||||
|
|
@ -173,67 +196,53 @@ static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
|
|||
}
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
DRM_WARN("Wrong CSC mode specified.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (enable)
|
||||
val = SUN8I_CSC_CTRL_EN;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val);
|
||||
}
|
||||
|
||||
static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable)
|
||||
{
|
||||
u32 val, mask;
|
||||
|
||||
mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
|
||||
|
||||
if (enable)
|
||||
val = mask;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE),
|
||||
mask, val);
|
||||
}
|
||||
|
||||
void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
|
||||
enum sun8i_csc_mode mode,
|
||||
enum drm_color_encoding encoding,
|
||||
enum drm_color_range range)
|
||||
static u32 sun8i_csc_get_mode(struct drm_plane_state *state)
|
||||
{
|
||||
const struct drm_format_info *format;
|
||||
|
||||
if (!state->crtc || !state->visible)
|
||||
return SUN8I_CSC_MODE_OFF;
|
||||
|
||||
format = state->fb->format;
|
||||
if (!format->is_yuv)
|
||||
return SUN8I_CSC_MODE_OFF;
|
||||
|
||||
switch (format->format) {
|
||||
case DRM_FORMAT_YVU411:
|
||||
case DRM_FORMAT_YVU420:
|
||||
case DRM_FORMAT_YVU422:
|
||||
case DRM_FORMAT_YVU444:
|
||||
return SUN8I_CSC_MODE_YVU2RGB;
|
||||
default:
|
||||
return SUN8I_CSC_MODE_YUV2RGB;
|
||||
}
|
||||
}
|
||||
|
||||
void sun8i_csc_config(struct sun8i_layer *layer,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
u32 mode = sun8i_csc_get_mode(state);
|
||||
u32 base;
|
||||
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE3) {
|
||||
sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer,
|
||||
mode, encoding, range);
|
||||
if (layer->cfg->de_type == SUN8I_MIXER_DE3) {
|
||||
sun8i_de3_ccsc_setup(layer->regs, layer->channel,
|
||||
mode, state->color_encoding,
|
||||
state->color_range);
|
||||
return;
|
||||
}
|
||||
|
||||
base = ccsc_base[mixer->cfg->ccsc][layer];
|
||||
base = ccsc_base[layer->cfg->ccsc][layer->channel];
|
||||
|
||||
sun8i_csc_set_coefficients(mixer->engine.regs, base,
|
||||
mode, encoding, range);
|
||||
}
|
||||
|
||||
void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable)
|
||||
{
|
||||
u32 base;
|
||||
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE3) {
|
||||
sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable);
|
||||
return;
|
||||
}
|
||||
|
||||
base = ccsc_base[mixer->cfg->ccsc][layer];
|
||||
|
||||
sun8i_csc_enable(mixer->engine.regs, base, enable);
|
||||
sun8i_csc_setup(layer->regs, base,
|
||||
mode, state->color_encoding,
|
||||
state->color_range);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
#include <drm/drm_color_mgmt.h>
|
||||
|
||||
struct sun8i_mixer;
|
||||
struct drm_plane_state;
|
||||
struct sun8i_layer;
|
||||
|
||||
/* VI channel CSC units offsets */
|
||||
#define CCSC00_OFFSET 0xAA050
|
||||
|
|
@ -22,16 +23,7 @@ struct sun8i_mixer;
|
|||
|
||||
#define SUN8I_CSC_CTRL_EN BIT(0)
|
||||
|
||||
enum sun8i_csc_mode {
|
||||
SUN8I_CSC_MODE_OFF,
|
||||
SUN8I_CSC_MODE_YUV2RGB,
|
||||
SUN8I_CSC_MODE_YVU2RGB,
|
||||
};
|
||||
|
||||
void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
|
||||
enum sun8i_csc_mode mode,
|
||||
enum drm_color_encoding encoding,
|
||||
enum drm_color_range range);
|
||||
void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable);
|
||||
void sun8i_csc_config(struct sun8i_layer *layer,
|
||||
struct drm_plane_state *state);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -251,24 +251,6 @@ int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void sun8i_layer_enable(struct sun8i_layer *layer, bool enable)
|
||||
{
|
||||
u32 ch_base = sun8i_channel_base(layer->mixer, layer->channel);
|
||||
u32 val, reg, mask;
|
||||
|
||||
if (layer->type == SUN8I_LAYER_TYPE_UI) {
|
||||
val = enable ? SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN : 0;
|
||||
mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
|
||||
reg = SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay);
|
||||
} else {
|
||||
val = enable ? SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN : 0;
|
||||
mask = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
|
||||
reg = SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay);
|
||||
}
|
||||
|
||||
regmap_update_bits(layer->mixer->engine.regs, reg, mask, val);
|
||||
}
|
||||
|
||||
static void sun8i_mixer_commit(struct sunxi_engine *engine,
|
||||
struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
|
|
@ -284,10 +266,10 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
|
|||
|
||||
drm_for_each_plane(plane, state->dev) {
|
||||
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
||||
int w, h, x, y, zpos;
|
||||
bool enable;
|
||||
int zpos;
|
||||
|
||||
if (!(plane->possible_crtcs & drm_crtc_mask(crtc)) || layer->mixer != mixer)
|
||||
if (!(plane->possible_crtcs & drm_crtc_mask(crtc)))
|
||||
continue;
|
||||
|
||||
plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
|
|
@ -296,23 +278,28 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
|
|||
|
||||
enable = plane_state->crtc && plane_state->visible;
|
||||
zpos = plane_state->normalized_zpos;
|
||||
x = plane_state->dst.x1;
|
||||
y = plane_state->dst.y1;
|
||||
w = drm_rect_width(&plane_state->dst);
|
||||
h = drm_rect_height(&plane_state->dst);
|
||||
|
||||
DRM_DEBUG_DRIVER(" plane %d: chan=%d ovl=%d en=%d zpos=%d\n",
|
||||
plane->base.id, layer->channel, layer->overlay,
|
||||
enable, zpos);
|
||||
|
||||
/*
|
||||
* We always update the layer enable bit, because it can clear
|
||||
* spontaneously for unknown reasons.
|
||||
*/
|
||||
sun8i_layer_enable(layer, enable);
|
||||
DRM_DEBUG_DRIVER(" plane %d: chan=%d ovl=%d en=%d zpos=%d x=%d y=%d w=%d h=%d\n",
|
||||
plane->base.id, layer->index, layer->overlay,
|
||||
enable, zpos, x, y, w, h);
|
||||
|
||||
if (!enable)
|
||||
continue;
|
||||
|
||||
/* Route layer to pipe based on zpos */
|
||||
route |= layer->channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
|
||||
route |= layer->index << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
|
||||
pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
|
||||
|
||||
regmap_write(bld_regs,
|
||||
SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
|
||||
SUN8I_MIXER_COORD(x, y));
|
||||
regmap_write(bld_regs,
|
||||
SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
|
||||
SUN8I_MIXER_SIZE(w, h));
|
||||
}
|
||||
|
||||
regmap_write(bld_regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route);
|
||||
|
|
@ -329,18 +316,30 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
|
|||
{
|
||||
struct drm_plane **planes;
|
||||
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
|
||||
int plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
|
||||
enum drm_plane_type type;
|
||||
unsigned int phy_index;
|
||||
int i;
|
||||
|
||||
planes = devm_kcalloc(drm->dev,
|
||||
mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
|
||||
sizeof(*planes), GFP_KERNEL);
|
||||
planes = devm_kcalloc(drm->dev, plane_cnt, sizeof(*planes), GFP_KERNEL);
|
||||
if (!planes)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < mixer->cfg->vi_num; i++) {
|
||||
struct sun8i_layer *layer;
|
||||
|
||||
layer = sun8i_vi_layer_init_one(drm, mixer, i);
|
||||
if (i == 0 && !mixer->cfg->ui_num)
|
||||
type = DRM_PLANE_TYPE_PRIMARY;
|
||||
else
|
||||
type = DRM_PLANE_TYPE_OVERLAY;
|
||||
|
||||
phy_index = i;
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
phy_index = mixer->cfg->map[i];
|
||||
|
||||
layer = sun8i_vi_layer_init_one(drm, type, mixer->engine.regs,
|
||||
i, phy_index, plane_cnt,
|
||||
&mixer->cfg->lay_cfg);
|
||||
if (IS_ERR(layer)) {
|
||||
dev_err(drm->dev,
|
||||
"Couldn't initialize overlay plane\n");
|
||||
|
|
@ -351,16 +350,28 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
|
|||
}
|
||||
|
||||
for (i = 0; i < mixer->cfg->ui_num; i++) {
|
||||
unsigned int index = mixer->cfg->vi_num + i;
|
||||
struct sun8i_layer *layer;
|
||||
|
||||
layer = sun8i_ui_layer_init_one(drm, mixer, i);
|
||||
if (i == 0)
|
||||
type = DRM_PLANE_TYPE_PRIMARY;
|
||||
else
|
||||
type = DRM_PLANE_TYPE_OVERLAY;
|
||||
|
||||
phy_index = index;
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
phy_index = mixer->cfg->map[index];
|
||||
|
||||
layer = sun8i_ui_layer_init_one(drm, type, mixer->engine.regs,
|
||||
index, phy_index, plane_cnt,
|
||||
&mixer->cfg->lay_cfg);
|
||||
if (IS_ERR(layer)) {
|
||||
dev_err(drm->dev, "Couldn't initialize %s plane\n",
|
||||
i ? "overlay" : "primary");
|
||||
return ERR_CAST(layer);
|
||||
}
|
||||
|
||||
planes[mixer->cfg->vi_num + i] = &layer->plane;
|
||||
planes[index] = &layer->plane;
|
||||
}
|
||||
|
||||
return planes;
|
||||
|
|
@ -693,119 +704,173 @@ static void sun8i_mixer_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 432000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_num = 2,
|
||||
.ui_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.mod_rate = 150000000,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 2,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 150000000,
|
||||
.vi_num = 2,
|
||||
.ui_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
|
||||
.ccsc = CCSC_D1_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_D1_MIXER0_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0x1,
|
||||
.scanline_yuv = 1024,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x1,
|
||||
.scanline_yuv = 1024,
|
||||
.ui_num = 0,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.ccsc = CCSC_MIXER1_LAYOUT,
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.de2_fcc_alpha = 1,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.de_type = SUN8I_MIXER_DE3,
|
||||
.vi_scaler_num = 1,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE3,
|
||||
.mod_rate = 600000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
||||
static const struct sun8i_mixer_cfg sun50i_h616_mixer0_cfg = {
|
||||
.ccsc = CCSC_MIXER0_LAYOUT,
|
||||
.lay_cfg = {
|
||||
.de_type = SUN8I_MIXER_DE33,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE33,
|
||||
.mod_rate = 600000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
.map = {0, 6, 7, 8},
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@
|
|||
#define DE3_CH_BASE 0x1000
|
||||
#define DE3_CH_SIZE 0x0800
|
||||
|
||||
#define DE33_CH_BASE 0x1000
|
||||
#define DE33_CH_SIZE 0x20000
|
||||
|
||||
#define SUN8I_MIXER_BLEND_PIPE_CTL(base) ((base) + 0)
|
||||
#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, x) ((base) + 0x4 + 0x10 * (x))
|
||||
#define SUN8I_MIXER_BLEND_ATTR_INSIZE(base, x) ((base) + 0x8 + 0x10 * (x))
|
||||
|
|
@ -161,29 +164,45 @@ enum sun8i_mixer_type {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct sun8i_mixer_cfg - mixer HW configuration
|
||||
* @vi_num: number of VI channels
|
||||
* @ui_num: number of UI channels
|
||||
* struct sun8i_layer_cfg - layer configuration
|
||||
* @vi_scaler_num: Number of VI scalers. Used on DE2 and DE3.
|
||||
* @scaler_mask: bitmask which tells which channel supports scaling
|
||||
* First, scaler supports for VI channels is defined and after that, scaler
|
||||
* support for UI channels. For example, if mixer has 2 VI channels without
|
||||
* scaler and 2 UI channels with scaler, bitmask would be 0xC.
|
||||
* @ccsc: select set of CCSC base addresses from the enumeration above.
|
||||
* @mod_rate: module clock rate that needs to be set in order to have
|
||||
* a functional block.
|
||||
* @de_type: sun8i_mixer_type enum representing the display engine generation.
|
||||
* @scaline_yuv: size of a scanline for VI scaler for YUV formats.
|
||||
* @map: channel map for DE variants processing YUV separately (DE33)
|
||||
* @de2_fcc_alpha: use FCC for missing DE2 VI alpha capability
|
||||
* Most DE2 cores has FCC. If number of VI planes is one, enable this.
|
||||
*/
|
||||
struct sun8i_mixer_cfg {
|
||||
int vi_num;
|
||||
int ui_num;
|
||||
struct sun8i_layer_cfg {
|
||||
unsigned int vi_scaler_num;
|
||||
int scaler_mask;
|
||||
int ccsc;
|
||||
unsigned long mod_rate;
|
||||
unsigned int de_type;
|
||||
unsigned int scanline_yuv;
|
||||
unsigned int map[6];
|
||||
unsigned int de2_fcc_alpha : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sun8i_mixer_cfg - mixer HW configuration
|
||||
* @lay_cfg: layer configuration
|
||||
* @vi_num: number of VI channels
|
||||
* @ui_num: number of UI channels
|
||||
* @de_type: sun8i_mixer_type enum representing the display engine generation.
|
||||
* @mod_rate: module clock rate that needs to be set in order to have
|
||||
* a functional block.
|
||||
* @map: channel map for DE variants processing YUV separately (DE33)
|
||||
*/
|
||||
|
||||
struct sun8i_mixer_cfg {
|
||||
struct sun8i_layer_cfg lay_cfg;
|
||||
int vi_num;
|
||||
int ui_num;
|
||||
unsigned int de_type;
|
||||
unsigned long mod_rate;
|
||||
unsigned int map[6];
|
||||
};
|
||||
|
||||
struct sun8i_mixer {
|
||||
|
|
@ -206,11 +225,13 @@ enum {
|
|||
};
|
||||
|
||||
struct sun8i_layer {
|
||||
struct drm_plane plane;
|
||||
struct sun8i_mixer *mixer;
|
||||
int type;
|
||||
int channel;
|
||||
int overlay;
|
||||
struct drm_plane plane;
|
||||
int type;
|
||||
int index;
|
||||
int channel;
|
||||
int overlay;
|
||||
struct regmap *regs;
|
||||
const struct sun8i_layer_cfg *cfg;
|
||||
};
|
||||
|
||||
static inline struct sun8i_layer *
|
||||
|
|
@ -239,14 +260,14 @@ sun8i_blender_regmap(struct sun8i_mixer *mixer)
|
|||
}
|
||||
|
||||
static inline u32
|
||||
sun8i_channel_base(struct sun8i_mixer *mixer, int channel)
|
||||
sun8i_channel_base(struct sun8i_layer *layer)
|
||||
{
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
return mixer->cfg->map[channel] * 0x20000 + DE2_CH_SIZE;
|
||||
else if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
|
||||
return DE3_CH_BASE + channel * DE3_CH_SIZE;
|
||||
if (layer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
return DE33_CH_BASE + layer->channel * DE33_CH_SIZE;
|
||||
else if (layer->cfg->de_type == SUN8I_MIXER_DE3)
|
||||
return DE3_CH_BASE + layer->channel * DE3_CH_SIZE;
|
||||
else
|
||||
return DE2_CH_BASE + channel * DE2_CH_SIZE;
|
||||
return DE2_CH_BASE + layer->channel * DE2_CH_SIZE;
|
||||
}
|
||||
|
||||
int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format);
|
||||
|
|
|
|||
|
|
@ -26,44 +26,49 @@
|
|||
#include "sun8i_ui_scaler.h"
|
||||
#include "sun8i_vi_scaler.h"
|
||||
|
||||
static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane)
|
||||
static void sun8i_ui_layer_disable(struct sun8i_layer *layer)
|
||||
{
|
||||
u32 mask, val, ch_base;
|
||||
u32 ch_base = sun8i_channel_base(layer);
|
||||
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
|
||||
mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK |
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK;
|
||||
|
||||
val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8);
|
||||
|
||||
val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
|
||||
|
||||
regmap_update_bits(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
|
||||
mask, val);
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay), 0);
|
||||
}
|
||||
|
||||
static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane,
|
||||
unsigned int zpos)
|
||||
static void sun8i_ui_layer_update_attributes(struct sun8i_layer *layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
const struct drm_format_info *fmt;
|
||||
u32 val, ch_base, hw_fmt;
|
||||
|
||||
ch_base = sun8i_channel_base(layer);
|
||||
fmt = state->fb->format;
|
||||
sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
||||
|
||||
val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(state->alpha >> 8);
|
||||
val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
|
||||
val |= hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
|
||||
val |= SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
|
||||
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay), val);
|
||||
}
|
||||
|
||||
static void sun8i_ui_layer_update_coord(struct sun8i_layer *layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
u32 src_w, src_h, dst_w, dst_h;
|
||||
struct regmap *bld_regs;
|
||||
u32 bld_base, ch_base;
|
||||
u32 outsize, insize;
|
||||
u32 hphase, vphase;
|
||||
u32 ch_base;
|
||||
|
||||
DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
|
||||
channel, overlay);
|
||||
layer->channel, layer->overlay);
|
||||
|
||||
bld_base = sun8i_blender_base(mixer);
|
||||
bld_regs = sun8i_blender_regmap(mixer);
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
ch_base = sun8i_channel_base(layer);
|
||||
|
||||
src_w = drm_rect_width(&state->src) >> 16;
|
||||
src_h = drm_rect_height(&state->src) >> 16;
|
||||
|
|
@ -80,10 +85,10 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
|||
DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
|
||||
state->src.x1 >> 16, state->src.y1 >> 16);
|
||||
DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay),
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, layer->overlay),
|
||||
insize);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
|
||||
insize);
|
||||
|
||||
|
|
@ -95,67 +100,27 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
|||
hscale = state->src_w / state->crtc_w;
|
||||
vscale = state->src_h / state->crtc_h;
|
||||
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
|
||||
sun8i_vi_scaler_setup(mixer, channel, src_w, src_h,
|
||||
dst_w, dst_h, hscale, vscale,
|
||||
hphase, vphase,
|
||||
if (layer->cfg->de_type == SUN8I_MIXER_DE33) {
|
||||
sun8i_vi_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
|
||||
hscale, vscale, hphase, vphase,
|
||||
state->fb->format);
|
||||
sun8i_vi_scaler_enable(mixer, channel, true);
|
||||
sun8i_vi_scaler_enable(layer, true);
|
||||
} else {
|
||||
sun8i_ui_scaler_setup(mixer, channel, src_w, src_h,
|
||||
dst_w, dst_h, hscale, vscale,
|
||||
hphase, vphase);
|
||||
sun8i_ui_scaler_enable(mixer, channel, true);
|
||||
sun8i_ui_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
|
||||
hscale, vscale, hphase, vphase);
|
||||
sun8i_ui_scaler_enable(layer, true);
|
||||
}
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("HW scaling is not needed\n");
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
sun8i_vi_scaler_enable(mixer, channel, false);
|
||||
if (layer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
sun8i_vi_scaler_enable(layer, false);
|
||||
else
|
||||
sun8i_ui_scaler_enable(mixer, channel, false);
|
||||
sun8i_ui_scaler_enable(layer, false);
|
||||
}
|
||||
|
||||
/* Set base coordinates */
|
||||
DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
|
||||
state->dst.x1, state->dst.y1);
|
||||
DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
|
||||
regmap_write(bld_regs,
|
||||
SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
|
||||
SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
|
||||
regmap_write(bld_regs,
|
||||
SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
|
||||
outsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
const struct drm_format_info *fmt;
|
||||
u32 val, ch_base, hw_fmt;
|
||||
int ret;
|
||||
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
|
||||
fmt = state->fb->format;
|
||||
ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
||||
if (ret || fmt->is_yuv) {
|
||||
DRM_DEBUG_DRIVER("Invalid format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
|
||||
regmap_update_bits(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane)
|
||||
static void sun8i_ui_layer_update_buffer(struct sun8i_layer *layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
|
|
@ -164,7 +129,7 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
|
|||
u32 ch_base;
|
||||
int bpp;
|
||||
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
ch_base = sun8i_channel_base(layer);
|
||||
|
||||
/* Get the physical address of the buffer in memory */
|
||||
gem = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
|
|
@ -181,17 +146,15 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
|
|||
|
||||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay),
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, layer->overlay),
|
||||
fb->pitches[0]);
|
||||
|
||||
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr);
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay),
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, layer->overlay),
|
||||
lower_32_bits(dma_addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
||||
|
|
@ -202,7 +165,9 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
|||
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
||||
struct drm_crtc *crtc = new_plane_state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
const struct drm_format_info *fmt;
|
||||
int min_scale, max_scale, ret;
|
||||
u32 hw_fmt;
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
|
@ -211,10 +176,17 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
fmt = new_plane_state->fb->format;
|
||||
ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
||||
if (ret || fmt->is_yuv) {
|
||||
DRM_DEBUG_DRIVER("Invalid plane format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
min_scale = DRM_PLANE_NO_SCALING;
|
||||
max_scale = DRM_PLANE_NO_SCALING;
|
||||
|
||||
if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
|
||||
if (layer->cfg->scaler_mask & BIT(layer->channel)) {
|
||||
min_scale = SUN8I_UI_SCALER_SCALE_MIN;
|
||||
max_scale = SUN8I_UI_SCALER_SCALE_MAX;
|
||||
}
|
||||
|
|
@ -232,20 +204,15 @@ static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
|
|||
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
||||
plane);
|
||||
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
||||
unsigned int zpos = new_state->normalized_zpos;
|
||||
struct sun8i_mixer *mixer = layer->mixer;
|
||||
|
||||
if (!new_state->crtc || !new_state->visible)
|
||||
if (!new_state->crtc || !new_state->visible) {
|
||||
sun8i_ui_layer_disable(layer);
|
||||
return;
|
||||
}
|
||||
|
||||
sun8i_ui_layer_update_coord(mixer, layer->channel,
|
||||
layer->overlay, plane, zpos);
|
||||
sun8i_ui_layer_update_alpha(mixer, layer->channel,
|
||||
layer->overlay, plane);
|
||||
sun8i_ui_layer_update_formats(mixer, layer->channel,
|
||||
layer->overlay, plane);
|
||||
sun8i_ui_layer_update_buffer(mixer, layer->channel,
|
||||
layer->overlay, plane);
|
||||
sun8i_ui_layer_update_attributes(layer, plane);
|
||||
sun8i_ui_layer_update_coord(layer, plane);
|
||||
sun8i_ui_layer_update_buffer(layer, plane);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
|
||||
|
|
@ -291,21 +258,25 @@ static const uint64_t sun8i_layer_modifiers[] = {
|
|||
};
|
||||
|
||||
struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
|
||||
struct sun8i_mixer *mixer,
|
||||
int index)
|
||||
enum drm_plane_type type,
|
||||
struct regmap *regs,
|
||||
int index, int phy_index,
|
||||
int plane_cnt,
|
||||
const struct sun8i_layer_cfg *cfg)
|
||||
{
|
||||
enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
|
||||
int channel = mixer->cfg->vi_num + index;
|
||||
struct sun8i_layer *layer;
|
||||
unsigned int plane_cnt;
|
||||
int ret;
|
||||
|
||||
layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
|
||||
if (!layer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (index == 0)
|
||||
type = DRM_PLANE_TYPE_PRIMARY;
|
||||
layer->type = SUN8I_LAYER_TYPE_UI;
|
||||
layer->index = index;
|
||||
layer->channel = phy_index;
|
||||
layer->overlay = 0;
|
||||
layer->regs = regs;
|
||||
layer->cfg = cfg;
|
||||
|
||||
/* possible crtcs are set later */
|
||||
ret = drm_universal_plane_init(drm, &layer->plane, 0,
|
||||
|
|
@ -318,15 +289,13 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
|
||||
|
||||
ret = drm_plane_create_alpha_property(&layer->plane);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't add alpha property\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = drm_plane_create_zpos_property(&layer->plane, channel,
|
||||
ret = drm_plane_create_zpos_property(&layer->plane, index,
|
||||
0, plane_cnt - 1);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't add zpos property\n");
|
||||
|
|
@ -334,10 +303,6 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
|
|||
}
|
||||
|
||||
drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
|
||||
layer->mixer = mixer;
|
||||
layer->type = SUN8I_LAYER_TYPE_UI;
|
||||
layer->channel = channel;
|
||||
layer->overlay = 0;
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ struct sun8i_mixer;
|
|||
struct sun8i_layer;
|
||||
|
||||
struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
|
||||
struct sun8i_mixer *mixer,
|
||||
int index);
|
||||
enum drm_plane_type type,
|
||||
struct regmap *regs,
|
||||
int index, int phy_index,
|
||||
int plane_cnt,
|
||||
const struct sun8i_layer_cfg *cfg);
|
||||
#endif /* _SUN8I_UI_LAYER_H_ */
|
||||
|
|
|
|||
|
|
@ -89,18 +89,18 @@ static const u32 lan2coefftab16[240] = {
|
|||
0x0b1c1603, 0x0d1c1502, 0x0e1d1401, 0x0f1d1301,
|
||||
};
|
||||
|
||||
static u32 sun8i_ui_scaler_base(struct sun8i_mixer *mixer, int channel)
|
||||
static u32 sun8i_ui_scaler_base(struct sun8i_layer *layer)
|
||||
{
|
||||
int vi_num = mixer->cfg->vi_num;
|
||||
int offset = layer->cfg->vi_scaler_num;
|
||||
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
|
||||
if (layer->cfg->de_type == SUN8I_MIXER_DE3)
|
||||
return DE3_VI_SCALER_UNIT_BASE +
|
||||
DE3_VI_SCALER_UNIT_SIZE * vi_num +
|
||||
DE3_UI_SCALER_UNIT_SIZE * (channel - vi_num);
|
||||
DE3_VI_SCALER_UNIT_SIZE * offset +
|
||||
DE3_UI_SCALER_UNIT_SIZE * (layer->channel - offset);
|
||||
else
|
||||
return DE2_VI_SCALER_UNIT_BASE +
|
||||
DE2_VI_SCALER_UNIT_SIZE * vi_num +
|
||||
DE2_UI_SCALER_UNIT_SIZE * (channel - vi_num);
|
||||
DE2_VI_SCALER_UNIT_SIZE * offset +
|
||||
DE2_UI_SCALER_UNIT_SIZE * (layer->channel - offset);
|
||||
}
|
||||
|
||||
static int sun8i_ui_scaler_coef_index(unsigned int step)
|
||||
|
|
@ -127,14 +127,11 @@ static int sun8i_ui_scaler_coef_index(unsigned int step)
|
|||
}
|
||||
}
|
||||
|
||||
void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
|
||||
void sun8i_ui_scaler_enable(struct sun8i_layer *layer, bool enable)
|
||||
{
|
||||
u32 val, base;
|
||||
|
||||
if (WARN_ON(layer < mixer->cfg->vi_num))
|
||||
return;
|
||||
|
||||
base = sun8i_ui_scaler_base(mixer, layer);
|
||||
base = sun8i_ui_scaler_base(layer);
|
||||
|
||||
if (enable)
|
||||
val = SUN8I_SCALER_GSU_CTRL_EN |
|
||||
|
|
@ -142,10 +139,10 @@ void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
|
|||
else
|
||||
val = 0;
|
||||
|
||||
regmap_write(mixer->engine.regs, SUN8I_SCALER_GSU_CTRL(base), val);
|
||||
regmap_write(layer->regs, SUN8I_SCALER_GSU_CTRL(base), val);
|
||||
}
|
||||
|
||||
void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
||||
void sun8i_ui_scaler_setup(struct sun8i_layer *layer,
|
||||
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
|
||||
u32 hscale, u32 vscale, u32 hphase, u32 vphase)
|
||||
{
|
||||
|
|
@ -153,10 +150,7 @@ void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
|||
int i, offset;
|
||||
u32 base;
|
||||
|
||||
if (WARN_ON(layer < mixer->cfg->vi_num))
|
||||
return;
|
||||
|
||||
base = sun8i_ui_scaler_base(mixer, layer);
|
||||
base = sun8i_ui_scaler_base(layer);
|
||||
|
||||
hphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16;
|
||||
vphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16;
|
||||
|
|
@ -166,22 +160,22 @@ void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
|||
insize = SUN8I_UI_SCALER_SIZE(src_w, src_h);
|
||||
outsize = SUN8I_UI_SCALER_SIZE(dst_w, dst_h);
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_OUTSIZE(base), outsize);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_INSIZE(base), insize);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_HSTEP(base), hscale);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_VSTEP(base), vscale);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_HPHASE(base), hphase);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_VPHASE(base), vphase);
|
||||
offset = sun8i_ui_scaler_coef_index(hscale) *
|
||||
SUN8I_UI_SCALER_COEFF_COUNT;
|
||||
for (i = 0; i < SUN8I_UI_SCALER_COEFF_COUNT; i++)
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_GSU_HCOEFF(base, i),
|
||||
lan2coefftab16[offset + i]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@
|
|||
#define SUN8I_SCALER_GSU_CTRL_EN BIT(0)
|
||||
#define SUN8I_SCALER_GSU_CTRL_COEFF_RDY BIT(4)
|
||||
|
||||
void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
|
||||
void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
||||
void sun8i_ui_scaler_enable(struct sun8i_layer *layer, bool enable);
|
||||
void sun8i_ui_scaler_setup(struct sun8i_layer *layer,
|
||||
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
|
||||
u32 hscale, u32 vscale, u32 hphase, u32 vphase);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,62 +14,71 @@
|
|||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include "sun4i_crtc.h"
|
||||
#include "sun8i_csc.h"
|
||||
#include "sun8i_mixer.h"
|
||||
#include "sun8i_vi_layer.h"
|
||||
#include "sun8i_vi_scaler.h"
|
||||
|
||||
static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane)
|
||||
static void sun8i_vi_layer_disable(struct sun8i_layer *layer)
|
||||
{
|
||||
u32 mask, val, ch_base;
|
||||
u32 ch_base = sun8i_channel_base(layer);
|
||||
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay), 0);
|
||||
}
|
||||
|
||||
if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
|
||||
SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
|
||||
val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
|
||||
(plane->state->alpha >> 8);
|
||||
static void sun8i_vi_layer_update_attributes(struct sun8i_layer *layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
const struct drm_format_info *fmt;
|
||||
u32 val, ch_base, hw_fmt;
|
||||
|
||||
val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
|
||||
ch_base = sun8i_channel_base(layer);
|
||||
fmt = state->fb->format;
|
||||
sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
||||
|
||||
val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
|
||||
if (!fmt->is_yuv)
|
||||
val |= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
|
||||
val |= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
|
||||
if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
val |= SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(state->alpha >> 8);
|
||||
val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
|
||||
SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
|
||||
SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
|
||||
}
|
||||
|
||||
regmap_update_bits(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
|
||||
overlay),
|
||||
mask, val);
|
||||
} else if (mixer->cfg->vi_num == 1) {
|
||||
regmap_update_bits(mixer->engine.regs,
|
||||
SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
|
||||
SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
|
||||
SUN8I_MIXER_FCC_GLOBAL_ALPHA
|
||||
(plane->state->alpha >> 8));
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay), val);
|
||||
|
||||
if (layer->cfg->de2_fcc_alpha) {
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
|
||||
SUN8I_MIXER_FCC_GLOBAL_ALPHA(state->alpha >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane,
|
||||
unsigned int zpos)
|
||||
static void sun8i_vi_layer_update_coord(struct sun8i_layer *layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(state->crtc);
|
||||
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(scrtc->engine);
|
||||
const struct drm_format_info *format = state->fb->format;
|
||||
u32 src_w, src_h, dst_w, dst_h;
|
||||
struct regmap *bld_regs;
|
||||
u32 bld_base, ch_base;
|
||||
u32 outsize, insize;
|
||||
u32 hphase, vphase;
|
||||
u32 hn = 0, hm = 0;
|
||||
u32 vn = 0, vm = 0;
|
||||
bool subsampled;
|
||||
u32 ch_base;
|
||||
|
||||
DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
|
||||
channel, overlay);
|
||||
layer->channel, layer->overlay);
|
||||
|
||||
bld_base = sun8i_blender_base(mixer);
|
||||
bld_regs = sun8i_blender_regmap(mixer);
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
ch_base = sun8i_channel_base(layer);
|
||||
|
||||
src_w = drm_rect_width(&state->src) >> 16;
|
||||
src_h = drm_rect_height(&state->src) >> 16;
|
||||
|
|
@ -106,10 +115,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
|||
(state->src.x1 >> 16) & ~(format->hsub - 1),
|
||||
(state->src.y1 >> 16) & ~(format->vsub - 1));
|
||||
DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, layer->overlay),
|
||||
insize);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
|
||||
insize);
|
||||
|
||||
|
|
@ -144,7 +153,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
|||
}
|
||||
|
||||
/* it seems that every RGB scaler has buffer for 2048 pixels */
|
||||
scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
|
||||
scanline = subsampled ? layer->cfg->scanline_yuv : 2048;
|
||||
|
||||
if (src_w > scanline) {
|
||||
DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
|
||||
|
|
@ -156,108 +165,34 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
|||
hscale = (src_w << 16) / dst_w;
|
||||
vscale = (src_h << 16) / dst_h;
|
||||
|
||||
sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
|
||||
dst_h, hscale, vscale, hphase, vphase,
|
||||
format);
|
||||
sun8i_vi_scaler_enable(mixer, channel, true);
|
||||
sun8i_vi_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
|
||||
hscale, vscale, hphase, vphase, format);
|
||||
sun8i_vi_scaler_enable(layer, true);
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("HW scaling is not needed\n");
|
||||
sun8i_vi_scaler_enable(mixer, channel, false);
|
||||
sun8i_vi_scaler_enable(layer, false);
|
||||
}
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(hm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(hm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(vm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(vm));
|
||||
|
||||
/* Set base coordinates */
|
||||
DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
|
||||
state->dst.x1, state->dst.y1);
|
||||
DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
|
||||
regmap_write(bld_regs,
|
||||
SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
|
||||
SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
|
||||
regmap_write(bld_regs,
|
||||
SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
|
||||
outsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
|
||||
{
|
||||
if (!format->is_yuv)
|
||||
return SUN8I_CSC_MODE_OFF;
|
||||
|
||||
switch (format->format) {
|
||||
case DRM_FORMAT_YVU411:
|
||||
case DRM_FORMAT_YVU420:
|
||||
case DRM_FORMAT_YVU422:
|
||||
case DRM_FORMAT_YVU444:
|
||||
return SUN8I_CSC_MODE_YVU2RGB;
|
||||
default:
|
||||
return SUN8I_CSC_MODE_YUV2RGB;
|
||||
}
|
||||
}
|
||||
|
||||
static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
u32 val, ch_base, csc_mode, hw_fmt;
|
||||
const struct drm_format_info *fmt;
|
||||
int ret;
|
||||
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
|
||||
fmt = state->fb->format;
|
||||
ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
|
||||
regmap_update_bits(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
|
||||
|
||||
csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
|
||||
if (csc_mode != SUN8I_CSC_MODE_OFF) {
|
||||
sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
|
||||
state->color_encoding,
|
||||
state->color_range);
|
||||
sun8i_csc_enable_ccsc(mixer, channel, true);
|
||||
} else {
|
||||
sun8i_csc_enable_ccsc(mixer, channel, false);
|
||||
}
|
||||
|
||||
if (!fmt->is_yuv)
|
||||
val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
|
||||
int overlay, struct drm_plane *plane)
|
||||
static void sun8i_vi_layer_update_buffer(struct sun8i_layer *layer,
|
||||
struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
|
|
@ -268,7 +203,7 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
|
|||
u32 ch_base;
|
||||
int i;
|
||||
|
||||
ch_base = sun8i_channel_base(mixer, channel);
|
||||
ch_base = sun8i_channel_base(layer);
|
||||
|
||||
/* Adjust x and y to be dividable by subsampling factor */
|
||||
src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
|
||||
|
|
@ -298,21 +233,19 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
|
|||
/* Set the line width */
|
||||
DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
|
||||
i + 1, fb->pitches[i]);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
|
||||
overlay, i),
|
||||
layer->overlay, i),
|
||||
fb->pitches[i]);
|
||||
|
||||
DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
|
||||
i + 1, &dma_addr);
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
|
||||
overlay, i),
|
||||
layer->overlay, i),
|
||||
lower_32_bits(dma_addr));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
||||
|
|
@ -323,7 +256,9 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
|||
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
||||
struct drm_crtc *crtc = new_plane_state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int min_scale, max_scale;
|
||||
const struct drm_format_info *fmt;
|
||||
int min_scale, max_scale, ret;
|
||||
u32 hw_fmt;
|
||||
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
|
@ -332,10 +267,17 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
fmt = new_plane_state->fb->format;
|
||||
ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
||||
if (ret) {
|
||||
DRM_DEBUG_DRIVER("Invalid plane format\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
min_scale = DRM_PLANE_NO_SCALING;
|
||||
max_scale = DRM_PLANE_NO_SCALING;
|
||||
|
||||
if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
|
||||
if (layer->cfg->scaler_mask & BIT(layer->channel)) {
|
||||
min_scale = SUN8I_VI_SCALER_SCALE_MIN;
|
||||
max_scale = SUN8I_VI_SCALER_SCALE_MAX;
|
||||
}
|
||||
|
|
@ -352,20 +294,16 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
|
|||
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
||||
plane);
|
||||
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
||||
unsigned int zpos = new_state->normalized_zpos;
|
||||
struct sun8i_mixer *mixer = layer->mixer;
|
||||
|
||||
if (!new_state->crtc || !new_state->visible)
|
||||
if (!new_state->crtc || !new_state->visible) {
|
||||
sun8i_vi_layer_disable(layer);
|
||||
return;
|
||||
}
|
||||
|
||||
sun8i_vi_layer_update_coord(mixer, layer->channel,
|
||||
layer->overlay, plane, zpos);
|
||||
sun8i_vi_layer_update_alpha(mixer, layer->channel,
|
||||
layer->overlay, plane);
|
||||
sun8i_vi_layer_update_formats(mixer, layer->channel,
|
||||
layer->overlay, plane);
|
||||
sun8i_vi_layer_update_buffer(mixer, layer->channel,
|
||||
layer->overlay, plane);
|
||||
sun8i_vi_layer_update_attributes(layer, plane);
|
||||
sun8i_vi_layer_update_coord(layer, plane);
|
||||
sun8i_csc_config(layer, new_state);
|
||||
sun8i_vi_layer_update_buffer(layer, plane);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
|
||||
|
|
@ -471,12 +409,14 @@ static const uint64_t sun8i_layer_modifiers[] = {
|
|||
};
|
||||
|
||||
struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
||||
struct sun8i_mixer *mixer,
|
||||
int index)
|
||||
enum drm_plane_type type,
|
||||
struct regmap *regs,
|
||||
int index, int phy_index,
|
||||
int plane_cnt,
|
||||
const struct sun8i_layer_cfg *cfg)
|
||||
{
|
||||
enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
|
||||
u32 supported_encodings, supported_ranges;
|
||||
unsigned int plane_cnt, format_count;
|
||||
unsigned int format_count;
|
||||
struct sun8i_layer *layer;
|
||||
const u32 *formats;
|
||||
int ret;
|
||||
|
|
@ -485,7 +425,14 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
|||
if (!layer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
layer->type = SUN8I_LAYER_TYPE_VI;
|
||||
layer->index = index;
|
||||
layer->channel = phy_index;
|
||||
layer->overlay = 0;
|
||||
layer->regs = regs;
|
||||
layer->cfg = cfg;
|
||||
|
||||
if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
formats = sun8i_vi_layer_de3_formats;
|
||||
format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
|
||||
} else {
|
||||
|
|
@ -493,9 +440,6 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
|||
format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
|
||||
}
|
||||
|
||||
if (!mixer->cfg->ui_num && index == 0)
|
||||
type = DRM_PLANE_TYPE_PRIMARY;
|
||||
|
||||
/* possible crtcs are set later */
|
||||
ret = drm_universal_plane_init(drm, &layer->plane, 0,
|
||||
&sun8i_vi_layer_funcs,
|
||||
|
|
@ -507,9 +451,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
|
||||
|
||||
if (mixer->cfg->vi_num == 1 || mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
if (layer->cfg->de2_fcc_alpha || layer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
ret = drm_plane_create_alpha_property(&layer->plane);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't add alpha property\n");
|
||||
|
|
@ -526,7 +468,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
|||
|
||||
supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
|
||||
BIT(DRM_COLOR_YCBCR_BT709);
|
||||
if (mixer->cfg->de_type >= SUN8I_MIXER_DE3)
|
||||
if (layer->cfg->de_type >= SUN8I_MIXER_DE3)
|
||||
supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
|
||||
|
||||
supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
|
||||
|
|
@ -543,10 +485,6 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
|||
}
|
||||
|
||||
drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
|
||||
layer->mixer = mixer;
|
||||
layer->type = SUN8I_LAYER_TYPE_VI;
|
||||
layer->channel = index;
|
||||
layer->overlay = 0;
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ struct sun8i_mixer;
|
|||
struct sun8i_layer;
|
||||
|
||||
struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
|
||||
struct sun8i_mixer *mixer,
|
||||
int index);
|
||||
enum drm_plane_type type,
|
||||
struct regmap *regs,
|
||||
int index, int phy_index,
|
||||
int plane_cnt,
|
||||
const struct sun8i_layer_cfg *cfg);
|
||||
#endif /* _SUN8I_VI_LAYER_H_ */
|
||||
|
|
|
|||
|
|
@ -833,16 +833,17 @@ static const u32 bicubic4coefftab32[480] = {
|
|||
0x1012110d, 0x1012110d, 0x1013110c, 0x1013110c,
|
||||
};
|
||||
|
||||
static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel)
|
||||
static u32 sun8i_vi_scaler_base(struct sun8i_layer *layer)
|
||||
{
|
||||
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
return sun8i_channel_base(mixer, channel) + 0x3000;
|
||||
else if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
|
||||
if (layer->cfg->de_type == SUN8I_MIXER_DE33)
|
||||
return DE33_VI_SCALER_UNIT_BASE +
|
||||
DE33_CH_SIZE * layer->channel;
|
||||
else if (layer->cfg->de_type == SUN8I_MIXER_DE3)
|
||||
return DE3_VI_SCALER_UNIT_BASE +
|
||||
DE3_VI_SCALER_UNIT_SIZE * channel;
|
||||
DE3_VI_SCALER_UNIT_SIZE * layer->channel;
|
||||
else
|
||||
return DE2_VI_SCALER_UNIT_BASE +
|
||||
DE2_VI_SCALER_UNIT_SIZE * channel;
|
||||
DE2_VI_SCALER_UNIT_SIZE * layer->channel;
|
||||
}
|
||||
|
||||
static int sun8i_vi_scaler_coef_index(unsigned int step)
|
||||
|
|
@ -909,11 +910,11 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base,
|
|||
}
|
||||
}
|
||||
|
||||
void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
|
||||
void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable)
|
||||
{
|
||||
u32 val, base;
|
||||
|
||||
base = sun8i_vi_scaler_base(mixer, layer);
|
||||
base = sun8i_vi_scaler_base(layer);
|
||||
|
||||
if (enable)
|
||||
val = SUN8I_SCALER_VSU_CTRL_EN |
|
||||
|
|
@ -921,11 +922,11 @@ void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
|
|||
else
|
||||
val = 0;
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_CTRL(base), val);
|
||||
}
|
||||
|
||||
void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
||||
void sun8i_vi_scaler_setup(struct sun8i_layer *layer,
|
||||
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
|
||||
u32 hscale, u32 vscale, u32 hphase, u32 vphase,
|
||||
const struct drm_format_info *format)
|
||||
|
|
@ -934,7 +935,7 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
|||
u32 insize, outsize;
|
||||
u32 base;
|
||||
|
||||
base = sun8i_vi_scaler_base(mixer, layer);
|
||||
base = sun8i_vi_scaler_base(layer);
|
||||
|
||||
hphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16;
|
||||
vphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16;
|
||||
|
|
@ -958,7 +959,7 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
|||
cvphase = vphase;
|
||||
}
|
||||
|
||||
if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
|
||||
u32 val;
|
||||
|
||||
if (format->hsub == 1 && format->vsub == 1)
|
||||
|
|
@ -966,36 +967,36 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
|||
else
|
||||
val = SUN50I_SCALER_VSU_SCALE_MODE_NORMAL;
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN50I_SCALER_VSU_SCALE_MODE(base), val);
|
||||
}
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_OUTSIZE(base), outsize);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_YINSIZE(base), insize);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_YHSTEP(base), hscale);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_YVSTEP(base), vscale);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_YHPHASE(base), hphase);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_YVPHASE(base), vphase);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_CINSIZE(base),
|
||||
SUN8I_VI_SCALER_SIZE(src_w / format->hsub,
|
||||
src_h / format->vsub));
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_CHSTEP(base),
|
||||
hscale / format->hsub);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_CVSTEP(base),
|
||||
vscale / format->vsub);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_CHPHASE(base), chphase);
|
||||
regmap_write(mixer->engine.regs,
|
||||
regmap_write(layer->regs,
|
||||
SUN8I_SCALER_VSU_CVPHASE(base), cvphase);
|
||||
sun8i_vi_scaler_set_coeff(mixer->engine.regs, base,
|
||||
sun8i_vi_scaler_set_coeff(layer->regs, base,
|
||||
hscale, vscale, format);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#define DE3_VI_SCALER_UNIT_BASE 0x20000
|
||||
#define DE3_VI_SCALER_UNIT_SIZE 0x08000
|
||||
|
||||
#define DE33_VI_SCALER_UNIT_BASE 0x4000
|
||||
|
||||
/* this two macros assumes 16 fractional bits which is standard in DRM */
|
||||
#define SUN8I_VI_SCALER_SCALE_MIN 1
|
||||
#define SUN8I_VI_SCALER_SCALE_MAX ((1UL << 20) - 1)
|
||||
|
|
@ -69,8 +71,8 @@
|
|||
#define SUN50I_SCALER_VSU_ANGLE_SHIFT(x) (((x) << 16) & 0xF)
|
||||
#define SUN50I_SCALER_VSU_ANGLE_OFFSET(x) ((x) & 0xFF)
|
||||
|
||||
void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
|
||||
void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
|
||||
void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable);
|
||||
void sun8i_vi_scaler_setup(struct sun8i_layer *layer,
|
||||
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
|
||||
u32 hscale, u32 vscale, u32 hphase, u32 vphase,
|
||||
const struct drm_format_info *format);
|
||||
|
|
|
|||
|
|
@ -248,8 +248,7 @@ static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,
|
|||
dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport);
|
||||
|
||||
if (crtc->state->event) {
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &ddev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
vblank->time = ktime_get();
|
||||
|
||||
|
|
|
|||
|
|
@ -58,12 +58,6 @@ static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
|
|||
};
|
||||
|
||||
const struct dispc_features dispc_k2g_feats = {
|
||||
.min_pclk_khz = 4375,
|
||||
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_DPI] = 150000,
|
||||
},
|
||||
|
||||
/*
|
||||
* XXX According TRM the RGB input buffer width up to 2560 should
|
||||
* work on 3 taps, but in practice it only works up to 1280.
|
||||
|
|
@ -146,11 +140,6 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
|
|||
};
|
||||
|
||||
const struct dispc_features dispc_am65x_feats = {
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_DPI] = 165000,
|
||||
[DISPC_VP_OLDI_AM65X] = 165000,
|
||||
},
|
||||
|
||||
.scaling = {
|
||||
.in_width_max_5tap_rgb = 1280,
|
||||
.in_width_max_3tap_rgb = 2560,
|
||||
|
|
@ -246,11 +235,6 @@ static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
|
|||
};
|
||||
|
||||
const struct dispc_features dispc_j721e_feats = {
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_DPI] = 170000,
|
||||
[DISPC_VP_INTERNAL] = 600000,
|
||||
},
|
||||
|
||||
.scaling = {
|
||||
.in_width_max_5tap_rgb = 2048,
|
||||
.in_width_max_3tap_rgb = 4096,
|
||||
|
|
@ -317,11 +301,6 @@ const struct dispc_features dispc_j721e_feats = {
|
|||
};
|
||||
|
||||
const struct dispc_features dispc_am625_feats = {
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_DPI] = 165000,
|
||||
[DISPC_VP_INTERNAL] = 170000,
|
||||
},
|
||||
|
||||
.scaling = {
|
||||
.in_width_max_5tap_rgb = 1280,
|
||||
.in_width_max_3tap_rgb = 2560,
|
||||
|
|
@ -378,15 +357,6 @@ const struct dispc_features dispc_am625_feats = {
|
|||
};
|
||||
|
||||
const struct dispc_features dispc_am62a7_feats = {
|
||||
/*
|
||||
* if the code reaches dispc_mode_valid with VP1,
|
||||
* it should return MODE_BAD.
|
||||
*/
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_TIED_OFF] = 0,
|
||||
[DISPC_VP_DPI] = 165000,
|
||||
},
|
||||
|
||||
.scaling = {
|
||||
.in_width_max_5tap_rgb = 1280,
|
||||
.in_width_max_3tap_rgb = 2560,
|
||||
|
|
@ -443,10 +413,6 @@ const struct dispc_features dispc_am62a7_feats = {
|
|||
};
|
||||
|
||||
const struct dispc_features dispc_am62l_feats = {
|
||||
.max_pclk_khz = {
|
||||
[DISPC_VP_DPI] = 165000,
|
||||
},
|
||||
|
||||
.subrev = DISPC_AM62L,
|
||||
|
||||
.common = "common",
|
||||
|
|
@ -1324,33 +1290,61 @@ static void dispc_vp_set_default_color(struct dispc_device *dispc,
|
|||
DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the percentage difference between the requested pixel clock rate
|
||||
* and the effective rate resulting from calculating the clock divider value.
|
||||
*/
|
||||
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
|
||||
{
|
||||
int r = rate / 100, rr = real_rate / 100;
|
||||
|
||||
return (unsigned int)(abs(((rr - r) * 100) / r));
|
||||
}
|
||||
|
||||
static int check_pixel_clock(struct dispc_device *dispc, u32 hw_videoport,
|
||||
unsigned long clock)
|
||||
{
|
||||
unsigned long round_clock;
|
||||
|
||||
/*
|
||||
* For VP's with external clocking, clock operations must be
|
||||
* delegated to respective driver, so we skip the check here.
|
||||
*/
|
||||
if (dispc->tidss->is_ext_vp_clk[hw_videoport])
|
||||
return 0;
|
||||
|
||||
round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
|
||||
/*
|
||||
* To keep the check consistent with dispc_vp_set_clk_rate(), we
|
||||
* use the same 5% check here.
|
||||
*/
|
||||
if (dispc_pclk_diff(clock, round_clock) > 5)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
|
||||
u32 hw_videoport,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
u32 hsw, hfp, hbp, vsw, vfp, vbp;
|
||||
enum dispc_vp_bus_type bus_type;
|
||||
int max_pclk;
|
||||
|
||||
bus_type = dispc->feat->vp_bus_type[hw_videoport];
|
||||
|
||||
max_pclk = dispc->feat->max_pclk_khz[bus_type];
|
||||
|
||||
if (WARN_ON(max_pclk == 0))
|
||||
if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
|
||||
return MODE_BAD;
|
||||
|
||||
if (mode->clock < dispc->feat->min_pclk_khz)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (mode->clock > max_pclk)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
if (mode->hdisplay > 4096)
|
||||
return MODE_BAD;
|
||||
|
||||
if (mode->vdisplay > 4096)
|
||||
return MODE_BAD;
|
||||
|
||||
if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
/* TODO: add interlace support */
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
|
@ -1414,17 +1408,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
|
|||
clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the percentage difference between the requested pixel clock rate
|
||||
* and the effective rate resulting from calculating the clock divider value.
|
||||
*/
|
||||
unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
|
||||
{
|
||||
int r = rate / 100, rr = real_rate / 100;
|
||||
|
||||
return (unsigned int)(abs(((rr - r) * 100) / r));
|
||||
}
|
||||
|
||||
int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
|
||||
unsigned long rate)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
|
|||
};
|
||||
|
||||
struct dispc_features {
|
||||
int min_pclk_khz;
|
||||
int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
|
||||
|
||||
struct dispc_features_scaling scaling;
|
||||
|
||||
enum dispc_dss_subrevision subrev;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ struct tidss_device {
|
|||
|
||||
const struct dispc_features *feat;
|
||||
struct dispc_device *dispc;
|
||||
bool is_ext_vp_clk[TIDSS_MAX_PORTS];
|
||||
|
||||
|
||||
unsigned int num_crtcs;
|
||||
struct drm_crtc *crtcs[TIDSS_MAX_PORTS];
|
||||
|
|
|
|||
|
|
@ -309,6 +309,25 @@ static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
|
|||
return input_fmts;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
tidss_oldi_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
|
||||
unsigned long round_clock;
|
||||
|
||||
round_clock = clk_round_rate(oldi->serial, mode->clock * 7 * 1000);
|
||||
/*
|
||||
* To keep the check consistent with dispc_vp_set_clk_rate(),
|
||||
* we use the same 5% check here.
|
||||
*/
|
||||
if (dispc_pclk_diff(mode->clock * 7 * 1000, round_clock) > 5)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
|
||||
.attach = tidss_oldi_bridge_attach,
|
||||
.atomic_pre_enable = tidss_oldi_atomic_pre_enable,
|
||||
|
|
@ -317,6 +336,7 @@ static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
|
|||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_reset = drm_atomic_helper_bridge_reset,
|
||||
.mode_valid = tidss_oldi_mode_valid,
|
||||
};
|
||||
|
||||
static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
|
||||
|
|
@ -430,6 +450,7 @@ void tidss_oldi_deinit(struct tidss_device *tidss)
|
|||
for (int i = 0; i < tidss->num_oldis; i++) {
|
||||
if (tidss->oldis[i]) {
|
||||
drm_bridge_remove(&tidss->oldis[i]->bridge);
|
||||
tidss->is_ext_vp_clk[tidss->oldis[i]->parent_vp] = false;
|
||||
tidss->oldis[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -580,6 +601,7 @@ int tidss_oldi_init(struct tidss_device *tidss)
|
|||
oldi->bridge.timings = &default_tidss_oldi_timings;
|
||||
|
||||
tidss->oldis[tidss->num_oldis++] = oldi;
|
||||
tidss->is_ext_vp_clk[oldi->parent_vp] = true;
|
||||
oldi->tidss = tidss;
|
||||
|
||||
drm_bridge_add(&oldi->bridge);
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ EXPORT_SYMBOL(ttm_device_swapout);
|
|||
* @dev: The core kernel device pointer for DMA mappings and allocations.
|
||||
* @mapping: The address space to use for this bo.
|
||||
* @vma_manager: A pointer to a vma manager.
|
||||
* @alloc_flags: TTM_ALLOCATION_ flags.
|
||||
* @alloc_flags: TTM_ALLOCATION_* flags.
|
||||
*
|
||||
* Initializes a struct ttm_device:
|
||||
* Returns:
|
||||
|
|
|
|||
|
|
@ -1067,7 +1067,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
|
|||
* @pool: the pool to initialize
|
||||
* @dev: device for DMA allocations and mappings
|
||||
* @nid: NUMA node to use for allocations
|
||||
* @alloc_flags: TTM_ALLOCATION_POOL_ flags
|
||||
* @alloc_flags: TTM_ALLOCATION_POOL_* flags
|
||||
*
|
||||
* Initialize the pool and its pool types.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -553,6 +553,9 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
|
|||
memcpy(&vfbs->uo, uo, sizeof(vfbs->uo));
|
||||
vmw_user_object_ref(&vfbs->uo);
|
||||
|
||||
if (vfbs->uo.buffer)
|
||||
vfbs->base.base.obj[0] = &vfbs->uo.buffer->tbo.base;
|
||||
|
||||
*out = &vfbs->base;
|
||||
|
||||
ret = drm_framebuffer_init(dev, &vfbs->base.base,
|
||||
|
|
|
|||
|
|
@ -247,9 +247,8 @@ vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
|
|||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vmw_private *vmw = vmw_priv(dev);
|
||||
unsigned int pipe = crtc->index;
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
return false;
|
||||
|
|
@ -281,8 +280,7 @@ vmw_vkms_enable_vblank(struct drm_crtc *crtc)
|
|||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vmw_private *vmw = vmw_priv(dev);
|
||||
unsigned int pipe = drm_crtc_index(crtc);
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
|
||||
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
|
||||
|
||||
if (!vmw->vkms_enabled)
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ struct ttm_device {
|
|||
struct list_head device_list;
|
||||
|
||||
/**
|
||||
* @alloc_flags: TTM_ALLOCATION_ flags.
|
||||
* @alloc_flags: TTM_ALLOCATION_* flags.
|
||||
*/
|
||||
unsigned int alloc_flags;
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct ttm_pool_type {
|
|||
*
|
||||
* @dev: the device we allocate pages for
|
||||
* @nid: which numa node to use
|
||||
* @alloc_flags: TTM_ALLOCATION_POOL_ flags
|
||||
* @alloc_flags: TTM_ALLOCATION_POOL_* flags
|
||||
* @caching: pools for each caching/order
|
||||
*/
|
||||
struct ttm_pool {
|
||||
|
|
|
|||
|
|
@ -443,7 +443,9 @@ enum amdxdna_drm_get_param {
|
|||
DRM_AMDXDNA_QUERY_FIRMWARE_VERSION = 8,
|
||||
DRM_AMDXDNA_GET_POWER_MODE,
|
||||
DRM_AMDXDNA_QUERY_TELEMETRY,
|
||||
DRM_AMDXDNA_QUERY_RESOURCE_INFO = 12,
|
||||
DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE,
|
||||
DRM_AMDXDNA_QUERY_RESOURCE_INFO,
|
||||
DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -462,6 +464,16 @@ struct amdxdna_drm_get_resource_info {
|
|||
__u64 npu_task_curr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdxdna_drm_attribute_state - State of an attribute
|
||||
*/
|
||||
struct amdxdna_drm_attribute_state {
|
||||
/** @state: enabled or disabled */
|
||||
__u8 state;
|
||||
/** @pad: MBZ */
|
||||
__u8 pad[7];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amdxdna_drm_query_telemetry_header - Telemetry data header
|
||||
*/
|
||||
|
|
@ -613,6 +625,8 @@ enum amdxdna_drm_set_param {
|
|||
DRM_AMDXDNA_SET_POWER_MODE,
|
||||
DRM_AMDXDNA_WRITE_AIE_MEM,
|
||||
DRM_AMDXDNA_WRITE_AIE_REG,
|
||||
DRM_AMDXDNA_SET_FORCE_PREEMPT,
|
||||
DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -54,32 +54,46 @@ extern "C" {
|
|||
* This asks the kernel to have the GPU execute a render command list.
|
||||
*/
|
||||
struct drm_panfrost_submit {
|
||||
|
||||
/** Address to GPU mapping of job descriptor */
|
||||
/**
|
||||
* @jc: Address to GPU mapping of job descriptor
|
||||
*/
|
||||
__u64 jc;
|
||||
|
||||
/** An optional array of sync objects to wait on before starting this job. */
|
||||
/**
|
||||
* @in_syncs: An optional array of sync objects to wait on
|
||||
* before starting this job.
|
||||
*/
|
||||
__u64 in_syncs;
|
||||
|
||||
/** Number of sync objects to wait on before starting this job. */
|
||||
/**
|
||||
* @in_sync_count: Number of sync objects to wait on before
|
||||
* starting this job.
|
||||
*/
|
||||
__u32 in_sync_count;
|
||||
|
||||
/** An optional sync object to place the completion fence in. */
|
||||
/**
|
||||
* @out_sync: An optional sync object to place the completion fence in.
|
||||
*/
|
||||
__u32 out_sync;
|
||||
|
||||
/** Pointer to a u32 array of the BOs that are referenced by the job. */
|
||||
/**
|
||||
* @bo_handles: Pointer to a u32 array of the BOs that are
|
||||
* referenced by the job.
|
||||
*/
|
||||
__u64 bo_handles;
|
||||
|
||||
/** Number of BO handles passed in (size is that times 4). */
|
||||
/**
|
||||
* @bo_handle_count: Number of BO handles passed in (size is
|
||||
* that times 4).
|
||||
*/
|
||||
__u32 bo_handle_count;
|
||||
|
||||
/** A combination of PANFROST_JD_REQ_* */
|
||||
/**
|
||||
* @requirements: A combination of PANFROST_JD_REQ_*
|
||||
*/
|
||||
__u32 requirements;
|
||||
|
||||
/** JM context handle. Zero if you want to use the default context. */
|
||||
/**
|
||||
* @jm_ctx_handle: JM context handle. Zero if you want to use the
|
||||
* default context.
|
||||
*/
|
||||
__u32 jm_ctx_handle;
|
||||
|
||||
/** Padding field. MBZ. */
|
||||
/**
|
||||
* @pad: Padding field. Must be zero.
|
||||
*/
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
|
|
@ -92,9 +106,18 @@ struct drm_panfrost_submit {
|
|||
* completed.
|
||||
*/
|
||||
struct drm_panfrost_wait_bo {
|
||||
/**
|
||||
* @handle: Handle for the object to wait for.
|
||||
*/
|
||||
__u32 handle;
|
||||
/**
|
||||
* @pad: Padding, must be zero-filled.
|
||||
*/
|
||||
__u32 pad;
|
||||
__s64 timeout_ns; /* absolute */
|
||||
/**
|
||||
* @timeout_ns: absolute number of nanoseconds to wait.
|
||||
*/
|
||||
__s64 timeout_ns;
|
||||
};
|
||||
|
||||
/* Valid flags to pass to drm_panfrost_create_bo */
|
||||
|
|
@ -107,16 +130,26 @@ struct drm_panfrost_wait_bo {
|
|||
* The flags argument is a bit mask of PANFROST_BO_* flags.
|
||||
*/
|
||||
struct drm_panfrost_create_bo {
|
||||
/**
|
||||
* @size: size of shmem/BO area to create (bytes)
|
||||
*/
|
||||
__u32 size;
|
||||
/**
|
||||
* @flags: see PANFROST_BO_* flags
|
||||
*/
|
||||
__u32 flags;
|
||||
/** Returned GEM handle for the BO. */
|
||||
/**
|
||||
* @handle: Returned GEM handle for the BO.
|
||||
*/
|
||||
__u32 handle;
|
||||
/* Pad, must be zero-filled. */
|
||||
/**
|
||||
* @pad: Padding, must be zero-filled.
|
||||
*/
|
||||
__u32 pad;
|
||||
/**
|
||||
* Returned offset for the BO in the GPU address space. This offset
|
||||
* is private to the DRM fd and is valid for the lifetime of the GEM
|
||||
* handle.
|
||||
* @offset: Returned offset for the BO in the GPU address space.
|
||||
* This offset is private to the DRM fd and is valid for the
|
||||
* lifetime of the GEM handle.
|
||||
*
|
||||
* This offset value will always be nonzero, since various HW
|
||||
* units treat 0 specially.
|
||||
|
|
@ -136,10 +169,17 @@ struct drm_panfrost_create_bo {
|
|||
* used in a future extension.
|
||||
*/
|
||||
struct drm_panfrost_mmap_bo {
|
||||
/** Handle for the object being mapped. */
|
||||
/**
|
||||
* @handle: Handle for the object being mapped.
|
||||
*/
|
||||
__u32 handle;
|
||||
/**
|
||||
* @flags: currently not used (should be zero)
|
||||
*/
|
||||
__u32 flags;
|
||||
/** offset into the drm node to use for subsequent mmap call. */
|
||||
/**
|
||||
* @offset: offset into the drm node to use for subsequent mmap call.
|
||||
*/
|
||||
__u64 offset;
|
||||
};
|
||||
|
||||
|
|
@ -196,7 +236,7 @@ struct drm_panfrost_get_param {
|
|||
__u64 value;
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns the offset for the BO in the GPU address space for this DRM fd.
|
||||
* This is the same value returned by drm_panfrost_create_bo, if that was called
|
||||
* from this DRM fd.
|
||||
|
|
@ -244,12 +284,14 @@ struct drm_panfrost_madvise {
|
|||
* struct drm_panfrost_set_label_bo - ioctl argument for labelling Panfrost BOs.
|
||||
*/
|
||||
struct drm_panfrost_set_label_bo {
|
||||
/** @handle: Handle of the buffer object to label. */
|
||||
/**
|
||||
* @handle: Handle of the buffer object to label.
|
||||
*/
|
||||
__u32 handle;
|
||||
|
||||
/** @pad: MBZ. */
|
||||
/**
|
||||
* @pad: Must be zero.
|
||||
*/
|
||||
__u32 pad;
|
||||
|
||||
/**
|
||||
* @label: User pointer to a NUL-terminated string
|
||||
*
|
||||
|
|
@ -330,10 +372,13 @@ enum drm_panfrost_jm_ctx_priority {
|
|||
};
|
||||
|
||||
struct drm_panfrost_jm_ctx_create {
|
||||
/** @handle: Handle of the created JM context */
|
||||
/**
|
||||
* @handle: Handle of the created JM context
|
||||
*/
|
||||
__u32 handle;
|
||||
|
||||
/** @priority: Context priority (see enum drm_panfrost_jm_ctx_priority). */
|
||||
/**
|
||||
* @priority: Context priority (see enum drm_panfrost_jm_ctx_priority).
|
||||
*/
|
||||
__u32 priority;
|
||||
};
|
||||
|
||||
|
|
@ -344,8 +389,9 @@ struct drm_panfrost_jm_ctx_destroy {
|
|||
* Must be a valid context handle returned by DRM_IOCTL_PANTHOR_JM_CTX_CREATE.
|
||||
*/
|
||||
__u32 handle;
|
||||
|
||||
/** @pad: Padding field, MBZ. */
|
||||
/**
|
||||
* @pad: Padding field, must be zero.
|
||||
*/
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user