Merge tag 'drm-misc-next-2025-10-21' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.19: UAPI Changes: amdxdna: - Support reading last hardware error Cross-subsystem Changes: dma-buf: - heaps: Create heap per CMA reserved location; Improve user-space documentation Core Changes: atomic: - Clean up and improve state-handling interfaces, update drivers bridge: - Improve ref counting buddy: - Optimize block management Driver Changes: amdxdna: - Fix runtime power management - Support firmware debug output ast: - Set quirks for each chip model atmel-hlcdc: - Set LCDC_ATTRE register in plane disable - Set correct values for plane scaler bochs: - Use vblank timer bridge: - synopsis: Support CEC; Init timer with correct frequency cirrus-qemu: - Use vblank timer imx: - Clean up ivu: - Update JSM API to 3.33.0 - Reset engine on more job errors - Return correct error codes for jobs komeda: - Use drm_ logging functions panel: - edp: Support AUO B116XAN02.0 panfrost: - Embed struct drm_driver in Panfrost device - Improve error handling - Clean up job handling panthor: - Support custom ASN_HASH for mt8196 renesas: - rz-du: Fix dependencies rockchip: - dsi: Add support for RK3368 - Fix LUT size for RK3386 sitronix: - Fix output position when clearing screens qaic: - Support dma-buf exports - Support new firmware's READ_DATA implementation - Replace kcalloc with memdup - Replace snprintf() with sysfs_emit() - Avoid overflows in arithmetics - Clean up - Fixes qxl: - Use vblank timer rockchip: - Clean up mode-setting code vgem: - Fix fence timer deadlock virtgpu: - Use vblank timer Signed-off-by: Simona Vetter <simona.vetter@ffwll.ch> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://lore.kernel.org/r/20251021111837.GA40643@linux.fritz.box
This commit is contained in:
@@ -17,6 +17,7 @@ properties:
|
||||
- rockchip,px30-mipi-dsi
|
||||
- rockchip,rk3128-mipi-dsi
|
||||
- rockchip,rk3288-mipi-dsi
|
||||
- rockchip,rk3368-mipi-dsi
|
||||
- rockchip,rk3399-mipi-dsi
|
||||
- rockchip,rk3568-mipi-dsi
|
||||
- rockchip,rv1126-mipi-dsi
|
||||
@@ -73,6 +74,7 @@ allOf:
|
||||
enum:
|
||||
- rockchip,px30-mipi-dsi
|
||||
- rockchip,rk3128-mipi-dsi
|
||||
- rockchip,rk3368-mipi-dsi
|
||||
- rockchip,rk3568-mipi-dsi
|
||||
- rockchip,rv1126-mipi-dsi
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ properties:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8196-mali
|
||||
- rockchip,rk3588-mali
|
||||
- const: arm,mali-valhall-csf # Mali Valhall GPU model/revision is fully discoverable
|
||||
|
||||
@@ -91,7 +92,6 @@ required:
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- mali-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@@ -108,6 +108,8 @@ allOf:
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
power-domain-names: false
|
||||
required:
|
||||
- mali-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
@@ -16,13 +16,52 @@ following heaps:
|
||||
|
||||
- The ``system`` heap allocates virtually contiguous, cacheable, buffers.
|
||||
|
||||
- The ``cma`` heap allocates physically contiguous, cacheable,
|
||||
buffers. Only present if a CMA region is present. Such a region is
|
||||
usually created either through the kernel commandline through the
|
||||
``cma`` parameter, a memory region Device-Tree node with the
|
||||
``linux,cma-default`` property set, or through the ``CMA_SIZE_MBYTES`` or
|
||||
``CMA_SIZE_PERCENTAGE`` Kconfig options. The heap's name in devtmpfs is
|
||||
``default_cma_region``. For backwards compatibility, when the
|
||||
``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option is set, a duplicate node is
|
||||
created following legacy naming conventions; the legacy name might be
|
||||
``reserved``, ``linux,cma``, or ``default-pool``.
|
||||
- The ``default_cma_region`` heap allocates physically contiguous,
|
||||
cacheable, buffers. Only present if a CMA region is present. Such a
|
||||
region is usually created either through the kernel commandline
|
||||
through the ``cma`` parameter, a memory region Device-Tree node with
|
||||
the ``linux,cma-default`` property set, or through the
|
||||
``CMA_SIZE_MBYTES`` or ``CMA_SIZE_PERCENTAGE`` Kconfig options. Prior
|
||||
to Linux 6.17, its name wasn't stable and could be called
|
||||
``reserved``, ``linux,cma``, or ``default-pool``, depending on the
|
||||
platform.
|
||||
|
||||
- A heap will be created for each reusable region in the device tree
|
||||
with the ``shared-dma-pool`` compatible, using the full device tree
|
||||
node name as its name. The buffer semantics are identical to
|
||||
``default-cma-region``.
|
||||
|
||||
Naming Convention
|
||||
=================
|
||||
|
||||
``dma-buf`` heaps name should meet a number of constraints:
|
||||
|
||||
- The name must be stable, and must not change from one version to the other.
|
||||
Userspace identifies heaps by their name, so if the names ever change, we
|
||||
would be likely to introduce regressions.
|
||||
|
||||
- The name must describe the memory region the heap will allocate from, and
|
||||
must uniquely identify it in a given platform. Since userspace applications
|
||||
use the heap name as the discriminant, it must be able to tell which heap it
|
||||
wants to use reliably if there's multiple heaps.
|
||||
|
||||
- The name must not mention implementation details, such as the allocator. The
|
||||
heap driver will change over time, and implementation details when it was
|
||||
introduced might not be relevant in the future.
|
||||
|
||||
- The name should describe properties of the buffers that would be allocated.
|
||||
Doing so will make heap identification easier for userspace. Such properties
|
||||
are:
|
||||
|
||||
- ``contiguous`` for physically contiguous buffers;
|
||||
|
||||
- ``protected`` for encrypted buffers not accessible the OS;
|
||||
|
||||
- The name may describe intended usage. Doing so will make heap identification
|
||||
easier for userspace applications and users.
|
||||
|
||||
For example, assuming a platform with a reserved memory region located
|
||||
at the RAM address 0x42000000, intended to allocate video framebuffers,
|
||||
physically contiguous, and backed by the CMA kernel allocator, good
|
||||
names would be ``memory@42000000-contiguous`` or ``video@42000000``, but
|
||||
``cma-video`` wouldn't.
|
||||
|
||||
13
MAINTAINERS
13
MAINTAINERS
@@ -2092,7 +2092,8 @@ F: drivers/gpu/drm/arm/display/komeda/
|
||||
ARM MALI PANFROST DRM DRIVER
|
||||
M: Boris Brezillon <boris.brezillon@collabora.com>
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
R: Steven Price <steven.price@arm.com>
|
||||
M: Steven Price <steven.price@arm.com>
|
||||
M: Adrián Larumbe <adrian.larumbe@collabora.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
@@ -7309,6 +7310,7 @@ F: Documentation/userspace-api/dma-buf-alloc-exchange.rst
|
||||
F: drivers/dma-buf/
|
||||
F: include/linux/*fence.h
|
||||
F: include/linux/dma-buf.h
|
||||
F: include/linux/dma-buf/
|
||||
F: include/linux/dma-resv.h
|
||||
K: \bdma_(?:buf|fence|resv)\b
|
||||
|
||||
@@ -7637,7 +7639,6 @@ F: drivers/accel/
|
||||
F: include/drm/drm_accel.h
|
||||
|
||||
DRM DRIVER FOR ALLWINNER DE2 AND DE3 ENGINE
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
R: Jernej Skrabec <jernej.skrabec@gmail.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
@@ -7747,7 +7748,8 @@ F: Documentation/devicetree/bindings/display/panel/panel-edp.yaml
|
||||
F: drivers/gpu/drm/panel/panel-edp.c
|
||||
|
||||
DRM DRIVER FOR GENERIC USB DISPLAY
|
||||
S: Orphan
|
||||
M: Ruben Wauters <rubenru09@aol.com>
|
||||
S: Maintained
|
||||
W: https://github.com/notro/gud/wiki
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/gud/
|
||||
@@ -7888,7 +7890,7 @@ DRM DRIVER for Qualcomm display hardware
|
||||
M: Rob Clark <robin.clark@oss.qualcomm.com>
|
||||
M: Dmitry Baryshkov <lumag@kernel.org>
|
||||
R: Abhinav Kumar <abhinav.kumar@linux.dev>
|
||||
R: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
|
||||
R: Jessica Zhang <jesszhan0024@gmail.com>
|
||||
R: Sean Paul <sean@poorly.run>
|
||||
R: Marijn Suijten <marijn.suijten@somainline.org>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
@@ -8251,7 +8253,6 @@ F: drivers/gpu/nova-core/
|
||||
F: rust/kernel/drm/
|
||||
|
||||
DRM DRIVERS FOR ALLWINNER A10
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
@@ -8601,7 +8602,7 @@ F: drivers/gpu/drm/clients/drm_log.c
|
||||
|
||||
DRM PANEL DRIVERS
|
||||
M: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
R: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
|
||||
R: Jessica Zhang <jesszhan0024@gmail.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
- Add debugfs support
|
||||
- Add debug BO support
|
||||
|
||||
@@ -226,11 +226,10 @@ out:
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
|
||||
aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size)
|
||||
{
|
||||
struct amdxdna_sched_job *job = handle;
|
||||
int ret = 0;
|
||||
u32 status;
|
||||
|
||||
if (unlikely(!data))
|
||||
goto out;
|
||||
@@ -240,8 +239,7 @@ aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = readl(data);
|
||||
XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
|
||||
job->drv_cmd->result = readl(data);
|
||||
|
||||
out:
|
||||
aie2_sched_notify(job);
|
||||
@@ -314,8 +312,18 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
|
||||
kref_get(&job->refcnt);
|
||||
fence = dma_fence_get(job->fence);
|
||||
|
||||
if (unlikely(!cmd_abo)) {
|
||||
ret = aie2_sync_bo(hwctx, job, aie2_sched_nocmd_resp_handler);
|
||||
if (job->drv_cmd) {
|
||||
switch (job->drv_cmd->opcode) {
|
||||
case SYNC_DEBUG_BO:
|
||||
ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
|
||||
break;
|
||||
case ATTACH_DEBUG_BO:
|
||||
ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -610,10 +618,14 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
||||
goto free_entity;
|
||||
}
|
||||
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto free_col_list;
|
||||
|
||||
ret = aie2_alloc_resource(hwctx);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Alloc hw resource failed, ret %d", ret);
|
||||
goto free_col_list;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
|
||||
@@ -628,6 +640,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
||||
XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret);
|
||||
goto release_resource;
|
||||
}
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
|
||||
hwctx->status = HWCTX_STAT_INIT;
|
||||
ndev = xdna->dev_handle;
|
||||
@@ -640,6 +653,8 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
||||
|
||||
release_resource:
|
||||
aie2_release_resource(hwctx);
|
||||
suspend_put:
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
free_col_list:
|
||||
kfree(hwctx->col_list);
|
||||
free_entity:
|
||||
@@ -759,6 +774,74 @@ free_cus:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq)
|
||||
{
|
||||
struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq);
|
||||
|
||||
if (!out_fence) {
|
||||
XDNA_ERR(hwctx->client->xdna, "Failed to get fence");
|
||||
return;
|
||||
}
|
||||
|
||||
dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT);
|
||||
dma_fence_put(out_fence);
|
||||
}
|
||||
|
||||
static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
|
||||
bool attach)
|
||||
{
|
||||
struct amdxdna_client *client = hwctx->client;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_drv_cmd cmd = { 0 };
|
||||
struct amdxdna_gem_obj *abo;
|
||||
u64 seq;
|
||||
int ret;
|
||||
|
||||
abo = amdxdna_gem_get_obj(client, bo_hdl, AMDXDNA_BO_DEV);
|
||||
if (!abo) {
|
||||
XDNA_ERR(xdna, "Get bo %d failed", bo_hdl);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (attach) {
|
||||
if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) {
|
||||
ret = -EBUSY;
|
||||
goto put_obj;
|
||||
}
|
||||
cmd.opcode = ATTACH_DEBUG_BO;
|
||||
} else {
|
||||
if (abo->assigned_hwctx != hwctx->id) {
|
||||
ret = -EINVAL;
|
||||
goto put_obj;
|
||||
}
|
||||
cmd.opcode = DETACH_DEBUG_BO;
|
||||
}
|
||||
|
||||
ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
|
||||
&bo_hdl, 1, hwctx->id, &seq);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Submit command failed");
|
||||
goto put_obj;
|
||||
}
|
||||
|
||||
aie2_cmd_wait(hwctx, seq);
|
||||
if (cmd.result) {
|
||||
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
|
||||
goto put_obj;
|
||||
}
|
||||
|
||||
if (attach)
|
||||
abo->assigned_hwctx = hwctx->id;
|
||||
else
|
||||
abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE;
|
||||
|
||||
XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name);
|
||||
|
||||
put_obj:
|
||||
amdxdna_gem_put_obj(abo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size)
|
||||
{
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
@@ -768,14 +851,40 @@ int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *bu
|
||||
case DRM_AMDXDNA_HWCTX_CONFIG_CU:
|
||||
return aie2_hwctx_cu_config(hwctx, buf, size);
|
||||
case DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF:
|
||||
return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, true);
|
||||
case DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF:
|
||||
return -EOPNOTSUPP;
|
||||
return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, false);
|
||||
default:
|
||||
XDNA_DBG(xdna, "Not supported type %d", type);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl)
|
||||
{
|
||||
struct amdxdna_client *client = hwctx->client;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_drv_cmd cmd = { 0 };
|
||||
u64 seq;
|
||||
int ret;
|
||||
|
||||
cmd.opcode = SYNC_DEBUG_BO;
|
||||
ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
|
||||
&debug_bo_hdl, 1, hwctx->id, &seq);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Submit command failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
aie2_cmd_wait(hwctx, seq);
|
||||
if (cmd.result) {
|
||||
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_populate_range(struct amdxdna_gem_obj *abo)
|
||||
{
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "aie2_msg_priv.h"
|
||||
#include "aie2_pci.h"
|
||||
#include "amdxdna_error.h"
|
||||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
@@ -46,6 +47,7 @@ enum aie_module_type {
|
||||
AIE_MEM_MOD = 0,
|
||||
AIE_CORE_MOD,
|
||||
AIE_PL_MOD,
|
||||
AIE_UNKNOWN_MOD,
|
||||
};
|
||||
|
||||
enum aie_error_category {
|
||||
@@ -143,6 +145,31 @@ static const struct aie_event_category aie_ml_shim_tile_event_cat[] = {
|
||||
EVENT_CATEGORY(74U, AIE_ERROR_LOCK),
|
||||
};
|
||||
|
||||
static const enum amdxdna_error_num aie_cat_err_num_map[] = {
|
||||
[AIE_ERROR_SATURATION] = AMDXDNA_ERROR_NUM_AIE_SATURATION,
|
||||
[AIE_ERROR_FP] = AMDXDNA_ERROR_NUM_AIE_FP,
|
||||
[AIE_ERROR_STREAM] = AMDXDNA_ERROR_NUM_AIE_STREAM,
|
||||
[AIE_ERROR_ACCESS] = AMDXDNA_ERROR_NUM_AIE_ACCESS,
|
||||
[AIE_ERROR_BUS] = AMDXDNA_ERROR_NUM_AIE_BUS,
|
||||
[AIE_ERROR_INSTRUCTION] = AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
|
||||
[AIE_ERROR_ECC] = AMDXDNA_ERROR_NUM_AIE_ECC,
|
||||
[AIE_ERROR_LOCK] = AMDXDNA_ERROR_NUM_AIE_LOCK,
|
||||
[AIE_ERROR_DMA] = AMDXDNA_ERROR_NUM_AIE_DMA,
|
||||
[AIE_ERROR_MEM_PARITY] = AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
|
||||
[AIE_ERROR_UNKNOWN] = AMDXDNA_ERROR_NUM_UNKNOWN,
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(aie_cat_err_num_map) == AIE_ERROR_UNKNOWN + 1);
|
||||
|
||||
static const enum amdxdna_error_module aie_err_mod_map[] = {
|
||||
[AIE_MEM_MOD] = AMDXDNA_ERROR_MODULE_AIE_MEMORY,
|
||||
[AIE_CORE_MOD] = AMDXDNA_ERROR_MODULE_AIE_CORE,
|
||||
[AIE_PL_MOD] = AMDXDNA_ERROR_MODULE_AIE_PL,
|
||||
[AIE_UNKNOWN_MOD] = AMDXDNA_ERROR_MODULE_UNKNOWN,
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(aie_err_mod_map) == AIE_UNKNOWN_MOD + 1);
|
||||
|
||||
static enum aie_error_category
|
||||
aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
|
||||
{
|
||||
@@ -176,12 +203,40 @@ aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
|
||||
if (event_id != lut[i].event_id)
|
||||
continue;
|
||||
|
||||
if (lut[i].category > AIE_ERROR_UNKNOWN)
|
||||
return AIE_ERROR_UNKNOWN;
|
||||
|
||||
return lut[i].category;
|
||||
}
|
||||
|
||||
return AIE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
static void aie2_update_last_async_error(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
|
||||
{
|
||||
struct aie_error *errs = err_info;
|
||||
enum amdxdna_error_module err_mod;
|
||||
enum aie_error_category aie_err;
|
||||
enum amdxdna_error_num err_num;
|
||||
struct aie_error *last_err;
|
||||
|
||||
last_err = &errs[num_err - 1];
|
||||
if (last_err->mod_type >= AIE_UNKNOWN_MOD) {
|
||||
err_num = aie_cat_err_num_map[AIE_ERROR_UNKNOWN];
|
||||
err_mod = aie_err_mod_map[AIE_UNKNOWN_MOD];
|
||||
} else {
|
||||
aie_err = aie_get_error_category(last_err->row,
|
||||
last_err->event_id,
|
||||
last_err->mod_type);
|
||||
err_num = aie_cat_err_num_map[aie_err];
|
||||
err_mod = aie_err_mod_map[last_err->mod_type];
|
||||
}
|
||||
|
||||
ndev->last_async_err.err_code = AMDXDNA_ERROR_ENCODE(err_num, err_mod);
|
||||
ndev->last_async_err.ts_us = ktime_to_us(ktime_get_real());
|
||||
ndev->last_async_err.ex_err_code = AMDXDNA_EXTRA_ERR_ENCODE(last_err->row, last_err->col);
|
||||
}
|
||||
|
||||
static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
|
||||
{
|
||||
struct aie_error *errs = err_info;
|
||||
@@ -264,29 +319,14 @@ static void aie2_error_worker(struct work_struct *err_work)
|
||||
}
|
||||
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
aie2_update_last_async_error(e->ndev, info->payload, info->err_cnt);
|
||||
|
||||
/* Re-sent this event to firmware */
|
||||
if (aie2_error_event_send(e))
|
||||
XDNA_WARN(xdna, "Unable to register async event");
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
}
|
||||
|
||||
int aie2_error_async_events_send(struct amdxdna_dev_hdl *ndev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
struct async_event *e;
|
||||
int i, ret;
|
||||
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
for (i = 0; i < ndev->async_events->event_cnt; i++) {
|
||||
e = &ndev->async_events->event[i];
|
||||
ret = aie2_error_event_send(e);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
@@ -341,6 +381,10 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
|
||||
e->size = ASYNC_BUF_SIZE;
|
||||
e->resp.status = MAX_AIE2_STATUS_CODE;
|
||||
INIT_WORK(&e->work, aie2_error_worker);
|
||||
|
||||
ret = aie2_error_event_send(e);
|
||||
if (ret)
|
||||
goto free_wq;
|
||||
}
|
||||
|
||||
ndev->async_events = events;
|
||||
@@ -349,6 +393,8 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
|
||||
events->event_cnt, events->size);
|
||||
return 0;
|
||||
|
||||
free_wq:
|
||||
destroy_workqueue(events->wq);
|
||||
free_buf:
|
||||
dma_free_noncoherent(xdna->ddev.dev, events->size, events->buf,
|
||||
events->addr, DMA_FROM_DEVICE);
|
||||
@@ -356,3 +402,18 @@ free_events:
|
||||
kfree(events);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args)
|
||||
{
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
|
||||
args->num_element = 1;
|
||||
args->element_size = sizeof(ndev->last_async_err);
|
||||
if (copy_to_user(u64_to_user_ptr(args->buffer),
|
||||
&ndev->last_async_err, args->element_size))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -749,7 +749,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int ret = 0;
|
||||
|
||||
req.src_addr = 0;
|
||||
req.dst_addr = abo->mem.dev_addr - hwctx->client->dev_heap->mem.dev_addr;
|
||||
req.dst_addr = amdxdna_dev_bo_offset(abo);
|
||||
req.size = abo->mem.size;
|
||||
|
||||
/* Device to Host */
|
||||
@@ -773,3 +773,32 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t))
|
||||
{
|
||||
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
|
||||
struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]);
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
struct config_debug_bo_req req;
|
||||
struct xdna_mailbox_msg msg;
|
||||
|
||||
if (job->drv_cmd->opcode == ATTACH_DEBUG_BO)
|
||||
req.config = DEBUG_BO_REGISTER;
|
||||
else
|
||||
req.config = DEBUG_BO_UNREGISTER;
|
||||
|
||||
req.offset = amdxdna_dev_bo_offset(abo);
|
||||
req.size = abo->mem.size;
|
||||
|
||||
XDNA_DBG(xdna, "offset 0x%llx size 0x%llx config %d",
|
||||
req.offset, req.size, req.config);
|
||||
|
||||
msg.handle = job;
|
||||
msg.notify_cb = notify_cb;
|
||||
msg.send_data = (u8 *)&req;
|
||||
msg.send_size = sizeof(req);
|
||||
msg.opcode = MSG_OP_CONFIG_DEBUG_BO;
|
||||
|
||||
return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ enum aie2_msg_opcode {
|
||||
MSG_OP_CONFIG_CU = 0x11,
|
||||
MSG_OP_CHAIN_EXEC_BUFFER_CF = 0x12,
|
||||
MSG_OP_CHAIN_EXEC_DPU = 0x13,
|
||||
MSG_OP_CONFIG_DEBUG_BO = 0x14,
|
||||
MSG_OP_MAX_XRT_OPCODE,
|
||||
MSG_OP_SUSPEND = 0x101,
|
||||
MSG_OP_RESUME = 0x102,
|
||||
@@ -365,4 +366,21 @@ struct sync_bo_req {
|
||||
struct sync_bo_resp {
|
||||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
|
||||
#define DEBUG_BO_UNREGISTER 0
|
||||
#define DEBUG_BO_REGISTER 1
|
||||
struct config_debug_bo_req {
|
||||
__u64 offset;
|
||||
__u64 size;
|
||||
/*
|
||||
* config operations.
|
||||
* DEBUG_BO_REGISTER: Register debug buffer
|
||||
* DEBUG_BO_UNREGISTER: Unregister debug buffer
|
||||
*/
|
||||
__u32 config;
|
||||
} __packed;
|
||||
|
||||
struct config_debug_bo_resp {
|
||||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
#endif /* _AIE2_MSG_PRIV_H_ */
|
||||
|
||||
@@ -924,6 +924,9 @@ static int aie2_get_array(struct amdxdna_client *client,
|
||||
case DRM_AMDXDNA_HW_CONTEXT_ALL:
|
||||
ret = aie2_query_ctx_status_array(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_HW_LAST_ASYNC_ERR:
|
||||
ret = aie2_get_array_async_error(xdna->dev_handle, args);
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
|
||||
ret = -EOPNOTSUPP;
|
||||
@@ -1001,6 +1004,7 @@ const struct amdxdna_dev_ops aie2_ops = {
|
||||
.hwctx_init = aie2_hwctx_init,
|
||||
.hwctx_fini = aie2_hwctx_fini,
|
||||
.hwctx_config = aie2_hwctx_config,
|
||||
.hwctx_sync_debug_bo = aie2_hwctx_sync_debug_bo,
|
||||
.cmd_submit = aie2_cmd_submit,
|
||||
.hmm_invalidate = aie2_hmm_invalidate,
|
||||
.get_array = aie2_get_array,
|
||||
|
||||
@@ -190,6 +190,8 @@ struct amdxdna_dev_hdl {
|
||||
|
||||
enum aie2_dev_status dev_status;
|
||||
u32 hwctx_num;
|
||||
|
||||
struct amdxdna_async_error last_async_err;
|
||||
};
|
||||
|
||||
#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
|
||||
@@ -253,8 +255,9 @@ void aie2_psp_stop(struct psp_device *psp);
|
||||
/* aie2_error.c */
|
||||
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev);
|
||||
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_error_async_events_send(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_error_async_msg_thread(void *data);
|
||||
int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev,
|
||||
struct amdxdna_drm_get_array *args);
|
||||
|
||||
/* aie2_message.c */
|
||||
int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev);
|
||||
@@ -284,11 +287,14 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
|
||||
/* aie2_hwctx.c */
|
||||
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
|
||||
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
|
||||
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
|
||||
int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
|
||||
void aie2_hwctx_suspend(struct amdxdna_client *client);
|
||||
int aie2_hwctx_resume(struct amdxdna_client *client);
|
||||
int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
|
||||
|
||||
@@ -328,6 +328,38 @@ unlock_srcu:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
|
||||
{
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_hwctx *hwctx;
|
||||
struct amdxdna_gem_obj *abo;
|
||||
struct drm_gem_object *gobj;
|
||||
int ret, idx;
|
||||
|
||||
if (!xdna->dev_info->ops->hwctx_sync_debug_bo)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
gobj = drm_gem_object_lookup(client->filp, debug_bo_hdl);
|
||||
if (!gobj)
|
||||
return -EINVAL;
|
||||
|
||||
abo = to_xdna_obj(gobj);
|
||||
guard(mutex)(&xdna->dev_lock);
|
||||
idx = srcu_read_lock(&client->hwctx_srcu);
|
||||
hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx);
|
||||
if (!hwctx) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_srcu;
|
||||
}
|
||||
|
||||
ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl);
|
||||
|
||||
unlock_srcu:
|
||||
srcu_read_unlock(&client->hwctx_srcu, idx);
|
||||
drm_gem_object_put(gobj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
|
||||
{
|
||||
@@ -393,6 +425,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
|
||||
}
|
||||
|
||||
int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
struct amdxdna_drv_cmd *drv_cmd,
|
||||
u32 cmd_bo_hdl, u32 *arg_bo_hdls, u32 arg_bo_cnt,
|
||||
u32 hwctx_hdl, u64 *seq)
|
||||
{
|
||||
@@ -406,6 +439,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
if (!job)
|
||||
return -ENOMEM;
|
||||
|
||||
job->drv_cmd = drv_cmd;
|
||||
|
||||
if (cmd_bo_hdl != AMDXDNA_INVALID_BO_HANDLE) {
|
||||
job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_CMD);
|
||||
if (!job->cmd_bo) {
|
||||
@@ -413,8 +448,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
ret = -EINVAL;
|
||||
goto free_job;
|
||||
}
|
||||
} else {
|
||||
job->cmd_bo = NULL;
|
||||
}
|
||||
|
||||
ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
|
||||
@@ -508,7 +541,7 @@ static int amdxdna_drm_submit_execbuf(struct amdxdna_client *client,
|
||||
}
|
||||
}
|
||||
|
||||
ret = amdxdna_cmd_submit(client, cmd_bo_hdl, arg_bo_hdls,
|
||||
ret = amdxdna_cmd_submit(client, NULL, cmd_bo_hdl, arg_bo_hdls,
|
||||
args->arg_count, args->hwctx, &args->seq);
|
||||
if (ret)
|
||||
XDNA_DBG(xdna, "Submit cmds failed, ret %d", ret);
|
||||
|
||||
@@ -95,6 +95,17 @@ struct amdxdna_hwctx {
|
||||
#define drm_job_to_xdna_job(j) \
|
||||
container_of(j, struct amdxdna_sched_job, base)
|
||||
|
||||
enum amdxdna_job_opcode {
|
||||
SYNC_DEBUG_BO,
|
||||
ATTACH_DEBUG_BO,
|
||||
DETACH_DEBUG_BO,
|
||||
};
|
||||
|
||||
struct amdxdna_drv_cmd {
|
||||
enum amdxdna_job_opcode opcode;
|
||||
u32 result;
|
||||
};
|
||||
|
||||
struct amdxdna_sched_job {
|
||||
struct drm_sched_job base;
|
||||
struct kref refcnt;
|
||||
@@ -106,6 +117,7 @@ struct amdxdna_sched_job {
|
||||
struct dma_fence *out_fence;
|
||||
bool job_done;
|
||||
u64 seq;
|
||||
struct amdxdna_drv_cmd *drv_cmd;
|
||||
struct amdxdna_gem_obj *cmd_bo;
|
||||
size_t bo_cnt;
|
||||
struct drm_gem_object *bos[] __counted_by(bo_cnt);
|
||||
@@ -143,9 +155,11 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job);
|
||||
void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
|
||||
int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
|
||||
int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
|
||||
int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
|
||||
|
||||
int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
u32 cmd_bo_hdls, u32 *arg_bo_hdls, u32 arg_bo_cnt,
|
||||
struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
|
||||
u32 *arg_bo_hdls, u32 arg_bo_cnt,
|
||||
u32 hwctx_hdl, u64 *seq);
|
||||
|
||||
int amdxdna_cmd_wait(struct amdxdna_client *client, u32 hwctx_hdl,
|
||||
|
||||
59
drivers/accel/amdxdna/amdxdna_error.h
Normal file
59
drivers/accel/amdxdna/amdxdna_error.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _AMDXDNA_ERROR_H_
|
||||
#define _AMDXDNA_ERROR_H_
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
#define AMDXDNA_ERR_DRV_AIE 4
|
||||
#define AMDXDNA_ERR_SEV_CRITICAL 3
|
||||
#define AMDXDNA_ERR_CLASS_AIE 2
|
||||
|
||||
#define AMDXDNA_ERR_NUM_MASK GENMASK_U64(15, 0)
|
||||
#define AMDXDNA_ERR_DRV_MASK GENMASK_U64(23, 16)
|
||||
#define AMDXDNA_ERR_SEV_MASK GENMASK_U64(31, 24)
|
||||
#define AMDXDNA_ERR_MOD_MASK GENMASK_U64(39, 32)
|
||||
#define AMDXDNA_ERR_CLASS_MASK GENMASK_U64(47, 40)
|
||||
|
||||
enum amdxdna_error_num {
|
||||
AMDXDNA_ERROR_NUM_AIE_SATURATION = 3,
|
||||
AMDXDNA_ERROR_NUM_AIE_FP,
|
||||
AMDXDNA_ERROR_NUM_AIE_STREAM,
|
||||
AMDXDNA_ERROR_NUM_AIE_ACCESS,
|
||||
AMDXDNA_ERROR_NUM_AIE_BUS,
|
||||
AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
|
||||
AMDXDNA_ERROR_NUM_AIE_ECC,
|
||||
AMDXDNA_ERROR_NUM_AIE_LOCK,
|
||||
AMDXDNA_ERROR_NUM_AIE_DMA,
|
||||
AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
|
||||
AMDXDNA_ERROR_NUM_UNKNOWN = 15,
|
||||
};
|
||||
|
||||
enum amdxdna_error_module {
|
||||
AMDXDNA_ERROR_MODULE_AIE_CORE = 3,
|
||||
AMDXDNA_ERROR_MODULE_AIE_MEMORY,
|
||||
AMDXDNA_ERROR_MODULE_AIE_SHIM,
|
||||
AMDXDNA_ERROR_MODULE_AIE_NOC,
|
||||
AMDXDNA_ERROR_MODULE_AIE_PL,
|
||||
AMDXDNA_ERROR_MODULE_UNKNOWN = 8,
|
||||
};
|
||||
|
||||
#define AMDXDNA_ERROR_ENCODE(err_num, err_mod) \
|
||||
(FIELD_PREP(AMDXDNA_ERR_NUM_MASK, err_num) | \
|
||||
FIELD_PREP_CONST(AMDXDNA_ERR_DRV_MASK, AMDXDNA_ERR_DRV_AIE) | \
|
||||
FIELD_PREP_CONST(AMDXDNA_ERR_SEV_MASK, AMDXDNA_ERR_SEV_CRITICAL) | \
|
||||
FIELD_PREP(AMDXDNA_ERR_MOD_MASK, err_mod) | \
|
||||
FIELD_PREP_CONST(AMDXDNA_ERR_CLASS_MASK, AMDXDNA_ERR_CLASS_AIE))
|
||||
|
||||
#define AMDXDNA_EXTRA_ERR_COL_MASK GENMASK_U64(7, 0)
|
||||
#define AMDXDNA_EXTRA_ERR_ROW_MASK GENMASK_U64(15, 8)
|
||||
|
||||
#define AMDXDNA_EXTRA_ERR_ENCODE(row, col) \
|
||||
(FIELD_PREP(AMDXDNA_EXTRA_ERR_COL_MASK, col) | \
|
||||
FIELD_PREP(AMDXDNA_EXTRA_ERR_ROW_MASK, row))
|
||||
|
||||
#endif /* _AMDXDNA_ERROR_H_ */
|
||||
@@ -962,6 +962,9 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
|
||||
XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
|
||||
args->handle, args->offset, args->size);
|
||||
|
||||
if (args->direction == SYNC_DIRECT_FROM_DEVICE)
|
||||
ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle);
|
||||
|
||||
put_obj:
|
||||
drm_gem_object_put(gobj);
|
||||
return ret;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _AMDXDNA_GEM_H_
|
||||
|
||||
#include <linux/hmm.h>
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
struct amdxdna_umap {
|
||||
struct vm_area_struct *vma;
|
||||
@@ -62,6 +63,11 @@ static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo)
|
||||
drm_gem_object_put(to_gobj(abo));
|
||||
}
|
||||
|
||||
static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
|
||||
{
|
||||
return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr;
|
||||
}
|
||||
|
||||
void amdxdna_umap_put(struct amdxdna_umap *mapp);
|
||||
|
||||
struct drm_gem_object *
|
||||
|
||||
@@ -27,9 +27,11 @@ MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin");
|
||||
/*
|
||||
* 0.0: Initial version
|
||||
* 0.1: Support getting all hardware contexts by DRM_IOCTL_AMDXDNA_GET_ARRAY
|
||||
* 0.2: Support getting last error hardware error
|
||||
* 0.3: Support firmware debug buffer
|
||||
*/
|
||||
#define AMDXDNA_DRIVER_MAJOR 0
|
||||
#define AMDXDNA_DRIVER_MINOR 1
|
||||
#define AMDXDNA_DRIVER_MINOR 3
|
||||
|
||||
/*
|
||||
* Bind the driver base on (vendor_id, device_id) pair and later use the
|
||||
|
||||
@@ -55,6 +55,7 @@ struct amdxdna_dev_ops {
|
||||
int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
|
||||
void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
|
||||
int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
|
||||
int (*hwctx_sync_debug_bo)(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
|
||||
void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
|
||||
int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
|
||||
int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
|
||||
const struct rt_config npu1_default_rt_cfg[] = {
|
||||
{ 2, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
|
||||
{ 4, 1, AIE2_RT_CFG_INIT }, /* Debug BO */
|
||||
{ 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
|
||||
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 */
|
||||
{ 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 */
|
||||
|
||||
@@ -46,12 +46,13 @@ static inline void ivpu_bo_unlock(struct ivpu_bo *bo)
|
||||
|
||||
static struct sg_table *ivpu_bo_map_attachment(struct ivpu_device *vdev, struct ivpu_bo *bo)
|
||||
{
|
||||
struct sg_table *sgt = bo->base.sgt;
|
||||
struct sg_table *sgt;
|
||||
|
||||
drm_WARN_ON(&vdev->drm, !bo->base.base.import_attach);
|
||||
|
||||
ivpu_bo_lock(bo);
|
||||
|
||||
sgt = bo->base.sgt;
|
||||
if (!sgt) {
|
||||
sgt = dma_buf_map_attachment(bo->base.base.import_attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(sgt))
|
||||
|
||||
@@ -564,6 +564,44 @@ static struct ivpu_job *ivpu_job_remove_from_submitted_jobs(struct ivpu_device *
|
||||
return job;
|
||||
}
|
||||
|
||||
bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status)
|
||||
{
|
||||
lockdep_assert_held(&vdev->submitted_jobs_lock);
|
||||
|
||||
switch (job_status) {
|
||||
case VPU_JSM_STATUS_PROCESSING_ERR:
|
||||
case VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN ... VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX:
|
||||
{
|
||||
struct ivpu_job *job = xa_load(&vdev->submitted_jobs_xa, job_id);
|
||||
|
||||
if (!job)
|
||||
return false;
|
||||
|
||||
/* Trigger an engine reset */
|
||||
guard(mutex)(&job->file_priv->lock);
|
||||
|
||||
job->job_status = job_status;
|
||||
|
||||
if (job->file_priv->has_mmu_faults)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Mark context as faulty and defer destruction of the job to jobs abort thread
|
||||
* handler to synchronize between both faults and jobs returning context violation
|
||||
* status and ensure both are handled in the same way
|
||||
*/
|
||||
job->file_priv->has_mmu_faults = true;
|
||||
queue_work(system_wq, &vdev->context_abort_work);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
/* Complete job with error status, engine reset not required */
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
|
||||
{
|
||||
struct ivpu_job *job;
|
||||
@@ -574,35 +612,22 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
|
||||
if (!job)
|
||||
return -ENOENT;
|
||||
|
||||
if (job_status == VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW) {
|
||||
guard(mutex)(&job->file_priv->lock);
|
||||
ivpu_job_remove_from_submitted_jobs(vdev, job_id);
|
||||
|
||||
if (job->job_status == VPU_JSM_STATUS_SUCCESS) {
|
||||
if (job->file_priv->has_mmu_faults)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Mark context as faulty and defer destruction of the job to jobs abort thread
|
||||
* handler to synchronize between both faults and jobs returning context violation
|
||||
* status and ensure both are handled in the same way
|
||||
*/
|
||||
job->file_priv->has_mmu_faults = true;
|
||||
queue_work(system_wq, &vdev->context_abort_work);
|
||||
return 0;
|
||||
job->job_status = DRM_IVPU_JOB_STATUS_ABORTED;
|
||||
else
|
||||
job->job_status = job_status;
|
||||
}
|
||||
|
||||
job = ivpu_job_remove_from_submitted_jobs(vdev, job_id);
|
||||
if (!job)
|
||||
return -ENOENT;
|
||||
|
||||
if (job->file_priv->has_mmu_faults)
|
||||
job_status = DRM_IVPU_JOB_STATUS_ABORTED;
|
||||
|
||||
job->bos[CMD_BUF_IDX]->job_status = job_status;
|
||||
job->bos[CMD_BUF_IDX]->job_status = job->job_status;
|
||||
dma_fence_signal(job->done_fence);
|
||||
|
||||
trace_job("done", job);
|
||||
ivpu_dbg(vdev, JOB, "Job complete: id %3u ctx %2d cmdq_id %u engine %d status 0x%x\n",
|
||||
job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx, job_status);
|
||||
job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx,
|
||||
job->job_status);
|
||||
|
||||
ivpu_job_destroy(job);
|
||||
ivpu_stop_job_timeout_detection(vdev);
|
||||
@@ -1022,7 +1047,9 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
|
||||
payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload;
|
||||
|
||||
mutex_lock(&vdev->submitted_jobs_lock);
|
||||
ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
|
||||
if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status))
|
||||
/* No engine error, complete the job normally */
|
||||
ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
|
||||
mutex_unlock(&vdev->submitted_jobs_lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ struct ivpu_cmdq {
|
||||
* @cmdq_id: Command queue ID used for submission
|
||||
* @job_id: Unique job ID for tracking and status reporting
|
||||
* @engine_idx: Engine index for job execution
|
||||
* @job_status: Status reported by firmware for this job
|
||||
* @primary_preempt_buf: Primary preemption buffer for job
|
||||
* @secondary_preempt_buf: Secondary preemption buffer for job (optional)
|
||||
* @bo_count: Number of buffer objects associated with this job
|
||||
@@ -64,6 +65,7 @@ struct ivpu_job {
|
||||
u32 cmdq_id;
|
||||
u32 job_id;
|
||||
u32 engine_idx;
|
||||
u32 job_status;
|
||||
struct ivpu_bo *primary_preempt_buf;
|
||||
struct ivpu_bo *secondary_preempt_buf;
|
||||
size_t bo_count;
|
||||
@@ -83,6 +85,7 @@ void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id)
|
||||
|
||||
void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
|
||||
void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
|
||||
bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status);
|
||||
void ivpu_context_abort_work_fn(struct work_struct *work);
|
||||
|
||||
void ivpu_jobs_abort_all(struct ivpu_device *vdev);
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
/*
|
||||
* Minor version changes when API backward compatibility is preserved.
|
||||
*/
|
||||
#define VPU_JSM_API_VER_MINOR 32
|
||||
#define VPU_JSM_API_VER_MINOR 33
|
||||
|
||||
/*
|
||||
* API header changed (field names, documentation, formatting) but API itself has not been changed
|
||||
*/
|
||||
#define VPU_JSM_API_VER_PATCH 5
|
||||
#define VPU_JSM_API_VER_PATCH 0
|
||||
|
||||
/*
|
||||
* Index in the API version table
|
||||
@@ -76,8 +76,11 @@
|
||||
#define VPU_JSM_STATUS_PREEMPTED_MID_INFERENCE 0xDU
|
||||
/* Job status returned when the job was preempted mid-command */
|
||||
#define VPU_JSM_STATUS_PREEMPTED_MID_COMMAND 0xDU
|
||||
/* Range of status codes that require engine reset */
|
||||
#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN 0xEU
|
||||
#define VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW 0xEU
|
||||
#define VPU_JSM_STATUS_MVNCI_PREEMPTION_TIMED_OUT 0xFU
|
||||
#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX 0x1FU
|
||||
|
||||
/*
|
||||
* Host <-> VPU IPC channels.
|
||||
@@ -404,8 +407,8 @@ struct vpu_hws_native_fence_log_header {
|
||||
/** Index of the first free entry in buffer. */
|
||||
u32 first_free_entry_idx;
|
||||
/**
|
||||
* Incremented each time NPU wraps around
|
||||
* the buffer to write next entry.
|
||||
* Incremented whenever the NPU wraps around the buffer and writes
|
||||
* to the first entry again.
|
||||
*/
|
||||
u32 wraparound_count;
|
||||
};
|
||||
@@ -454,10 +457,17 @@ struct vpu_hws_native_fence_log_buffer {
|
||||
* Host <-> VPU IPC messages types.
|
||||
*/
|
||||
enum vpu_ipc_msg_type {
|
||||
/** Unsupported command */
|
||||
VPU_JSM_MSG_UNKNOWN = 0xFFFFFFFF,
|
||||
|
||||
/* IPC Host -> Device, Async commands */
|
||||
/** IPC Host -> Device, base id for async commands */
|
||||
VPU_JSM_MSG_ASYNC_CMD = 0x1100,
|
||||
/**
|
||||
* Reset engine. The NPU cancels all the jobs currently executing on the target
|
||||
* engine making the engine become idle and then does a HW reset, before returning
|
||||
* to the host.
|
||||
* @see struct vpu_ipc_msg_payload_engine_reset
|
||||
*/
|
||||
VPU_JSM_MSG_ENGINE_RESET = VPU_JSM_MSG_ASYNC_CMD,
|
||||
/**
|
||||
* Preempt engine. The NPU stops (preempts) all the jobs currently
|
||||
@@ -467,6 +477,7 @@ enum vpu_ipc_msg_type {
|
||||
* the target engine, but it stops processing them (until the queue doorbell
|
||||
* is rung again); the host is responsible to reset the job queue, either
|
||||
* after preemption or when resubmitting jobs to the queue.
|
||||
* @see vpu_ipc_msg_payload_engine_preempt
|
||||
*/
|
||||
VPU_JSM_MSG_ENGINE_PREEMPT = 0x1101,
|
||||
/**
|
||||
@@ -583,23 +594,32 @@ enum vpu_ipc_msg_type {
|
||||
* @see vpu_ipc_msg_payload_hws_resume_engine
|
||||
*/
|
||||
VPU_JSM_MSG_HWS_ENGINE_RESUME = 0x111b,
|
||||
/* Control command: Enable survivability/DCT mode */
|
||||
/**
|
||||
* Control command: Enable survivability/DCT mode
|
||||
* @see vpu_ipc_msg_payload_pwr_dct_control
|
||||
*/
|
||||
VPU_JSM_MSG_DCT_ENABLE = 0x111c,
|
||||
/* Control command: Disable survivability/DCT mode */
|
||||
/**
|
||||
* Control command: Disable survivability/DCT mode
|
||||
* This command has no payload
|
||||
*/
|
||||
VPU_JSM_MSG_DCT_DISABLE = 0x111d,
|
||||
/**
|
||||
* Dump VPU state. To be used for debug purposes only.
|
||||
* NOTE: Please introduce new ASYNC commands before this one. *
|
||||
* This command has no payload.
|
||||
* NOTE: Please introduce new ASYNC commands before this one.
|
||||
*/
|
||||
VPU_JSM_MSG_STATE_DUMP = 0x11FF,
|
||||
|
||||
/* IPC Host -> Device, General commands */
|
||||
/** IPC Host -> Device, base id for general commands */
|
||||
VPU_JSM_MSG_GENERAL_CMD = 0x1200,
|
||||
/** Unsupported command */
|
||||
VPU_JSM_MSG_BLOB_DEINIT_DEPRECATED = VPU_JSM_MSG_GENERAL_CMD,
|
||||
/**
|
||||
* Control dyndbg behavior by executing a dyndbg command; equivalent to
|
||||
* Linux command:
|
||||
* @verbatim echo '<dyndbg_cmd>' > <debugfs>/dynamic_debug/control @endverbatim
|
||||
* @see vpu_ipc_msg_payload_dyndbg_control
|
||||
*/
|
||||
VPU_JSM_MSG_DYNDBG_CONTROL = 0x1201,
|
||||
/**
|
||||
@@ -607,7 +627,10 @@ enum vpu_ipc_msg_type {
|
||||
*/
|
||||
VPU_JSM_MSG_PWR_D0I3_ENTER = 0x1202,
|
||||
|
||||
/* IPC Device -> Host, Job completion */
|
||||
/**
|
||||
* IPC Device -> Host, Job completion
|
||||
* @see struct vpu_ipc_msg_payload_job_done
|
||||
*/
|
||||
VPU_JSM_MSG_JOB_DONE = 0x2100,
|
||||
/**
|
||||
* IPC Device -> Host, Fence signalled
|
||||
@@ -622,6 +645,10 @@ enum vpu_ipc_msg_type {
|
||||
* @see vpu_ipc_msg_payload_engine_reset_done
|
||||
*/
|
||||
VPU_JSM_MSG_ENGINE_RESET_DONE = VPU_JSM_MSG_ASYNC_CMD_DONE,
|
||||
/**
|
||||
* Preempt complete message
|
||||
* @see vpu_ipc_msg_payload_engine_preempt_done
|
||||
*/
|
||||
VPU_JSM_MSG_ENGINE_PREEMPT_DONE = 0x2201,
|
||||
VPU_JSM_MSG_REGISTER_DB_DONE = 0x2202,
|
||||
VPU_JSM_MSG_UNREGISTER_DB_DONE = 0x2203,
|
||||
@@ -729,13 +756,20 @@ enum vpu_ipc_msg_type {
|
||||
* @see vpu_ipc_msg_payload_hws_resume_engine
|
||||
*/
|
||||
VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE = 0x221c,
|
||||
/* Response to control command: Enable survivability/DCT mode */
|
||||
/**
|
||||
* Response to control command: Enable survivability/DCT mode
|
||||
* This command has no payload
|
||||
*/
|
||||
VPU_JSM_MSG_DCT_ENABLE_DONE = 0x221d,
|
||||
/* Response to control command: Disable survivability/DCT mode */
|
||||
/**
|
||||
* Response to control command: Disable survivability/DCT mode
|
||||
* This command has no payload
|
||||
*/
|
||||
VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e,
|
||||
/**
|
||||
* Response to state dump control command.
|
||||
* NOTE: Please introduce new ASYNC responses before this one. *
|
||||
* This command has no payload.
|
||||
* NOTE: Please introduce new ASYNC responses before this one.
|
||||
*/
|
||||
VPU_JSM_MSG_STATE_DUMP_RSP = 0x22FF,
|
||||
|
||||
@@ -753,20 +787,25 @@ enum vpu_ipc_msg_type {
|
||||
|
||||
enum vpu_ipc_msg_status { VPU_JSM_MSG_FREE, VPU_JSM_MSG_ALLOCATED };
|
||||
|
||||
/*
|
||||
* Host <-> LRT IPC message payload definitions
|
||||
/**
|
||||
* Engine reset request payload
|
||||
* @see VPU_JSM_MSG_ENGINE_RESET
|
||||
*/
|
||||
struct vpu_ipc_msg_payload_engine_reset {
|
||||
/* Engine to be reset. */
|
||||
/** Engine to be reset. */
|
||||
u32 engine_idx;
|
||||
/* Reserved */
|
||||
/** Reserved */
|
||||
u32 reserved_0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Engine preemption request struct
|
||||
* @see VPU_JSM_MSG_ENGINE_PREEMPT
|
||||
*/
|
||||
struct vpu_ipc_msg_payload_engine_preempt {
|
||||
/* Engine to be preempted. */
|
||||
/** Engine to be preempted. */
|
||||
u32 engine_idx;
|
||||
/* ID of the preemption request. */
|
||||
/** ID of the preemption request. */
|
||||
u32 preempt_id;
|
||||
};
|
||||
|
||||
@@ -935,20 +974,24 @@ struct vpu_jsm_metric_streamer_update {
|
||||
u64 next_buffer_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Device -> host job completion message.
|
||||
* @see VPU_JSM_MSG_JOB_DONE
|
||||
*/
|
||||
struct vpu_ipc_msg_payload_job_done {
|
||||
/* Engine to which the job was submitted. */
|
||||
/** Engine to which the job was submitted. */
|
||||
u32 engine_idx;
|
||||
/* Index of the doorbell to which the job was submitted */
|
||||
/** Index of the doorbell to which the job was submitted */
|
||||
u32 db_idx;
|
||||
/* ID of the completed job */
|
||||
/** ID of the completed job */
|
||||
u32 job_id;
|
||||
/* Status of the completed job */
|
||||
/** Status of the completed job */
|
||||
u32 job_status;
|
||||
/* Host SSID */
|
||||
/** Host SSID */
|
||||
u32 host_ssid;
|
||||
/* Zero Padding */
|
||||
/** Zero Padding */
|
||||
u32 reserved_0;
|
||||
/* Command queue id */
|
||||
/** Command queue id */
|
||||
u64 cmdq_id;
|
||||
};
|
||||
|
||||
@@ -997,10 +1040,14 @@ struct vpu_ipc_msg_payload_engine_reset_done {
|
||||
impacted_contexts[VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS];
|
||||
};
|
||||
|
||||
/**
|
||||
* Preemption response struct
|
||||
* @see VPU_JSM_MSG_ENGINE_PREEMPT_DONE
|
||||
*/
|
||||
struct vpu_ipc_msg_payload_engine_preempt_done {
|
||||
/* Engine preempted. */
|
||||
/** Engine preempted. */
|
||||
u32 engine_idx;
|
||||
/* ID of the preemption request. */
|
||||
/** ID of the preemption request. */
|
||||
u32 preempt_id;
|
||||
};
|
||||
|
||||
@@ -1552,29 +1599,24 @@ struct vpu_jsm_metric_counter_descriptor {
|
||||
};
|
||||
|
||||
/**
|
||||
* Payload for VPU_JSM_MSG_DYNDBG_CONTROL requests.
|
||||
* Payload for @ref VPU_JSM_MSG_DYNDBG_CONTROL requests.
|
||||
*
|
||||
* VPU_JSM_MSG_DYNDBG_CONTROL are used to control the VPU FW Dynamic Debug
|
||||
* feature, which allows developers to selectively enable / disable MVLOG_DEBUG
|
||||
* messages. This is equivalent to the Dynamic Debug functionality provided by
|
||||
* Linux
|
||||
* (https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html)
|
||||
* The host can control Dynamic Debug behavior by sending dyndbg commands, which
|
||||
* have the same syntax as Linux
|
||||
* dyndbg commands.
|
||||
* VPU_JSM_MSG_DYNDBG_CONTROL requests are used to control the VPU FW dynamic debug
|
||||
* feature, which allows developers to selectively enable/disable code to obtain
|
||||
* additional FW information. This is equivalent to the dynamic debug functionality
|
||||
* provided by Linux. The host can control dynamic debug behavior by sending dyndbg
|
||||
* commands, using the same syntax as for Linux dynamic debug commands.
|
||||
*
|
||||
* NOTE: in order for MVLOG_DEBUG messages to be actually printed, the host
|
||||
* still has to set the logging level to MVLOG_DEBUG, using the
|
||||
* VPU_JSM_MSG_TRACE_SET_CONFIG command.
|
||||
* @see https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html.
|
||||
*
|
||||
* The host can see the current dynamic debug configuration by executing a
|
||||
* special 'show' command. The dyndbg configuration will be printed to the
|
||||
* configured logging destination using MVLOG_INFO logging level.
|
||||
* NOTE:
|
||||
* As the dynamic debug feature uses MVLOG messages to provide information, the host
|
||||
* must first set the logging level to MVLOG_DEBUG, using the @ref VPU_JSM_MSG_TRACE_SET_CONFIG
|
||||
* command.
|
||||
*/
|
||||
struct vpu_ipc_msg_payload_dyndbg_control {
|
||||
/**
|
||||
* Dyndbg command (same format as Linux dyndbg); must be a NULL-terminated
|
||||
* string.
|
||||
* Dyndbg command to be executed.
|
||||
*/
|
||||
char dyndbg_cmd[VPU_DYNDBG_CMD_MAX_LEN];
|
||||
};
|
||||
@@ -1595,7 +1637,7 @@ struct vpu_ipc_msg_payload_pwr_d0i3_enter {
|
||||
};
|
||||
|
||||
/**
|
||||
* Payload for VPU_JSM_MSG_DCT_ENABLE message.
|
||||
* Payload for @ref VPU_JSM_MSG_DCT_ENABLE message.
|
||||
*
|
||||
* Default values for DCT active/inactive times are 5.3ms and 30ms respectively,
|
||||
* corresponding to a 85% duty cycle. This payload allows the host to tune these
|
||||
@@ -1652,28 +1694,28 @@ union vpu_ipc_msg_payload {
|
||||
struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control;
|
||||
};
|
||||
|
||||
/*
|
||||
* Host <-> LRT IPC message base structure.
|
||||
/**
|
||||
* Host <-> NPU IPC message base structure.
|
||||
*
|
||||
* NOTE: All instances of this object must be aligned on a 64B boundary
|
||||
* to allow proper handling of VPU cache operations.
|
||||
*/
|
||||
struct vpu_jsm_msg {
|
||||
/* Reserved */
|
||||
/** Reserved */
|
||||
u64 reserved_0;
|
||||
/* Message type, see vpu_ipc_msg_type enum. */
|
||||
/** Message type, see @ref vpu_ipc_msg_type. */
|
||||
u32 type;
|
||||
/* Buffer status, see vpu_ipc_msg_status enum. */
|
||||
/** Buffer status, see @ref vpu_ipc_msg_status. */
|
||||
u32 status;
|
||||
/*
|
||||
/**
|
||||
* Request ID, provided by the host in a request message and passed
|
||||
* back by VPU in the response message.
|
||||
*/
|
||||
u32 request_id;
|
||||
/* Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */
|
||||
/** Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */
|
||||
u32 result;
|
||||
u64 reserved_1;
|
||||
/* Message payload depending on message type, see vpu_ipc_msg_payload union. */
|
||||
/** Message payload depending on message type, see vpu_ipc_msg_payload union. */
|
||||
union vpu_ipc_msg_payload payload;
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
@@ -655,8 +656,9 @@ static int encode_activate(struct qaic_device *qdev, void *trans, struct wrapper
|
||||
return -EINVAL;
|
||||
|
||||
nelem = in_trans->queue_size;
|
||||
size = (get_dbc_req_elem_size() + get_dbc_rsp_elem_size()) * nelem;
|
||||
if (size / nelem != get_dbc_req_elem_size() + get_dbc_rsp_elem_size())
|
||||
if (check_mul_overflow((u32)(get_dbc_req_elem_size() + get_dbc_rsp_elem_size()),
|
||||
nelem,
|
||||
&size))
|
||||
return -EINVAL;
|
||||
|
||||
if (size + QAIC_DBC_Q_GAP + QAIC_DBC_Q_BUF_ALIGN < size)
|
||||
@@ -810,7 +812,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
|
||||
}
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (user_len != user_msg->len)
|
||||
@@ -1079,7 +1081,6 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
|
||||
|
||||
list_for_each_entry(w, &wrappers->list, list) {
|
||||
kref_get(&w->ref_count);
|
||||
retry_count = 0;
|
||||
ret = mhi_queue_buf(qdev->cntl_ch, DMA_TO_DEVICE, &w->msg, w->len,
|
||||
list_is_last(&w->list, &wrappers->list) ? MHI_EOT : MHI_CHAIN);
|
||||
if (ret) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/srcu.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
@@ -165,7 +166,7 @@ static void free_slice(struct kref *kref)
|
||||
drm_gem_object_put(&slice->bo->base);
|
||||
sg_free_table(slice->sgt);
|
||||
kfree(slice->sgt);
|
||||
kfree(slice->reqs);
|
||||
kvfree(slice->reqs);
|
||||
kfree(slice);
|
||||
}
|
||||
|
||||
@@ -404,7 +405,7 @@ static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
|
||||
goto free_sgt;
|
||||
}
|
||||
|
||||
slice->reqs = kcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
|
||||
slice->reqs = kvcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
|
||||
if (!slice->reqs) {
|
||||
ret = -ENOMEM;
|
||||
goto free_slice;
|
||||
@@ -430,7 +431,7 @@ static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
|
||||
return 0;
|
||||
|
||||
free_req:
|
||||
kfree(slice->reqs);
|
||||
kvfree(slice->reqs);
|
||||
free_slice:
|
||||
kfree(slice);
|
||||
free_sgt:
|
||||
@@ -643,8 +644,36 @@ static void qaic_free_object(struct drm_gem_object *obj)
|
||||
kfree(bo);
|
||||
}
|
||||
|
||||
static struct sg_table *qaic_get_sg_table(struct drm_gem_object *obj)
|
||||
{
|
||||
struct qaic_bo *bo = to_qaic_bo(obj);
|
||||
struct scatterlist *sg, *sg_in;
|
||||
struct sg_table *sgt, *sgt_in;
|
||||
int i;
|
||||
|
||||
sgt_in = bo->sgt;
|
||||
|
||||
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (sg_alloc_table(sgt, sgt_in->orig_nents, GFP_KERNEL)) {
|
||||
kfree(sgt);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
sg = sgt->sgl;
|
||||
for_each_sgtable_sg(sgt_in, sg_in, i) {
|
||||
memcpy(sg, sg_in, sizeof(*sg));
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
return sgt;
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs qaic_gem_funcs = {
|
||||
.free = qaic_free_object,
|
||||
.get_sg_table = qaic_get_sg_table,
|
||||
.print_info = qaic_gem_print_info,
|
||||
.mmap = qaic_gem_object_mmap,
|
||||
.vm_ops = &drm_vm_ops,
|
||||
@@ -953,8 +982,9 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
|
||||
if (args->hdr.count == 0)
|
||||
return -EINVAL;
|
||||
|
||||
arg_size = args->hdr.count * sizeof(*slice_ent);
|
||||
if (arg_size / args->hdr.count != sizeof(*slice_ent))
|
||||
if (check_mul_overflow((unsigned long)args->hdr.count,
|
||||
(unsigned long)sizeof(*slice_ent),
|
||||
&arg_size))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE))
|
||||
@@ -984,18 +1014,12 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
|
||||
|
||||
user_data = u64_to_user_ptr(args->data);
|
||||
|
||||
slice_ent = kzalloc(arg_size, GFP_KERNEL);
|
||||
if (!slice_ent) {
|
||||
ret = -EINVAL;
|
||||
slice_ent = memdup_user(user_data, arg_size);
|
||||
if (IS_ERR(slice_ent)) {
|
||||
ret = PTR_ERR(slice_ent);
|
||||
goto unlock_dev_srcu;
|
||||
}
|
||||
|
||||
ret = copy_from_user(slice_ent, user_data, arg_size);
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto free_slice_ent;
|
||||
}
|
||||
|
||||
obj = drm_gem_object_lookup(file_priv, args->hdr.handle);
|
||||
if (!obj) {
|
||||
ret = -ENOENT;
|
||||
@@ -1300,8 +1324,6 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
|
||||
int usr_rcu_id, qdev_rcu_id;
|
||||
struct qaic_device *qdev;
|
||||
struct qaic_user *usr;
|
||||
u8 __user *user_data;
|
||||
unsigned long n;
|
||||
u64 received_ts;
|
||||
u32 queue_level;
|
||||
u64 submit_ts;
|
||||
@@ -1314,20 +1336,12 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
|
||||
received_ts = ktime_get_ns();
|
||||
|
||||
size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec);
|
||||
n = (unsigned long)size * args->hdr.count;
|
||||
if (args->hdr.count == 0 || n / args->hdr.count != size)
|
||||
if (args->hdr.count == 0)
|
||||
return -EINVAL;
|
||||
|
||||
user_data = u64_to_user_ptr(args->data);
|
||||
|
||||
exec = kcalloc(args->hdr.count, size, GFP_KERNEL);
|
||||
if (!exec)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(exec, user_data, n)) {
|
||||
ret = -EFAULT;
|
||||
goto free_exec;
|
||||
}
|
||||
exec = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, size);
|
||||
if (IS_ERR(exec))
|
||||
return PTR_ERR(exec);
|
||||
|
||||
usr = file_priv->driver_priv;
|
||||
usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
|
||||
@@ -1396,7 +1410,6 @@ unlock_dev_srcu:
|
||||
srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
|
||||
unlock_usr_srcu:
|
||||
srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
|
||||
free_exec:
|
||||
kfree(exec);
|
||||
return ret;
|
||||
}
|
||||
@@ -1749,7 +1762,8 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file
|
||||
struct qaic_device *qdev;
|
||||
struct qaic_user *usr;
|
||||
struct qaic_bo *bo;
|
||||
int ret, i;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
usr = file_priv->driver_priv;
|
||||
usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
|
||||
@@ -1770,18 +1784,12 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file
|
||||
goto unlock_dev_srcu;
|
||||
}
|
||||
|
||||
ent = kcalloc(args->hdr.count, sizeof(*ent), GFP_KERNEL);
|
||||
if (!ent) {
|
||||
ret = -EINVAL;
|
||||
ent = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, sizeof(*ent));
|
||||
if (IS_ERR(ent)) {
|
||||
ret = PTR_ERR(ent);
|
||||
goto unlock_dev_srcu;
|
||||
}
|
||||
|
||||
ret = copy_from_user(ent, u64_to_user_ptr(args->data), args->hdr.count * sizeof(*ent));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto free_ent;
|
||||
}
|
||||
|
||||
for (i = 0; i < args->hdr.count; i++) {
|
||||
obj = drm_gem_object_lookup(file_priv, ent[i].handle);
|
||||
if (!obj) {
|
||||
@@ -1789,6 +1797,16 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file
|
||||
goto free_ent;
|
||||
}
|
||||
bo = to_qaic_bo(obj);
|
||||
if (!bo->sliced) {
|
||||
drm_gem_object_put(obj);
|
||||
ret = -EINVAL;
|
||||
goto free_ent;
|
||||
}
|
||||
if (bo->dbc->id != args->hdr.dbc_id) {
|
||||
drm_gem_object_put(obj);
|
||||
ret = -EINVAL;
|
||||
goto free_ent;
|
||||
}
|
||||
/*
|
||||
* perf stats ioctl is called before wait ioctl is complete then
|
||||
* the latency information is invalid.
|
||||
|
||||
@@ -514,21 +514,21 @@ static ssize_t ce_count_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ce_count);
|
||||
return sysfs_emit(buf, "%d\n", qdev->ce_count);
|
||||
}
|
||||
|
||||
static ssize_t ue_count_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_count);
|
||||
return sysfs_emit(buf, "%d\n", qdev->ue_count);
|
||||
}
|
||||
|
||||
static ssize_t ue_nonfatal_count_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_nf_count);
|
||||
return sysfs_emit(buf, "%d\n", qdev->ue_nf_count);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(ce_count);
|
||||
|
||||
@@ -159,6 +159,7 @@ struct sahara_context {
|
||||
struct sahara_packet *rx;
|
||||
struct work_struct fw_work;
|
||||
struct work_struct dump_work;
|
||||
struct work_struct read_data_work;
|
||||
struct mhi_device *mhi_dev;
|
||||
const char * const *image_table;
|
||||
u32 table_size;
|
||||
@@ -174,7 +175,10 @@ struct sahara_context {
|
||||
u64 dump_image_offset;
|
||||
void *mem_dump_freespace;
|
||||
u64 dump_images_left;
|
||||
u32 read_data_offset;
|
||||
u32 read_data_length;
|
||||
bool is_mem_dump_mode;
|
||||
bool non_streaming;
|
||||
};
|
||||
|
||||
static const char * const aic100_image_table[] = {
|
||||
@@ -216,6 +220,11 @@ static const char * const aic200_image_table[] = {
|
||||
[75] = "qcom/aic200/pvs.bin",
|
||||
};
|
||||
|
||||
static bool is_streaming(struct sahara_context *context)
|
||||
{
|
||||
return !context->non_streaming;
|
||||
}
|
||||
|
||||
static int sahara_find_image(struct sahara_context *context, u32 image_id)
|
||||
{
|
||||
int ret;
|
||||
@@ -265,6 +274,8 @@ static void sahara_send_reset(struct sahara_context *context)
|
||||
int ret;
|
||||
|
||||
context->is_mem_dump_mode = false;
|
||||
context->read_data_offset = 0;
|
||||
context->read_data_length = 0;
|
||||
|
||||
context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
|
||||
context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
|
||||
@@ -319,9 +330,39 @@ static void sahara_hello(struct sahara_context *context)
|
||||
dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret);
|
||||
}
|
||||
|
||||
static int read_data_helper(struct sahara_context *context, int buf_index)
|
||||
{
|
||||
enum mhi_flags mhi_flag;
|
||||
u32 pkt_data_len;
|
||||
int ret;
|
||||
|
||||
pkt_data_len = min(context->read_data_length, SAHARA_PACKET_MAX_SIZE);
|
||||
|
||||
memcpy(context->tx[buf_index],
|
||||
&context->firmware->data[context->read_data_offset],
|
||||
pkt_data_len);
|
||||
|
||||
context->read_data_offset += pkt_data_len;
|
||||
context->read_data_length -= pkt_data_len;
|
||||
|
||||
if (is_streaming(context) || !context->read_data_length)
|
||||
mhi_flag = MHI_EOT;
|
||||
else
|
||||
mhi_flag = MHI_CHAIN;
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
|
||||
context->tx[buf_index], pkt_data_len, mhi_flag);
|
||||
if (ret) {
|
||||
dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sahara_read_data(struct sahara_context *context)
|
||||
{
|
||||
u32 image_id, data_offset, data_len, pkt_data_len;
|
||||
u32 image_id, data_offset, data_len;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@@ -357,7 +398,7 @@ static void sahara_read_data(struct sahara_context *context)
|
||||
* and is not needed here on error.
|
||||
*/
|
||||
|
||||
if (data_len > SAHARA_TRANSFER_MAX_SIZE) {
|
||||
if (context->non_streaming && data_len > SAHARA_TRANSFER_MAX_SIZE) {
|
||||
dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data len %d exceeds max xfer size %d\n",
|
||||
data_len, SAHARA_TRANSFER_MAX_SIZE);
|
||||
sahara_send_reset(context);
|
||||
@@ -378,22 +419,18 @@ static void sahara_read_data(struct sahara_context *context)
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < SAHARA_NUM_TX_BUF && data_len; ++i) {
|
||||
pkt_data_len = min(data_len, SAHARA_PACKET_MAX_SIZE);
|
||||
context->read_data_offset = data_offset;
|
||||
context->read_data_length = data_len;
|
||||
|
||||
memcpy(context->tx[i], &context->firmware->data[data_offset], pkt_data_len);
|
||||
if (is_streaming(context)) {
|
||||
schedule_work(&context->read_data_work);
|
||||
return;
|
||||
}
|
||||
|
||||
data_offset += pkt_data_len;
|
||||
data_len -= pkt_data_len;
|
||||
|
||||
ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
|
||||
context->tx[i], pkt_data_len,
|
||||
!data_len ? MHI_EOT : MHI_CHAIN);
|
||||
if (ret) {
|
||||
dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < SAHARA_NUM_TX_BUF && context->read_data_length; ++i) {
|
||||
ret = read_data_helper(context, i);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,6 +575,7 @@ static void sahara_parse_dump_table(struct sahara_context *context)
|
||||
struct sahara_memory_dump_meta_v1 *dump_meta;
|
||||
u64 table_nents;
|
||||
u64 dump_length;
|
||||
u64 mul_bytes;
|
||||
int ret;
|
||||
u64 i;
|
||||
|
||||
@@ -551,8 +589,9 @@ static void sahara_parse_dump_table(struct sahara_context *context)
|
||||
dev_table[i].description[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
|
||||
dev_table[i].filename[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
|
||||
|
||||
dump_length = size_add(dump_length, le64_to_cpu(dev_table[i].length));
|
||||
if (dump_length == SIZE_MAX) {
|
||||
if (check_add_overflow(dump_length,
|
||||
le64_to_cpu(dev_table[i].length),
|
||||
&dump_length)) {
|
||||
/* Discard the dump */
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
@@ -568,14 +607,17 @@ static void sahara_parse_dump_table(struct sahara_context *context)
|
||||
dev_table[i].filename);
|
||||
}
|
||||
|
||||
dump_length = size_add(dump_length, sizeof(*dump_meta));
|
||||
if (dump_length == SIZE_MAX) {
|
||||
if (check_add_overflow(dump_length, (u64)sizeof(*dump_meta), &dump_length)) {
|
||||
/* Discard the dump */
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
}
|
||||
dump_length = size_add(dump_length, size_mul(sizeof(*image_out_table), table_nents));
|
||||
if (dump_length == SIZE_MAX) {
|
||||
if (check_mul_overflow((u64)sizeof(*image_out_table), table_nents, &mul_bytes)) {
|
||||
/* Discard the dump */
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
}
|
||||
if (check_add_overflow(dump_length, mul_bytes, &dump_length)) {
|
||||
/* Discard the dump */
|
||||
sahara_send_reset(context);
|
||||
return;
|
||||
@@ -615,7 +657,7 @@ static void sahara_parse_dump_table(struct sahara_context *context)
|
||||
|
||||
/* Request the first chunk of the first image */
|
||||
context->dump_image = &image_out_table[0];
|
||||
dump_length = min(context->dump_image->length, SAHARA_READ_MAX_SIZE);
|
||||
dump_length = min_t(u64, context->dump_image->length, SAHARA_READ_MAX_SIZE);
|
||||
/* Avoid requesting EOI sized data so that we can identify errors */
|
||||
if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
|
||||
dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
|
||||
@@ -663,7 +705,7 @@ static void sahara_parse_dump_image(struct sahara_context *context)
|
||||
|
||||
/* Get next image chunk */
|
||||
dump_length = context->dump_image->length - context->dump_image_offset;
|
||||
dump_length = min(dump_length, SAHARA_READ_MAX_SIZE);
|
||||
dump_length = min_t(u64, dump_length, SAHARA_READ_MAX_SIZE);
|
||||
/* Avoid requesting EOI sized data so that we can identify errors */
|
||||
if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
|
||||
dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
|
||||
@@ -742,6 +784,13 @@ error:
|
||||
sahara_send_reset(context);
|
||||
}
|
||||
|
||||
static void sahara_read_data_processing(struct work_struct *work)
|
||||
{
|
||||
struct sahara_context *context = container_of(work, struct sahara_context, read_data_work);
|
||||
|
||||
read_data_helper(context, 0);
|
||||
}
|
||||
|
||||
static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
|
||||
{
|
||||
struct sahara_context *context;
|
||||
@@ -756,35 +805,57 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
|
||||
if (!context->rx)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* AIC100 defines SAHARA_TRANSFER_MAX_SIZE as the largest value it
|
||||
* will request for READ_DATA. This is larger than
|
||||
* SAHARA_PACKET_MAX_SIZE, and we need 9x SAHARA_PACKET_MAX_SIZE to
|
||||
* cover SAHARA_TRANSFER_MAX_SIZE. When the remote side issues a
|
||||
* READ_DATA, it requires a transfer of the exact size requested. We
|
||||
* can use MHI_CHAIN to link multiple buffers into a single transfer
|
||||
* but the remote side will not consume the buffers until it sees an
|
||||
* EOT, thus we need to allocate enough buffers to put in the tx fifo
|
||||
* to cover an entire READ_DATA request of the max size.
|
||||
*/
|
||||
for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
|
||||
context->tx[i] = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
|
||||
if (!context->tx[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
context->mhi_dev = mhi_dev;
|
||||
INIT_WORK(&context->fw_work, sahara_processing);
|
||||
INIT_WORK(&context->dump_work, sahara_dump_processing);
|
||||
|
||||
if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
|
||||
context->image_table = aic200_image_table;
|
||||
context->table_size = ARRAY_SIZE(aic200_image_table);
|
||||
} else {
|
||||
context->image_table = aic100_image_table;
|
||||
context->table_size = ARRAY_SIZE(aic100_image_table);
|
||||
context->non_streaming = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are two firmware implementations for READ_DATA handling.
|
||||
* The older "SBL" implementation defines a Sahara transfer size, and
|
||||
* expects that the response is a single transport transfer. If the
|
||||
* FW wants to transfer a file that is larger than the transfer size,
|
||||
* the FW will issue multiple READ_DATA commands. For this
|
||||
* implementation, we need to allocate enough buffers to contain the
|
||||
* entire Sahara transfer size.
|
||||
*
|
||||
* The newer "XBL" implementation does not define a maximum transfer
|
||||
* size and instead expects the data to be streamed over using the
|
||||
* transport level MTU. The FW will issue a single READ_DATA command
|
||||
* of whatever size, and consume multiple transport level transfers
|
||||
* until the expected amount of data is consumed. For this
|
||||
* implementation we only need a single buffer of the transport MTU
|
||||
* but we'll need to be able to use it multiple times for a single
|
||||
* READ_DATA request.
|
||||
*
|
||||
* AIC100 is the SBL implementation and defines SAHARA_TRANSFER_MAX_SIZE
|
||||
* and we need 9x SAHARA_PACKET_MAX_SIZE to cover that. We can use
|
||||
* MHI_CHAIN to link multiple buffers into a single transfer but the
|
||||
* remote side will not consume the buffers until it sees an EOT, thus
|
||||
* we need to allocate enough buffers to put in the tx fifo to cover an
|
||||
* entire READ_DATA request of the max size.
|
||||
*
|
||||
* AIC200 is the XBL implementation, and so a single buffer will work.
|
||||
*/
|
||||
for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
|
||||
context->tx[i] = devm_kzalloc(&mhi_dev->dev,
|
||||
SAHARA_PACKET_MAX_SIZE,
|
||||
GFP_KERNEL);
|
||||
if (!context->tx[i])
|
||||
return -ENOMEM;
|
||||
if (is_streaming(context))
|
||||
break;
|
||||
}
|
||||
|
||||
context->mhi_dev = mhi_dev;
|
||||
INIT_WORK(&context->fw_work, sahara_processing);
|
||||
INIT_WORK(&context->dump_work, sahara_dump_processing);
|
||||
INIT_WORK(&context->read_data_work, sahara_read_data_processing);
|
||||
|
||||
context->active_image_id = SAHARA_IMAGE_ID_NONE;
|
||||
dev_set_drvdata(&mhi_dev->dev, context);
|
||||
|
||||
@@ -814,6 +885,10 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev)
|
||||
|
||||
static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
|
||||
{
|
||||
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
if (!mhi_result->transaction_status && context->read_data_length && is_streaming(context))
|
||||
schedule_work(&context->read_data_work);
|
||||
}
|
||||
|
||||
static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
|
||||
|
||||
@@ -12,13 +12,3 @@ config DMABUF_HEAPS_CMA
|
||||
Choose this option to enable dma-buf CMA heap. This heap is backed
|
||||
by the Contiguous Memory Allocator (CMA). If your system has these
|
||||
regions, you should say Y here.
|
||||
|
||||
config DMABUF_HEAPS_CMA_LEGACY
|
||||
bool "Legacy DMA-BUF CMA Heap"
|
||||
default y
|
||||
depends on DMABUF_HEAPS_CMA
|
||||
help
|
||||
Add a duplicate CMA-backed dma-buf heap with legacy naming derived
|
||||
from the CMA area's devicetree node, or "reserved" if the area is not
|
||||
defined in the devicetree. This uses the same underlying allocator as
|
||||
CONFIG_DMABUF_HEAPS_CMA.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <linux/cma.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-buf/heaps/cma.h>
|
||||
#include <linux/dma-heap.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/err.h>
|
||||
@@ -21,12 +22,27 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#define DEFAULT_CMA_NAME "default_cma_region"
|
||||
|
||||
static struct cma *dma_areas[MAX_CMA_AREAS] __initdata;
|
||||
static unsigned int dma_areas_num __initdata;
|
||||
|
||||
int __init dma_heap_cma_register_heap(struct cma *cma)
|
||||
{
|
||||
if (dma_areas_num >= ARRAY_SIZE(dma_areas))
|
||||
return -EINVAL;
|
||||
|
||||
dma_areas[dma_areas_num++] = cma;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cma_heap {
|
||||
struct dma_heap *heap;
|
||||
struct cma *cma;
|
||||
@@ -395,33 +411,30 @@ static int __init __add_cma_heap(struct cma *cma, const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init add_default_cma_heap(void)
|
||||
static int __init add_cma_heaps(void)
|
||||
{
|
||||
struct cma *default_cma = dev_get_cma_area(NULL);
|
||||
const char *legacy_cma_name;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!default_cma)
|
||||
return 0;
|
||||
if (default_cma) {
|
||||
ret = __add_cma_heap(default_cma, DEFAULT_CMA_NAME);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = __add_cma_heap(default_cma, DEFAULT_CMA_NAME);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (i = 0; i < dma_areas_num; i++) {
|
||||
struct cma *cma = dma_areas[i];
|
||||
|
||||
if (IS_ENABLED(CONFIG_DMABUF_HEAPS_CMA_LEGACY)) {
|
||||
legacy_cma_name = cma_get_name(default_cma);
|
||||
if (!strcmp(legacy_cma_name, DEFAULT_CMA_NAME)) {
|
||||
pr_warn("legacy name and default name are the same, skipping legacy heap\n");
|
||||
return 0;
|
||||
ret = __add_cma_heap(cma, cma_get_name(cma));
|
||||
if (ret) {
|
||||
pr_warn("Failed to add CMA heap %s", cma_get_name(cma));
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = __add_cma_heap(default_cma, legacy_cma_name);
|
||||
if (ret)
|
||||
pr_warn("failed to add legacy heap: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(add_default_cma_heap);
|
||||
module_init(add_cma_heaps);
|
||||
MODULE_DESCRIPTION("DMA-BUF CMA Heap");
|
||||
|
||||
@@ -5219,7 +5219,7 @@ int amdgpu_device_suspend(struct drm_device *dev, bool notify_clients)
|
||||
dev_warn(adev->dev, "smart shift update failed\n");
|
||||
|
||||
if (notify_clients)
|
||||
drm_client_dev_suspend(adev_to_drm(adev), false);
|
||||
drm_client_dev_suspend(adev_to_drm(adev));
|
||||
|
||||
cancel_delayed_work_sync(&adev->delayed_init_work);
|
||||
|
||||
@@ -5353,7 +5353,7 @@ exit:
|
||||
flush_delayed_work(&adev->delayed_init_work);
|
||||
|
||||
if (notify_clients)
|
||||
drm_client_dev_resume(adev_to_drm(adev), false);
|
||||
drm_client_dev_resume(adev_to_drm(adev));
|
||||
|
||||
amdgpu_ras_resume(adev);
|
||||
|
||||
@@ -5958,7 +5958,7 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context)
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
drm_client_dev_resume(adev_to_drm(tmp_adev), false);
|
||||
drm_client_dev_resume(adev_to_drm(tmp_adev));
|
||||
|
||||
/*
|
||||
* The GPU enters bad state once faulty pages
|
||||
@@ -6293,7 +6293,7 @@ static void amdgpu_device_halt_activities(struct amdgpu_device *adev,
|
||||
*/
|
||||
amdgpu_unregister_gpu_instance(tmp_adev);
|
||||
|
||||
drm_client_dev_suspend(adev_to_drm(tmp_adev), false);
|
||||
drm_client_dev_suspend(adev_to_drm(tmp_adev));
|
||||
|
||||
/* disable ras on ALL IPs */
|
||||
if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) &&
|
||||
|
||||
@@ -12446,7 +12446,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
int j = state->num_private_objs-1;
|
||||
|
||||
dm_atomic_destroy_state(obj,
|
||||
state->private_objs[i].state);
|
||||
state->private_objs[i].state_to_destroy);
|
||||
|
||||
/* If i is not at the end of the array then the
|
||||
* last element needs to be moved to where i was
|
||||
@@ -12457,7 +12457,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
state->private_objs[j];
|
||||
|
||||
state->private_objs[j].ptr = NULL;
|
||||
state->private_objs[j].state = NULL;
|
||||
state->private_objs[j].state_to_destroy = NULL;
|
||||
state->private_objs[j].old_state = NULL;
|
||||
state->private_objs[j].new_state = NULL;
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
static int
|
||||
komeda_crtc_prepare(struct komeda_crtc *kcrtc)
|
||||
{
|
||||
struct drm_device *drm = kcrtc->base.dev;
|
||||
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(kcrtc->base.state);
|
||||
@@ -128,8 +129,8 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc)
|
||||
|
||||
err = mdev->funcs->change_opmode(mdev, new_mode);
|
||||
if (err) {
|
||||
DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
|
||||
mdev->dpmode, new_mode);
|
||||
drm_err(drm, "failed to change opmode: 0x%x -> 0x%x.\n,",
|
||||
mdev->dpmode, new_mode);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -142,18 +143,18 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc)
|
||||
if (new_mode != KOMEDA_MODE_DUAL_DISP) {
|
||||
err = clk_set_rate(mdev->aclk, komeda_crtc_get_aclk(kcrtc_st));
|
||||
if (err)
|
||||
DRM_ERROR("failed to set aclk.\n");
|
||||
drm_err(drm, "failed to set aclk.\n");
|
||||
err = clk_prepare_enable(mdev->aclk);
|
||||
if (err)
|
||||
DRM_ERROR("failed to enable aclk.\n");
|
||||
drm_err(drm, "failed to enable aclk.\n");
|
||||
}
|
||||
|
||||
err = clk_set_rate(master->pxlclk, mode->crtc_clock * 1000);
|
||||
if (err)
|
||||
DRM_ERROR("failed to set pxlclk for pipe%d\n", master->id);
|
||||
drm_err(drm, "failed to set pxlclk for pipe%d\n", master->id);
|
||||
err = clk_prepare_enable(master->pxlclk);
|
||||
if (err)
|
||||
DRM_ERROR("failed to enable pxl clk for pipe%d.\n", master->id);
|
||||
drm_err(drm, "failed to enable pxl clk for pipe%d.\n", master->id);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&mdev->lock);
|
||||
@@ -164,6 +165,7 @@ unlock:
|
||||
static int
|
||||
komeda_crtc_unprepare(struct komeda_crtc *kcrtc)
|
||||
{
|
||||
struct drm_device *drm = kcrtc->base.dev;
|
||||
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
|
||||
struct komeda_pipeline *master = kcrtc->master;
|
||||
u32 new_mode;
|
||||
@@ -180,8 +182,8 @@ komeda_crtc_unprepare(struct komeda_crtc *kcrtc)
|
||||
|
||||
err = mdev->funcs->change_opmode(mdev, new_mode);
|
||||
if (err) {
|
||||
DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
|
||||
mdev->dpmode, new_mode);
|
||||
drm_err(drm, "failed to change opmode: 0x%x -> 0x%x.\n,",
|
||||
mdev->dpmode, new_mode);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -200,6 +202,7 @@ unlock:
|
||||
void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
|
||||
struct komeda_events *evts)
|
||||
{
|
||||
struct drm_device *drm = kcrtc->base.dev;
|
||||
struct drm_crtc *crtc = &kcrtc->base;
|
||||
u32 events = evts->pipes[kcrtc->master->id];
|
||||
|
||||
@@ -212,7 +215,7 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
|
||||
if (wb_conn)
|
||||
drm_writeback_signal_completion(&wb_conn->base, 0);
|
||||
else
|
||||
DRM_WARN("CRTC[%d]: EOW happen but no wb_connector.\n",
|
||||
drm_warn(drm, "CRTC[%d]: EOW happen but no wb_connector.\n",
|
||||
drm_crtc_index(&kcrtc->base));
|
||||
}
|
||||
/* will handle it together with the write back support */
|
||||
@@ -236,7 +239,7 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
|
||||
crtc->state->event = NULL;
|
||||
drm_crtc_send_vblank_event(crtc, event);
|
||||
} else {
|
||||
DRM_WARN("CRTC[%d]: FLIP happened but no pending commit.\n",
|
||||
drm_warn(drm, "CRTC[%d]: FLIP happened but no pending commit.\n",
|
||||
drm_crtc_index(&kcrtc->base));
|
||||
}
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
@@ -309,7 +312,7 @@ komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc,
|
||||
|
||||
/* wait the flip take affect.*/
|
||||
if (wait_for_completion_timeout(flip_done, HZ) == 0) {
|
||||
DRM_ERROR("wait pipe%d flip done timeout\n", kcrtc->master->id);
|
||||
drm_err(drm, "wait pipe%d flip done timeout\n", kcrtc->master->id);
|
||||
if (!input_flip_done) {
|
||||
unsigned long flags;
|
||||
|
||||
@@ -562,6 +565,7 @@ static const struct drm_crtc_funcs komeda_crtc_funcs = {
|
||||
int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
|
||||
struct komeda_dev *mdev)
|
||||
{
|
||||
struct drm_device *drm = &kms->base;
|
||||
struct komeda_crtc *crtc;
|
||||
struct komeda_pipeline *master;
|
||||
char str[16];
|
||||
@@ -581,7 +585,7 @@ int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
|
||||
else
|
||||
sprintf(str, "None");
|
||||
|
||||
DRM_INFO("CRTC-%d: master(pipe-%d) slave(%s).\n",
|
||||
drm_info(drm, "CRTC-%d: master(pipe-%d) slave(%s).\n",
|
||||
kms->n_crtcs, master->id, str);
|
||||
|
||||
kms->n_crtcs++;
|
||||
@@ -613,6 +617,7 @@ static int komeda_attach_bridge(struct device *dev,
|
||||
struct komeda_pipeline *pipe,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct drm_bridge *bridge;
|
||||
int err;
|
||||
|
||||
@@ -624,7 +629,7 @@ static int komeda_attach_bridge(struct device *dev,
|
||||
|
||||
err = drm_bridge_attach(encoder, bridge, NULL, 0);
|
||||
if (err)
|
||||
dev_err(dev, "bridge_attach() failed for pipe: %s\n",
|
||||
drm_err(drm, "bridge_attach() failed for pipe: %s\n",
|
||||
of_node_full_name(pipe->of_node));
|
||||
|
||||
return err;
|
||||
|
||||
@@ -263,7 +263,7 @@ static int malidp_se_check_scaling(struct malidp_plane *mp,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state =
|
||||
drm_atomic_get_existing_crtc_state(state->state, state->crtc);
|
||||
drm_atomic_get_new_crtc_state(state->state, state->crtc);
|
||||
struct malidp_crtc_state *mc;
|
||||
u32 src_w, src_h;
|
||||
int ret;
|
||||
|
||||
@@ -94,12 +94,7 @@ int armada_drm_plane_atomic_check(struct drm_plane *plane,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state)
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state,
|
||||
crtc);
|
||||
else
|
||||
crtc_state = crtc->state;
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
|
||||
0,
|
||||
INT_MAX, true, false);
|
||||
|
||||
@@ -211,6 +211,11 @@ void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post)
|
||||
__ast_device_set_tx_chip(ast, tx_chip);
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2000_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 31,
|
||||
.crtc_mem_req_threshold_high = 47,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2000_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -228,7 +233,9 @@ struct drm_device *ast_2000_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2000_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2000_dclk_table;
|
||||
|
||||
ast_2000_detect_tx_chip(ast, need_post);
|
||||
|
||||
|
||||
@@ -432,6 +432,11 @@ static void ast_2100_detect_widescreen(struct ast_device *ast)
|
||||
ast->support_wuxga = true;
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2100_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 47,
|
||||
.crtc_mem_req_threshold_high = 63,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -449,7 +454,9 @@ struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2100_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2000_dclk_table;
|
||||
|
||||
ast_2000_detect_tx_chip(ast, need_post);
|
||||
|
||||
|
||||
@@ -43,6 +43,11 @@ static void ast_2200_detect_widescreen(struct ast_device *ast)
|
||||
ast->support_wuxga = true;
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2200_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 47,
|
||||
.crtc_mem_req_threshold_high = 63,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2200_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -60,7 +65,9 @@ struct drm_device *ast_2200_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2200_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2000_dclk_table;
|
||||
|
||||
ast_2000_detect_tx_chip(ast, need_post);
|
||||
|
||||
|
||||
@@ -1407,6 +1407,11 @@ static void ast_2300_detect_widescreen(struct ast_device *ast)
|
||||
ast->support_wuxga = true;
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2300_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 96,
|
||||
.crtc_mem_req_threshold_high = 120,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2300_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -1424,7 +1429,9 @@ struct drm_device *ast_2300_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2300_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2000_dclk_table;
|
||||
|
||||
ast_2300_detect_tx_chip(ast);
|
||||
|
||||
|
||||
@@ -44,6 +44,11 @@ static void ast_2400_detect_widescreen(struct ast_device *ast)
|
||||
ast->support_wuxga = true;
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2400_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 96,
|
||||
.crtc_mem_req_threshold_high = 120,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2400_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -61,7 +66,9 @@ struct drm_device *ast_2400_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2400_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2000_dclk_table;
|
||||
|
||||
ast_2300_detect_tx_chip(ast);
|
||||
|
||||
|
||||
@@ -618,6 +618,12 @@ static void ast_2500_detect_widescreen(struct ast_device *ast)
|
||||
ast->support_wuxga = true;
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2500_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 96,
|
||||
.crtc_mem_req_threshold_high = 120,
|
||||
.crtc_hsync_precatch_needed = true,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2500_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -635,7 +641,9 @@ struct drm_device *ast_2500_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2500_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2500_dclk_table;
|
||||
|
||||
ast_2300_detect_tx_chip(ast);
|
||||
|
||||
|
||||
@@ -59,6 +59,13 @@ static void ast_2600_detect_widescreen(struct ast_device *ast)
|
||||
ast->support_wuxga = true;
|
||||
}
|
||||
|
||||
static const struct ast_device_quirks ast_2600_device_quirks = {
|
||||
.crtc_mem_req_threshold_low = 160,
|
||||
.crtc_mem_req_threshold_high = 224,
|
||||
.crtc_hsync_precatch_needed = true,
|
||||
.crtc_hsync_add4_needed = true,
|
||||
};
|
||||
|
||||
struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
|
||||
const struct drm_driver *drv,
|
||||
enum ast_chip chip,
|
||||
@@ -76,7 +83,9 @@ struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
|
||||
return ERR_CAST(ast);
|
||||
dev = &ast->base;
|
||||
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs);
|
||||
ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2600_device_quirks);
|
||||
|
||||
ast->dclk_table = ast_2500_dclk_table;
|
||||
|
||||
ast_2300_detect_tx_chip(ast);
|
||||
|
||||
|
||||
@@ -51,8 +51,10 @@ void ast_device_init(struct ast_device *ast,
|
||||
enum ast_chip chip,
|
||||
enum ast_config_mode config_mode,
|
||||
void __iomem *regs,
|
||||
void __iomem *ioregs)
|
||||
void __iomem *ioregs,
|
||||
const struct ast_device_quirks *quirks)
|
||||
{
|
||||
ast->quirks = quirks;
|
||||
ast->chip = chip;
|
||||
ast->config_mode = config_mode;
|
||||
ast->regs = regs;
|
||||
|
||||
@@ -164,9 +164,31 @@ to_ast_connector(struct drm_connector *connector)
|
||||
* Device
|
||||
*/
|
||||
|
||||
struct ast_device_quirks {
|
||||
/*
|
||||
* CRTC memory request threshold
|
||||
*/
|
||||
unsigned char crtc_mem_req_threshold_low;
|
||||
unsigned char crtc_mem_req_threshold_high;
|
||||
|
||||
/*
|
||||
* Adjust hsync values to load next scanline early. Signalled
|
||||
* by AST2500PreCatchCRT in VBIOS mode flags.
|
||||
*/
|
||||
bool crtc_hsync_precatch_needed;
|
||||
|
||||
/*
|
||||
* Workaround for modes with HSync Time that is not a multiple
|
||||
* of 8 (e.g., 1920x1080@60Hz, HSync +44 pixels).
|
||||
*/
|
||||
bool crtc_hsync_add4_needed;
|
||||
};
|
||||
|
||||
struct ast_device {
|
||||
struct drm_device base;
|
||||
|
||||
const struct ast_device_quirks *quirks;
|
||||
|
||||
void __iomem *regs;
|
||||
void __iomem *ioregs;
|
||||
void __iomem *dp501_fw_buf;
|
||||
@@ -174,6 +196,8 @@ struct ast_device {
|
||||
enum ast_config_mode config_mode;
|
||||
enum ast_chip chip;
|
||||
|
||||
const struct ast_vbios_dclk_info *dclk_table;
|
||||
|
||||
void __iomem *vram;
|
||||
unsigned long vram_base;
|
||||
unsigned long vram_size;
|
||||
@@ -412,7 +436,8 @@ void ast_device_init(struct ast_device *ast,
|
||||
enum ast_chip chip,
|
||||
enum ast_config_mode config_mode,
|
||||
void __iomem *regs,
|
||||
void __iomem *ioregs);
|
||||
void __iomem *ioregs,
|
||||
const struct ast_device_quirks *quirks);
|
||||
void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip);
|
||||
|
||||
/* ast_2000.c */
|
||||
|
||||
@@ -241,16 +241,15 @@ static void ast_set_std_reg(struct ast_device *ast,
|
||||
ast_set_index_reg(ast, AST_IO_VGAGRI, i, stdtable->gr[i]);
|
||||
}
|
||||
|
||||
static void ast_set_crtc_reg(struct ast_device *ast,
|
||||
struct drm_display_mode *mode,
|
||||
static void ast_set_crtc_reg(struct ast_device *ast, struct drm_display_mode *mode,
|
||||
const struct ast_vbios_enhtable *vmode)
|
||||
{
|
||||
u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0;
|
||||
u16 temp, precache = 0;
|
||||
u16 temp;
|
||||
unsigned char crtc_hsync_precatch = 0;
|
||||
|
||||
if ((IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) &&
|
||||
(vmode->flags & AST2500PreCatchCRT))
|
||||
precache = 40;
|
||||
if (ast->quirks->crtc_hsync_precatch_needed && (vmode->flags & AST2500PreCatchCRT))
|
||||
crtc_hsync_precatch = 40;
|
||||
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00);
|
||||
|
||||
@@ -276,12 +275,12 @@ static void ast_set_crtc_reg(struct ast_device *ast,
|
||||
jregAD |= 0x01; /* HBE D[5] */
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x03, 0xE0, (temp & 0x1f));
|
||||
|
||||
temp = ((mode->crtc_hsync_start-precache) >> 3) - 1;
|
||||
temp = ((mode->crtc_hsync_start - crtc_hsync_precatch) >> 3) - 1;
|
||||
if (temp & 0x100)
|
||||
jregAC |= 0x40; /* HRS D[5] */
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x04, 0x00, temp);
|
||||
|
||||
temp = (((mode->crtc_hsync_end-precache) >> 3) - 1) & 0x3f;
|
||||
temp = (((mode->crtc_hsync_end - crtc_hsync_precatch) >> 3) - 1) & 0x3f;
|
||||
if (temp & 0x20)
|
||||
jregAD |= 0x04; /* HRE D[5] */
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05));
|
||||
@@ -289,8 +288,7 @@ static void ast_set_crtc_reg(struct ast_device *ast,
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAC, 0x00, jregAC);
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAD, 0x00, jregAD);
|
||||
|
||||
// Workaround for HSync Time non octave pixels (1920x1080@60Hz HSync 44 pixels);
|
||||
if (IS_AST_GEN7(ast) && (mode->crtc_vdisplay == 1080))
|
||||
if (ast->quirks->crtc_hsync_add4_needed && mode->crtc_vdisplay == 1080)
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x02);
|
||||
else
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x00);
|
||||
@@ -348,7 +346,7 @@ static void ast_set_crtc_reg(struct ast_device *ast,
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x09, 0xdf, jreg09);
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAE, 0x00, (jregAE | 0x80));
|
||||
|
||||
if (precache)
|
||||
if (crtc_hsync_precatch)
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x80);
|
||||
else
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x00);
|
||||
@@ -370,12 +368,7 @@ static void ast_set_dclk_reg(struct ast_device *ast,
|
||||
struct drm_display_mode *mode,
|
||||
const struct ast_vbios_enhtable *vmode)
|
||||
{
|
||||
const struct ast_vbios_dclk_info *clk_info;
|
||||
|
||||
if (IS_AST_GEN6(ast) || IS_AST_GEN7(ast))
|
||||
clk_info = &ast_2500_dclk_table[vmode->dclk_index];
|
||||
else
|
||||
clk_info = &ast_2000_dclk_table[vmode->dclk_index];
|
||||
const struct ast_vbios_dclk_info *clk_info = &ast->dclk_table[vmode->dclk_index];
|
||||
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc0, 0x00, clk_info->param1);
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc1, 0x00, clk_info->param2);
|
||||
@@ -415,20 +408,11 @@ static void ast_set_color_reg(struct ast_device *ast,
|
||||
|
||||
static void ast_set_crtthd_reg(struct ast_device *ast)
|
||||
{
|
||||
/* Set Threshold */
|
||||
if (IS_AST_GEN7(ast)) {
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0xe0);
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0xa0);
|
||||
} else if (IS_AST_GEN6(ast) || IS_AST_GEN5(ast) || IS_AST_GEN4(ast)) {
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x78);
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x60);
|
||||
} else if (IS_AST_GEN3(ast) || IS_AST_GEN2(ast)) {
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x3f);
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x2f);
|
||||
} else {
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x2f);
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x1f);
|
||||
}
|
||||
u8 vgacra6 = ast->quirks->crtc_mem_req_threshold_low;
|
||||
u8 vgacra7 = ast->quirks->crtc_mem_req_threshold_high;
|
||||
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, vgacra7);
|
||||
ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, vgacra6);
|
||||
}
|
||||
|
||||
static void ast_set_sync_reg(struct ast_device *ast,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
@@ -215,32 +216,32 @@ static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
!(status & ATMEL_XLCDC_CM),
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register CMSTS timeout\n");
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_SD);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
status & ATMEL_XLCDC_SD,
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register SDSTS timeout\n");
|
||||
}
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
!(status & ATMEL_HLCDC_DISP),
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register DISPSTS timeout\n");
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
!(status & ATMEL_HLCDC_SYNC),
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register LCDSTS timeout\n");
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
!(status & ATMEL_HLCDC_PIXEL_CLK),
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n");
|
||||
|
||||
clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
|
||||
pinctrl_pm_select_sleep_state(dev->dev);
|
||||
@@ -269,32 +270,32 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
status & ATMEL_HLCDC_PIXEL_CLK,
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n");
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
status & ATMEL_HLCDC_SYNC,
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register LCDSTS timeout\n");
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
status & ATMEL_HLCDC_DISP,
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register DISPSTS timeout\n");
|
||||
|
||||
if (crtc->dc->desc->is_xlcdc) {
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_CM);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
status & ATMEL_XLCDC_CM,
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register CMSTS timeout\n");
|
||||
|
||||
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_SD);
|
||||
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
|
||||
!(status & ATMEL_XLCDC_SD),
|
||||
10, 1000))
|
||||
dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
|
||||
drm_warn(dev, "Atmel LCDC status register SDSTS timeout\n");
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
|
||||
@@ -724,19 +724,19 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
|
||||
|
||||
ret = atmel_hlcdc_create_outputs(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret);
|
||||
drm_err(dev, "failed to create HLCDC outputs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = atmel_hlcdc_create_planes(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to create planes: %d\n", ret);
|
||||
drm_err(dev, "failed to create planes: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = atmel_hlcdc_crtc_create(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to create crtc\n");
|
||||
drm_err(dev, "failed to create crtc\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -778,7 +778,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
|
||||
|
||||
ret = clk_prepare_enable(dc->hlcdc->periph_clk);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable periph_clk\n");
|
||||
drm_err(dev, "failed to enable periph_clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -786,13 +786,13 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
|
||||
|
||||
ret = drm_vblank_init(dev, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to initialize vblank\n");
|
||||
drm_err(dev, "failed to initialize vblank\n");
|
||||
goto err_periph_clk_disable;
|
||||
}
|
||||
|
||||
ret = atmel_hlcdc_dc_modeset_init(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to initialize mode setting\n");
|
||||
drm_err(dev, "failed to initialize mode setting\n");
|
||||
goto err_periph_clk_disable;
|
||||
}
|
||||
|
||||
@@ -802,7 +802,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
|
||||
ret = atmel_hlcdc_dc_irq_install(dev, dc->hlcdc->irq);
|
||||
pm_runtime_put_sync(dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "failed to install IRQ handler\n");
|
||||
drm_err(dev, "failed to install IRQ handler\n");
|
||||
goto err_periph_clk_disable;
|
||||
}
|
||||
|
||||
|
||||
@@ -378,7 +378,8 @@ struct atmel_lcdc_dc_ops {
|
||||
void (*lcdc_update_buffers)(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_state *state,
|
||||
u32 sr, int i);
|
||||
void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane);
|
||||
void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_dc *dc);
|
||||
void (*lcdc_update_general_settings)(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_plane_state *state);
|
||||
void (*lcdc_atomic_update)(struct atmel_hlcdc_plane *plane,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
#include "atmel_hlcdc_dc.h"
|
||||
@@ -92,7 +93,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
|
||||
output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
|
||||
of_node_put(ep);
|
||||
if (output->bus_fmt < 0) {
|
||||
dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);
|
||||
drm_err(dev, "endpoint %d: invalid bus width\n", endpoint);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -365,13 +365,34 @@ void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
|
||||
xfactor);
|
||||
|
||||
/*
|
||||
* With YCbCr 4:2:2 and YCbYcr 4:2:0 window resampling, configuration
|
||||
* register LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT is half
|
||||
* With YCbCr 4:2:0 window resampling, configuration register
|
||||
* LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT values are half
|
||||
* the value of yfactor and xfactor.
|
||||
*
|
||||
* On the other hand, with YCbCr 4:2:2 window resampling, only the
|
||||
* configuration register LCDC_HEOCFG27.HXSCFACT value is half the value
|
||||
* of the xfactor; the value of LCDC_HEOCFG25.VXSCFACT is yfactor (no
|
||||
* division by 2).
|
||||
*/
|
||||
if (state->base.fb->format->format == DRM_FORMAT_YUV420) {
|
||||
switch (state->base.fb->format->format) {
|
||||
/* YCbCr 4:2:2 */
|
||||
case DRM_FORMAT_YUYV:
|
||||
case DRM_FORMAT_UYVY:
|
||||
case DRM_FORMAT_YVYU:
|
||||
case DRM_FORMAT_VYUY:
|
||||
case DRM_FORMAT_YUV422:
|
||||
case DRM_FORMAT_NV61:
|
||||
xfactor /= 2;
|
||||
break;
|
||||
|
||||
/* YCbCr 4:2:0 */
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_NV21:
|
||||
yfactor /= 2;
|
||||
xfactor /= 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2,
|
||||
@@ -714,7 +735,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
|
||||
if (!hstate->base.crtc || WARN_ON(!fb))
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, s->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, s->crtc);
|
||||
mode = &crtc_state->adjusted_mode;
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(s, crtc_state,
|
||||
@@ -816,7 +837,8 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
|
||||
static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_dc *dc)
|
||||
{
|
||||
/* Disable interrupts */
|
||||
atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
|
||||
@@ -832,7 +854,8 @@ static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
|
||||
atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
|
||||
}
|
||||
|
||||
static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
|
||||
static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,
|
||||
struct atmel_hlcdc_dc *dc)
|
||||
{
|
||||
/* Disable interrupts */
|
||||
atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR,
|
||||
@@ -842,6 +865,15 @@ static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
|
||||
atmel_hlcdc_layer_write_reg(&plane->layer,
|
||||
ATMEL_XLCDC_LAYER_ENR, 0);
|
||||
|
||||
/*
|
||||
* Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,
|
||||
* (where xxx indicates each layer) requires writing one to the
|
||||
* Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.
|
||||
*/
|
||||
regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |
|
||||
ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |
|
||||
ATMEL_XLCDC_HEO_UPDATE);
|
||||
|
||||
/* Clear all pending interrupts */
|
||||
atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);
|
||||
}
|
||||
@@ -852,7 +884,7 @@ static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
|
||||
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
|
||||
struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
|
||||
|
||||
dc->desc->ops->lcdc_atomic_disable(plane);
|
||||
dc->desc->ops->lcdc_atomic_disable(plane, dc);
|
||||
}
|
||||
|
||||
static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane,
|
||||
@@ -1034,7 +1066,7 @@ static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,
|
||||
if (isr &
|
||||
(ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
|
||||
ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
|
||||
dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
|
||||
drm_dbg(plane->base.dev, "overrun on plane %s\n",
|
||||
desc->name);
|
||||
}
|
||||
|
||||
@@ -1051,7 +1083,7 @@ static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,
|
||||
if (isr &
|
||||
(ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) |
|
||||
ATMEL_XLCDC_LAYER_OVR_IRQ(2)))
|
||||
dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
|
||||
drm_dbg(plane->base.dev, "overrun on plane %s\n",
|
||||
desc->name);
|
||||
}
|
||||
|
||||
@@ -1140,7 +1172,7 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
|
||||
if (state) {
|
||||
if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
|
||||
kfree(state);
|
||||
dev_err(p->dev->dev,
|
||||
drm_err(p->dev,
|
||||
"Failed to allocate initial plane state\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,14 @@ config DRM_DW_HDMI_QP
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_MMIO
|
||||
|
||||
config DRM_DW_HDMI_QP_CEC
|
||||
bool "Synopsis Designware QP CEC interface"
|
||||
depends on DRM_DW_HDMI_QP
|
||||
select DRM_DISPLAY_HDMI_CEC_HELPER
|
||||
help
|
||||
Support the CEC interface which is part of the Synopsys
|
||||
Designware HDMI QP block.
|
||||
|
||||
config DRM_DW_MIPI_DSI
|
||||
tristate
|
||||
select DRM_KMS_HELPER
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <drm/bridge/dw_hdmi_qp.h>
|
||||
#include <drm/display/drm_hdmi_helper.h>
|
||||
#include <drm/display/drm_hdmi_cec_helper.h>
|
||||
#include <drm/display/drm_hdmi_state_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
@@ -26,6 +27,8 @@
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_modes.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include <sound/hdmi-codec.h>
|
||||
|
||||
#include "dw-hdmi-qp.h"
|
||||
@@ -131,17 +134,34 @@ struct dw_hdmi_qp_i2c {
|
||||
bool is_segment;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
struct dw_hdmi_qp_cec {
|
||||
struct drm_connector *connector;
|
||||
int irq;
|
||||
u32 addresses;
|
||||
struct cec_msg rx_msg;
|
||||
u8 tx_status;
|
||||
bool tx_done;
|
||||
bool rx_done;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct dw_hdmi_qp {
|
||||
struct drm_bridge bridge;
|
||||
|
||||
struct device *dev;
|
||||
struct dw_hdmi_qp_i2c *i2c;
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
struct dw_hdmi_qp_cec *cec;
|
||||
#endif
|
||||
|
||||
struct {
|
||||
const struct dw_hdmi_qp_phy_ops *ops;
|
||||
void *data;
|
||||
} phy;
|
||||
|
||||
unsigned long ref_clk_rate;
|
||||
struct regmap *regm;
|
||||
|
||||
unsigned long tmds_char_rate;
|
||||
@@ -965,6 +985,179 @@ static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
static irqreturn_t dw_hdmi_qp_cec_hardirq(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
u32 stat;
|
||||
|
||||
stat = dw_hdmi_qp_read(hdmi, CEC_INT_STATUS);
|
||||
if (stat == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
dw_hdmi_qp_write(hdmi, stat, CEC_INT_CLEAR);
|
||||
|
||||
if (stat & CEC_STAT_LINE_ERR) {
|
||||
cec->tx_status = CEC_TX_STATUS_ERROR;
|
||||
cec->tx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
} else if (stat & CEC_STAT_DONE) {
|
||||
cec->tx_status = CEC_TX_STATUS_OK;
|
||||
cec->tx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
} else if (stat & CEC_STAT_NACK) {
|
||||
cec->tx_status = CEC_TX_STATUS_NACK;
|
||||
cec->tx_done = true;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
if (stat & CEC_STAT_EOM) {
|
||||
unsigned int len, i, val;
|
||||
|
||||
val = dw_hdmi_qp_read(hdmi, CEC_RX_COUNT_STATUS);
|
||||
len = (val & 0xf) + 1;
|
||||
|
||||
if (len > sizeof(cec->rx_msg.msg))
|
||||
len = sizeof(cec->rx_msg.msg);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
val = dw_hdmi_qp_read(hdmi, CEC_RX_DATA3_0 + i * 4);
|
||||
cec->rx_msg.msg[i * 4] = val & 0xff;
|
||||
cec->rx_msg.msg[i * 4 + 1] = (val >> 8) & 0xff;
|
||||
cec->rx_msg.msg[i * 4 + 2] = (val >> 16) & 0xff;
|
||||
cec->rx_msg.msg[i * 4 + 3] = (val >> 24) & 0xff;
|
||||
}
|
||||
|
||||
dw_hdmi_qp_write(hdmi, 1, CEC_LOCK_CONTROL);
|
||||
|
||||
cec->rx_msg.len = len;
|
||||
cec->rx_done = true;
|
||||
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_cec_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
|
||||
if (cec->tx_done) {
|
||||
cec->tx_done = false;
|
||||
drm_connector_hdmi_cec_transmit_attempt_done(cec->connector,
|
||||
cec->tx_status);
|
||||
}
|
||||
|
||||
if (cec->rx_done) {
|
||||
cec->rx_done = false;
|
||||
drm_connector_hdmi_cec_received_msg(cec->connector, &cec->rx_msg);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_init(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
|
||||
cec->connector = connector;
|
||||
|
||||
dw_hdmi_qp_write(hdmi, 0, CEC_TX_COUNT);
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
dw_hdmi_qp_write(hdmi, 0, CEC_INT_MASK_N);
|
||||
|
||||
return devm_request_threaded_irq(hdmi->dev, cec->irq,
|
||||
dw_hdmi_qp_cec_hardirq,
|
||||
dw_hdmi_qp_cec_thread, IRQF_SHARED,
|
||||
dev_name(hdmi->dev), hdmi);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_log_addr(struct drm_bridge *bridge, u8 logical_addr)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
struct dw_hdmi_qp_cec *cec = hdmi->cec;
|
||||
|
||||
if (logical_addr == CEC_LOG_ADDR_INVALID)
|
||||
cec->addresses = 0;
|
||||
else
|
||||
cec->addresses |= BIT(logical_addr) | CEC_ADDR_BROADCAST;
|
||||
|
||||
dw_hdmi_qp_write(hdmi, cec->addresses, CEC_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_enable(struct drm_bridge *bridge, bool enable)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
unsigned int irqs;
|
||||
u32 swdisable;
|
||||
|
||||
if (!enable) {
|
||||
dw_hdmi_qp_write(hdmi, 0, CEC_INT_MASK_N);
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
|
||||
swdisable = dw_hdmi_qp_read(hdmi, GLOBAL_SWDISABLE);
|
||||
swdisable = swdisable | CEC_SWDISABLE;
|
||||
dw_hdmi_qp_write(hdmi, swdisable, GLOBAL_SWDISABLE);
|
||||
} else {
|
||||
swdisable = dw_hdmi_qp_read(hdmi, GLOBAL_SWDISABLE);
|
||||
swdisable = swdisable & ~CEC_SWDISABLE;
|
||||
dw_hdmi_qp_write(hdmi, swdisable, GLOBAL_SWDISABLE);
|
||||
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
dw_hdmi_qp_write(hdmi, 1, CEC_LOCK_CONTROL);
|
||||
|
||||
dw_hdmi_qp_cec_log_addr(bridge, CEC_LOG_ADDR_INVALID);
|
||||
|
||||
irqs = CEC_STAT_LINE_ERR | CEC_STAT_NACK | CEC_STAT_EOM |
|
||||
CEC_STAT_DONE;
|
||||
dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
|
||||
dw_hdmi_qp_write(hdmi, irqs, CEC_INT_MASK_N);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
for (i = 0; i < msg->len; i++) {
|
||||
if (!(i % 4))
|
||||
val = msg->msg[i];
|
||||
if ((i % 4) == 1)
|
||||
val |= msg->msg[i] << 8;
|
||||
if ((i % 4) == 2)
|
||||
val |= msg->msg[i] << 16;
|
||||
if ((i % 4) == 3)
|
||||
val |= msg->msg[i] << 24;
|
||||
|
||||
if (i == (msg->len - 1) || (i % 4) == 3)
|
||||
dw_hdmi_qp_write(hdmi, val, CEC_TX_DATA3_0 + (i / 4) * 4);
|
||||
}
|
||||
|
||||
dw_hdmi_qp_write(hdmi, msg->len - 1, CEC_TX_COUNT);
|
||||
dw_hdmi_qp_write(hdmi, CEC_CTRL_START, CEC_TX_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define dw_hdmi_qp_cec_init NULL
|
||||
#define dw_hdmi_qp_cec_enable NULL
|
||||
#define dw_hdmi_qp_cec_log_addr NULL
|
||||
#define dw_hdmi_qp_cec_transmit NULL
|
||||
#endif /* CONFIG_DRM_DW_HDMI_QP_CEC */
|
||||
|
||||
static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
@@ -979,6 +1172,10 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
||||
.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
|
||||
.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
|
||||
.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
|
||||
.hdmi_cec_init = dw_hdmi_qp_cec_init,
|
||||
.hdmi_cec_enable = dw_hdmi_qp_cec_enable,
|
||||
.hdmi_cec_log_addr = dw_hdmi_qp_cec_log_addr,
|
||||
.hdmi_cec_transmit = dw_hdmi_qp_cec_transmit,
|
||||
};
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
|
||||
@@ -1014,13 +1211,11 @@ static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi)
|
||||
{
|
||||
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N);
|
||||
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N);
|
||||
dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0);
|
||||
dw_hdmi_qp_write(hdmi, hdmi->ref_clk_rate, TIMER_BASE_CONFIG0);
|
||||
|
||||
/* Software reset */
|
||||
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
|
||||
|
||||
dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
|
||||
|
||||
dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
|
||||
|
||||
/* Clear DONE and ERROR interrupts */
|
||||
@@ -1066,6 +1261,13 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||||
hdmi->phy.ops = plat_data->phy_ops;
|
||||
hdmi->phy.data = plat_data->phy_data;
|
||||
|
||||
if (plat_data->ref_clk_rate) {
|
||||
hdmi->ref_clk_rate = plat_data->ref_clk_rate;
|
||||
} else {
|
||||
hdmi->ref_clk_rate = 428571429;
|
||||
dev_warn(dev, "Set ref_clk_rate to vendor default\n");
|
||||
}
|
||||
|
||||
dw_hdmi_qp_init_hw(hdmi);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, plat_data->main_irq,
|
||||
@@ -1093,6 +1295,22 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||||
hdmi->bridge.hdmi_audio_dev = dev;
|
||||
hdmi->bridge.hdmi_audio_dai_port = 1;
|
||||
|
||||
#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
|
||||
if (plat_data->cec_irq) {
|
||||
hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI_CEC_ADAPTER;
|
||||
hdmi->bridge.hdmi_cec_dev = dev;
|
||||
hdmi->bridge.hdmi_cec_adapter_name = dev_name(dev);
|
||||
|
||||
hdmi->cec = devm_kzalloc(hdmi->dev, sizeof(*hdmi->cec), GFP_KERNEL);
|
||||
if (!hdmi->cec)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hdmi->cec->irq = plat_data->cec_irq;
|
||||
} else {
|
||||
dev_warn(dev, "Disabled CEC support due to missing IRQ\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@@ -488,9 +488,23 @@
|
||||
#define AUDPKT_VBIT_OVR0 0xf24
|
||||
/* CEC Registers */
|
||||
#define CEC_TX_CONTROL 0x1000
|
||||
#define CEC_CTRL_CLEAR BIT(0)
|
||||
#define CEC_CTRL_START BIT(0)
|
||||
#define CEC_STATUS 0x1004
|
||||
#define CEC_STAT_DONE BIT(0)
|
||||
#define CEC_STAT_NACK BIT(1)
|
||||
#define CEC_STAT_ARBLOST BIT(2)
|
||||
#define CEC_STAT_LINE_ERR BIT(3)
|
||||
#define CEC_STAT_RETRANS_FAIL BIT(4)
|
||||
#define CEC_STAT_DISCARD BIT(5)
|
||||
#define CEC_STAT_TX_BUSY BIT(8)
|
||||
#define CEC_STAT_RX_BUSY BIT(9)
|
||||
#define CEC_STAT_DRIVE_ERR BIT(10)
|
||||
#define CEC_STAT_EOM BIT(11)
|
||||
#define CEC_STAT_NOTIFY_ERR BIT(12)
|
||||
#define CEC_CONFIG 0x1008
|
||||
#define CEC_ADDR 0x100c
|
||||
#define CEC_ADDR_BROADCAST BIT(15)
|
||||
#define CEC_TX_COUNT 0x1020
|
||||
#define CEC_TX_DATA3_0 0x1024
|
||||
#define CEC_TX_DATA7_4 0x1028
|
||||
|
||||
@@ -62,26 +62,20 @@ err_drm_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int drm_fbdev_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
|
||||
static int drm_fbdev_client_suspend(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||
|
||||
if (holds_console_lock)
|
||||
drm_fb_helper_set_suspend(fb_helper, true);
|
||||
else
|
||||
drm_fb_helper_set_suspend_unlocked(fb_helper, true);
|
||||
drm_fb_helper_set_suspend_unlocked(fb_helper, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_fbdev_client_resume(struct drm_client_dev *client, bool holds_console_lock)
|
||||
static int drm_fbdev_client_resume(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||
|
||||
if (holds_console_lock)
|
||||
drm_fb_helper_set_suspend(fb_helper, false);
|
||||
else
|
||||
drm_fb_helper_set_suspend_unlocked(fb_helper, false);
|
||||
drm_fb_helper_set_suspend_unlocked(fb_helper, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ static int drm_log_client_hotplug(struct drm_client_dev *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_lock)
|
||||
static int drm_log_client_suspend(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_log *dlog = client_to_drm_log(client);
|
||||
|
||||
@@ -328,7 +328,7 @@ static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_l
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lock)
|
||||
static int drm_log_client_resume(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_log *dlog = client_to_drm_log(client);
|
||||
|
||||
|
||||
@@ -618,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
|
||||
* Bridge Connector Initialisation
|
||||
*/
|
||||
|
||||
static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
|
||||
{
|
||||
struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
|
||||
|
||||
drm_bridge_put(bridge_connector->bridge_edid);
|
||||
drm_bridge_put(bridge_connector->bridge_hpd);
|
||||
drm_bridge_put(bridge_connector->bridge_detect);
|
||||
drm_bridge_put(bridge_connector->bridge_modes);
|
||||
drm_bridge_put(bridge_connector->bridge_hdmi);
|
||||
drm_bridge_put(bridge_connector->bridge_hdmi_audio);
|
||||
drm_bridge_put(bridge_connector->bridge_dp_audio);
|
||||
drm_bridge_put(bridge_connector->bridge_hdmi_cec);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_bridge_connector_init - Initialise a connector for a chain of bridges
|
||||
* @drm: the DRM device
|
||||
@@ -638,7 +652,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
struct drm_bridge_connector *bridge_connector;
|
||||
struct drm_connector *connector;
|
||||
struct i2c_adapter *ddc = NULL;
|
||||
struct drm_bridge *panel_bridge = NULL;
|
||||
struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_edid __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_hpd __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_detect __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_modes __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_hdmi __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_hdmi_audio __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_dp_audio __free(drm_bridge_put) = NULL;
|
||||
struct drm_bridge *bridge_hdmi_cec __free(drm_bridge_put) = NULL;
|
||||
unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
|
||||
unsigned int max_bpc = 8;
|
||||
bool support_hdcp = false;
|
||||
@@ -649,6 +671,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
if (!bridge_connector)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
bridge_connector->encoder = encoder;
|
||||
|
||||
/*
|
||||
@@ -672,22 +698,30 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
if (!bridge->ycbcr_420_allowed)
|
||||
connector->ycbcr_420_allowed = false;
|
||||
|
||||
if (bridge->ops & DRM_BRIDGE_OP_EDID)
|
||||
bridge_connector->bridge_edid = bridge;
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HPD)
|
||||
bridge_connector->bridge_hpd = bridge;
|
||||
if (bridge->ops & DRM_BRIDGE_OP_DETECT)
|
||||
bridge_connector->bridge_detect = bridge;
|
||||
if (bridge->ops & DRM_BRIDGE_OP_MODES)
|
||||
bridge_connector->bridge_modes = bridge;
|
||||
if (bridge->ops & DRM_BRIDGE_OP_EDID) {
|
||||
drm_bridge_put(bridge_edid);
|
||||
bridge_edid = drm_bridge_get(bridge);
|
||||
}
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HPD) {
|
||||
drm_bridge_put(bridge_hpd);
|
||||
bridge_hpd = drm_bridge_get(bridge);
|
||||
}
|
||||
if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
|
||||
drm_bridge_put(bridge_detect);
|
||||
bridge_detect = drm_bridge_get(bridge);
|
||||
}
|
||||
if (bridge->ops & DRM_BRIDGE_OP_MODES) {
|
||||
drm_bridge_put(bridge_modes);
|
||||
bridge_modes = drm_bridge_get(bridge);
|
||||
}
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
|
||||
if (bridge_connector->bridge_hdmi)
|
||||
if (bridge_hdmi)
|
||||
return ERR_PTR(-EBUSY);
|
||||
if (!bridge->funcs->hdmi_write_infoframe ||
|
||||
!bridge->funcs->hdmi_clear_infoframe)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
bridge_connector->bridge_hdmi = bridge;
|
||||
bridge_hdmi = drm_bridge_get(bridge);
|
||||
|
||||
if (bridge->supported_formats)
|
||||
supported_formats = bridge->supported_formats;
|
||||
@@ -696,10 +730,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
}
|
||||
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
|
||||
if (bridge_connector->bridge_hdmi_audio)
|
||||
if (bridge_hdmi_audio)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
if (bridge_connector->bridge_dp_audio)
|
||||
if (bridge_dp_audio)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
|
||||
@@ -710,14 +744,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
!bridge->funcs->hdmi_audio_shutdown)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
bridge_connector->bridge_hdmi_audio = bridge;
|
||||
bridge_hdmi_audio = drm_bridge_get(bridge);
|
||||
}
|
||||
|
||||
if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
|
||||
if (bridge_connector->bridge_dp_audio)
|
||||
if (bridge_dp_audio)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
if (bridge_connector->bridge_hdmi_audio)
|
||||
if (bridge_hdmi_audio)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
if (!bridge->hdmi_audio_max_i2s_playback_channels &&
|
||||
@@ -728,7 +762,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
!bridge->funcs->dp_audio_shutdown)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
bridge_connector->bridge_dp_audio = bridge;
|
||||
bridge_dp_audio = drm_bridge_get(bridge);
|
||||
}
|
||||
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
|
||||
@@ -739,10 +773,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
}
|
||||
|
||||
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
|
||||
if (bridge_connector->bridge_hdmi_cec)
|
||||
if (bridge_hdmi_cec)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
bridge_connector->bridge_hdmi_cec = bridge;
|
||||
bridge_hdmi_cec = drm_bridge_get(bridge);
|
||||
|
||||
if (!bridge->funcs->hdmi_cec_enable ||
|
||||
!bridge->funcs->hdmi_cec_log_addr ||
|
||||
@@ -762,7 +796,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
ddc = bridge->ddc;
|
||||
|
||||
if (drm_bridge_is_panel(bridge))
|
||||
panel_bridge = bridge;
|
||||
panel_bridge = drm_bridge_get(bridge);
|
||||
|
||||
if (bridge->support_hdcp)
|
||||
support_hdcp = true;
|
||||
@@ -771,13 +805,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (bridge_connector->bridge_hdmi) {
|
||||
if (bridge_hdmi) {
|
||||
if (!connector->ycbcr_420_allowed)
|
||||
supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420);
|
||||
|
||||
ret = drmm_connector_hdmi_init(drm, connector,
|
||||
bridge_connector->bridge_hdmi->vendor,
|
||||
bridge_connector->bridge_hdmi->product,
|
||||
bridge_hdmi->vendor,
|
||||
bridge_hdmi->product,
|
||||
&drm_bridge_connector_funcs,
|
||||
&drm_bridge_connector_hdmi_funcs,
|
||||
connector_type, ddc,
|
||||
@@ -793,15 +827,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (bridge_connector->bridge_hdmi_audio ||
|
||||
bridge_connector->bridge_dp_audio) {
|
||||
if (bridge_hdmi_audio || bridge_dp_audio) {
|
||||
struct device *dev;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
if (bridge_connector->bridge_hdmi_audio)
|
||||
bridge = bridge_connector->bridge_hdmi_audio;
|
||||
if (bridge_hdmi_audio)
|
||||
bridge = bridge_hdmi_audio;
|
||||
else
|
||||
bridge = bridge_connector->bridge_dp_audio;
|
||||
bridge = bridge_dp_audio;
|
||||
|
||||
dev = bridge->hdmi_audio_dev;
|
||||
|
||||
@@ -815,9 +848,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (bridge_connector->bridge_hdmi_cec &&
|
||||
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
|
||||
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
|
||||
if (bridge_hdmi_cec &&
|
||||
bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
|
||||
struct drm_bridge *bridge = bridge_hdmi_cec;
|
||||
|
||||
ret = drmm_connector_hdmi_cec_notifier_register(connector,
|
||||
NULL,
|
||||
@@ -826,9 +859,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (bridge_connector->bridge_hdmi_cec &&
|
||||
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
|
||||
struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
|
||||
if (bridge_hdmi_cec &&
|
||||
bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
|
||||
struct drm_bridge *bridge = bridge_hdmi_cec;
|
||||
|
||||
ret = drmm_connector_hdmi_cec_register(connector,
|
||||
&drm_bridge_connector_hdmi_cec_funcs,
|
||||
@@ -841,9 +874,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
|
||||
drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
|
||||
|
||||
if (bridge_connector->bridge_hpd)
|
||||
if (bridge_hpd)
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
else if (bridge_connector->bridge_detect)
|
||||
else if (bridge_detect)
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT
|
||||
| DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
@@ -854,6 +887,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
|
||||
IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER))
|
||||
drm_connector_attach_content_protection_property(connector, true);
|
||||
|
||||
bridge_connector->bridge_edid = drm_bridge_get(bridge_edid);
|
||||
bridge_connector->bridge_hpd = drm_bridge_get(bridge_hpd);
|
||||
bridge_connector->bridge_detect = drm_bridge_get(bridge_detect);
|
||||
bridge_connector->bridge_modes = drm_bridge_get(bridge_modes);
|
||||
bridge_connector->bridge_hdmi = drm_bridge_get(bridge_hdmi);
|
||||
bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge_hdmi_audio);
|
||||
bridge_connector->bridge_dp_audio = drm_bridge_get(bridge_dp_audio);
|
||||
bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge_hdmi_cec);
|
||||
|
||||
return connector;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
|
||||
|
||||
@@ -207,9 +207,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
||||
continue;
|
||||
|
||||
connector->funcs->atomic_destroy_state(connector,
|
||||
state->connectors[i].state);
|
||||
state->connectors[i].state_to_destroy);
|
||||
state->connectors[i].ptr = NULL;
|
||||
state->connectors[i].state = NULL;
|
||||
state->connectors[i].state_to_destroy = NULL;
|
||||
state->connectors[i].old_state = NULL;
|
||||
state->connectors[i].new_state = NULL;
|
||||
drm_connector_put(connector);
|
||||
@@ -222,10 +222,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
||||
continue;
|
||||
|
||||
crtc->funcs->atomic_destroy_state(crtc,
|
||||
state->crtcs[i].state);
|
||||
state->crtcs[i].state_to_destroy);
|
||||
|
||||
state->crtcs[i].ptr = NULL;
|
||||
state->crtcs[i].state = NULL;
|
||||
state->crtcs[i].state_to_destroy = NULL;
|
||||
state->crtcs[i].old_state = NULL;
|
||||
state->crtcs[i].new_state = NULL;
|
||||
|
||||
@@ -242,9 +242,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
||||
continue;
|
||||
|
||||
plane->funcs->atomic_destroy_state(plane,
|
||||
state->planes[i].state);
|
||||
state->planes[i].state_to_destroy);
|
||||
state->planes[i].ptr = NULL;
|
||||
state->planes[i].state = NULL;
|
||||
state->planes[i].state_to_destroy = NULL;
|
||||
state->planes[i].old_state = NULL;
|
||||
state->planes[i].new_state = NULL;
|
||||
}
|
||||
@@ -253,9 +253,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
|
||||
struct drm_private_obj *obj = state->private_objs[i].ptr;
|
||||
|
||||
obj->funcs->atomic_destroy_state(obj,
|
||||
state->private_objs[i].state);
|
||||
state->private_objs[i].state_to_destroy);
|
||||
state->private_objs[i].ptr = NULL;
|
||||
state->private_objs[i].state = NULL;
|
||||
state->private_objs[i].state_to_destroy = NULL;
|
||||
state->private_objs[i].old_state = NULL;
|
||||
state->private_objs[i].new_state = NULL;
|
||||
}
|
||||
@@ -349,7 +349,7 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
|
||||
|
||||
WARN_ON(!state->acquire_ctx);
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
if (crtc_state)
|
||||
return crtc_state;
|
||||
|
||||
@@ -361,7 +361,7 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
|
||||
if (!crtc_state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
state->crtcs[index].state = crtc_state;
|
||||
state->crtcs[index].state_to_destroy = crtc_state;
|
||||
state->crtcs[index].old_state = crtc->state;
|
||||
state->crtcs[index].new_state = crtc_state;
|
||||
state->crtcs[index].ptr = crtc;
|
||||
@@ -480,8 +480,8 @@ static int drm_atomic_connector_check(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
if (state->crtc)
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
|
||||
state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state->state,
|
||||
state->crtc);
|
||||
|
||||
if (writeback_job->fb && !crtc_state->active) {
|
||||
drm_dbg_atomic(connector->dev,
|
||||
@@ -534,7 +534,7 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
|
||||
WARN_ON(plane->old_fb);
|
||||
WARN_ON(plane->crtc);
|
||||
|
||||
plane_state = drm_atomic_get_existing_plane_state(state, plane);
|
||||
plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
if (plane_state)
|
||||
return plane_state;
|
||||
|
||||
@@ -546,7 +546,7 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
|
||||
if (!plane_state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
state->planes[index].state = plane_state;
|
||||
state->planes[index].state_to_destroy = plane_state;
|
||||
state->planes[index].ptr = plane;
|
||||
state->planes[index].old_state = plane->state;
|
||||
state->planes[index].new_state = plane_state;
|
||||
@@ -831,14 +831,14 @@ struct drm_private_state *
|
||||
drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
|
||||
struct drm_private_obj *obj)
|
||||
{
|
||||
int index, num_objs, i, ret;
|
||||
int index, num_objs, ret;
|
||||
size_t size;
|
||||
struct __drm_private_objs_state *arr;
|
||||
struct drm_private_state *obj_state;
|
||||
|
||||
for (i = 0; i < state->num_private_objs; i++)
|
||||
if (obj == state->private_objs[i].ptr)
|
||||
return state->private_objs[i].state;
|
||||
obj_state = drm_atomic_get_new_private_obj_state(state, obj);
|
||||
if (obj_state)
|
||||
return obj_state;
|
||||
|
||||
ret = drm_modeset_lock(&obj->lock, state->acquire_ctx);
|
||||
if (ret)
|
||||
@@ -858,7 +858,7 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
|
||||
if (!obj_state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
state->private_objs[index].state = obj_state;
|
||||
state->private_objs[index].state_to_destroy = obj_state;
|
||||
state->private_objs[index].old_state = obj->state;
|
||||
state->private_objs[index].new_state = obj_state;
|
||||
state->private_objs[index].ptr = obj;
|
||||
@@ -1152,15 +1152,16 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
|
||||
state->num_connector = alloc;
|
||||
}
|
||||
|
||||
if (state->connectors[index].state)
|
||||
return state->connectors[index].state;
|
||||
connector_state = drm_atomic_get_new_connector_state(state, connector);
|
||||
if (connector_state)
|
||||
return connector_state;
|
||||
|
||||
connector_state = connector->funcs->atomic_duplicate_state(connector);
|
||||
if (!connector_state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
drm_connector_get(connector);
|
||||
state->connectors[index].state = connector_state;
|
||||
state->connectors[index].state_to_destroy = connector_state;
|
||||
state->connectors[index].old_state = connector->state;
|
||||
state->connectors[index].new_state = connector_state;
|
||||
state->connectors[index].ptr = connector;
|
||||
|
||||
@@ -3236,7 +3236,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
old_conn_state->state = state;
|
||||
new_conn_state->state = NULL;
|
||||
|
||||
state->connectors[i].state = old_conn_state;
|
||||
state->connectors[i].state_to_destroy = old_conn_state;
|
||||
connector->state = new_conn_state;
|
||||
}
|
||||
|
||||
@@ -3246,7 +3246,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
old_crtc_state->state = state;
|
||||
new_crtc_state->state = NULL;
|
||||
|
||||
state->crtcs[i].state = old_crtc_state;
|
||||
state->crtcs[i].state_to_destroy = old_crtc_state;
|
||||
crtc->state = new_crtc_state;
|
||||
|
||||
if (new_crtc_state->commit) {
|
||||
@@ -3266,7 +3266,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
old_plane_state->state = state;
|
||||
new_plane_state->state = NULL;
|
||||
|
||||
state->planes[i].state = old_plane_state;
|
||||
state->planes[i].state_to_destroy = old_plane_state;
|
||||
plane->state = new_plane_state;
|
||||
}
|
||||
drm_panic_unlock(state->dev, flags);
|
||||
@@ -3277,7 +3277,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
|
||||
old_obj_state->state = state;
|
||||
new_obj_state->state = NULL;
|
||||
|
||||
state->private_objs[i].state = old_obj_state;
|
||||
state->private_objs[i].state_to_destroy = old_obj_state;
|
||||
obj->state = new_obj_state;
|
||||
}
|
||||
|
||||
|
||||
@@ -1086,12 +1086,12 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
|
||||
struct drm_encoder *encoder = bridge->encoder;
|
||||
struct drm_bridge_state *last_bridge_state;
|
||||
unsigned int i, num_out_bus_fmts = 0;
|
||||
struct drm_bridge *last_bridge;
|
||||
u32 *out_bus_fmts;
|
||||
int ret = 0;
|
||||
|
||||
last_bridge = list_last_entry(&encoder->bridge_chain,
|
||||
struct drm_bridge, chain_node);
|
||||
struct drm_bridge *last_bridge __free(drm_bridge_put) =
|
||||
drm_bridge_get(list_last_entry(&encoder->bridge_chain,
|
||||
struct drm_bridge, chain_node));
|
||||
last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
|
||||
last_bridge);
|
||||
|
||||
|
||||
@@ -12,8 +12,17 @@
|
||||
|
||||
#include <drm/drm_buddy.h>
|
||||
|
||||
enum drm_buddy_free_tree {
|
||||
DRM_BUDDY_CLEAR_TREE = 0,
|
||||
DRM_BUDDY_DIRTY_TREE,
|
||||
DRM_BUDDY_MAX_FREE_TREES,
|
||||
};
|
||||
|
||||
static struct kmem_cache *slab_blocks;
|
||||
|
||||
#define for_each_free_tree(tree) \
|
||||
for ((tree) = 0; (tree) < DRM_BUDDY_MAX_FREE_TREES; (tree)++)
|
||||
|
||||
static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *parent,
|
||||
unsigned int order,
|
||||
@@ -31,6 +40,8 @@ static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
|
||||
block->header |= order;
|
||||
block->parent = parent;
|
||||
|
||||
RB_CLEAR_NODE(&block->rb);
|
||||
|
||||
BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED);
|
||||
return block;
|
||||
}
|
||||
@@ -41,23 +52,64 @@ static void drm_block_free(struct drm_buddy *mm,
|
||||
kmem_cache_free(slab_blocks, block);
|
||||
}
|
||||
|
||||
static void list_insert_sorted(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
static enum drm_buddy_free_tree
|
||||
get_block_tree(struct drm_buddy_block *block)
|
||||
{
|
||||
struct drm_buddy_block *node;
|
||||
struct list_head *head;
|
||||
return drm_buddy_block_is_clear(block) ?
|
||||
DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE;
|
||||
}
|
||||
|
||||
head = &mm->free_list[drm_buddy_block_order(block)];
|
||||
if (list_empty(head)) {
|
||||
list_add(&block->link, head);
|
||||
return;
|
||||
}
|
||||
static struct drm_buddy_block *
|
||||
rbtree_get_free_block(const struct rb_node *node)
|
||||
{
|
||||
return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry(node, head, link)
|
||||
if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node))
|
||||
break;
|
||||
static struct drm_buddy_block *
|
||||
rbtree_last_free_block(struct rb_root *root)
|
||||
{
|
||||
return rbtree_get_free_block(rb_last(root));
|
||||
}
|
||||
|
||||
__list_add(&block->link, node->link.prev, &node->link);
|
||||
static bool rbtree_is_empty(struct rb_root *root)
|
||||
{
|
||||
return RB_EMPTY_ROOT(root);
|
||||
}
|
||||
|
||||
static bool drm_buddy_block_offset_less(const struct drm_buddy_block *block,
|
||||
const struct drm_buddy_block *node)
|
||||
{
|
||||
return drm_buddy_block_offset(block) < drm_buddy_block_offset(node);
|
||||
}
|
||||
|
||||
static bool rbtree_block_offset_less(struct rb_node *block,
|
||||
const struct rb_node *node)
|
||||
{
|
||||
return drm_buddy_block_offset_less(rbtree_get_free_block(block),
|
||||
rbtree_get_free_block(node));
|
||||
}
|
||||
|
||||
static void rbtree_insert(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block,
|
||||
enum drm_buddy_free_tree tree)
|
||||
{
|
||||
rb_add(&block->rb,
|
||||
&mm->free_trees[tree][drm_buddy_block_order(block)],
|
||||
rbtree_block_offset_less);
|
||||
}
|
||||
|
||||
static void rbtree_remove(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
{
|
||||
unsigned int order = drm_buddy_block_order(block);
|
||||
enum drm_buddy_free_tree tree;
|
||||
struct rb_root *root;
|
||||
|
||||
tree = get_block_tree(block);
|
||||
root = &mm->free_trees[tree][order];
|
||||
|
||||
rb_erase(&block->rb, root);
|
||||
RB_CLEAR_NODE(&block->rb);
|
||||
}
|
||||
|
||||
static void clear_reset(struct drm_buddy_block *block)
|
||||
@@ -70,29 +122,34 @@ static void mark_cleared(struct drm_buddy_block *block)
|
||||
block->header |= DRM_BUDDY_HEADER_CLEAR;
|
||||
}
|
||||
|
||||
static void mark_allocated(struct drm_buddy_block *block)
|
||||
static void mark_allocated(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
{
|
||||
block->header &= ~DRM_BUDDY_HEADER_STATE;
|
||||
block->header |= DRM_BUDDY_ALLOCATED;
|
||||
|
||||
list_del(&block->link);
|
||||
rbtree_remove(mm, block);
|
||||
}
|
||||
|
||||
static void mark_free(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
{
|
||||
enum drm_buddy_free_tree tree;
|
||||
|
||||
block->header &= ~DRM_BUDDY_HEADER_STATE;
|
||||
block->header |= DRM_BUDDY_FREE;
|
||||
|
||||
list_insert_sorted(mm, block);
|
||||
tree = get_block_tree(block);
|
||||
rbtree_insert(mm, block, tree);
|
||||
}
|
||||
|
||||
static void mark_split(struct drm_buddy_block *block)
|
||||
static void mark_split(struct drm_buddy *mm,
|
||||
struct drm_buddy_block *block)
|
||||
{
|
||||
block->header &= ~DRM_BUDDY_HEADER_STATE;
|
||||
block->header |= DRM_BUDDY_SPLIT;
|
||||
|
||||
list_del(&block->link);
|
||||
rbtree_remove(mm, block);
|
||||
}
|
||||
|
||||
static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
|
||||
@@ -148,7 +205,7 @@ static unsigned int __drm_buddy_free(struct drm_buddy *mm,
|
||||
mark_cleared(parent);
|
||||
}
|
||||
|
||||
list_del(&buddy->link);
|
||||
rbtree_remove(mm, buddy);
|
||||
if (force_merge && drm_buddy_block_is_clear(buddy))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, buddy);
|
||||
|
||||
@@ -169,7 +226,7 @@ static int __force_merge(struct drm_buddy *mm,
|
||||
u64 end,
|
||||
unsigned int min_order)
|
||||
{
|
||||
unsigned int order;
|
||||
unsigned int tree, order;
|
||||
int i;
|
||||
|
||||
if (!min_order)
|
||||
@@ -178,44 +235,48 @@ static int __force_merge(struct drm_buddy *mm,
|
||||
if (min_order > mm->max_order)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = min_order - 1; i >= 0; i--) {
|
||||
struct drm_buddy_block *block, *prev;
|
||||
for_each_free_tree(tree) {
|
||||
for (i = min_order - 1; i >= 0; i--) {
|
||||
struct rb_node *iter = rb_last(&mm->free_trees[tree][i]);
|
||||
|
||||
list_for_each_entry_safe_reverse(block, prev, &mm->free_list[i], link) {
|
||||
struct drm_buddy_block *buddy;
|
||||
u64 block_start, block_end;
|
||||
while (iter) {
|
||||
struct drm_buddy_block *block, *buddy;
|
||||
u64 block_start, block_end;
|
||||
|
||||
if (!block->parent)
|
||||
continue;
|
||||
block = rbtree_get_free_block(iter);
|
||||
iter = rb_prev(iter);
|
||||
|
||||
block_start = drm_buddy_block_offset(block);
|
||||
block_end = block_start + drm_buddy_block_size(mm, block) - 1;
|
||||
if (!block || !block->parent)
|
||||
continue;
|
||||
|
||||
if (!contains(start, end, block_start, block_end))
|
||||
continue;
|
||||
block_start = drm_buddy_block_offset(block);
|
||||
block_end = block_start + drm_buddy_block_size(mm, block) - 1;
|
||||
|
||||
buddy = __get_buddy(block);
|
||||
if (!drm_buddy_block_is_free(buddy))
|
||||
continue;
|
||||
if (!contains(start, end, block_start, block_end))
|
||||
continue;
|
||||
|
||||
WARN_ON(drm_buddy_block_is_clear(block) ==
|
||||
drm_buddy_block_is_clear(buddy));
|
||||
buddy = __get_buddy(block);
|
||||
if (!drm_buddy_block_is_free(buddy))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the prev block is same as buddy, don't access the
|
||||
* block in the next iteration as we would free the
|
||||
* buddy block as part of the free function.
|
||||
*/
|
||||
if (prev == buddy)
|
||||
prev = list_prev_entry(prev, link);
|
||||
WARN_ON(drm_buddy_block_is_clear(block) ==
|
||||
drm_buddy_block_is_clear(buddy));
|
||||
|
||||
list_del(&block->link);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
/*
|
||||
* Advance to the next node when the current node is the buddy,
|
||||
* as freeing the block will also remove its buddy from the tree.
|
||||
*/
|
||||
if (iter == &buddy->rb)
|
||||
iter = rb_prev(iter);
|
||||
|
||||
order = __drm_buddy_free(mm, block, true);
|
||||
if (order >= min_order)
|
||||
return 0;
|
||||
rbtree_remove(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
|
||||
order = __drm_buddy_free(mm, block, true);
|
||||
if (order >= min_order)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,8 +297,8 @@ static int __force_merge(struct drm_buddy *mm,
|
||||
*/
|
||||
int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
|
||||
{
|
||||
unsigned int i;
|
||||
u64 offset;
|
||||
unsigned int i, j, root_count = 0;
|
||||
u64 offset = 0;
|
||||
|
||||
if (size < chunk_size)
|
||||
return -EINVAL;
|
||||
@@ -258,14 +319,22 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
|
||||
|
||||
BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER);
|
||||
|
||||
mm->free_list = kmalloc_array(mm->max_order + 1,
|
||||
sizeof(struct list_head),
|
||||
GFP_KERNEL);
|
||||
if (!mm->free_list)
|
||||
mm->free_trees = kmalloc_array(DRM_BUDDY_MAX_FREE_TREES,
|
||||
sizeof(*mm->free_trees),
|
||||
GFP_KERNEL);
|
||||
if (!mm->free_trees)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i <= mm->max_order; ++i)
|
||||
INIT_LIST_HEAD(&mm->free_list[i]);
|
||||
for_each_free_tree(i) {
|
||||
mm->free_trees[i] = kmalloc_array(mm->max_order + 1,
|
||||
sizeof(struct rb_root),
|
||||
GFP_KERNEL);
|
||||
if (!mm->free_trees[i])
|
||||
goto out_free_tree;
|
||||
|
||||
for (j = 0; j <= mm->max_order; ++j)
|
||||
mm->free_trees[i][j] = RB_ROOT;
|
||||
}
|
||||
|
||||
mm->n_roots = hweight64(size);
|
||||
|
||||
@@ -273,10 +342,7 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
|
||||
sizeof(struct drm_buddy_block *),
|
||||
GFP_KERNEL);
|
||||
if (!mm->roots)
|
||||
goto out_free_list;
|
||||
|
||||
offset = 0;
|
||||
i = 0;
|
||||
goto out_free_tree;
|
||||
|
||||
/*
|
||||
* Split into power-of-two blocks, in case we are given a size that is
|
||||
@@ -296,24 +362,26 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
|
||||
|
||||
mark_free(mm, root);
|
||||
|
||||
BUG_ON(i > mm->max_order);
|
||||
BUG_ON(root_count > mm->max_order);
|
||||
BUG_ON(drm_buddy_block_size(mm, root) < chunk_size);
|
||||
|
||||
mm->roots[i] = root;
|
||||
mm->roots[root_count] = root;
|
||||
|
||||
offset += root_size;
|
||||
size -= root_size;
|
||||
i++;
|
||||
root_count++;
|
||||
} while (size);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_roots:
|
||||
while (i--)
|
||||
drm_block_free(mm, mm->roots[i]);
|
||||
while (root_count--)
|
||||
drm_block_free(mm, mm->roots[root_count]);
|
||||
kfree(mm->roots);
|
||||
out_free_list:
|
||||
kfree(mm->free_list);
|
||||
out_free_tree:
|
||||
while (i--)
|
||||
kfree(mm->free_trees[i]);
|
||||
kfree(mm->free_trees);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_buddy_init);
|
||||
@@ -323,7 +391,7 @@ EXPORT_SYMBOL(drm_buddy_init);
|
||||
*
|
||||
* @mm: DRM buddy manager to free
|
||||
*
|
||||
* Cleanup memory manager resources and the freelist
|
||||
* Cleanup memory manager resources and the freetree
|
||||
*/
|
||||
void drm_buddy_fini(struct drm_buddy *mm)
|
||||
{
|
||||
@@ -349,8 +417,9 @@ void drm_buddy_fini(struct drm_buddy *mm)
|
||||
|
||||
WARN_ON(mm->avail != mm->size);
|
||||
|
||||
for_each_free_tree(i)
|
||||
kfree(mm->free_trees[i]);
|
||||
kfree(mm->roots);
|
||||
kfree(mm->free_list);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_buddy_fini);
|
||||
|
||||
@@ -374,8 +443,7 @@ static int split_block(struct drm_buddy *mm,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mark_free(mm, block->left);
|
||||
mark_free(mm, block->right);
|
||||
mark_split(mm, block);
|
||||
|
||||
if (drm_buddy_block_is_clear(block)) {
|
||||
mark_cleared(block->left);
|
||||
@@ -383,7 +451,8 @@ static int split_block(struct drm_buddy *mm,
|
||||
clear_reset(block);
|
||||
}
|
||||
|
||||
mark_split(block);
|
||||
mark_free(mm, block->left);
|
||||
mark_free(mm, block->right);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -412,10 +481,11 @@ EXPORT_SYMBOL(drm_get_buddy);
|
||||
* @is_clear: blocks clear state
|
||||
*
|
||||
* Reset the clear state based on @is_clear value for each block
|
||||
* in the freelist.
|
||||
* in the freetree.
|
||||
*/
|
||||
void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear)
|
||||
{
|
||||
enum drm_buddy_free_tree src_tree, dst_tree;
|
||||
u64 root_size, size, start;
|
||||
unsigned int order;
|
||||
int i;
|
||||
@@ -430,19 +500,24 @@ void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear)
|
||||
size -= root_size;
|
||||
}
|
||||
|
||||
for (i = 0; i <= mm->max_order; ++i) {
|
||||
struct drm_buddy_block *block;
|
||||
src_tree = is_clear ? DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE;
|
||||
dst_tree = is_clear ? DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE;
|
||||
|
||||
list_for_each_entry_reverse(block, &mm->free_list[i], link) {
|
||||
if (is_clear != drm_buddy_block_is_clear(block)) {
|
||||
if (is_clear) {
|
||||
mark_cleared(block);
|
||||
mm->clear_avail += drm_buddy_block_size(mm, block);
|
||||
} else {
|
||||
clear_reset(block);
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
}
|
||||
for (i = 0; i <= mm->max_order; ++i) {
|
||||
struct rb_root *root = &mm->free_trees[src_tree][i];
|
||||
struct drm_buddy_block *block, *tmp;
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
|
||||
rbtree_remove(mm, block);
|
||||
if (is_clear) {
|
||||
mark_cleared(block);
|
||||
mm->clear_avail += drm_buddy_block_size(mm, block);
|
||||
} else {
|
||||
clear_reset(block);
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
}
|
||||
|
||||
rbtree_insert(mm, block, dst_tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,23 +707,17 @@ __drm_buddy_alloc_range_bias(struct drm_buddy *mm,
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
get_maxblock(struct drm_buddy *mm, unsigned int order,
|
||||
unsigned long flags)
|
||||
get_maxblock(struct drm_buddy *mm,
|
||||
unsigned int order,
|
||||
enum drm_buddy_free_tree tree)
|
||||
{
|
||||
struct drm_buddy_block *max_block = NULL, *block = NULL;
|
||||
struct rb_root *root;
|
||||
unsigned int i;
|
||||
|
||||
for (i = order; i <= mm->max_order; ++i) {
|
||||
struct drm_buddy_block *tmp_block;
|
||||
|
||||
list_for_each_entry_reverse(tmp_block, &mm->free_list[i], link) {
|
||||
if (block_incompatible(tmp_block, flags))
|
||||
continue;
|
||||
|
||||
block = tmp_block;
|
||||
break;
|
||||
}
|
||||
|
||||
root = &mm->free_trees[tree][i];
|
||||
block = rbtree_last_free_block(root);
|
||||
if (!block)
|
||||
continue;
|
||||
|
||||
@@ -667,46 +736,44 @@ get_maxblock(struct drm_buddy *mm, unsigned int order,
|
||||
}
|
||||
|
||||
static struct drm_buddy_block *
|
||||
alloc_from_freelist(struct drm_buddy *mm,
|
||||
alloc_from_freetree(struct drm_buddy *mm,
|
||||
unsigned int order,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct drm_buddy_block *block = NULL;
|
||||
struct rb_root *root;
|
||||
enum drm_buddy_free_tree tree;
|
||||
unsigned int tmp;
|
||||
int err;
|
||||
|
||||
tree = (flags & DRM_BUDDY_CLEAR_ALLOCATION) ?
|
||||
DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE;
|
||||
|
||||
if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
|
||||
block = get_maxblock(mm, order, flags);
|
||||
block = get_maxblock(mm, order, tree);
|
||||
if (block)
|
||||
/* Store the obtained block order */
|
||||
tmp = drm_buddy_block_order(block);
|
||||
} else {
|
||||
for (tmp = order; tmp <= mm->max_order; ++tmp) {
|
||||
struct drm_buddy_block *tmp_block;
|
||||
|
||||
list_for_each_entry_reverse(tmp_block, &mm->free_list[tmp], link) {
|
||||
if (block_incompatible(tmp_block, flags))
|
||||
continue;
|
||||
|
||||
block = tmp_block;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get RB tree root for this order and tree */
|
||||
root = &mm->free_trees[tree][tmp];
|
||||
block = rbtree_last_free_block(root);
|
||||
if (block)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!block) {
|
||||
/* Fallback method */
|
||||
/* Try allocating from the other tree */
|
||||
tree = (tree == DRM_BUDDY_CLEAR_TREE) ?
|
||||
DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE;
|
||||
|
||||
for (tmp = order; tmp <= mm->max_order; ++tmp) {
|
||||
if (!list_empty(&mm->free_list[tmp])) {
|
||||
block = list_last_entry(&mm->free_list[tmp],
|
||||
struct drm_buddy_block,
|
||||
link);
|
||||
if (block)
|
||||
break;
|
||||
}
|
||||
root = &mm->free_trees[tree][tmp];
|
||||
block = rbtree_last_free_block(root);
|
||||
if (block)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!block)
|
||||
@@ -771,7 +838,7 @@ static int __alloc_range(struct drm_buddy *mm,
|
||||
|
||||
if (contains(start, end, block_start, block_end)) {
|
||||
if (drm_buddy_block_is_free(block)) {
|
||||
mark_allocated(block);
|
||||
mark_allocated(mm, block);
|
||||
total_allocated += drm_buddy_block_size(mm, block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
@@ -849,10 +916,9 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm,
|
||||
{
|
||||
u64 rhs_offset, lhs_offset, lhs_size, filled;
|
||||
struct drm_buddy_block *block;
|
||||
struct list_head *list;
|
||||
unsigned int tree, order;
|
||||
LIST_HEAD(blocks_lhs);
|
||||
unsigned long pages;
|
||||
unsigned int order;
|
||||
u64 modify_size;
|
||||
int err;
|
||||
|
||||
@@ -862,35 +928,45 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm,
|
||||
if (order == 0)
|
||||
return -ENOSPC;
|
||||
|
||||
list = &mm->free_list[order];
|
||||
if (list_empty(list))
|
||||
return -ENOSPC;
|
||||
for_each_free_tree(tree) {
|
||||
struct rb_root *root;
|
||||
struct rb_node *iter;
|
||||
|
||||
list_for_each_entry_reverse(block, list, link) {
|
||||
/* Allocate blocks traversing RHS */
|
||||
rhs_offset = drm_buddy_block_offset(block);
|
||||
err = __drm_buddy_alloc_range(mm, rhs_offset, size,
|
||||
&filled, blocks);
|
||||
if (!err || err != -ENOSPC)
|
||||
return err;
|
||||
root = &mm->free_trees[tree][order];
|
||||
if (rbtree_is_empty(root))
|
||||
continue;
|
||||
|
||||
lhs_size = max((size - filled), min_block_size);
|
||||
if (!IS_ALIGNED(lhs_size, min_block_size))
|
||||
lhs_size = round_up(lhs_size, min_block_size);
|
||||
iter = rb_last(root);
|
||||
while (iter) {
|
||||
block = rbtree_get_free_block(iter);
|
||||
|
||||
/* Allocate blocks traversing LHS */
|
||||
lhs_offset = drm_buddy_block_offset(block) - lhs_size;
|
||||
err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size,
|
||||
NULL, &blocks_lhs);
|
||||
if (!err) {
|
||||
list_splice(&blocks_lhs, blocks);
|
||||
return 0;
|
||||
} else if (err != -ENOSPC) {
|
||||
/* Allocate blocks traversing RHS */
|
||||
rhs_offset = drm_buddy_block_offset(block);
|
||||
err = __drm_buddy_alloc_range(mm, rhs_offset, size,
|
||||
&filled, blocks);
|
||||
if (!err || err != -ENOSPC)
|
||||
return err;
|
||||
|
||||
lhs_size = max((size - filled), min_block_size);
|
||||
if (!IS_ALIGNED(lhs_size, min_block_size))
|
||||
lhs_size = round_up(lhs_size, min_block_size);
|
||||
|
||||
/* Allocate blocks traversing LHS */
|
||||
lhs_offset = drm_buddy_block_offset(block) - lhs_size;
|
||||
err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size,
|
||||
NULL, &blocks_lhs);
|
||||
if (!err) {
|
||||
list_splice(&blocks_lhs, blocks);
|
||||
return 0;
|
||||
} else if (err != -ENOSPC) {
|
||||
drm_buddy_free_list_internal(mm, blocks);
|
||||
return err;
|
||||
}
|
||||
/* Free blocks for the next iteration */
|
||||
drm_buddy_free_list_internal(mm, blocks);
|
||||
return err;
|
||||
|
||||
iter = rb_prev(iter);
|
||||
}
|
||||
/* Free blocks for the next iteration */
|
||||
drm_buddy_free_list_internal(mm, blocks);
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
@@ -976,7 +1052,7 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
|
||||
list_add(&block->tmp_link, &dfs);
|
||||
err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL);
|
||||
if (err) {
|
||||
mark_allocated(block);
|
||||
mark_allocated(mm, block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
@@ -999,8 +1075,8 @@ __drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
return __drm_buddy_alloc_range_bias(mm, start, end,
|
||||
order, flags);
|
||||
else
|
||||
/* Allocate from freelist */
|
||||
return alloc_from_freelist(mm, order, flags);
|
||||
/* Allocate from freetree */
|
||||
return alloc_from_freetree(mm, order, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1017,8 +1093,8 @@ __drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
* alloc_range_bias() called on range limitations, which traverses
|
||||
* the tree and returns the desired block.
|
||||
*
|
||||
* alloc_from_freelist() called when *no* range restrictions
|
||||
* are enforced, which picks the block from the freelist.
|
||||
* alloc_from_freetree() called when *no* range restrictions
|
||||
* are enforced, which picks the block from the freetree.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, error code on failure.
|
||||
@@ -1120,7 +1196,7 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
|
||||
}
|
||||
} while (1);
|
||||
|
||||
mark_allocated(block);
|
||||
mark_allocated(mm, block);
|
||||
mm->avail -= drm_buddy_block_size(mm, block);
|
||||
if (drm_buddy_block_is_clear(block))
|
||||
mm->clear_avail -= drm_buddy_block_size(mm, block);
|
||||
@@ -1201,12 +1277,18 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)
|
||||
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
|
||||
|
||||
for (order = mm->max_order; order >= 0; order--) {
|
||||
struct drm_buddy_block *block;
|
||||
struct drm_buddy_block *block, *tmp;
|
||||
struct rb_root *root;
|
||||
u64 count = 0, free;
|
||||
unsigned int tree;
|
||||
|
||||
list_for_each_entry(block, &mm->free_list[order], link) {
|
||||
BUG_ON(!drm_buddy_block_is_free(block));
|
||||
count++;
|
||||
for_each_free_tree(tree) {
|
||||
root = &mm->free_trees[tree][order];
|
||||
|
||||
rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
|
||||
BUG_ON(!drm_buddy_block_is_free(block));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
drm_printf(p, "order-%2d ", order);
|
||||
|
||||
@@ -122,7 +122,7 @@ void drm_client_dev_restore(struct drm_device *dev)
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
}
|
||||
|
||||
static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
|
||||
static int drm_client_suspend(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_device *dev = client->dev;
|
||||
int ret = 0;
|
||||
@@ -131,7 +131,7 @@ static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_
|
||||
return 0;
|
||||
|
||||
if (client->funcs && client->funcs->suspend)
|
||||
ret = client->funcs->suspend(client, holds_console_lock);
|
||||
ret = client->funcs->suspend(client);
|
||||
drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
|
||||
|
||||
client->suspended = true;
|
||||
@@ -139,20 +139,20 @@ static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_
|
||||
return ret;
|
||||
}
|
||||
|
||||
void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock)
|
||||
void drm_client_dev_suspend(struct drm_device *dev)
|
||||
{
|
||||
struct drm_client_dev *client;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_for_each_entry(client, &dev->clientlist, list) {
|
||||
if (!client->suspended)
|
||||
drm_client_suspend(client, holds_console_lock);
|
||||
drm_client_suspend(client);
|
||||
}
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_client_dev_suspend);
|
||||
|
||||
static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock)
|
||||
static int drm_client_resume(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_device *dev = client->dev;
|
||||
int ret = 0;
|
||||
@@ -161,7 +161,7 @@ static int drm_client_resume(struct drm_client_dev *client, bool holds_console_l
|
||||
return 0;
|
||||
|
||||
if (client->funcs && client->funcs->resume)
|
||||
ret = client->funcs->resume(client, holds_console_lock);
|
||||
ret = client->funcs->resume(client);
|
||||
drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
|
||||
|
||||
client->suspended = false;
|
||||
@@ -172,14 +172,14 @@ static int drm_client_resume(struct drm_client_dev *client, bool holds_console_l
|
||||
return ret;
|
||||
}
|
||||
|
||||
void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock)
|
||||
void drm_client_dev_resume(struct drm_device *dev)
|
||||
{
|
||||
struct drm_client_dev *client;
|
||||
|
||||
mutex_lock(&dev->clientlist_mutex);
|
||||
list_for_each_entry(client, &dev->clientlist, list) {
|
||||
if (client->suspended)
|
||||
drm_client_resume(client, holds_console_lock);
|
||||
drm_client_resume(client);
|
||||
}
|
||||
mutex_unlock(&dev->clientlist_mutex);
|
||||
}
|
||||
|
||||
@@ -1048,7 +1048,7 @@ retry:
|
||||
plane_state->crtc->base.id,
|
||||
plane_state->crtc->name, fb->base.id);
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
|
||||
|
||||
ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
|
||||
if (ret)
|
||||
|
||||
@@ -967,7 +967,7 @@ drm_vram_helper_mode_valid_internal(struct drm_device *dev,
|
||||
|
||||
max_fbpages = (vmm->vram_size / 2) >> PAGE_SHIFT;
|
||||
|
||||
fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
|
||||
fbsize = (u32)mode->hdisplay * mode->vdisplay * max_bpp;
|
||||
fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
|
||||
|
||||
if (fbpages > max_fbpages)
|
||||
|
||||
@@ -691,7 +691,7 @@ int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev,
|
||||
const struct drm_simple_display_pipe_funcs *funcs,
|
||||
const struct drm_display_mode *mode, unsigned int rotation)
|
||||
{
|
||||
size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
|
||||
size_t bufsize = (u32)mode->vdisplay * mode->hdisplay * sizeof(u16);
|
||||
|
||||
dbidev->drm.mode_config.preferred_depth = 16;
|
||||
|
||||
|
||||
@@ -203,10 +203,10 @@ int drm_mode_config_helper_suspend(struct drm_device *dev)
|
||||
if (dev->mode_config.poll_enabled)
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
|
||||
drm_client_dev_suspend(dev, false);
|
||||
drm_client_dev_suspend(dev);
|
||||
state = drm_atomic_helper_suspend(dev);
|
||||
if (IS_ERR(state)) {
|
||||
drm_client_dev_resume(dev, false);
|
||||
drm_client_dev_resume(dev);
|
||||
|
||||
/*
|
||||
* Don't enable polling if it was never initialized
|
||||
@@ -252,7 +252,7 @@ int drm_mode_config_helper_resume(struct drm_device *dev)
|
||||
DRM_ERROR("Failed to resume (%d)\n", ret);
|
||||
dev->mode_config.suspend_state = NULL;
|
||||
|
||||
drm_client_dev_resume(dev, false);
|
||||
drm_client_dev_resume(dev);
|
||||
|
||||
/*
|
||||
* Don't enable polling if it is not initialized
|
||||
|
||||
@@ -58,7 +58,7 @@ static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
|
||||
struct drm_plane_state *state = &exynos_state->base;
|
||||
struct drm_crtc *crtc = state->crtc;
|
||||
struct drm_crtc_state *crtc_state =
|
||||
drm_atomic_get_existing_crtc_state(state->state, crtc);
|
||||
drm_atomic_get_new_crtc_state(state->state, crtc);
|
||||
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
|
||||
int crtc_x, crtc_y;
|
||||
unsigned int crtc_w, crtc_h;
|
||||
|
||||
@@ -69,7 +69,7 @@ static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format
|
||||
height = drm_rect_height(rect);
|
||||
len = drm_format_info_min_pitch(format, 0, width) * height;
|
||||
|
||||
buf = kmalloc(width * height, GFP_KERNEL);
|
||||
buf = kmalloc_array(height, width, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -978,7 +978,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
|
||||
intel_runtime_pm_disable(&i915->runtime_pm);
|
||||
intel_power_domains_disable(display);
|
||||
|
||||
drm_client_dev_suspend(&i915->drm, false);
|
||||
drm_client_dev_suspend(&i915->drm);
|
||||
if (intel_display_device_present(display)) {
|
||||
drm_kms_helper_poll_disable(&i915->drm);
|
||||
intel_display_driver_disable_user_access(display);
|
||||
@@ -1061,7 +1061,7 @@ static int i915_drm_suspend(struct drm_device *dev)
|
||||
/* We do a lot of poking in a lot of registers, make sure they work
|
||||
* properly. */
|
||||
intel_power_domains_disable(display);
|
||||
drm_client_dev_suspend(dev, false);
|
||||
drm_client_dev_suspend(dev);
|
||||
if (intel_display_device_present(display)) {
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
intel_display_driver_disable_user_access(display);
|
||||
@@ -1245,7 +1245,7 @@ static int i915_drm_resume(struct drm_device *dev)
|
||||
|
||||
intel_opregion_resume(display);
|
||||
|
||||
drm_client_dev_resume(dev, false);
|
||||
drm_client_dev_resume(dev);
|
||||
|
||||
intel_power_domains_enable(display);
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
#include "dc-pe.h"
|
||||
|
||||
#define PIXENGCFG_STATIC 0x8
|
||||
#define POWERDOWN BIT(4)
|
||||
#define SYNC_MODE BIT(8)
|
||||
#define SINGLE 0
|
||||
#define DIV_MASK GENMASK(23, 16)
|
||||
#define DIV(x) FIELD_PREP(DIV_MASK, (x))
|
||||
#define DIV_RESET 0x80
|
||||
#define SYNC_MODE BIT(8)
|
||||
#define SINGLE 0
|
||||
#define POWERDOWN BIT(4)
|
||||
|
||||
#define PIXENGCFG_DYNAMIC 0xc
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#define SYNC_TRIGGER BIT(0)
|
||||
|
||||
#define STATICCONTROL 0x8
|
||||
#define PERFCOUNTMODE BIT(12)
|
||||
#define KICK_MODE BIT(8)
|
||||
#define EXTERNAL BIT(8)
|
||||
#define PERFCOUNTMODE BIT(12)
|
||||
|
||||
#define CONTROL 0xc
|
||||
#define GAMMAAPPLYENABLE BIT(0)
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
|
||||
#define FGINCTRL 0x5c
|
||||
#define FGINCTRLPANIC 0x60
|
||||
#define FGDM_MASK GENMASK(2, 0)
|
||||
#define ENPRIMALPHA BIT(3)
|
||||
#define ENSECALPHA BIT(4)
|
||||
#define ENPRIMALPHA BIT(3)
|
||||
#define FGDM_MASK GENMASK(2, 0)
|
||||
|
||||
#define FGCCR 0x64
|
||||
#define CCGREEN(x) FIELD_PREP(GENMASK(19, 10), (x))
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
#define BASEADDRESSAUTOUPDATE(x) FIELD_PREP(BASEADDRESSAUTOUPDATE_MASK, (x))
|
||||
|
||||
/* BURSTBUFFERMANAGEMENT */
|
||||
#define LINEMODE_MASK BIT(31)
|
||||
#define SETBURSTLENGTH_MASK GENMASK(12, 8)
|
||||
#define SETBURSTLENGTH(x) FIELD_PREP(SETBURSTLENGTH_MASK, (x))
|
||||
#define SETNUMBUFFERS_MASK GENMASK(7, 0)
|
||||
#define SETNUMBUFFERS(x) FIELD_PREP(SETNUMBUFFERS_MASK, (x))
|
||||
#define LINEMODE_MASK BIT(31)
|
||||
|
||||
/* SOURCEBUFFERATTRIBUTES */
|
||||
#define BITSPERPIXEL_MASK GENMASK(21, 16)
|
||||
@@ -31,20 +31,20 @@
|
||||
#define STRIDE(x) FIELD_PREP(STRIDE_MASK, (x) - 1)
|
||||
|
||||
/* SOURCEBUFFERDIMENSION */
|
||||
#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
|
||||
#define LINECOUNT(x) FIELD_PREP(GENMASK(29, 16), (x))
|
||||
#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
|
||||
|
||||
/* LAYEROFFSET */
|
||||
#define LAYERXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
|
||||
#define LAYERYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x))
|
||||
#define LAYERXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
|
||||
|
||||
/* CLIPWINDOWOFFSET */
|
||||
#define CLIPWINDOWXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
|
||||
#define CLIPWINDOWYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x))
|
||||
#define CLIPWINDOWXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
|
||||
|
||||
/* CLIPWINDOWDIMENSIONS */
|
||||
#define CLIPWINDOWWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1)
|
||||
#define CLIPWINDOWHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x) - 1)
|
||||
#define CLIPWINDOWWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1)
|
||||
|
||||
enum dc_linemode {
|
||||
/*
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
#define A_SHIFT(x) FIELD_PREP_CONST(GENMASK(4, 0), (x))
|
||||
|
||||
/* LAYERPROPERTY */
|
||||
#define SOURCEBUFFERENABLE BIT(31)
|
||||
#define YUVCONVERSIONMODE_MASK GENMASK(18, 17)
|
||||
#define YUVCONVERSIONMODE(x) FIELD_PREP(YUVCONVERSIONMODE_MASK, (x))
|
||||
#define SOURCEBUFFERENABLE BIT(31)
|
||||
|
||||
/* FRAMEDIMENSIONS */
|
||||
#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
|
||||
#define FRAMEHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x))
|
||||
#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
|
||||
|
||||
/* CONTROL */
|
||||
#define INPUTSELECT_MASK GENMASK(4, 3)
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
#include "dc-pe.h"
|
||||
|
||||
#define PIXENGCFG_DYNAMIC 0x8
|
||||
#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0)
|
||||
#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \
|
||||
FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x))
|
||||
#define PIXENGCFG_DYNAMIC_SEC_SEL_MASK GENMASK(13, 8)
|
||||
#define PIXENGCFG_DYNAMIC_SEC_SEL(x) \
|
||||
FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x))
|
||||
#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0)
|
||||
#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \
|
||||
FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x))
|
||||
|
||||
#define STATICCONTROL 0x8
|
||||
#define SHDTOKSEL_MASK GENMASK(4, 3)
|
||||
@@ -37,24 +37,24 @@
|
||||
#define BLENDCONTROL 0x10
|
||||
#define ALPHA_MASK GENMASK(23, 16)
|
||||
#define ALPHA(x) FIELD_PREP(ALPHA_MASK, (x))
|
||||
#define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0)
|
||||
#define PRIM_C_BLD_FUNC(x) \
|
||||
FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x))
|
||||
#define SEC_C_BLD_FUNC_MASK GENMASK(6, 4)
|
||||
#define SEC_C_BLD_FUNC(x) \
|
||||
FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x))
|
||||
#define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8)
|
||||
#define PRIM_A_BLD_FUNC(x) \
|
||||
FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x))
|
||||
#define SEC_A_BLD_FUNC_MASK GENMASK(14, 12)
|
||||
#define SEC_A_BLD_FUNC(x) \
|
||||
FIELD_PREP(SEC_A_BLD_FUNC_MASK, (x))
|
||||
#define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8)
|
||||
#define PRIM_A_BLD_FUNC(x) \
|
||||
FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x))
|
||||
#define SEC_C_BLD_FUNC_MASK GENMASK(6, 4)
|
||||
#define SEC_C_BLD_FUNC(x) \
|
||||
FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x))
|
||||
#define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0)
|
||||
#define PRIM_C_BLD_FUNC(x) \
|
||||
FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x))
|
||||
|
||||
#define POSITION 0x14
|
||||
#define XPOS_MASK GENMASK(15, 0)
|
||||
#define XPOS(x) FIELD_PREP(XPOS_MASK, (x))
|
||||
#define YPOS_MASK GENMASK(31, 16)
|
||||
#define YPOS(x) FIELD_PREP(YPOS_MASK, (x))
|
||||
#define XPOS_MASK GENMASK(15, 0)
|
||||
#define XPOS(x) FIELD_PREP(XPOS_MASK, (x))
|
||||
|
||||
enum dc_lb_blend_func {
|
||||
DC_LAYERBLEND_BLEND_ZERO,
|
||||
|
||||
@@ -106,7 +106,7 @@ dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)
|
||||
}
|
||||
|
||||
crtc_state =
|
||||
drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
|
||||
drm_atomic_get_new_crtc_state(state, plane_state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -159,8 +159,8 @@ static int dcss_plane_atomic_check(struct drm_plane *plane,
|
||||
dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
WARN_ON(!dma_obj);
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
|
||||
hdisplay = crtc_state->adjusted_mode.hdisplay;
|
||||
vdisplay = crtc_state->adjusted_mode.vdisplay;
|
||||
|
||||
@@ -386,8 +386,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
||||
return -EINVAL;
|
||||
|
||||
crtc_state =
|
||||
drm_atomic_get_existing_crtc_state(state,
|
||||
new_state->crtc);
|
||||
drm_atomic_get_new_crtc_state(state, new_state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -247,8 +247,8 @@ static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct ingenic_drm_private_state *priv_state;
|
||||
unsigned int next_id;
|
||||
|
||||
priv_state = ingenic_drm_get_priv_state(priv, state);
|
||||
if (WARN_ON(IS_ERR(priv_state)))
|
||||
priv_state = ingenic_drm_get_new_priv_state(priv, state);
|
||||
if (WARN_ON(!priv_state))
|
||||
return;
|
||||
|
||||
/* Set addresses of our DMA descriptor chains */
|
||||
@@ -340,6 +340,7 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
crtc);
|
||||
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
|
||||
struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL;
|
||||
struct ingenic_drm_private_state *priv_state;
|
||||
|
||||
if (crtc_state->gamma_lut &&
|
||||
drm_color_lut_size(crtc_state->gamma_lut) != ARRAY_SIZE(priv->dma_hwdescs->palette)) {
|
||||
@@ -347,6 +348,11 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We will need the state in atomic_enable, so let's make sure it's part of the state */
|
||||
priv_state = ingenic_drm_get_priv_state(priv, state);
|
||||
if (IS_ERR(priv_state))
|
||||
return PTR_ERR(priv_state);
|
||||
|
||||
if (drm_atomic_crtc_needs_modeset(crtc_state) && priv->soc_info->has_osd) {
|
||||
f1_state = drm_atomic_get_plane_state(crtc_state->state,
|
||||
&priv->f1);
|
||||
@@ -471,8 +477,7 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane,
|
||||
if (priv->soc_info->plane_f0_not_working && plane == &priv->f0)
|
||||
return -EINVAL;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state,
|
||||
crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -580,7 +580,7 @@ static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -705,7 +705,7 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane,
|
||||
ipu->sharpness = val;
|
||||
|
||||
if (state->crtc) {
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -129,8 +129,7 @@ static int kmb_plane_atomic_check(struct drm_plane *plane,
|
||||
}
|
||||
can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY);
|
||||
crtc_state =
|
||||
drm_atomic_get_existing_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
|
||||
return drm_atomic_helper_check_plane_state(new_plane_state,
|
||||
crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
|
||||
@@ -96,8 +96,8 @@ static int logicvc_plane_atomic_check(struct drm_plane *drm_plane,
|
||||
if (!new_state->crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(new_state->state,
|
||||
new_state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
|
||||
new_state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ static int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
|
||||
if (!crtc_state->active)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -122,7 +122,8 @@ static int mtk_plane_atomic_async_check(struct drm_plane *plane,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, new_plane_state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
|
||||
@@ -336,8 +336,7 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane,
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state,
|
||||
crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -373,8 +372,8 @@ static int mdp5_plane_atomic_async_check(struct drm_plane *plane,
|
||||
int min_scale, max_scale;
|
||||
int ret;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state,
|
||||
new_plane_state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -765,7 +765,7 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
|
||||
{
|
||||
struct nouveau_display *disp = nouveau_display(dev);
|
||||
|
||||
drm_client_dev_suspend(dev, false);
|
||||
drm_client_dev_suspend(dev);
|
||||
|
||||
if (drm_drv_uses_atomic_modeset(dev)) {
|
||||
if (!runtime) {
|
||||
@@ -796,7 +796,7 @@ nouveau_display_resume(struct drm_device *dev, bool runtime)
|
||||
}
|
||||
}
|
||||
|
||||
drm_client_dev_resume(dev, false);
|
||||
drm_client_dev_resume(dev);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -229,7 +229,7 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
/* we should have a crtc state if the plane is attached to a crtc */
|
||||
if (WARN_ON(!crtc_state))
|
||||
return 0;
|
||||
|
||||
@@ -1888,6 +1888,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"),
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"),
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x203d, &delay_200_500_e50, "B140HTN02.0"),
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x205c, &delay_200_500_e50, "B116XAN02.0"),
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x208d, &delay_200_500_e50, "B140HTN02.1"),
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"),
|
||||
EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"),
|
||||
|
||||
@@ -74,7 +74,7 @@ static int panfrost_devfreq_get_dev_status(struct device *dev,
|
||||
|
||||
spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
|
||||
|
||||
dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
|
||||
dev_dbg(pfdev->base.dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
|
||||
status->busy_time, status->total_time,
|
||||
status->busy_time / (status->total_time / 100),
|
||||
status->current_frequency / 1000 / 1000);
|
||||
@@ -119,7 +119,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
|
||||
int ret;
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long cur_freq;
|
||||
struct device *dev = &pfdev->pdev->dev;
|
||||
struct device *dev = pfdev->base.dev;
|
||||
struct devfreq *devfreq;
|
||||
struct thermal_cooling_device *cooling;
|
||||
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
static int panfrost_reset_init(struct panfrost_device *pfdev)
|
||||
{
|
||||
pfdev->rstc = devm_reset_control_array_get_optional_exclusive(pfdev->dev);
|
||||
pfdev->rstc = devm_reset_control_array_get_optional_exclusive(pfdev->base.dev);
|
||||
if (IS_ERR(pfdev->rstc)) {
|
||||
dev_err(pfdev->dev, "get reset failed %ld\n", PTR_ERR(pfdev->rstc));
|
||||
dev_err(pfdev->base.dev, "get reset failed %ld\n", PTR_ERR(pfdev->rstc));
|
||||
return PTR_ERR(pfdev->rstc);
|
||||
}
|
||||
|
||||
@@ -39,22 +39,22 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
|
||||
int err;
|
||||
unsigned long rate;
|
||||
|
||||
pfdev->clock = devm_clk_get(pfdev->dev, NULL);
|
||||
pfdev->clock = devm_clk_get(pfdev->base.dev, NULL);
|
||||
if (IS_ERR(pfdev->clock)) {
|
||||
dev_err(pfdev->dev, "get clock failed %ld\n", PTR_ERR(pfdev->clock));
|
||||
dev_err(pfdev->base.dev, "get clock failed %ld\n", PTR_ERR(pfdev->clock));
|
||||
return PTR_ERR(pfdev->clock);
|
||||
}
|
||||
|
||||
rate = clk_get_rate(pfdev->clock);
|
||||
dev_info(pfdev->dev, "clock rate = %lu\n", rate);
|
||||
dev_info(pfdev->base.dev, "clock rate = %lu\n", rate);
|
||||
|
||||
err = clk_prepare_enable(pfdev->clock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pfdev->bus_clock = devm_clk_get_optional(pfdev->dev, "bus");
|
||||
pfdev->bus_clock = devm_clk_get_optional(pfdev->base.dev, "bus");
|
||||
if (IS_ERR(pfdev->bus_clock)) {
|
||||
dev_err(pfdev->dev, "get bus_clock failed %ld\n",
|
||||
dev_err(pfdev->base.dev, "get bus_clock failed %ld\n",
|
||||
PTR_ERR(pfdev->bus_clock));
|
||||
err = PTR_ERR(pfdev->bus_clock);
|
||||
goto disable_clock;
|
||||
@@ -62,7 +62,7 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
|
||||
|
||||
if (pfdev->bus_clock) {
|
||||
rate = clk_get_rate(pfdev->bus_clock);
|
||||
dev_info(pfdev->dev, "bus_clock rate = %lu\n", rate);
|
||||
dev_info(pfdev->base.dev, "bus_clock rate = %lu\n", rate);
|
||||
|
||||
err = clk_prepare_enable(pfdev->bus_clock);
|
||||
if (err)
|
||||
@@ -87,7 +87,7 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
pfdev->regulators = devm_kcalloc(pfdev->dev, pfdev->comp->num_supplies,
|
||||
pfdev->regulators = devm_kcalloc(pfdev->base.dev, pfdev->comp->num_supplies,
|
||||
sizeof(*pfdev->regulators),
|
||||
GFP_KERNEL);
|
||||
if (!pfdev->regulators)
|
||||
@@ -96,12 +96,12 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
|
||||
for (i = 0; i < pfdev->comp->num_supplies; i++)
|
||||
pfdev->regulators[i].supply = pfdev->comp->supply_names[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(pfdev->dev,
|
||||
ret = devm_regulator_bulk_get(pfdev->base.dev,
|
||||
pfdev->comp->num_supplies,
|
||||
pfdev->regulators);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(pfdev->dev, "failed to get regulators: %d\n",
|
||||
dev_err(pfdev->base.dev, "failed to get regulators: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -109,7 +109,7 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
|
||||
ret = regulator_bulk_enable(pfdev->comp->num_supplies,
|
||||
pfdev->regulators);
|
||||
if (ret < 0) {
|
||||
dev_err(pfdev->dev, "failed to enable regulators: %d\n", ret);
|
||||
dev_err(pfdev->base.dev, "failed to enable regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
|
||||
int err;
|
||||
int i, num_domains;
|
||||
|
||||
num_domains = of_count_phandle_with_args(pfdev->dev->of_node,
|
||||
num_domains = of_count_phandle_with_args(pfdev->base.dev->of_node,
|
||||
"power-domains",
|
||||
"#power-domain-cells");
|
||||
|
||||
@@ -156,7 +156,7 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
|
||||
return 0;
|
||||
|
||||
if (num_domains != pfdev->comp->num_pm_domains) {
|
||||
dev_err(pfdev->dev,
|
||||
dev_err(pfdev->base.dev,
|
||||
"Incorrect number of power domains: %d provided, %d needed\n",
|
||||
num_domains, pfdev->comp->num_pm_domains);
|
||||
return -EINVAL;
|
||||
@@ -168,20 +168,21 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
|
||||
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
pfdev->pm_domain_devs[i] =
|
||||
dev_pm_domain_attach_by_name(pfdev->dev,
|
||||
pfdev->comp->pm_domain_names[i]);
|
||||
dev_pm_domain_attach_by_name(pfdev->base.dev,
|
||||
pfdev->comp->pm_domain_names[i]);
|
||||
if (IS_ERR_OR_NULL(pfdev->pm_domain_devs[i])) {
|
||||
err = PTR_ERR(pfdev->pm_domain_devs[i]) ? : -ENODATA;
|
||||
pfdev->pm_domain_devs[i] = NULL;
|
||||
dev_err(pfdev->dev,
|
||||
dev_err(pfdev->base.dev,
|
||||
"failed to get pm-domain %s(%d): %d\n",
|
||||
pfdev->comp->pm_domain_names[i], i, err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pfdev->pm_domain_links[i] = device_link_add(pfdev->dev,
|
||||
pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE);
|
||||
pfdev->pm_domain_links[i] =
|
||||
device_link_add(pfdev->base.dev,
|
||||
pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE);
|
||||
if (!pfdev->pm_domain_links[i]) {
|
||||
dev_err(pfdev->pm_domain_devs[i],
|
||||
"adding device link failed!\n");
|
||||
@@ -220,20 +221,20 @@ int panfrost_device_init(struct panfrost_device *pfdev)
|
||||
|
||||
err = panfrost_reset_init(pfdev);
|
||||
if (err) {
|
||||
dev_err(pfdev->dev, "reset init failed %d\n", err);
|
||||
dev_err(pfdev->base.dev, "reset init failed %d\n", err);
|
||||
goto out_pm_domain;
|
||||
}
|
||||
|
||||
err = panfrost_clk_init(pfdev);
|
||||
if (err) {
|
||||
dev_err(pfdev->dev, "clk init failed %d\n", err);
|
||||
dev_err(pfdev->base.dev, "clk init failed %d\n", err);
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
err = panfrost_devfreq_init(pfdev);
|
||||
if (err) {
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(pfdev->dev, "devfreq init failed %d\n", err);
|
||||
dev_err(pfdev->base.dev, "devfreq init failed %d\n", err);
|
||||
goto out_clk;
|
||||
}
|
||||
|
||||
@@ -244,7 +245,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
|
||||
goto out_devfreq;
|
||||
}
|
||||
|
||||
pfdev->iomem = devm_platform_ioremap_resource(pfdev->pdev, 0);
|
||||
pfdev->iomem = devm_platform_ioremap_resource(to_platform_device(pfdev->base.dev), 0);
|
||||
if (IS_ERR(pfdev->iomem)) {
|
||||
err = PTR_ERR(pfdev->iomem);
|
||||
goto out_regulator;
|
||||
@@ -258,7 +259,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
|
||||
if (err)
|
||||
goto out_gpu;
|
||||
|
||||
err = panfrost_job_init(pfdev);
|
||||
err = panfrost_jm_init(pfdev);
|
||||
if (err)
|
||||
goto out_mmu;
|
||||
|
||||
@@ -268,7 +269,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
|
||||
|
||||
return 0;
|
||||
out_job:
|
||||
panfrost_job_fini(pfdev);
|
||||
panfrost_jm_fini(pfdev);
|
||||
out_mmu:
|
||||
panfrost_mmu_fini(pfdev);
|
||||
out_gpu:
|
||||
@@ -289,7 +290,7 @@ out_pm_domain:
|
||||
void panfrost_device_fini(struct panfrost_device *pfdev)
|
||||
{
|
||||
panfrost_perfcnt_fini(pfdev);
|
||||
panfrost_job_fini(pfdev);
|
||||
panfrost_jm_fini(pfdev);
|
||||
panfrost_mmu_fini(pfdev);
|
||||
panfrost_gpu_fini(pfdev);
|
||||
panfrost_devfreq_fini(pfdev);
|
||||
@@ -399,13 +400,16 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
|
||||
return false;
|
||||
}
|
||||
|
||||
void panfrost_device_reset(struct panfrost_device *pfdev)
|
||||
void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
|
||||
{
|
||||
panfrost_gpu_soft_reset(pfdev);
|
||||
|
||||
panfrost_gpu_power_on(pfdev);
|
||||
panfrost_mmu_reset(pfdev);
|
||||
panfrost_job_enable_interrupts(pfdev);
|
||||
|
||||
panfrost_jm_reset_interrupts(pfdev);
|
||||
if (enable_job_int)
|
||||
panfrost_jm_enable_interrupts(pfdev);
|
||||
}
|
||||
|
||||
static int panfrost_device_runtime_resume(struct device *dev)
|
||||
@@ -429,7 +433,7 @@ static int panfrost_device_runtime_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
panfrost_device_reset(pfdev);
|
||||
panfrost_device_reset(pfdev, true);
|
||||
panfrost_devfreq_resume(pfdev);
|
||||
|
||||
return 0;
|
||||
@@ -447,11 +451,11 @@ static int panfrost_device_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev_get_drvdata(dev);
|
||||
|
||||
if (!panfrost_job_is_idle(pfdev))
|
||||
if (!panfrost_jm_is_idle(pfdev))
|
||||
return -EBUSY;
|
||||
|
||||
panfrost_devfreq_suspend(pfdev);
|
||||
panfrost_job_suspend_irq(pfdev);
|
||||
panfrost_jm_suspend_irq(pfdev);
|
||||
panfrost_mmu_suspend_irq(pfdev);
|
||||
panfrost_gpu_suspend_irq(pfdev);
|
||||
panfrost_gpu_power_off(pfdev);
|
||||
|
||||
@@ -26,6 +26,10 @@ struct panfrost_perfcnt;
|
||||
|
||||
#define MAX_PM_DOMAINS 5
|
||||
|
||||
#define ALL_JS_INT_MASK \
|
||||
(GENMASK(16 + NUM_JOB_SLOTS - 1, 16) | \
|
||||
GENMASK(NUM_JOB_SLOTS - 1, 0))
|
||||
|
||||
enum panfrost_drv_comp_bits {
|
||||
PANFROST_COMP_BIT_GPU,
|
||||
PANFROST_COMP_BIT_JOB,
|
||||
@@ -124,9 +128,7 @@ struct panfrost_device_debugfs {
|
||||
};
|
||||
|
||||
struct panfrost_device {
|
||||
struct device *dev;
|
||||
struct drm_device *ddev;
|
||||
struct platform_device *pdev;
|
||||
struct drm_device base;
|
||||
int gpu_irq;
|
||||
int mmu_irq;
|
||||
|
||||
@@ -145,7 +147,6 @@ struct panfrost_device {
|
||||
DECLARE_BITMAP(is_suspended, PANFROST_COMP_BIT_MAX);
|
||||
|
||||
spinlock_t as_lock;
|
||||
unsigned long as_in_use_mask;
|
||||
unsigned long as_alloc_mask;
|
||||
unsigned long as_faulty_mask;
|
||||
struct list_head as_lru_list;
|
||||
@@ -222,7 +223,7 @@ static inline bool panfrost_high_prio_allowed(struct drm_file *file)
|
||||
|
||||
static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev)
|
||||
{
|
||||
return ddev->dev_private;
|
||||
return container_of(ddev, struct panfrost_device, base);
|
||||
}
|
||||
|
||||
static inline int panfrost_model_cmp(struct panfrost_device *pfdev, s32 id)
|
||||
@@ -248,7 +249,7 @@ int panfrost_unstable_ioctl_check(void);
|
||||
|
||||
int panfrost_device_init(struct panfrost_device *pfdev);
|
||||
void panfrost_device_fini(struct panfrost_device *pfdev);
|
||||
void panfrost_device_reset(struct panfrost_device *pfdev);
|
||||
void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int);
|
||||
|
||||
extern const struct dev_pm_ops panfrost_pm_ops;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(pfdev->dev);
|
||||
ret = pm_runtime_resume_and_get(pfdev->base.dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -44,14 +44,14 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
|
||||
*arg = panfrost_timestamp_read(pfdev);
|
||||
panfrost_cycle_counter_put(pfdev);
|
||||
|
||||
pm_runtime_put(pfdev->dev);
|
||||
pm_runtime_put(pfdev->base.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file)
|
||||
{
|
||||
struct drm_panfrost_get_param *param = data;
|
||||
struct panfrost_device *pfdev = ddev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(ddev);
|
||||
int ret;
|
||||
|
||||
if (param->pad != 0)
|
||||
@@ -283,7 +283,7 @@ fail:
|
||||
static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
struct panfrost_file_priv *file_priv = file->driver_priv;
|
||||
struct drm_panfrost_submit *args = data;
|
||||
struct drm_syncobj *sync_out = NULL;
|
||||
@@ -457,7 +457,7 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
|
||||
{
|
||||
struct panfrost_file_priv *priv = file_priv->driver_priv;
|
||||
struct drm_panfrost_madvise *args = data;
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
struct drm_gem_object *gem_obj;
|
||||
struct panfrost_gem_object *bo;
|
||||
int ret = 0;
|
||||
@@ -590,7 +590,7 @@ static int
|
||||
panfrost_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
int ret;
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
struct panfrost_file_priv *panfrost_priv;
|
||||
|
||||
panfrost_priv = kzalloc(sizeof(*panfrost_priv), GFP_KERNEL);
|
||||
@@ -606,7 +606,7 @@ panfrost_open(struct drm_device *dev, struct drm_file *file)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = panfrost_job_open(file);
|
||||
ret = panfrost_jm_open(file);
|
||||
if (ret)
|
||||
goto err_job;
|
||||
|
||||
@@ -625,7 +625,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file)
|
||||
struct panfrost_file_priv *panfrost_priv = file->driver_priv;
|
||||
|
||||
panfrost_perfcnt_close(file);
|
||||
panfrost_job_close(file);
|
||||
panfrost_jm_close(file);
|
||||
|
||||
panfrost_mmu_ctx_put(panfrost_priv->mmu);
|
||||
kfree(panfrost_priv);
|
||||
@@ -668,30 +668,25 @@ static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev,
|
||||
* job spent on the GPU.
|
||||
*/
|
||||
|
||||
static const char * const engine_names[] = {
|
||||
"fragment", "vertex-tiler", "compute-only"
|
||||
};
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(engine_names) != NUM_JOB_SLOTS);
|
||||
|
||||
for (i = 0; i < NUM_JOB_SLOTS - 1; i++) {
|
||||
if (pfdev->profile_mode) {
|
||||
drm_printf(p, "drm-engine-%s:\t%llu ns\n",
|
||||
engine_names[i], panfrost_priv->engine_usage.elapsed_ns[i]);
|
||||
panfrost_engine_names[i],
|
||||
panfrost_priv->engine_usage.elapsed_ns[i]);
|
||||
drm_printf(p, "drm-cycles-%s:\t%llu\n",
|
||||
engine_names[i], panfrost_priv->engine_usage.cycles[i]);
|
||||
panfrost_engine_names[i],
|
||||
panfrost_priv->engine_usage.cycles[i]);
|
||||
}
|
||||
drm_printf(p, "drm-maxfreq-%s:\t%lu Hz\n",
|
||||
engine_names[i], pfdev->pfdevfreq.fast_rate);
|
||||
panfrost_engine_names[i], pfdev->pfdevfreq.fast_rate);
|
||||
drm_printf(p, "drm-curfreq-%s:\t%lu Hz\n",
|
||||
engine_names[i], pfdev->pfdevfreq.current_frequency);
|
||||
panfrost_engine_names[i], pfdev->pfdevfreq.current_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
static void panfrost_show_fdinfo(struct drm_printer *p, struct drm_file *file)
|
||||
{
|
||||
struct drm_device *dev = file->minor->dev;
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(file->minor->dev);
|
||||
|
||||
panfrost_gpu_show_fdinfo(pfdev, file->driver_priv, p);
|
||||
|
||||
@@ -708,8 +703,7 @@ static const struct file_operations panfrost_drm_driver_fops = {
|
||||
static int panthor_gems_show(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
|
||||
|
||||
panfrost_gem_debugfs_print_bos(pfdev, m);
|
||||
|
||||
@@ -758,7 +752,8 @@ static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
|
||||
}
|
||||
|
||||
static struct drm_info_list panthor_debugfs_list[] = {
|
||||
{"gems", panthor_gems_show, 0, NULL},
|
||||
{"gems",
|
||||
panthor_gems_show, 0, NULL},
|
||||
};
|
||||
|
||||
static int panthor_gems_debugfs_init(struct drm_minor *minor)
|
||||
@@ -865,15 +860,12 @@ static const struct drm_driver panfrost_drm_driver = {
|
||||
static int panfrost_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct panfrost_device *pfdev;
|
||||
struct drm_device *ddev;
|
||||
int err;
|
||||
|
||||
pfdev = devm_kzalloc(&pdev->dev, sizeof(*pfdev), GFP_KERNEL);
|
||||
if (!pfdev)
|
||||
return -ENOMEM;
|
||||
|
||||
pfdev->pdev = pdev;
|
||||
pfdev->dev = &pdev->dev;
|
||||
pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
|
||||
struct panfrost_device, base);
|
||||
if (IS_ERR(pfdev))
|
||||
return PTR_ERR(pfdev);
|
||||
|
||||
platform_set_drvdata(pdev, pfdev);
|
||||
|
||||
@@ -883,14 +875,6 @@ static int panfrost_probe(struct platform_device *pdev)
|
||||
|
||||
pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
|
||||
|
||||
/* Allocate and initialize the DRM device. */
|
||||
ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev);
|
||||
if (IS_ERR(ddev))
|
||||
return PTR_ERR(ddev);
|
||||
|
||||
ddev->dev_private = pfdev;
|
||||
pfdev->ddev = ddev;
|
||||
|
||||
mutex_init(&pfdev->shrinker_lock);
|
||||
INIT_LIST_HEAD(&pfdev->shrinker_list);
|
||||
|
||||
@@ -901,51 +885,47 @@ static int panfrost_probe(struct platform_device *pdev)
|
||||
goto err_out0;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(pfdev->dev);
|
||||
pm_runtime_mark_last_busy(pfdev->dev);
|
||||
pm_runtime_enable(pfdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */
|
||||
pm_runtime_use_autosuspend(pfdev->dev);
|
||||
pm_runtime_set_active(pfdev->base.dev);
|
||||
pm_runtime_mark_last_busy(pfdev->base.dev);
|
||||
pm_runtime_enable(pfdev->base.dev);
|
||||
pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
|
||||
pm_runtime_use_autosuspend(pfdev->base.dev);
|
||||
|
||||
/*
|
||||
* Register the DRM device with the core and the connectors with
|
||||
* sysfs
|
||||
*/
|
||||
err = drm_dev_register(ddev, 0);
|
||||
err = drm_dev_register(&pfdev->base, 0);
|
||||
if (err < 0)
|
||||
goto err_out1;
|
||||
|
||||
err = panfrost_gem_shrinker_init(ddev);
|
||||
err = panfrost_gem_shrinker_init(&pfdev->base);
|
||||
if (err)
|
||||
goto err_out2;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out2:
|
||||
drm_dev_unregister(ddev);
|
||||
drm_dev_unregister(&pfdev->base);
|
||||
err_out1:
|
||||
pm_runtime_disable(pfdev->dev);
|
||||
pm_runtime_disable(pfdev->base.dev);
|
||||
panfrost_device_fini(pfdev);
|
||||
pm_runtime_set_suspended(pfdev->dev);
|
||||
pm_runtime_set_suspended(pfdev->base.dev);
|
||||
err_out0:
|
||||
drm_dev_put(ddev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void panfrost_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panfrost_device *pfdev = platform_get_drvdata(pdev);
|
||||
struct drm_device *ddev = pfdev->ddev;
|
||||
|
||||
drm_dev_unregister(ddev);
|
||||
panfrost_gem_shrinker_cleanup(ddev);
|
||||
drm_dev_unregister(&pfdev->base);
|
||||
panfrost_gem_shrinker_cleanup(&pfdev->base);
|
||||
|
||||
pm_runtime_get_sync(pfdev->dev);
|
||||
pm_runtime_disable(pfdev->dev);
|
||||
pm_runtime_get_sync(pfdev->base.dev);
|
||||
pm_runtime_disable(pfdev->base.dev);
|
||||
panfrost_device_fini(pfdev);
|
||||
pm_runtime_set_suspended(pfdev->dev);
|
||||
|
||||
drm_dev_put(ddev);
|
||||
pm_runtime_set_suspended(pfdev->base.dev);
|
||||
}
|
||||
|
||||
static ssize_t profiling_show(struct device *dev,
|
||||
|
||||
@@ -163,7 +163,7 @@ void panfrost_core_dump(struct panfrost_job *job)
|
||||
iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
|
||||
__GFP_NORETRY);
|
||||
if (!iter.start) {
|
||||
dev_warn(pfdev->dev, "failed to allocate devcoredump file\n");
|
||||
dev_warn(pfdev->base.dev, "failed to allocate devcoredump file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -204,14 +204,14 @@ void panfrost_core_dump(struct panfrost_job *job)
|
||||
mapping = job->mappings[i];
|
||||
|
||||
if (!bo->base.sgt) {
|
||||
dev_err(pfdev->dev, "Panfrost Dump: BO has no sgt, cannot dump\n");
|
||||
dev_err(pfdev->base.dev, "Panfrost Dump: BO has no sgt, cannot dump\n");
|
||||
iter.hdr->bomap.valid = 0;
|
||||
goto dump_header;
|
||||
}
|
||||
|
||||
ret = drm_gem_vmap(&bo->base.base, &map);
|
||||
if (ret) {
|
||||
dev_err(pfdev->dev, "Panfrost Dump: couldn't map Buffer Object\n");
|
||||
dev_err(pfdev->base.dev, "Panfrost Dump: couldn't map Buffer Object\n");
|
||||
iter.hdr->bomap.valid = 0;
|
||||
goto dump_header;
|
||||
}
|
||||
@@ -237,5 +237,5 @@ dump_header: panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BO, iter.data +
|
||||
}
|
||||
panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_TRAILER, iter.data);
|
||||
|
||||
dev_coredumpv(pfdev->dev, iter.start, iter.data - iter.start, GFP_KERNEL);
|
||||
dev_coredumpv(pfdev->base.dev, iter.start, iter.data - iter.start, GFP_KERNEL);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev,
|
||||
|
||||
static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo)
|
||||
{
|
||||
struct panfrost_device *pfdev = bo->base.base.dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(bo->base.base.dev);
|
||||
|
||||
if (list_empty(&bo->debugfs.node))
|
||||
return;
|
||||
@@ -48,7 +48,7 @@ static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo) {}
|
||||
static void panfrost_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct panfrost_gem_object *bo = to_panfrost_bo(obj);
|
||||
struct panfrost_device *pfdev = obj->dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
|
||||
|
||||
/*
|
||||
* Make sure the BO is no longer inserted in the shrinker list before
|
||||
@@ -76,7 +76,7 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
|
||||
|
||||
for (i = 0; i < n_sgt; i++) {
|
||||
if (bo->sgts[i].sgl) {
|
||||
dma_unmap_sgtable(pfdev->dev, &bo->sgts[i],
|
||||
dma_unmap_sgtable(pfdev->base.dev, &bo->sgts[i],
|
||||
DMA_BIDIRECTIONAL, 0);
|
||||
sg_free_table(&bo->sgts[i]);
|
||||
}
|
||||
@@ -284,7 +284,7 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = {
|
||||
*/
|
||||
struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
struct panfrost_gem_object *obj;
|
||||
|
||||
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
||||
|
||||
@@ -97,7 +97,7 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
*/
|
||||
int panfrost_gem_shrinker_init(struct drm_device *dev)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
|
||||
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
|
||||
if (!pfdev->shrinker)
|
||||
@@ -120,7 +120,7 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
|
||||
*/
|
||||
void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
|
||||
if (pfdev->shrinker)
|
||||
shrinker_free(pfdev->shrinker);
|
||||
|
||||
@@ -36,12 +36,12 @@ static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data)
|
||||
u64 address = (u64) gpu_read(pfdev, GPU_FAULT_ADDRESS_HI) << 32;
|
||||
address |= gpu_read(pfdev, GPU_FAULT_ADDRESS_LO);
|
||||
|
||||
dev_warn(pfdev->dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n",
|
||||
dev_warn(pfdev->base.dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n",
|
||||
fault_status, panfrost_exception_name(fault_status & 0xFF),
|
||||
address);
|
||||
|
||||
if (state & GPU_IRQ_MULTIPLE_FAULT)
|
||||
dev_warn(pfdev->dev, "There were multiple GPU faults - some have not been reported\n");
|
||||
dev_warn(pfdev->base.dev, "There were multiple GPU faults - some have not been reported\n");
|
||||
|
||||
gpu_write(pfdev, GPU_INT_MASK, 0);
|
||||
}
|
||||
@@ -72,13 +72,13 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
|
||||
val, val & GPU_IRQ_RESET_COMPLETED, 10, 10000);
|
||||
|
||||
if (ret) {
|
||||
dev_err(pfdev->dev, "gpu soft reset timed out, attempting hard reset\n");
|
||||
dev_err(pfdev->base.dev, "gpu soft reset timed out, attempting hard reset\n");
|
||||
|
||||
gpu_write(pfdev, GPU_CMD, GPU_CMD_HARD_RESET);
|
||||
ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, val,
|
||||
val & GPU_IRQ_RESET_COMPLETED, 100, 10000);
|
||||
if (ret) {
|
||||
dev_err(pfdev->dev, "gpu hard reset timed out\n");
|
||||
dev_err(pfdev->base.dev, "gpu hard reset timed out\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
|
||||
* All in-flight jobs should have released their cycle
|
||||
* counter references upon reset, but let us make sure
|
||||
*/
|
||||
if (drm_WARN_ON(pfdev->ddev, atomic_read(&pfdev->cycle_counter.use_count) != 0))
|
||||
if (drm_WARN_ON(&pfdev->base, atomic_read(&pfdev->cycle_counter.use_count) != 0))
|
||||
atomic_set(&pfdev->cycle_counter.use_count, 0);
|
||||
|
||||
return 0;
|
||||
@@ -240,9 +240,10 @@ static const struct panfrost_model gpu_models[] = {
|
||||
/* MediaTek MT8188 Mali-G57 MC3 */
|
||||
GPU_MODEL(g57, 0x9093,
|
||||
GPU_REV(g57, 0, 0)),
|
||||
{0},
|
||||
};
|
||||
|
||||
static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
|
||||
static int panfrost_gpu_init_features(struct panfrost_device *pfdev)
|
||||
{
|
||||
u32 gpu_id, num_js, major, minor, status, rev;
|
||||
const char *name = "unknown";
|
||||
@@ -327,16 +328,22 @@ static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!model->name) {
|
||||
dev_err(pfdev->base.dev, "GPU model not found: mali-%s id rev %#x %#x\n",
|
||||
name, gpu_id, rev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bitmap_from_u64(pfdev->features.hw_features, hw_feat);
|
||||
bitmap_from_u64(pfdev->features.hw_issues, hw_issues);
|
||||
|
||||
dev_info(pfdev->dev, "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x",
|
||||
dev_info(pfdev->base.dev, "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x",
|
||||
name, gpu_id, major, minor, status);
|
||||
dev_info(pfdev->dev, "features: %64pb, issues: %64pb",
|
||||
dev_info(pfdev->base.dev, "features: %64pb, issues: %64pb",
|
||||
pfdev->features.hw_features,
|
||||
pfdev->features.hw_issues);
|
||||
|
||||
dev_info(pfdev->dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x",
|
||||
dev_info(pfdev->base.dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x",
|
||||
pfdev->features.l2_features,
|
||||
pfdev->features.core_features,
|
||||
pfdev->features.tiler_features,
|
||||
@@ -345,8 +352,10 @@ static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
|
||||
pfdev->features.as_present,
|
||||
pfdev->features.js_present);
|
||||
|
||||
dev_info(pfdev->dev, "shader_present=0x%0llx l2_present=0x%0llx",
|
||||
dev_info(pfdev->base.dev, "shader_present=0x%0llx l2_present=0x%0llx",
|
||||
pfdev->features.shader_present, pfdev->features.l2_present);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void panfrost_cycle_counter_get(struct panfrost_device *pfdev)
|
||||
@@ -411,7 +420,7 @@ static u64 panfrost_get_core_mask(struct panfrost_device *pfdev)
|
||||
*/
|
||||
core_mask = ~(pfdev->features.l2_present - 1) &
|
||||
(pfdev->features.l2_present - 2);
|
||||
dev_info_once(pfdev->dev, "using only 1st core group (%lu cores from %lu)\n",
|
||||
dev_info_once(pfdev->base.dev, "using only 1st core group (%lu cores from %lu)\n",
|
||||
hweight64(core_mask),
|
||||
hweight64(pfdev->features.shader_present));
|
||||
|
||||
@@ -432,7 +441,7 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
|
||||
val, val == (pfdev->features.l2_present & core_mask),
|
||||
10, 20000);
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "error powering up gpu L2");
|
||||
dev_err(pfdev->base.dev, "error powering up gpu L2");
|
||||
|
||||
gpu_write(pfdev, SHADER_PWRON_LO,
|
||||
pfdev->features.shader_present & core_mask);
|
||||
@@ -440,13 +449,13 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
|
||||
val, val == (pfdev->features.shader_present & core_mask),
|
||||
10, 20000);
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "error powering up gpu shader");
|
||||
dev_err(pfdev->base.dev, "error powering up gpu shader");
|
||||
|
||||
gpu_write(pfdev, TILER_PWRON_LO, pfdev->features.tiler_present);
|
||||
ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO,
|
||||
val, val == pfdev->features.tiler_present, 10, 1000);
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "error powering up gpu tiler");
|
||||
dev_err(pfdev->base.dev, "error powering up gpu tiler");
|
||||
}
|
||||
|
||||
void panfrost_gpu_power_off(struct panfrost_device *pfdev)
|
||||
@@ -458,19 +467,19 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
|
||||
ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_PWRTRANS_LO,
|
||||
val, !val, 1, 2000);
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "shader power transition timeout");
|
||||
dev_err(pfdev->base.dev, "shader power transition timeout");
|
||||
|
||||
gpu_write(pfdev, TILER_PWROFF_LO, pfdev->features.tiler_present);
|
||||
ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_PWRTRANS_LO,
|
||||
val, !val, 1, 2000);
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "tiler power transition timeout");
|
||||
dev_err(pfdev->base.dev, "tiler power transition timeout");
|
||||
|
||||
gpu_write(pfdev, L2_PWROFF_LO, pfdev->features.l2_present);
|
||||
ret = readl_poll_timeout(pfdev->iomem + L2_PWRTRANS_LO,
|
||||
val, !val, 0, 2000);
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "l2 power transition timeout");
|
||||
dev_err(pfdev->base.dev, "l2 power transition timeout");
|
||||
}
|
||||
|
||||
void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
|
||||
@@ -489,23 +498,26 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
panfrost_gpu_init_features(pfdev);
|
||||
|
||||
err = dma_set_mask_and_coherent(pfdev->dev,
|
||||
DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features)));
|
||||
err = panfrost_gpu_init_features(pfdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dma_set_max_seg_size(pfdev->dev, UINT_MAX);
|
||||
err = dma_set_mask_and_coherent(pfdev->base.dev,
|
||||
DMA_BIT_MASK(FIELD_GET(0xff00,
|
||||
pfdev->features.mmu_features)));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
|
||||
dma_set_max_seg_size(pfdev->base.dev, UINT_MAX);
|
||||
|
||||
pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "gpu");
|
||||
if (pfdev->gpu_irq < 0)
|
||||
return pfdev->gpu_irq;
|
||||
|
||||
err = devm_request_irq(pfdev->dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
|
||||
err = devm_request_irq(pfdev->base.dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev);
|
||||
if (err) {
|
||||
dev_err(pfdev->dev, "failed to request gpu irq");
|
||||
dev_err(pfdev->base.dev, "failed to request gpu irq");
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -525,9 +537,9 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev)
|
||||
|
||||
if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION)) {
|
||||
/* Flush reduction only makes sense when the GPU is kept powered on between jobs */
|
||||
if (pm_runtime_get_if_in_use(pfdev->dev)) {
|
||||
if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
|
||||
flush_id = gpu_read(pfdev, GPU_LATEST_FLUSH_ID);
|
||||
pm_runtime_put(pfdev->dev);
|
||||
pm_runtime_put(pfdev->base.dev);
|
||||
return flush_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
#define job_write(dev, reg, data) writel(data, dev->iomem + (reg))
|
||||
#define job_read(dev, reg) readl(dev->iomem + (reg))
|
||||
|
||||
const char * const panfrost_engine_names[] = {
|
||||
"fragment", "vertex-tiler", "compute-only"
|
||||
};
|
||||
|
||||
struct panfrost_queue_state {
|
||||
struct drm_gpu_scheduler sched;
|
||||
u64 fence_context;
|
||||
@@ -95,7 +99,7 @@ static struct dma_fence *panfrost_fence_create(struct panfrost_device *pfdev, in
|
||||
if (!fence)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fence->dev = pfdev->ddev;
|
||||
fence->dev = &pfdev->base;
|
||||
fence->queue = js_num;
|
||||
fence->seqno = ++js->queue[js_num].emit_seqno;
|
||||
dma_fence_init(&fence->base, &panfrost_fence_ops, &js->job_lock,
|
||||
@@ -196,7 +200,7 @@ panfrost_enqueue_job(struct panfrost_device *pfdev, int slot,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
|
||||
static int panfrost_job_hw_submit(struct panfrost_job *job, int js)
|
||||
{
|
||||
struct panfrost_device *pfdev = job->pfdev;
|
||||
unsigned int subslot;
|
||||
@@ -204,17 +208,22 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
|
||||
u64 jc_head = job->jc;
|
||||
int ret;
|
||||
|
||||
panfrost_devfreq_record_busy(&pfdev->pfdevfreq);
|
||||
|
||||
ret = pm_runtime_get_sync(pfdev->dev);
|
||||
ret = pm_runtime_get_sync(pfdev->base.dev);
|
||||
if (ret < 0)
|
||||
return;
|
||||
goto err_hwsubmit;
|
||||
|
||||
if (WARN_ON(job_read(pfdev, JS_COMMAND_NEXT(js)))) {
|
||||
return;
|
||||
ret = -EINVAL;
|
||||
goto err_hwsubmit;
|
||||
}
|
||||
|
||||
cfg = panfrost_mmu_as_get(pfdev, job->mmu);
|
||||
ret = panfrost_mmu_as_get(pfdev, job->mmu);
|
||||
if (ret < 0)
|
||||
goto err_hwsubmit;
|
||||
|
||||
cfg = ret;
|
||||
|
||||
panfrost_devfreq_record_busy(&pfdev->pfdevfreq);
|
||||
|
||||
job_write(pfdev, JS_HEAD_NEXT_LO(js), lower_32_bits(jc_head));
|
||||
job_write(pfdev, JS_HEAD_NEXT_HI(js), upper_32_bits(jc_head));
|
||||
@@ -257,11 +266,17 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
|
||||
}
|
||||
|
||||
job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START);
|
||||
dev_dbg(pfdev->dev,
|
||||
dev_dbg(pfdev->base.dev,
|
||||
"JS: Submitting atom %p to js[%d][%d] with head=0x%llx AS %d",
|
||||
job, js, subslot, jc_head, cfg & 0xf);
|
||||
}
|
||||
spin_unlock(&pfdev->js->job_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_hwsubmit:
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int panfrost_acquire_object_fences(struct drm_gem_object **bos,
|
||||
@@ -384,6 +399,7 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job)
|
||||
struct panfrost_device *pfdev = job->pfdev;
|
||||
int slot = panfrost_job_get_slot(job);
|
||||
struct dma_fence *fence = NULL;
|
||||
int ret;
|
||||
|
||||
if (job->ctx->destroyed)
|
||||
return ERR_PTR(-ECANCELED);
|
||||
@@ -405,27 +421,27 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job)
|
||||
dma_fence_put(job->done_fence);
|
||||
job->done_fence = dma_fence_get(fence);
|
||||
|
||||
panfrost_job_hw_submit(job, slot);
|
||||
ret = panfrost_job_hw_submit(job, slot);
|
||||
if (ret) {
|
||||
dma_fence_put(fence);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return fence;
|
||||
}
|
||||
|
||||
void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
|
||||
void panfrost_jm_reset_interrupts(struct panfrost_device *pfdev)
|
||||
{
|
||||
int j;
|
||||
u32 irq_mask = 0;
|
||||
|
||||
clear_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
|
||||
|
||||
for (j = 0; j < NUM_JOB_SLOTS; j++) {
|
||||
irq_mask |= MK_JS_MASK(j);
|
||||
}
|
||||
|
||||
job_write(pfdev, JOB_INT_CLEAR, irq_mask);
|
||||
job_write(pfdev, JOB_INT_MASK, irq_mask);
|
||||
job_write(pfdev, JOB_INT_CLEAR, ALL_JS_INT_MASK);
|
||||
}
|
||||
|
||||
void panfrost_job_suspend_irq(struct panfrost_device *pfdev)
|
||||
void panfrost_jm_enable_interrupts(struct panfrost_device *pfdev)
|
||||
{
|
||||
clear_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
|
||||
job_write(pfdev, JOB_INT_MASK, ALL_JS_INT_MASK);
|
||||
}
|
||||
|
||||
void panfrost_jm_suspend_irq(struct panfrost_device *pfdev)
|
||||
{
|
||||
set_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
|
||||
|
||||
@@ -442,12 +458,12 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev,
|
||||
bool signal_fence = true;
|
||||
|
||||
if (!panfrost_exception_is_fault(js_status)) {
|
||||
dev_dbg(pfdev->dev, "js event, js=%d, status=%s, head=0x%x, tail=0x%x",
|
||||
dev_dbg(pfdev->base.dev, "js event, js=%d, status=%s, head=0x%x, tail=0x%x",
|
||||
js, exception_name,
|
||||
job_read(pfdev, JS_HEAD_LO(js)),
|
||||
job_read(pfdev, JS_TAIL_LO(js)));
|
||||
} else {
|
||||
dev_err(pfdev->dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x",
|
||||
dev_err(pfdev->base.dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x",
|
||||
js, exception_name,
|
||||
job_read(pfdev, JS_HEAD_LO(js)),
|
||||
job_read(pfdev, JS_TAIL_LO(js)));
|
||||
@@ -479,7 +495,7 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev,
|
||||
if (signal_fence)
|
||||
dma_fence_signal_locked(job->done_fence);
|
||||
|
||||
pm_runtime_put_autosuspend(pfdev->dev);
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
|
||||
if (panfrost_exception_needs_reset(pfdev, js_status)) {
|
||||
atomic_set(&pfdev->reset.pending, 1);
|
||||
@@ -487,8 +503,8 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev,
|
||||
}
|
||||
}
|
||||
|
||||
static void panfrost_job_handle_done(struct panfrost_device *pfdev,
|
||||
struct panfrost_job *job)
|
||||
static void panfrost_jm_handle_done(struct panfrost_device *pfdev,
|
||||
struct panfrost_job *job)
|
||||
{
|
||||
/* Set ->jc to 0 to avoid re-submitting an already finished job (can
|
||||
* happen when we receive the DONE interrupt while doing a GPU reset).
|
||||
@@ -498,10 +514,10 @@ static void panfrost_job_handle_done(struct panfrost_device *pfdev,
|
||||
panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
|
||||
|
||||
dma_fence_signal_locked(job->done_fence);
|
||||
pm_runtime_put_autosuspend(pfdev->dev);
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
}
|
||||
|
||||
static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
|
||||
static void panfrost_jm_handle_irq(struct panfrost_device *pfdev, u32 status)
|
||||
{
|
||||
struct panfrost_job *done[NUM_JOB_SLOTS][2] = {};
|
||||
struct panfrost_job *failed[NUM_JOB_SLOTS] = {};
|
||||
@@ -576,7 +592,7 @@ static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(done[0]) && done[j][i]; i++)
|
||||
panfrost_job_handle_done(pfdev, done[j][i]);
|
||||
panfrost_jm_handle_done(pfdev, done[j][i]);
|
||||
}
|
||||
|
||||
/* And finally we requeue jobs that were waiting in the second slot
|
||||
@@ -594,7 +610,7 @@ static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
|
||||
struct panfrost_job *canceled = panfrost_dequeue_job(pfdev, j);
|
||||
|
||||
dma_fence_set_error(canceled->done_fence, -ECANCELED);
|
||||
panfrost_job_handle_done(pfdev, canceled);
|
||||
panfrost_jm_handle_done(pfdev, canceled);
|
||||
} else if (!atomic_read(&pfdev->reset.pending)) {
|
||||
/* Requeue the job we removed if no reset is pending */
|
||||
job_write(pfdev, JS_COMMAND_NEXT(j), JS_COMMAND_START);
|
||||
@@ -602,15 +618,15 @@ static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
|
||||
}
|
||||
}
|
||||
|
||||
static void panfrost_job_handle_irqs(struct panfrost_device *pfdev)
|
||||
static void panfrost_jm_handle_irqs(struct panfrost_device *pfdev)
|
||||
{
|
||||
u32 status = job_read(pfdev, JOB_INT_RAWSTAT);
|
||||
|
||||
while (status) {
|
||||
pm_runtime_mark_last_busy(pfdev->dev);
|
||||
pm_runtime_mark_last_busy(pfdev->base.dev);
|
||||
|
||||
spin_lock(&pfdev->js->job_lock);
|
||||
panfrost_job_handle_irq(pfdev, status);
|
||||
panfrost_jm_handle_irq(pfdev, status);
|
||||
spin_unlock(&pfdev->js->job_lock);
|
||||
status = job_read(pfdev, JOB_INT_RAWSTAT);
|
||||
}
|
||||
@@ -688,10 +704,10 @@ panfrost_reset(struct panfrost_device *pfdev,
|
||||
10, 10000);
|
||||
|
||||
if (ret)
|
||||
dev_err(pfdev->dev, "Soft-stop failed\n");
|
||||
dev_err(pfdev->base.dev, "Soft-stop failed\n");
|
||||
|
||||
/* Handle the remaining interrupts before we reset. */
|
||||
panfrost_job_handle_irqs(pfdev);
|
||||
panfrost_jm_handle_irqs(pfdev);
|
||||
|
||||
/* Remaining interrupts have been handled, but we might still have
|
||||
* stuck jobs. Let's make sure the PM counters stay balanced by
|
||||
@@ -706,7 +722,7 @@ panfrost_reset(struct panfrost_device *pfdev,
|
||||
if (pfdev->jobs[i][j]->requirements & PANFROST_JD_REQ_CYCLE_COUNT ||
|
||||
pfdev->jobs[i][j]->is_profiled)
|
||||
panfrost_cycle_counter_put(pfdev->jobs[i][j]->pfdev);
|
||||
pm_runtime_put_noidle(pfdev->dev);
|
||||
pm_runtime_put_noidle(pfdev->base.dev);
|
||||
panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
|
||||
}
|
||||
}
|
||||
@@ -714,12 +730,7 @@ panfrost_reset(struct panfrost_device *pfdev,
|
||||
spin_unlock(&pfdev->js->job_lock);
|
||||
|
||||
/* Proceed with reset now. */
|
||||
panfrost_device_reset(pfdev);
|
||||
|
||||
/* panfrost_device_reset() unmasks job interrupts, but we want to
|
||||
* keep them masked a bit longer.
|
||||
*/
|
||||
job_write(pfdev, JOB_INT_MASK, 0);
|
||||
panfrost_device_reset(pfdev, false);
|
||||
|
||||
/* GPU has been reset, we can clear the reset pending bit. */
|
||||
atomic_set(&pfdev->reset.pending, 0);
|
||||
@@ -741,9 +752,7 @@ panfrost_reset(struct panfrost_device *pfdev,
|
||||
drm_sched_start(&pfdev->js->queue[i].sched, 0);
|
||||
|
||||
/* Re-enable job interrupts now that everything has been restarted. */
|
||||
job_write(pfdev, JOB_INT_MASK,
|
||||
GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
|
||||
GENMASK(NUM_JOB_SLOTS - 1, 0));
|
||||
panfrost_jm_enable_interrupts(pfdev);
|
||||
|
||||
dma_fence_end_signalling(cookie);
|
||||
}
|
||||
@@ -774,11 +783,11 @@ static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job
|
||||
synchronize_irq(pfdev->js->irq);
|
||||
|
||||
if (dma_fence_is_signaled(job->done_fence)) {
|
||||
dev_warn(pfdev->dev, "unexpectedly high interrupt latency\n");
|
||||
dev_warn(pfdev->base.dev, "unexpectedly high interrupt latency\n");
|
||||
return DRM_GPU_SCHED_STAT_NO_HANG;
|
||||
}
|
||||
|
||||
dev_err(pfdev->dev, "gpu sched timeout, js=%d, config=0x%x, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p",
|
||||
dev_err(pfdev->base.dev, "gpu sched timeout, js=%d, config=0x%x, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p",
|
||||
js,
|
||||
job_read(pfdev, JS_CONFIG(js)),
|
||||
job_read(pfdev, JS_STATUS(js)),
|
||||
@@ -808,22 +817,20 @@ static const struct drm_sched_backend_ops panfrost_sched_ops = {
|
||||
.free_job = panfrost_job_free
|
||||
};
|
||||
|
||||
static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
|
||||
static irqreturn_t panfrost_jm_irq_handler_thread(int irq, void *data)
|
||||
{
|
||||
struct panfrost_device *pfdev = data;
|
||||
|
||||
panfrost_job_handle_irqs(pfdev);
|
||||
panfrost_jm_handle_irqs(pfdev);
|
||||
|
||||
/* Enable interrupts only if we're not about to get suspended */
|
||||
if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
|
||||
job_write(pfdev, JOB_INT_MASK,
|
||||
GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
|
||||
GENMASK(NUM_JOB_SLOTS - 1, 0));
|
||||
job_write(pfdev, JOB_INT_MASK, ALL_JS_INT_MASK);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
|
||||
static irqreturn_t panfrost_jm_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct panfrost_device *pfdev = data;
|
||||
u32 status;
|
||||
@@ -839,19 +846,20 @@ static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
int panfrost_job_init(struct panfrost_device *pfdev)
|
||||
int panfrost_jm_init(struct panfrost_device *pfdev)
|
||||
{
|
||||
struct drm_sched_init_args args = {
|
||||
.ops = &panfrost_sched_ops,
|
||||
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
|
||||
.credit_limit = 2,
|
||||
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
|
||||
.name = "pan_js",
|
||||
.dev = pfdev->dev,
|
||||
.dev = pfdev->base.dev,
|
||||
};
|
||||
struct panfrost_job_slot *js;
|
||||
int ret, j;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(panfrost_engine_names) != NUM_JOB_SLOTS);
|
||||
|
||||
/* All GPUs have two entries per queue, but without jobchain
|
||||
* disambiguation stopping the right job in the close path is tricky,
|
||||
* so let's just advertise one entry in that case.
|
||||
@@ -859,24 +867,25 @@ int panfrost_job_init(struct panfrost_device *pfdev)
|
||||
if (!panfrost_has_hw_feature(pfdev, HW_FEATURE_JOBCHAIN_DISAMBIGUATION))
|
||||
args.credit_limit = 1;
|
||||
|
||||
pfdev->js = js = devm_kzalloc(pfdev->dev, sizeof(*js), GFP_KERNEL);
|
||||
js = devm_kzalloc(pfdev->base.dev, sizeof(*js), GFP_KERNEL);
|
||||
if (!js)
|
||||
return -ENOMEM;
|
||||
pfdev->js = js;
|
||||
|
||||
INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
|
||||
spin_lock_init(&js->job_lock);
|
||||
|
||||
js->irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job");
|
||||
js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
|
||||
if (js->irq < 0)
|
||||
return js->irq;
|
||||
|
||||
ret = devm_request_threaded_irq(pfdev->dev, js->irq,
|
||||
panfrost_job_irq_handler,
|
||||
panfrost_job_irq_handler_thread,
|
||||
ret = devm_request_threaded_irq(pfdev->base.dev, js->irq,
|
||||
panfrost_jm_irq_handler,
|
||||
panfrost_jm_irq_handler_thread,
|
||||
IRQF_SHARED, KBUILD_MODNAME "-job",
|
||||
pfdev);
|
||||
if (ret) {
|
||||
dev_err(pfdev->dev, "failed to request job irq");
|
||||
dev_err(pfdev->base.dev, "failed to request job irq");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -887,15 +896,17 @@ int panfrost_job_init(struct panfrost_device *pfdev)
|
||||
|
||||
for (j = 0; j < NUM_JOB_SLOTS; j++) {
|
||||
js->queue[j].fence_context = dma_fence_context_alloc(1);
|
||||
args.name = panfrost_engine_names[j];
|
||||
|
||||
ret = drm_sched_init(&js->queue[j].sched, &args);
|
||||
if (ret) {
|
||||
dev_err(pfdev->dev, "Failed to create scheduler: %d.", ret);
|
||||
dev_err(pfdev->base.dev, "Failed to create scheduler: %d.", ret);
|
||||
goto err_sched;
|
||||
}
|
||||
}
|
||||
|
||||
panfrost_job_enable_interrupts(pfdev);
|
||||
panfrost_jm_reset_interrupts(pfdev);
|
||||
panfrost_jm_enable_interrupts(pfdev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -907,7 +918,7 @@ err_sched:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void panfrost_job_fini(struct panfrost_device *pfdev)
|
||||
void panfrost_jm_fini(struct panfrost_device *pfdev)
|
||||
{
|
||||
struct panfrost_job_slot *js = pfdev->js;
|
||||
int j;
|
||||
@@ -922,7 +933,7 @@ void panfrost_job_fini(struct panfrost_device *pfdev)
|
||||
destroy_workqueue(pfdev->reset.wq);
|
||||
}
|
||||
|
||||
int panfrost_job_open(struct drm_file *file)
|
||||
int panfrost_jm_open(struct drm_file *file)
|
||||
{
|
||||
struct panfrost_file_priv *panfrost_priv = file->driver_priv;
|
||||
int ret;
|
||||
@@ -944,7 +955,7 @@ int panfrost_job_open(struct drm_file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void panfrost_job_close(struct drm_file *file)
|
||||
void panfrost_jm_close(struct drm_file *file)
|
||||
{
|
||||
struct panfrost_file_priv *panfrost_priv = file->driver_priv;
|
||||
struct panfrost_jm_ctx *jm_ctx;
|
||||
@@ -956,7 +967,7 @@ void panfrost_job_close(struct drm_file *file)
|
||||
xa_destroy(&panfrost_priv->jm_ctxs);
|
||||
}
|
||||
|
||||
int panfrost_job_is_idle(struct panfrost_device *pfdev)
|
||||
int panfrost_jm_is_idle(struct panfrost_device *pfdev)
|
||||
{
|
||||
struct panfrost_job_slot *js = pfdev->js;
|
||||
int i;
|
||||
|
||||
@@ -53,6 +53,8 @@ struct panfrost_jm_ctx {
|
||||
struct drm_sched_entity slot_entity[NUM_JOB_SLOTS];
|
||||
};
|
||||
|
||||
extern const char * const panfrost_engine_names[];
|
||||
|
||||
int panfrost_jm_ctx_create(struct drm_file *file,
|
||||
struct drm_panfrost_jm_ctx_create *args);
|
||||
int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle);
|
||||
@@ -60,15 +62,16 @@ void panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx);
|
||||
struct panfrost_jm_ctx *panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx);
|
||||
struct panfrost_jm_ctx *panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle);
|
||||
|
||||
int panfrost_job_init(struct panfrost_device *pfdev);
|
||||
void panfrost_job_fini(struct panfrost_device *pfdev);
|
||||
int panfrost_job_open(struct drm_file *file);
|
||||
void panfrost_job_close(struct drm_file *file);
|
||||
int panfrost_jm_init(struct panfrost_device *pfdev);
|
||||
void panfrost_jm_fini(struct panfrost_device *pfdev);
|
||||
int panfrost_jm_open(struct drm_file *file);
|
||||
void panfrost_jm_close(struct drm_file *file);
|
||||
void panfrost_jm_reset_interrupts(struct panfrost_device *pfdev);
|
||||
void panfrost_jm_enable_interrupts(struct panfrost_device *pfdev);
|
||||
void panfrost_jm_suspend_irq(struct panfrost_device *pfdev);
|
||||
int panfrost_jm_is_idle(struct panfrost_device *pfdev);
|
||||
int panfrost_job_get_slot(struct panfrost_job *job);
|
||||
int panfrost_job_push(struct panfrost_job *job);
|
||||
void panfrost_job_put(struct panfrost_job *job);
|
||||
void panfrost_job_enable_interrupts(struct panfrost_device *pfdev);
|
||||
void panfrost_job_suspend_irq(struct panfrost_device *pfdev);
|
||||
int panfrost_job_is_idle(struct panfrost_device *pfdev);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -81,7 +81,7 @@ static int wait_ready(struct panfrost_device *pfdev, u32 as_nr)
|
||||
if (ret) {
|
||||
/* The GPU hung, let's trigger a reset */
|
||||
panfrost_device_schedule_reset(pfdev);
|
||||
dev_err(pfdev->dev, "AS_ACTIVE bit stuck\n");
|
||||
dev_err(pfdev->base.dev, "AS_ACTIVE bit stuck\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -222,7 +222,7 @@ static int mmu_cfg_init_aarch64_4k(struct panfrost_mmu *mmu)
|
||||
struct io_pgtable_cfg *pgtbl_cfg = &mmu->pgtbl_cfg;
|
||||
struct panfrost_device *pfdev = mmu->pfdev;
|
||||
|
||||
if (drm_WARN_ON(pfdev->ddev, pgtbl_cfg->arm_lpae_s1_cfg.ttbr &
|
||||
if (drm_WARN_ON(&pfdev->base, pgtbl_cfg->arm_lpae_s1_cfg.ttbr &
|
||||
~AS_TRANSTAB_AARCH64_4K_ADDR_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -253,12 +253,12 @@ static int panfrost_mmu_cfg_init(struct panfrost_mmu *mmu,
|
||||
return mmu_cfg_init_mali_lpae(mmu);
|
||||
default:
|
||||
/* This should never happen */
|
||||
drm_WARN(pfdev->ddev, 1, "Invalid pgtable format");
|
||||
drm_WARN(&pfdev->base, 1, "Invalid pgtable format");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
|
||||
int panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
|
||||
{
|
||||
int as;
|
||||
|
||||
@@ -300,7 +300,10 @@ u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
|
||||
if (!atomic_read(&lru_mmu->as_count))
|
||||
break;
|
||||
}
|
||||
WARN_ON(&lru_mmu->list == &pfdev->as_lru_list);
|
||||
if (WARN_ON(&lru_mmu->list == &pfdev->as_lru_list)) {
|
||||
as = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_del_init(&lru_mmu->list);
|
||||
as = lru_mmu->as;
|
||||
@@ -315,7 +318,9 @@ u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
|
||||
atomic_set(&mmu->as_count, 1);
|
||||
list_add(&mmu->list, &pfdev->as_lru_list);
|
||||
|
||||
dev_dbg(pfdev->dev, "Assigned AS%d to mmu %p, alloc_mask=%lx", as, mmu, pfdev->as_alloc_mask);
|
||||
dev_dbg(pfdev->base.dev,
|
||||
"Assigned AS%d to mmu %p, alloc_mask=%lx",
|
||||
as, mmu, pfdev->as_alloc_mask);
|
||||
|
||||
panfrost_mmu_enable(pfdev, mmu);
|
||||
|
||||
@@ -381,13 +386,30 @@ static void panfrost_mmu_flush_range(struct panfrost_device *pfdev,
|
||||
if (mmu->as < 0)
|
||||
return;
|
||||
|
||||
pm_runtime_get_noresume(pfdev->dev);
|
||||
pm_runtime_get_noresume(pfdev->base.dev);
|
||||
|
||||
/* Flush the PTs only if we're already awake */
|
||||
if (pm_runtime_active(pfdev->dev))
|
||||
if (pm_runtime_active(pfdev->base.dev))
|
||||
mmu_hw_do_operation(pfdev, mmu, iova, size, AS_COMMAND_FLUSH_PT);
|
||||
|
||||
pm_runtime_put_autosuspend(pfdev->dev);
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
}
|
||||
|
||||
static void mmu_unmap_range(struct panfrost_mmu *mmu, u64 iova, size_t len)
|
||||
{
|
||||
struct io_pgtable_ops *ops = mmu->pgtbl_ops;
|
||||
size_t pgsize, unmapped_len = 0;
|
||||
size_t unmapped_page, pgcount;
|
||||
|
||||
while (unmapped_len < len) {
|
||||
pgsize = get_pgsize(iova, len - unmapped_len, &pgcount);
|
||||
|
||||
unmapped_page = ops->unmap_pages(ops, iova, pgsize, pgcount, NULL);
|
||||
WARN_ON(unmapped_page != pgsize * pgcount);
|
||||
|
||||
iova += pgsize * pgcount;
|
||||
unmapped_len += pgsize * pgcount;
|
||||
}
|
||||
}
|
||||
|
||||
static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
|
||||
@@ -396,22 +418,30 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
|
||||
unsigned int count;
|
||||
struct scatterlist *sgl;
|
||||
struct io_pgtable_ops *ops = mmu->pgtbl_ops;
|
||||
size_t total_mapped = 0;
|
||||
u64 start_iova = iova;
|
||||
int ret;
|
||||
|
||||
for_each_sgtable_dma_sg(sgt, sgl, count) {
|
||||
unsigned long paddr = sg_dma_address(sgl);
|
||||
size_t len = sg_dma_len(sgl);
|
||||
|
||||
dev_dbg(pfdev->dev, "map: as=%d, iova=%llx, paddr=%lx, len=%zx", mmu->as, iova, paddr, len);
|
||||
dev_dbg(pfdev->base.dev,
|
||||
"map: as=%d, iova=%llx, paddr=%lx, len=%zx",
|
||||
mmu->as, iova, paddr, len);
|
||||
|
||||
while (len) {
|
||||
size_t pgcount, mapped = 0;
|
||||
size_t pgsize = get_pgsize(iova | paddr, len, &pgcount);
|
||||
|
||||
ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot,
|
||||
ret = ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot,
|
||||
GFP_KERNEL, &mapped);
|
||||
if (ret)
|
||||
goto err_unmap_pages;
|
||||
|
||||
/* Don't get stuck if things have gone wrong */
|
||||
mapped = max(mapped, pgsize);
|
||||
total_mapped += mapped;
|
||||
iova += mapped;
|
||||
paddr += mapped;
|
||||
len -= mapped;
|
||||
@@ -421,6 +451,10 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
|
||||
panfrost_mmu_flush_range(pfdev, mmu, start_iova, iova - start_iova);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unmap_pages:
|
||||
mmu_unmap_range(mmu, start_iova, total_mapped);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
|
||||
@@ -431,6 +465,7 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
|
||||
struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
|
||||
struct sg_table *sgt;
|
||||
int prot = IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(mapping->active))
|
||||
return 0;
|
||||
@@ -442,11 +477,18 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
|
||||
if (WARN_ON(IS_ERR(sgt)))
|
||||
return PTR_ERR(sgt);
|
||||
|
||||
mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
|
||||
prot, sgt);
|
||||
ret = mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
|
||||
prot, sgt);
|
||||
if (ret)
|
||||
goto err_put_pages;
|
||||
|
||||
mapping->active = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_pages:
|
||||
drm_gem_shmem_put_pages_locked(shmem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
|
||||
@@ -462,7 +504,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
|
||||
if (WARN_ON(!mapping->active))
|
||||
return;
|
||||
|
||||
dev_dbg(pfdev->dev, "unmap: as=%d, iova=%llx, len=%zx",
|
||||
dev_dbg(pfdev->base.dev, "unmap: as=%d, iova=%llx, len=%zx",
|
||||
mapping->mmu->as, iova, len);
|
||||
|
||||
while (unmapped_len < len) {
|
||||
@@ -559,7 +601,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
|
||||
|
||||
bo = bomapping->obj;
|
||||
if (!bo->is_heap) {
|
||||
dev_WARN(pfdev->dev, "matching BO is not heap type (GPU VA = %llx)",
|
||||
dev_WARN(pfdev->base.dev, "matching BO is not heap type (GPU VA = %llx)",
|
||||
bomapping->mmnode.start << PAGE_SHIFT);
|
||||
ret = -EINVAL;
|
||||
goto err_bo;
|
||||
@@ -595,10 +637,12 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
|
||||
refcount_set(&bo->base.pages_use_count, 1);
|
||||
} else {
|
||||
pages = bo->base.pages;
|
||||
if (pages[page_offset]) {
|
||||
/* Pages are already mapped, bail out. */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
sgt = &bo->sgts[page_offset / (SZ_2M / PAGE_SIZE)];
|
||||
if (sgt->sgl) {
|
||||
/* Pages are already mapped, bail out. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
mapping = bo->base.base.filp->f_mapping;
|
||||
@@ -620,23 +664,24 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
|
||||
}
|
||||
}
|
||||
|
||||
sgt = &bo->sgts[page_offset / (SZ_2M / PAGE_SIZE)];
|
||||
ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
|
||||
NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = dma_map_sgtable(pfdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
|
||||
ret = dma_map_sgtable(pfdev->base.dev, sgt, DMA_BIDIRECTIONAL, 0);
|
||||
if (ret)
|
||||
goto err_map;
|
||||
|
||||
mmu_map_sg(pfdev, bomapping->mmu, addr,
|
||||
IOMMU_WRITE | IOMMU_READ | IOMMU_CACHE | IOMMU_NOEXEC, sgt);
|
||||
ret = mmu_map_sg(pfdev, bomapping->mmu, addr,
|
||||
IOMMU_WRITE | IOMMU_READ | IOMMU_CACHE | IOMMU_NOEXEC, sgt);
|
||||
if (ret)
|
||||
goto err_mmu_map_sg;
|
||||
|
||||
bomapping->active = true;
|
||||
bo->heap_rss_size += SZ_2M;
|
||||
|
||||
dev_dbg(pfdev->dev, "mapped page fault @ AS%d %llx", as, addr);
|
||||
dev_dbg(pfdev->base.dev, "mapped page fault @ AS%d %llx", as, addr);
|
||||
|
||||
out:
|
||||
dma_resv_unlock(obj->resv);
|
||||
@@ -645,6 +690,8 @@ out:
|
||||
|
||||
return 0;
|
||||
|
||||
err_mmu_map_sg:
|
||||
dma_unmap_sgtable(pfdev->base.dev, sgt, DMA_BIDIRECTIONAL, 0);
|
||||
err_map:
|
||||
sg_free_table(sgt);
|
||||
err_unlock:
|
||||
@@ -662,13 +709,12 @@ static void panfrost_mmu_release_ctx(struct kref *kref)
|
||||
|
||||
spin_lock(&pfdev->as_lock);
|
||||
if (mmu->as >= 0) {
|
||||
pm_runtime_get_noresume(pfdev->dev);
|
||||
if (pm_runtime_active(pfdev->dev))
|
||||
pm_runtime_get_noresume(pfdev->base.dev);
|
||||
if (pm_runtime_active(pfdev->base.dev))
|
||||
panfrost_mmu_disable(pfdev, mmu->as);
|
||||
pm_runtime_put_autosuspend(pfdev->dev);
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
|
||||
clear_bit(mmu->as, &pfdev->as_alloc_mask);
|
||||
clear_bit(mmu->as, &pfdev->as_in_use_mask);
|
||||
list_del(&mmu->list);
|
||||
}
|
||||
spin_unlock(&pfdev->as_lock);
|
||||
@@ -726,7 +772,7 @@ struct panfrost_mmu *panfrost_mmu_ctx_create(struct panfrost_device *pfdev)
|
||||
|
||||
if (pfdev->comp->gpu_quirks & BIT(GPU_QUIRK_FORCE_AARCH64_PGTABLE)) {
|
||||
if (!panfrost_has_hw_feature(pfdev, HW_FEATURE_AARCH64_MMU)) {
|
||||
dev_err_once(pfdev->dev,
|
||||
dev_err_once(pfdev->base.dev,
|
||||
"AARCH64_4K page table not supported\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
@@ -755,7 +801,7 @@ struct panfrost_mmu *panfrost_mmu_ctx_create(struct panfrost_device *pfdev)
|
||||
.oas = pa_bits,
|
||||
.coherent_walk = pfdev->coherent,
|
||||
.tlb = &mmu_tlb_ops,
|
||||
.iommu_dev = pfdev->dev,
|
||||
.iommu_dev = pfdev->base.dev,
|
||||
};
|
||||
|
||||
mmu->pgtbl_ops = alloc_io_pgtable_ops(fmt, &mmu->pgtbl_cfg, mmu);
|
||||
@@ -848,7 +894,7 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
|
||||
|
||||
if (ret) {
|
||||
/* terminal fault, print info about the fault */
|
||||
dev_err(pfdev->dev,
|
||||
dev_err(pfdev->base.dev,
|
||||
"Unhandled Page fault in AS%d at VA 0x%016llX\n"
|
||||
"Reason: %s\n"
|
||||
"raw fault status: 0x%X\n"
|
||||
@@ -896,18 +942,18 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
|
||||
pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu");
|
||||
if (pfdev->mmu_irq < 0)
|
||||
return pfdev->mmu_irq;
|
||||
|
||||
err = devm_request_threaded_irq(pfdev->dev, pfdev->mmu_irq,
|
||||
err = devm_request_threaded_irq(pfdev->base.dev, pfdev->mmu_irq,
|
||||
panfrost_mmu_irq_handler,
|
||||
panfrost_mmu_irq_handler_thread,
|
||||
IRQF_SHARED, KBUILD_MODNAME "-mmu",
|
||||
pfdev);
|
||||
|
||||
if (err) {
|
||||
dev_err(pfdev->dev, "failed to request mmu irq");
|
||||
dev_err(pfdev->base.dev, "failed to request mmu irq");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#ifndef __PANFROST_MMU_H__
|
||||
#define __PANFROST_MMU_H__
|
||||
|
||||
struct panfrost_device;
|
||||
struct panfrost_gem_mapping;
|
||||
struct panfrost_file_priv;
|
||||
struct panfrost_mmu;
|
||||
@@ -16,7 +17,7 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev);
|
||||
void panfrost_mmu_reset(struct panfrost_device *pfdev);
|
||||
void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev);
|
||||
|
||||
u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
|
||||
int panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
|
||||
void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
|
||||
|
||||
struct panfrost_mmu *panfrost_mmu_ctx_get(struct panfrost_mmu *mmu);
|
||||
|
||||
@@ -84,11 +84,11 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
|
||||
else if (perfcnt->user)
|
||||
return -EBUSY;
|
||||
|
||||
ret = pm_runtime_get_sync(pfdev->dev);
|
||||
ret = pm_runtime_get_sync(pfdev->base.dev);
|
||||
if (ret < 0)
|
||||
goto err_put_pm;
|
||||
|
||||
bo = drm_gem_shmem_create(pfdev->ddev, perfcnt->bosize);
|
||||
bo = drm_gem_shmem_create(&pfdev->base, perfcnt->bosize);
|
||||
if (IS_ERR(bo)) {
|
||||
ret = PTR_ERR(bo);
|
||||
goto err_put_pm;
|
||||
@@ -130,9 +130,11 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
|
||||
goto err_vunmap;
|
||||
}
|
||||
|
||||
perfcnt->user = user;
|
||||
ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
|
||||
if (ret < 0)
|
||||
goto err_vunmap;
|
||||
|
||||
as = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
|
||||
as = ret;
|
||||
cfg = GPU_PERFCNT_CFG_AS(as) |
|
||||
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
|
||||
|
||||
@@ -164,6 +166,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
|
||||
/* The BO ref is retained by the mapping. */
|
||||
drm_gem_object_put(&bo->base);
|
||||
|
||||
perfcnt->user = user;
|
||||
|
||||
return 0;
|
||||
|
||||
err_vunmap:
|
||||
@@ -175,7 +179,7 @@ err_close_bo:
|
||||
err_put_bo:
|
||||
drm_gem_object_put(&bo->base);
|
||||
err_put_pm:
|
||||
pm_runtime_put(pfdev->dev);
|
||||
pm_runtime_put(pfdev->base.dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -203,7 +207,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
|
||||
panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
|
||||
panfrost_gem_mapping_put(perfcnt->mapping);
|
||||
perfcnt->mapping = NULL;
|
||||
pm_runtime_put_autosuspend(pfdev->dev);
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -211,7 +215,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
|
||||
int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
|
||||
struct drm_panfrost_perfcnt_enable *req = data;
|
||||
int ret;
|
||||
@@ -238,7 +242,7 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
|
||||
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct panfrost_device *pfdev = dev->dev_private;
|
||||
struct panfrost_device *pfdev = to_panfrost_device(dev);
|
||||
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
|
||||
struct drm_panfrost_perfcnt_dump *req = data;
|
||||
void __user *user_ptr = (void __user *)(uintptr_t)req->buf_ptr;
|
||||
@@ -273,12 +277,12 @@ void panfrost_perfcnt_close(struct drm_file *file_priv)
|
||||
struct panfrost_device *pfdev = pfile->pfdev;
|
||||
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
|
||||
|
||||
pm_runtime_get_sync(pfdev->dev);
|
||||
pm_runtime_get_sync(pfdev->base.dev);
|
||||
mutex_lock(&perfcnt->lock);
|
||||
if (perfcnt->user == pfile)
|
||||
panfrost_perfcnt_disable_locked(pfdev, file_priv);
|
||||
mutex_unlock(&perfcnt->lock);
|
||||
pm_runtime_put_autosuspend(pfdev->dev);
|
||||
pm_runtime_put_autosuspend(pfdev->base.dev);
|
||||
}
|
||||
|
||||
int panfrost_perfcnt_init(struct panfrost_device *pfdev)
|
||||
@@ -316,7 +320,7 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
|
||||
COUNTERS_PER_BLOCK * BYTES_PER_COUNTER;
|
||||
}
|
||||
|
||||
perfcnt = devm_kzalloc(pfdev->dev, sizeof(*perfcnt), GFP_KERNEL);
|
||||
perfcnt = devm_kzalloc(pfdev->base.dev, sizeof(*perfcnt), GFP_KERNEL);
|
||||
if (!perfcnt)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -146,10 +146,9 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
|
||||
ptdev->devfreq = pdevfreq;
|
||||
|
||||
ret = devm_pm_opp_set_regulators(dev, reg_names);
|
||||
if (ret) {
|
||||
if (ret && ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user