mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 18:43:12 -04:00
Merge tag 'drm-misc-next-2026-08-06' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.3: UAPI Changes: - Remove the default udmabuf size limit of 64MB. Cross-subsystem Changes: - Add dmemcg support for eviction, and hook it up for amdgpu and xe. Core Changes: - Changes to TTM to be more aggressive when allocating below protection limit! - Improve dt binding documentation for renesas. - Add helper to convert physical address back to buddy block, add that to and improve its kunit test. Driver Changes: - Assorted small fixes to ti-sn65dsi86, panthor, imagination, omapdrm, bridge/synopsys, panel-edp, ssd130x, panel/tdo-tl070wsh30. - Add Sharp LQ120P1JX51 panel. - Add dmemcg support to nouveau. - Various updates and improvements to sun4i, among which YUV and 4k support. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patch.msgid.link/917d462a-8976-4a15-bec4-4513ec51c5c0@linux.intel.com
This commit is contained in:
@@ -28,6 +28,7 @@ properties:
|
||||
- const: renesas,r9a09g057-mipi-dsi
|
||||
|
||||
- enum:
|
||||
- renesas,r9a08g046-mipi-dsi # RZ/G3L
|
||||
- renesas,r9a09g057-mipi-dsi # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/renesas,r9a08g046-lvds.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/G3L LVDS Encoder
|
||||
|
||||
maintainers:
|
||||
- Biju Das <biju.das.jz@bp.renesas.com>
|
||||
- Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
|
||||
|
||||
description: |
|
||||
This binding describes the LVDS encoder embedded in the Renesas RZ/G3L
|
||||
SoC. The encoder can operate in LVDS Single-link mode with 4 lanes
|
||||
(Data) + 1 lane (Clock).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: renesas,r9a08g046-lvds
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Peripheral clock
|
||||
- description: PHY clock
|
||||
- description: Dot clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: phyclk
|
||||
- const: dotclk
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: LVDS_RESET_N
|
||||
- description: MIPI_DSI_PRESET_N
|
||||
- description: MIPI_DSI_CMN_RSTB
|
||||
- description: MIPI_DSI_ARESET_N
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: lvdrst
|
||||
- const: prst
|
||||
- const: rst
|
||||
- const: arst
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Input channel, directly connected to the Display Unit.
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: |
|
||||
Output channel, directly connected to the LVDS panel or bridge.
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- power-domains
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/renesas,r9a08g046-cpg.h>
|
||||
|
||||
lvds@108a0000 {
|
||||
compatible = "renesas,r9a08g046-lvds";
|
||||
reg = <0x108a0000 0x10000>;
|
||||
clocks = <&cpg CPG_MOD R9A08G046_MIPI_DSI_PCLK>,
|
||||
<&cpg CPG_MOD R9A08G046_LVDS_PLLCLK>,
|
||||
<&cpg CPG_MOD R9A08G046_LVDS_CLK_DOT0>;
|
||||
clock-names = "pclk", "phyclk", "dotclk";
|
||||
resets = <&cpg R9A08G046_LVDS_RESET_N>,
|
||||
<&cpg R9A08G046_MIPI_DSI_PRESET_N>,
|
||||
<&cpg R9A08G046_MIPI_DSI_CMN_RSTB>,
|
||||
<&cpg R9A08G046_MIPI_DSI_ARESET_N>;
|
||||
reset-names = "lvdrst", "prst", "rst", "arst";
|
||||
power-domains = <&cpg>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
lvds0_in: endpoint {
|
||||
remote-endpoint = <&du_out_lvds0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
lvds0_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -19,6 +19,7 @@ properties:
|
||||
- anbernic,rg-ds-display-bottom
|
||||
- anbernic,rg-ds-display-top
|
||||
- chongzhou,cz101b4001
|
||||
- dcltek,dt300250
|
||||
- kingdisplay,kd101ne3-40ti
|
||||
- melfas,lmfbx101117480
|
||||
- radxa,display-10hd-ad001
|
||||
|
||||
@@ -37,6 +37,8 @@ properties:
|
||||
- ampire,am-480272h3tmqw-t01h
|
||||
# Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel
|
||||
- ampire,am-800480l1tmqw-t00h
|
||||
# Ampire AM-800480N3TZQW-00H 5" WVGA TFT LCD panel
|
||||
- ampire,am-800480n3tzqw-00h
|
||||
# Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
|
||||
- ampire,am800480r3tmqwa1h
|
||||
# Ampire AM-800600P5TMQW-TB8H 8.0" SVGA TFT LCD panel
|
||||
|
||||
@@ -22,6 +22,8 @@ properties:
|
||||
enum:
|
||||
# Anberic RG353V-V2 5.0" 640x480 TFT LCD panel
|
||||
- anbernic,rg353v-panel-v2
|
||||
# AYANEO Pocket DS lower 5.0" 1024x768 IPS LCD panel
|
||||
- ayaneo,pocket-ds-lower-panel
|
||||
# GameForce Chi 3.5" 640x480 TFT LCD panel
|
||||
- gameforce,chi-panel
|
||||
# Powkiddy RGB10MAX3 5.0" 720x1280 TFT LCD panel
|
||||
|
||||
@@ -20,6 +20,7 @@ properties:
|
||||
- enum:
|
||||
- renesas,r9a07g043u-du # RZ/G2UL
|
||||
- renesas,r9a07g044-du # RZ/G2{L,LC}
|
||||
- renesas,r9a08g046-du # RZ/G3L
|
||||
- renesas,r9a09g057-du # RZ/V2H(P)
|
||||
- renesas,r9a09g077-du # RZ/T2H
|
||||
- items:
|
||||
@@ -65,7 +66,7 @@ properties:
|
||||
model-dependent. Each port shall have a single endpoint.
|
||||
|
||||
patternProperties:
|
||||
"^port@[0-1]$":
|
||||
"^port@[0-2]$":
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
unevaluatedProperties: false
|
||||
|
||||
@@ -108,6 +109,7 @@ allOf:
|
||||
port@0:
|
||||
description: DPI
|
||||
port@1: false
|
||||
port@2: false
|
||||
|
||||
required:
|
||||
- port@0
|
||||
@@ -124,10 +126,31 @@ allOf:
|
||||
description: DSI
|
||||
port@1:
|
||||
description: DPI
|
||||
port@2: false
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a08g046-du
|
||||
then:
|
||||
properties:
|
||||
ports:
|
||||
properties:
|
||||
port@0:
|
||||
description: DSI
|
||||
port@1:
|
||||
description: DPI
|
||||
port@2:
|
||||
description: LVDS
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- port@2
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -140,6 +163,7 @@ allOf:
|
||||
port@0:
|
||||
description: DSI
|
||||
port@1: false
|
||||
port@2: false
|
||||
|
||||
required:
|
||||
- port@0
|
||||
|
||||
@@ -409,6 +409,8 @@ patternProperties:
|
||||
description: DataImage, Inc.
|
||||
"^davicom,.*":
|
||||
description: DAVICOM Semiconductor, Inc.
|
||||
"^dcltek,.*":
|
||||
description: DCL Technologies Inc.
|
||||
"^deepcomputing,.*":
|
||||
description: DeepComputing (HK) Limited
|
||||
"^dell,.*":
|
||||
@@ -1253,6 +1255,8 @@ patternProperties:
|
||||
description: OpenWrt
|
||||
"^option,.*":
|
||||
description: Option NV
|
||||
"^optologic,.*":
|
||||
description: Opto Logic S.A.
|
||||
"^oranth,.*":
|
||||
description: Shenzhen Oranth Technology Co., Ltd.
|
||||
"^ORCL,.*":
|
||||
|
||||
@@ -20,9 +20,9 @@ static int list_limit = 1024;
|
||||
module_param(list_limit, int, 0644);
|
||||
MODULE_PARM_DESC(list_limit, "udmabuf_create_list->count limit. Default is 1024.");
|
||||
|
||||
static int size_limit_mb = 64;
|
||||
static int size_limit_mb = INT_MAX;
|
||||
module_param(size_limit_mb, int, 0644);
|
||||
MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is 64.");
|
||||
MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is INT_MAX.");
|
||||
|
||||
struct udmabuf {
|
||||
pgoff_t pagecount;
|
||||
|
||||
@@ -7,7 +7,8 @@ config GPU_BUDDY
|
||||
|
||||
config GPU_BUDDY_KUNIT_TEST
|
||||
tristate "KUnit tests for GPU buddy allocator" if !KUNIT_ALL_TESTS
|
||||
depends on GPU_BUDDY && KUNIT
|
||||
depends on KUNIT
|
||||
select GPU_BUDDY
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
KUnit tests for the GPU buddy allocator.
|
||||
|
||||
@@ -630,6 +630,59 @@ void gpu_buddy_free_block(struct gpu_buddy *mm,
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_free_block);
|
||||
|
||||
/**
|
||||
* gpu_buddy_allocated_addr_to_block - given relative address find the allocated block
|
||||
*
|
||||
* @mm: GPU buddy manager
|
||||
* @addr: Relative address
|
||||
*
|
||||
* Returns:
|
||||
* gpu_buddy_block on success, NULL or error code on failure
|
||||
*/
|
||||
struct gpu_buddy_block *gpu_buddy_allocated_addr_to_block(struct gpu_buddy *mm, u64 addr)
|
||||
{
|
||||
struct gpu_buddy_block *block;
|
||||
LIST_HEAD(dfs);
|
||||
u64 end;
|
||||
int i;
|
||||
|
||||
gpu_buddy_driver_lock_held(mm);
|
||||
|
||||
end = addr + mm->chunk_size - 1;
|
||||
for (i = 0; i < mm->n_roots; ++i)
|
||||
list_add_tail(&mm->roots[i]->tmp_link, &dfs);
|
||||
|
||||
do {
|
||||
u64 block_start;
|
||||
u64 block_end;
|
||||
|
||||
block = list_first_entry_or_null(&dfs,
|
||||
struct gpu_buddy_block,
|
||||
tmp_link);
|
||||
if (!block)
|
||||
break;
|
||||
|
||||
list_del(&block->tmp_link);
|
||||
|
||||
block_start = gpu_buddy_block_offset(block);
|
||||
block_end = block_start + gpu_buddy_block_size(mm, block) - 1;
|
||||
|
||||
if (!overlaps(addr, end, block_start, block_end))
|
||||
continue;
|
||||
|
||||
if (gpu_buddy_block_is_allocated(block))
|
||||
return block;
|
||||
else if (gpu_buddy_block_is_free(block))
|
||||
return NULL;
|
||||
|
||||
list_add(&block->right->tmp_link, &dfs);
|
||||
list_add(&block->left->tmp_link, &dfs);
|
||||
} while (1);
|
||||
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
EXPORT_SYMBOL(gpu_buddy_allocated_addr_to_block);
|
||||
|
||||
static void __gpu_buddy_free_list(struct gpu_buddy *mm,
|
||||
struct list_head *objects,
|
||||
bool mark_clear,
|
||||
|
||||
@@ -1526,7 +1526,7 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
|
||||
dma_resv_for_each_fence(&resv_cursor, bo->base.resv,
|
||||
DMA_RESV_USAGE_BOOKKEEP, f) {
|
||||
if (amdkfd_fence_check_mm(f, current->mm) &&
|
||||
!(place->flags & TTM_PL_FLAG_CONTIGUOUS))
|
||||
!(place && (place->flags & TTM_PL_FLAG_CONTIGUOUS)))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/cgroup_dmem.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <drm/ttm/ttm_range_manager.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_buddy.h>
|
||||
@@ -905,6 +907,38 @@ static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
|
||||
.debug = amdgpu_vram_mgr_debug
|
||||
};
|
||||
|
||||
static const struct dmem_cgroup_ops amdgpu_vram_mgr_dmem_ops;
|
||||
|
||||
static int amdgpu_vram_mgr_dmem_reclaim(struct dmem_cgroup_pool_state *pool,
|
||||
u64 target_bytes, void *priv)
|
||||
{
|
||||
struct ttm_resource_manager *man = priv;
|
||||
struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev);
|
||||
int ret, idx;
|
||||
|
||||
if (!drm_dev_enter(adev_to_drm(adev), &idx))
|
||||
return -ENODEV;
|
||||
|
||||
ret = pm_runtime_get_sync(adev_to_drm(adev)->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ttm_resource_manager_dmem_reclaim(pool, target_bytes, priv);
|
||||
|
||||
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
|
||||
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
|
||||
|
||||
out:
|
||||
drm_dev_exit(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dmem_cgroup_ops amdgpu_vram_mgr_dmem_ops = {
|
||||
.reclaim = amdgpu_vram_mgr_dmem_reclaim,
|
||||
};
|
||||
|
||||
/**
|
||||
* amdgpu_vram_mgr_init - init VRAM manager and DRM MM
|
||||
*
|
||||
@@ -916,11 +950,9 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
|
||||
{
|
||||
struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr;
|
||||
struct ttm_resource_manager *man = &mgr->manager;
|
||||
struct dmem_cgroup_region *cg;
|
||||
int err;
|
||||
|
||||
man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size);
|
||||
if (IS_ERR(man->cg))
|
||||
return PTR_ERR(man->cg);
|
||||
ttm_resource_manager_init(man, &adev->mman.bdev,
|
||||
adev->gmc.real_vram_size);
|
||||
|
||||
@@ -935,6 +967,20 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cg = dmem_cgroup_register_region(&(struct dmem_cgroup_init){
|
||||
.size = adev->gmc.real_vram_size,
|
||||
.ops = &amdgpu_vram_mgr_dmem_ops,
|
||||
.reclaim_priv = man,
|
||||
},
|
||||
"drm/%s/vram", adev_to_drm(adev)->unique);
|
||||
if (IS_ERR(cg)) {
|
||||
gpu_buddy_fini(&mgr->mm);
|
||||
return PTR_ERR(cg);
|
||||
}
|
||||
|
||||
mgr->cg_region = cg;
|
||||
ttm_resource_manager_set_dmem_region(man, cg);
|
||||
|
||||
ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager);
|
||||
ttm_resource_manager_set_used(man, true);
|
||||
return 0;
|
||||
@@ -958,6 +1004,19 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
|
||||
ttm_resource_manager_set_used(man, false);
|
||||
|
||||
ret = ttm_resource_manager_evict_all(&adev->mman.bdev, man);
|
||||
|
||||
/*
|
||||
* Unregister the dmem cgroup region regardless of the evict_all()
|
||||
* result and before any further teardown. This drains in-flight
|
||||
* reclaim callbacks and blocks new ones, so no reclaim can reference
|
||||
* the manager once we start freeing it. It must run after evict_all()
|
||||
* so that ttm_resource_free() can still uncharge via man->cg during
|
||||
* eviction. Clear man->cg afterwards.
|
||||
*/
|
||||
dmem_cgroup_unregister_region(mgr->cg_region);
|
||||
mgr->cg_region = NULL;
|
||||
ttm_resource_manager_set_dmem_region(man, NULL);
|
||||
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include <linux/gpu_buddy.h>
|
||||
|
||||
struct dmem_cgroup_region;
|
||||
|
||||
struct amdgpu_vram_mgr {
|
||||
struct ttm_resource_manager manager;
|
||||
struct gpu_buddy mm;
|
||||
@@ -36,6 +38,7 @@ struct amdgpu_vram_mgr {
|
||||
atomic64_t vis_usage;
|
||||
u64 default_page_size;
|
||||
struct list_head allocated_vres_list;
|
||||
struct dmem_cgroup_region *cg_region;
|
||||
};
|
||||
|
||||
struct amdgpu_vres_task {
|
||||
|
||||
@@ -1471,11 +1471,11 @@ static const struct adv7511_chip_info adv7535_chip_info = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id adv7511_i2c_ids[] = {
|
||||
{ "adv7511", (kernel_ulong_t)&adv7511_chip_info },
|
||||
{ "adv7511w", (kernel_ulong_t)&adv7511_chip_info },
|
||||
{ "adv7513", (kernel_ulong_t)&adv7511_chip_info },
|
||||
{ "adv7533", (kernel_ulong_t)&adv7533_chip_info },
|
||||
{ "adv7535", (kernel_ulong_t)&adv7535_chip_info },
|
||||
{ .name = "adv7511", .driver_data = (kernel_ulong_t)&adv7511_chip_info },
|
||||
{ .name = "adv7511w", .driver_data = (kernel_ulong_t)&adv7511_chip_info },
|
||||
{ .name = "adv7513", .driver_data = (kernel_ulong_t)&adv7511_chip_info },
|
||||
{ .name = "adv7533", .driver_data = (kernel_ulong_t)&adv7533_chip_info },
|
||||
{ .name = "adv7535", .driver_data = (kernel_ulong_t)&adv7535_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
|
||||
|
||||
@@ -783,7 +783,7 @@ static void anx6345_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id anx6345_id[] = {
|
||||
{ "anx6345" },
|
||||
{ .name = "anx6345" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, anx6345_id);
|
||||
|
||||
@@ -2993,8 +2993,8 @@ static void anx7625_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id anx7625_id[] = {
|
||||
{ "anx7625" },
|
||||
{}
|
||||
{ .name = "anx7625" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, anx7625_id);
|
||||
|
||||
@@ -784,8 +784,8 @@ static struct mipi_dsi_driver chipone_dsi_driver = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id chipone_i2c_id[] = {
|
||||
{ "chipone,icn6211" },
|
||||
{},
|
||||
{ .name = "chipone,icn6211" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, chipone_i2c_id);
|
||||
|
||||
|
||||
@@ -601,7 +601,7 @@ static const struct of_device_id ch7033_dt_ids[] = {
|
||||
MODULE_DEVICE_TABLE(of, ch7033_dt_ids);
|
||||
|
||||
static const struct i2c_device_id ch7033_ids[] = {
|
||||
{ "ch7033" },
|
||||
{ .name = "ch7033" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ch7033_ids);
|
||||
|
||||
@@ -107,4 +107,15 @@ config DRM_IMX93_MIPI_DSI
|
||||
Choose this to enable MIPI DSI controller found in Freescale i.MX93
|
||||
processor.
|
||||
|
||||
config DRM_IMX93_PARALLEL_DISP_FMT_CONVERTER
|
||||
tristate "NXP i.MX91/i.MX93 parallel display format converter"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
help
|
||||
On i.MX93 and i.MX91 SoCs the parallel display format output is
|
||||
controlled via the MEDIAMIX BLK-CTRL DISPLAY_MUX.
|
||||
|
||||
Say 'Y' or 'M' if you use the parallel display output path on a
|
||||
i.MX93 or i.MX91 SoC.
|
||||
|
||||
endif # ARCH_MXC || COMPILE_TEST
|
||||
|
||||
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_COMBINER) += imx8qxp-pixel-combiner.o
|
||||
obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o
|
||||
obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o
|
||||
obj-$(CONFIG_DRM_IMX93_MIPI_DSI) += imx93-mipi-dsi.o
|
||||
obj-$(CONFIG_DRM_IMX93_PARALLEL_DISP_FMT_CONVERTER) += imx93-pdfc.o
|
||||
|
||||
222
drivers/gpu/drm/bridge/imx/imx93-pdfc.c
Normal file
222
drivers/gpu/drm/bridge/imx/imx93-pdfc.c
Normal file
@@ -0,0 +1,222 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/*
|
||||
* Copyright 2022-2025 NXP
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
|
||||
#define IMX93_DISPLAY_MUX_REG 0x60
|
||||
#define PARALLEL_DISP_FORMAT GENMASK(10, 8)
|
||||
#define FORMAT_RGB888_TO_RGB888 FIELD_PREP(PARALLEL_DISP_FORMAT, 0)
|
||||
#define FORMAT_RGB888_TO_RGB666 FIELD_PREP(PARALLEL_DISP_FORMAT, 1)
|
||||
#define FORMAT_RGB565_TO_RGB565 FIELD_PREP(PARALLEL_DISP_FORMAT, 2)
|
||||
|
||||
struct imx93_pdfc {
|
||||
struct drm_bridge bridge;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
u32 phy_bus_width;
|
||||
};
|
||||
|
||||
static struct imx93_pdfc *bridge_to_imx93_pdfc(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct imx93_pdfc, bridge);
|
||||
}
|
||||
|
||||
static int
|
||||
imx93_pdfc_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
return drm_bridge_attach(bridge->encoder, bridge->next_bridge, bridge, flags);
|
||||
}
|
||||
|
||||
static void imx93_pdfc_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
struct drm_atomic_commit *state)
|
||||
{
|
||||
struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge);
|
||||
const struct drm_bridge_state *bridge_state;
|
||||
unsigned int mask = PARALLEL_DISP_FORMAT;
|
||||
unsigned int val;
|
||||
|
||||
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
|
||||
|
||||
switch (bridge_state->output_bus_cfg.format) {
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
case MEDIA_BUS_FMT_FIXED:
|
||||
val = FORMAT_RGB888_TO_RGB888;
|
||||
if (pdfc->phy_bus_width == 18) {
|
||||
/*
|
||||
* Can be valid if physical bus limitation exists,
|
||||
* therefore use dev_dbg().
|
||||
*/
|
||||
dev_dbg(pdfc->dev, "Truncate two LSBs from each color\n");
|
||||
val = FORMAT_RGB888_TO_RGB666;
|
||||
}
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
val = FORMAT_RGB888_TO_RGB666;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
val = FORMAT_RGB565_TO_RGB565;
|
||||
break;
|
||||
}
|
||||
|
||||
regmap_update_bits(pdfc->regmap, IMX93_DISPLAY_MUX_REG, mask, val);
|
||||
}
|
||||
|
||||
/* TODO: Add YUV formats */
|
||||
static const u32 imx93_pdfc_bus_output_fmts[] = {
|
||||
MEDIA_BUS_FMT_FIXED,
|
||||
MEDIA_BUS_FMT_RGB888_1X24,
|
||||
MEDIA_BUS_FMT_RGB666_1X18,
|
||||
MEDIA_BUS_FMT_RGB565_1X16,
|
||||
};
|
||||
|
||||
static bool imx93_pdfc_bus_output_fmt_supported(u32 fmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(imx93_pdfc_bus_output_fmts); i++) {
|
||||
if (imx93_pdfc_bus_output_fmts[i] == fmt)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static u32 *
|
||||
imx93_pdfc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state,
|
||||
u32 output_fmt,
|
||||
unsigned int *num_input_fmts)
|
||||
{
|
||||
u32 *input_fmts;
|
||||
|
||||
*num_input_fmts = 0;
|
||||
|
||||
if (!imx93_pdfc_bus_output_fmt_supported(output_fmt))
|
||||
return NULL;
|
||||
|
||||
input_fmts = kmalloc_obj(*input_fmts);
|
||||
if (!input_fmts)
|
||||
return NULL;
|
||||
|
||||
*num_input_fmts = 1;
|
||||
|
||||
switch (output_fmt) {
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
input_fmts[0] = output_fmt;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
case MEDIA_BUS_FMT_FIXED:
|
||||
input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
break;
|
||||
}
|
||||
|
||||
return input_fmts;
|
||||
}
|
||||
|
||||
static int imx93_pdfc_bridge_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge);
|
||||
u32 format = bridge_state->output_bus_cfg.format;
|
||||
|
||||
if (imx93_pdfc_bus_output_fmt_supported(format))
|
||||
return 0;
|
||||
|
||||
dev_warn(pdfc->dev, "Unsupported output bus format: 0x%x\n", format);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs funcs = {
|
||||
.attach = imx93_pdfc_bridge_attach,
|
||||
.atomic_enable = imx93_pdfc_bridge_atomic_enable,
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_get_input_bus_fmts = imx93_pdfc_bridge_atomic_get_input_bus_fmts,
|
||||
.atomic_check = imx93_pdfc_bridge_atomic_check,
|
||||
.atomic_create_state = drm_atomic_helper_bridge_create_state,
|
||||
};
|
||||
|
||||
static int imx93_pdfc_bridge_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct drm_bridge *next_bridge;
|
||||
struct imx93_pdfc *pdfc;
|
||||
struct device_node *ep;
|
||||
int err;
|
||||
|
||||
pdfc = devm_drm_bridge_alloc(dev, struct imx93_pdfc, bridge, &funcs);
|
||||
if (IS_ERR(pdfc))
|
||||
return PTR_ERR(pdfc);
|
||||
|
||||
pdfc->regmap = syscon_node_to_regmap(dev->of_node->parent);
|
||||
if (IS_ERR(pdfc->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(pdfc->regmap),
|
||||
"failed to get regmap\n");
|
||||
|
||||
/* No limits per default */
|
||||
pdfc->phy_bus_width = 24;
|
||||
|
||||
/* Get output ep (port1/endpoint) */
|
||||
ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
|
||||
if (ep) {
|
||||
err = of_property_read_u32(ep, "bus-width", &pdfc->phy_bus_width);
|
||||
of_node_put(ep);
|
||||
|
||||
/* bus-width is optional but it must have valid data if present */
|
||||
if (err && err != -EINVAL)
|
||||
return dev_err_probe(dev, err,
|
||||
"failed to query bus-width\n");
|
||||
}
|
||||
|
||||
next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
|
||||
if (IS_ERR(next_bridge))
|
||||
return dev_err_probe(dev, PTR_ERR(next_bridge),
|
||||
"failed to get next bridge\n");
|
||||
pdfc->dev = dev;
|
||||
pdfc->bridge.of_node = dev->of_node;
|
||||
pdfc->bridge.type = DRM_MODE_CONNECTOR_DPI;
|
||||
pdfc->bridge.next_bridge = drm_bridge_get(next_bridge);
|
||||
|
||||
return devm_drm_bridge_add(dev, &pdfc->bridge);
|
||||
}
|
||||
|
||||
static const struct of_device_id imx93_pdfc_dt_ids[] = {
|
||||
{ .compatible = "nxp,imx91-pdfc", },
|
||||
{ .compatible = "nxp,imx93-pdfc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx93_pdfc_dt_ids);
|
||||
|
||||
static struct platform_driver imx93_pdfc_bridge_driver = {
|
||||
.probe = imx93_pdfc_bridge_probe,
|
||||
.driver = {
|
||||
.of_match_table = imx93_pdfc_dt_ids,
|
||||
.name = "imx93_pdfc",
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx93_pdfc_bridge_driver);
|
||||
|
||||
MODULE_DESCRIPTION("NXP i.MX93 parallel display format configuration driver");
|
||||
MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -910,7 +910,7 @@ static const struct of_device_id it6263_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, it6263_of_match);
|
||||
|
||||
static const struct i2c_device_id it6263_i2c_ids[] = {
|
||||
{ "it6263" },
|
||||
{ .name = "it6263" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, it6263_i2c_ids);
|
||||
|
||||
@@ -3652,7 +3652,7 @@ static void it6505_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id it6505_id[] = {
|
||||
{ "it6505" },
|
||||
{ .name = "it6505" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -572,7 +572,7 @@ static void lt8713sx_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static struct i2c_device_id lt8713sx_id[] = {
|
||||
{ "lontium,lt8713sx", 0 },
|
||||
{ .name = "lontium,lt8713sx" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
||||
@@ -677,7 +677,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(lt8912_bridge_pm_ops, lt8912_bridge_suspend, lt8
|
||||
static int lt8912_get_regulators(struct lt8912 *lt)
|
||||
{
|
||||
unsigned int i;
|
||||
const char * const supply_names[] = {
|
||||
static const char * const supply_names[] = {
|
||||
"vdd", "vccmipirx", "vccsysclk", "vcclvdstx",
|
||||
"vcchdmitx", "vcclvdspll", "vcchdmipll"
|
||||
};
|
||||
@@ -820,8 +820,8 @@ static const struct of_device_id lt8912_dt_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, lt8912_dt_match);
|
||||
|
||||
static const struct i2c_device_id lt8912_id[] = {
|
||||
{ "lt8912" },
|
||||
{}
|
||||
{ .name = "lt8912" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lt8912_id);
|
||||
|
||||
|
||||
@@ -773,8 +773,8 @@ static void lt9211_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lt9211_id[] = {
|
||||
{ "lontium,lt9211" },
|
||||
{},
|
||||
{ .name = "lontium,lt9211" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lt9211_id);
|
||||
|
||||
|
||||
@@ -1229,8 +1229,8 @@ static void lt9611_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lt9611_id[] = {
|
||||
{ "lontium,lt9611" },
|
||||
{}
|
||||
{ .name = "lontium,lt9611" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lt9611_id);
|
||||
|
||||
|
||||
@@ -899,7 +899,7 @@ static void lt9611uxc_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lt9611uxc_id[] = {
|
||||
{ "lontium,lt9611uxc" },
|
||||
{ .name = "lontium,lt9611uxc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
||||
@@ -317,8 +317,8 @@ static void stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = {
|
||||
{ "stdp4028_ge_fw" },
|
||||
{}
|
||||
{ .name = "stdp4028_ge_fw" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table);
|
||||
|
||||
@@ -364,8 +364,8 @@ static void stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = {
|
||||
{ "stdp2690_ge_fw" },
|
||||
{}
|
||||
{ .name = "stdp2690_ge_fw" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table);
|
||||
|
||||
|
||||
@@ -323,8 +323,8 @@ static void ptn3460_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ptn3460_i2c_table[] = {
|
||||
{ "ptn3460" },
|
||||
{}
|
||||
{ .name = "ptn3460" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table);
|
||||
|
||||
|
||||
@@ -534,10 +534,10 @@ static void ps8622_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ps8622_i2c_table[] = {
|
||||
/* Device type, max_lane_count */
|
||||
{"ps8622", 1},
|
||||
{"ps8625", 2},
|
||||
{},
|
||||
/* Device type, driver_data holds the maximal lane_count */
|
||||
{ .name = "ps8622", .driver_data = 1 },
|
||||
{ .name = "ps8625", .driver_data = 2 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);
|
||||
|
||||
|
||||
@@ -1235,7 +1235,7 @@ static const struct of_device_id sii902x_dt_ids[] = {
|
||||
MODULE_DEVICE_TABLE(of, sii902x_dt_ids);
|
||||
|
||||
static const struct i2c_device_id sii902x_i2c_ids[] = {
|
||||
{ "sii9022" },
|
||||
{ .name = "sii9022" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids);
|
||||
|
||||
@@ -944,7 +944,7 @@ static const struct of_device_id sii9234_dt_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, sii9234_dt_match);
|
||||
|
||||
static const struct i2c_device_id sii9234_id[] = {
|
||||
{ "SII9234" },
|
||||
{ .name = "SII9234" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sii9234_id);
|
||||
|
||||
@@ -2374,7 +2374,7 @@ static const struct of_device_id sii8620_dt_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, sii8620_dt_match);
|
||||
|
||||
static const struct i2c_device_id sii8620_id[] = {
|
||||
{ "sii8620" },
|
||||
{ .name = "sii8620" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -1949,7 +1949,6 @@ static const struct regmap_config dw_dp_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
.max_register = DW_DP_MAX_REGISTER,
|
||||
.rd_table = &dw_dp_readable_table,
|
||||
};
|
||||
|
||||
@@ -3389,7 +3389,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "reg-io-width must be 1 or 4\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
ret = -EINVAL;
|
||||
goto err_res;
|
||||
}
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
@@ -907,7 +907,6 @@ static const struct regmap_config dw_mipi_dsi2_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct dw_mipi_dsi2 *
|
||||
|
||||
@@ -292,25 +292,17 @@ static int tc358762_probe(struct mipi_dsi_device *dsi)
|
||||
ctx->bridge.of_node = dev->of_node;
|
||||
ctx->bridge.pre_enable_prev_first = true;
|
||||
|
||||
drm_bridge_add(&ctx->bridge);
|
||||
ret = devm_drm_bridge_add(dev, &ctx->bridge);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
ret = devm_mipi_dsi_attach(dev, dsi);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to attach dsi\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tc358762_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
mipi_dsi_detach(dsi);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
}
|
||||
|
||||
static const struct of_device_id tc358762_of_match[] = {
|
||||
{ .compatible = "toshiba,tc358762" },
|
||||
{ }
|
||||
@@ -319,7 +311,6 @@ MODULE_DEVICE_TABLE(of, tc358762_of_match);
|
||||
|
||||
static struct mipi_dsi_driver tc358762_driver = {
|
||||
.probe = tc358762_probe,
|
||||
.remove = tc358762_remove,
|
||||
.driver = {
|
||||
.name = "tc358762",
|
||||
.of_match_table = tc358762_of_match,
|
||||
|
||||
@@ -376,25 +376,17 @@ static int tc358764_probe(struct mipi_dsi_device *dsi)
|
||||
ctx->bridge.of_node = dev->of_node;
|
||||
ctx->bridge.pre_enable_prev_first = true;
|
||||
|
||||
drm_bridge_add(&ctx->bridge);
|
||||
ret = devm_drm_bridge_add(dev, &ctx->bridge);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
ret = devm_mipi_dsi_attach(dev, dsi);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to attach dsi\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tc358764_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct tc358764 *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
mipi_dsi_detach(dsi);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
}
|
||||
|
||||
static const struct of_device_id tc358764_of_match[] = {
|
||||
{ .compatible = "toshiba,tc358764" },
|
||||
{ }
|
||||
@@ -403,7 +395,6 @@ MODULE_DEVICE_TABLE(of, tc358764_of_match);
|
||||
|
||||
static struct mipi_dsi_driver tc358764_driver = {
|
||||
.probe = tc358764_probe,
|
||||
.remove = tc358764_remove,
|
||||
.driver = {
|
||||
.name = "tc358764",
|
||||
.of_match_table = tc358764_of_match,
|
||||
|
||||
@@ -2613,7 +2613,7 @@ static void tc_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tc358767_i2c_ids[] = {
|
||||
{ "tc358767" },
|
||||
{ .name = "tc358767" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids);
|
||||
|
||||
@@ -1362,8 +1362,8 @@ static const struct regmap_config tc358768_regmap_config = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id tc358768_i2c_ids[] = {
|
||||
{ "tc358768" },
|
||||
{ "tc358778" },
|
||||
{ .name = "tc358768" },
|
||||
{ .name = "tc358778" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc358768_i2c_ids);
|
||||
|
||||
@@ -727,8 +727,8 @@ static void tc_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tc358775_i2c_ids[] = {
|
||||
{ "tc358765", TC358765, },
|
||||
{ "tc358775", TC358775, },
|
||||
{ .name = "tc358765", .driver_data = TC358765 },
|
||||
{ .name = "tc358775", .driver_data = TC358775 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc358775_i2c_ids);
|
||||
|
||||
@@ -2064,7 +2064,7 @@ MODULE_DEVICE_TABLE(of, tda998x_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id tda998x_ids[] = {
|
||||
{ "tda998x" },
|
||||
{ .name = "tda998x" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tda998x_ids);
|
||||
|
||||
@@ -389,7 +389,7 @@ static void dlpc3433_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dlpc3433_id[] = {
|
||||
{ "ti,dlpc3433" },
|
||||
{ .name = "ti,dlpc3433" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dlpc3433_id);
|
||||
|
||||
@@ -1072,9 +1072,9 @@ static void sn65dsi83_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sn65dsi83_id[] = {
|
||||
{ "ti,sn65dsi83", MODEL_SN65DSI83 },
|
||||
{ "ti,sn65dsi84", MODEL_SN65DSI84 },
|
||||
{},
|
||||
{ .name = "ti,sn65dsi83", .driver_data = MODEL_SN65DSI83 },
|
||||
{ .name = "ti,sn65dsi84", .driver_data = MODEL_SN65DSI84 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sn65dsi83_id);
|
||||
|
||||
|
||||
@@ -2080,7 +2080,7 @@ static int ti_sn65dsi86_probe(struct i2c_client *client)
|
||||
dev_name(pdata->dev), pdata);
|
||||
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request interrupt\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2116,8 +2116,8 @@ static int ti_sn65dsi86_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ti_sn65dsi86_id[] = {
|
||||
{ "ti,sn65dsi86" },
|
||||
{}
|
||||
{ .name = "ti,sn65dsi86" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ti_sn65dsi86_id);
|
||||
|
||||
|
||||
@@ -437,7 +437,7 @@ static void tfp410_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tfp410_i2c_ids[] = {
|
||||
{ "tfp410" },
|
||||
{ .name = "tfp410" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids);
|
||||
|
||||
@@ -960,17 +960,19 @@ static void drmm_cg_unregister_region(struct drm_device *dev, void *arg)
|
||||
* drmm_cgroup_register_region - Register a region of a DRM device to cgroups
|
||||
* @dev: device for region
|
||||
* @region_name: Region name for registering
|
||||
* @size: Size of region in bytes
|
||||
* @init: Initialization parameters for the region.
|
||||
*
|
||||
* This decreases the ref-count of @dev by one. The device is destroyed if the
|
||||
* ref-count drops to zero.
|
||||
*/
|
||||
struct dmem_cgroup_region *drmm_cgroup_register_region(struct drm_device *dev, const char *region_name, u64 size)
|
||||
struct dmem_cgroup_region *
|
||||
drmm_cgroup_register_region(struct drm_device *dev, const char *region_name,
|
||||
const struct dmem_cgroup_init *init)
|
||||
{
|
||||
struct dmem_cgroup_region *region;
|
||||
int ret;
|
||||
|
||||
region = dmem_cgroup_register_region(size, "drm/%s/%s", dev->unique, region_name);
|
||||
region = dmem_cgroup_register_region(init, "drm/%s/%s", dev->unique, region_name);
|
||||
if (IS_ERR_OR_NULL(region))
|
||||
return region;
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ pvr_kccb_used_slot_count_locked(struct pvr_device *pvr_dev)
|
||||
* pvr_kccb_send_cmd_reserved_powered() - Send command to the KCCB, with the PM ref
|
||||
* held and a slot pre-reserved
|
||||
* @pvr_dev: Device pointer.
|
||||
* @cmd: Command to sent.
|
||||
* @cmd: Command to send.
|
||||
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
|
||||
*/
|
||||
void
|
||||
@@ -361,7 +361,7 @@ static int pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev)
|
||||
/**
|
||||
* pvr_kccb_send_cmd_powered() - Send command to the KCCB, with a PM ref held
|
||||
* @pvr_dev: Device pointer.
|
||||
* @cmd: Command to sent.
|
||||
* @cmd: Command to send.
|
||||
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
|
||||
*
|
||||
* Returns:
|
||||
@@ -385,7 +385,7 @@ pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd
|
||||
/**
|
||||
* pvr_kccb_send_cmd() - Send command to the KCCB
|
||||
* @pvr_dev: Device pointer.
|
||||
* @cmd: Command to sent.
|
||||
* @cmd: Command to send.
|
||||
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
|
||||
*
|
||||
* Returns:
|
||||
|
||||
36
drivers/gpu/drm/imagination/pvr_check.h
Normal file
36
drivers/gpu/drm/imagination/pvr_check.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright (c) 2026 Imagination Technologies Ltd. */
|
||||
|
||||
#ifndef PVR_CHECK_H
|
||||
#define PVR_CHECK_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#define OFFSET_CHECK(type, member, offset) \
|
||||
static_assert(offsetof(type, member) == (offset), \
|
||||
"offsetof(" #type ", " #member ") incorrect")
|
||||
|
||||
#define SIZE_CHECK(type, size) \
|
||||
static_assert(sizeof(type) == (size), #type " is incorrect size")
|
||||
|
||||
#define ALIGN_CHECK(type, align) \
|
||||
static_assert(__alignof__(type) <= (align), #type " has incorrect alignment")
|
||||
|
||||
/*
|
||||
* Where the last member of a struct is a flexible array member, using
|
||||
* SIZE_CHECK() is pointless. If the structure is not already padded to
|
||||
* alignment without the flexible array member, sizeof() will not match the
|
||||
* offset of the flexible array member and the "correct" sizeof() value is
|
||||
* completely meaningless.
|
||||
*
|
||||
* In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final
|
||||
* field is a flexible array member and that it behaves as expected.
|
||||
*/
|
||||
#define FLEX_ARRAY_CHECK(type, member) \
|
||||
static_assert(flex_array_size((type *)NULL, member, 1) == \
|
||||
sizeof_field(type, member[0]), \
|
||||
#type "->" #member " is incorrect size")
|
||||
|
||||
#endif /* PVR_CHECK_H */
|
||||
@@ -22,12 +22,12 @@ struct pvr_device_features {
|
||||
bool has_coreid_per_os;
|
||||
bool has_dynamic_dust_power;
|
||||
bool has_ecc_rams;
|
||||
bool has_fb_cdc_v4;
|
||||
bool has_fbc_max_default_descriptors;
|
||||
bool has_fbc_max_large_descriptors;
|
||||
bool has_fbcdc;
|
||||
bool has_fbcdc_algorithm;
|
||||
bool has_fbcdc_architecture;
|
||||
bool has_fbc_max_default_descriptors;
|
||||
bool has_fbc_max_large_descriptors;
|
||||
bool has_fb_cdc_v4;
|
||||
bool has_gpu_multicore_support;
|
||||
bool has_gpu_virtualisation;
|
||||
bool has_gs_rta_support;
|
||||
|
||||
@@ -84,8 +84,10 @@ struct pvr_gem_object {
|
||||
/**
|
||||
* @base: The underlying &struct drm_gem_shmem_object.
|
||||
*
|
||||
* Do not access this member directly, instead call
|
||||
* shem_gem_from_pvr_gem().
|
||||
* .. note::
|
||||
*
|
||||
* This member should not be accessed directly, but instead by
|
||||
* calling shmem_gem_from_pvr_gem().
|
||||
*/
|
||||
struct drm_gem_shmem_object base;
|
||||
|
||||
@@ -97,12 +99,6 @@ struct pvr_gem_object {
|
||||
* changed after creation.
|
||||
*
|
||||
* Must be a combination of DRM_PVR_BO_* and/or PVR_BO_* flags.
|
||||
*
|
||||
* .. note::
|
||||
*
|
||||
* This member is declared const to indicate that none of these
|
||||
* options may change or be changed throughout the object's
|
||||
* lifetime.
|
||||
*/
|
||||
u64 flags;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "pvr_rogue_fwif_client.h"
|
||||
|
||||
#define MAX_DEADLINE_MS 30000
|
||||
#define SCHED_TIMEOUT_PERIOD (2 * MAX_DEADLINE_MS)
|
||||
|
||||
#define CTX_COMPUTE_CCCB_SIZE_LOG2 15
|
||||
#define CTX_FRAG_CCCB_SIZE_LOG2 15
|
||||
@@ -1283,7 +1284,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
|
||||
.submit_wq = pvr_dev->sched_wq,
|
||||
.credit_limit = 64 * 1024,
|
||||
.hang_limit = 1,
|
||||
.timeout = msecs_to_jiffies(500),
|
||||
.timeout = msecs_to_jiffies(SCHED_TIMEOUT_PERIOD),
|
||||
.timeout_wq = pvr_dev->sched_wq,
|
||||
.name = "pvr-queue",
|
||||
.dev = pvr_dev->base.dev,
|
||||
|
||||
@@ -4,31 +4,7 @@
|
||||
#ifndef PVR_ROGUE_FWIF_CHECK_H
|
||||
#define PVR_ROGUE_FWIF_CHECK_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#define OFFSET_CHECK(type, member, offset) \
|
||||
static_assert(offsetof(type, member) == (offset), \
|
||||
"offsetof(" #type ", " #member ") incorrect")
|
||||
|
||||
#define SIZE_CHECK(type, size) \
|
||||
static_assert(sizeof(type) == (size), #type " is incorrect size")
|
||||
|
||||
/*
|
||||
* Where the last member of a struct is a flexible array member, using
|
||||
* SIZE_CHECK() is pointless. If the structure is not already padded to
|
||||
* alignment without the flexible array member, sizeof() will not match the
|
||||
* offset of the flexible array member and the "correct" sizeof() value is
|
||||
* completely meaningless.
|
||||
*
|
||||
* In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final
|
||||
* field is a flexible array member and that it behaves as expected.
|
||||
*/
|
||||
#define FLEX_ARRAY_CHECK(type, member) \
|
||||
static_assert(flex_array_size((type *)NULL, member, 1) == \
|
||||
sizeof_field(type, member[0]), \
|
||||
#type "->" #member " is incorrect size")
|
||||
#include "pvr_check.h"
|
||||
|
||||
OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0);
|
||||
OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200);
|
||||
|
||||
@@ -4,14 +4,7 @@
|
||||
#ifndef PVR_ROGUE_FWIF_CLIENT_CHECK_H
|
||||
#define PVR_ROGUE_FWIF_CLIENT_CHECK_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
|
||||
#define OFFSET_CHECK(type, member, offset) \
|
||||
static_assert(offsetof(type, member) == (offset), \
|
||||
"offsetof(" #type ", " #member ") incorrect")
|
||||
|
||||
#define SIZE_CHECK(type, size) \
|
||||
static_assert(sizeof(type) == (size), #type " is incorrect size")
|
||||
#include "pvr_check.h"
|
||||
|
||||
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_ctrl_stream_base, 0);
|
||||
OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu_border_colour_table, 8);
|
||||
|
||||
@@ -4,14 +4,7 @@
|
||||
#ifndef PVR_ROGUE_FWIF_SHARED_CHECK_H
|
||||
#define PVR_ROGUE_FWIF_SHARED_CHECK_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
|
||||
#define OFFSET_CHECK(type, member, offset) \
|
||||
static_assert(offsetof(type, member) == (offset), \
|
||||
"offsetof(" #type ", " #member ") incorrect")
|
||||
|
||||
#define SIZE_CHECK(type, size) \
|
||||
static_assert(sizeof(type) == (size), #type " is incorrect size")
|
||||
#include "pvr_check.h"
|
||||
|
||||
OFFSET_CHECK(struct rogue_fwif_dma_addr, dev_addr, 0);
|
||||
OFFSET_CHECK(struct rogue_fwif_dma_addr, fw_addr, 8);
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
/* 0 MiB to 4 MiB, size of 4 MiB : RESERVED */
|
||||
|
||||
/* 0x00_0040_0000 - 0x7F_FFC0_0000 **/
|
||||
/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED **/
|
||||
/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED */
|
||||
|
||||
/* 0x80_0000_0000 ************************************************************/
|
||||
|
||||
/* 0x80_0000_0000 - 0x9F_FFFF_FFFF **/
|
||||
/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP **/
|
||||
/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP */
|
||||
#define ROGUE_GENERAL_HEAP_BASE 0x8000000000ull
|
||||
#define ROGUE_GENERAL_HEAP_SIZE SZ_128G
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#define ROGUE_PDSCODEDATA_HEAP_SIZE SZ_4G
|
||||
|
||||
/* 0xDB_0000_0000 - 0xDB_FFFF_FFFF */
|
||||
/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN **/
|
||||
/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN */
|
||||
/*
|
||||
* The BRN63142 quirk workaround requires Region Header memory to be at the top
|
||||
* of a 16GiB aligned range. This is so when masked with 0x03FFFFFFFF the
|
||||
@@ -82,7 +82,7 @@
|
||||
/* 0xE1_0000_0000 - 0xE1_BFFF_FFFF */
|
||||
/* 900 GiB to 903 GiB, size of 3 GiB : RESERVED */
|
||||
|
||||
/* 0xE1_C000_000 - 0xE1_FFFF_FFFF */
|
||||
/* 0xE1_C000_0000 - 0xE1_FFFF_FFFF */
|
||||
/* 903 GiB to 904 GiB, reserved 1 GiB, : FIRMWARE_HEAP */
|
||||
#define ROGUE_FW_HEAP_BASE 0xE1C0000000ull
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
/* 904 GiB to 912 GiB, size of 8 GiB : FREE */
|
||||
|
||||
/* 0xE4_0000_0000 - 0xE7_FFFF_FFFF */
|
||||
/* 912 GiB to 968 GiB, size of 16 GiB : TRANSFER_FRAG */
|
||||
/* 912 GiB to 928 GiB, size of 16 GiB : TRANSFER_FRAG */
|
||||
#define ROGUE_TRANSFER_FRAG_HEAP_BASE 0xE400000000ull
|
||||
#define ROGUE_TRANSFER_FRAG_HEAP_SIZE SZ_16G
|
||||
|
||||
|
||||
@@ -4,55 +4,32 @@
|
||||
#ifndef PVR_ROGUE_MIPS_CHECK_H
|
||||
#define PVR_ROGUE_MIPS_CHECK_H
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include "pvr_check.h"
|
||||
|
||||
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) == 0,
|
||||
"offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_hi) == 4,
|
||||
"offsetof(struct rogue_mips_tlb_entry, tlb_hi) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo0) == 8,
|
||||
"offsetof(struct rogue_mips_tlb_entry, tlb_lo0) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo1) == 12,
|
||||
"offsetof(struct rogue_mips_tlb_entry, tlb_lo1) incorrect");
|
||||
static_assert(sizeof(struct rogue_mips_tlb_entry) == 16,
|
||||
"struct rogue_mips_tlb_entry is incorrect size");
|
||||
OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_page_mask, 0);
|
||||
OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_hi, 4);
|
||||
OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_lo0, 8);
|
||||
OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_lo1, 12);
|
||||
SIZE_CHECK(struct rogue_mips_tlb_entry, 16);
|
||||
|
||||
static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_in) == 0,
|
||||
"offsetof(struct rogue_mips_remap_entry, remap_addr_in) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_out) == 4,
|
||||
"offsetof(struct rogue_mips_remap_entry, remap_addr_out) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_remap_entry, remap_region_size) == 8,
|
||||
"offsetof(struct rogue_mips_remap_entry, remap_region_size) incorrect");
|
||||
static_assert(sizeof(struct rogue_mips_remap_entry) == 12,
|
||||
"struct rogue_mips_remap_entry is incorrect size");
|
||||
OFFSET_CHECK(struct rogue_mips_remap_entry, remap_addr_in, 0);
|
||||
OFFSET_CHECK(struct rogue_mips_remap_entry, remap_addr_out, 4);
|
||||
OFFSET_CHECK(struct rogue_mips_remap_entry, remap_region_size, 8);
|
||||
SIZE_CHECK(struct rogue_mips_remap_entry, 12);
|
||||
|
||||
static_assert(offsetof(struct rogue_mips_state, error_state) == 0,
|
||||
"offsetof(struct rogue_mips_state, error_state) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, error_epc) == 4,
|
||||
"offsetof(struct rogue_mips_state, error_epc) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, status_register) == 8,
|
||||
"offsetof(struct rogue_mips_state, status_register) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, cause_register) == 12,
|
||||
"offsetof(struct rogue_mips_state, cause_register) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, bad_register) == 16,
|
||||
"offsetof(struct rogue_mips_state, bad_register) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, epc) == 20,
|
||||
"offsetof(struct rogue_mips_state, epc) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, sp) == 24,
|
||||
"offsetof(struct rogue_mips_state, sp) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, debug) == 28,
|
||||
"offsetof(struct rogue_mips_state, debug) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, depc) == 32,
|
||||
"offsetof(struct rogue_mips_state, depc) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, bad_instr) == 36,
|
||||
"offsetof(struct rogue_mips_state, bad_instr) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, unmapped_address) == 40,
|
||||
"offsetof(struct rogue_mips_state, unmapped_address) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, tlb) == 44,
|
||||
"offsetof(struct rogue_mips_state, tlb) incorrect");
|
||||
static_assert(offsetof(struct rogue_mips_state, remap) == 300,
|
||||
"offsetof(struct rogue_mips_state, remap) incorrect");
|
||||
static_assert(sizeof(struct rogue_mips_state) == 684,
|
||||
"struct rogue_mips_state is incorrect size");
|
||||
OFFSET_CHECK(struct rogue_mips_state, error_state, 0);
|
||||
OFFSET_CHECK(struct rogue_mips_state, error_epc, 4);
|
||||
OFFSET_CHECK(struct rogue_mips_state, status_register, 8);
|
||||
OFFSET_CHECK(struct rogue_mips_state, cause_register, 12);
|
||||
OFFSET_CHECK(struct rogue_mips_state, bad_register, 16);
|
||||
OFFSET_CHECK(struct rogue_mips_state, epc, 20);
|
||||
OFFSET_CHECK(struct rogue_mips_state, sp, 24);
|
||||
OFFSET_CHECK(struct rogue_mips_state, debug, 28);
|
||||
OFFSET_CHECK(struct rogue_mips_state, depc, 32);
|
||||
OFFSET_CHECK(struct rogue_mips_state, bad_instr, 36);
|
||||
OFFSET_CHECK(struct rogue_mips_state, unmapped_address, 40);
|
||||
OFFSET_CHECK(struct rogue_mips_state, tlb, 44);
|
||||
OFFSET_CHECK(struct rogue_mips_state, remap, 300);
|
||||
SIZE_CHECK(struct rogue_mips_state, 684);
|
||||
|
||||
#endif /* PVR_ROGUE_MIPS_CHECK_H */
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#ifndef __NV50_KMS_BASE_H__
|
||||
#define __NV50_KMS_BASE_H__
|
||||
#include <drm/drm_blend.h>
|
||||
|
||||
#include "wndw.h"
|
||||
|
||||
int base507c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
|
||||
@@ -24,4 +26,8 @@ extern const struct nv50_wndw_func base907c;
|
||||
int base917c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
|
||||
|
||||
int nv50_base_new(struct nouveau_drm *, int head, struct nv50_wndw **);
|
||||
|
||||
/* The only blend mode supported by base channels */
|
||||
#define BASE_BLEND_MODES BIT(DRM_MODE_BLEND_PIXEL_NONE)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -297,6 +297,7 @@ base507c = {
|
||||
.image_set = base507c_image_set,
|
||||
.image_clr = base507c_image_clr,
|
||||
.update = base507c_update,
|
||||
.blend_modes = BASE_BLEND_MODES,
|
||||
};
|
||||
|
||||
int
|
||||
|
||||
@@ -93,6 +93,7 @@ base827c = {
|
||||
.image_set = base827c_image_set,
|
||||
.image_clr = base507c_image_clr,
|
||||
.update = base507c_update,
|
||||
.blend_modes = BASE_BLEND_MODES,
|
||||
};
|
||||
|
||||
int
|
||||
|
||||
@@ -205,6 +205,7 @@ base907c = {
|
||||
.image_set = base907c_image_set,
|
||||
.image_clr = base507c_image_clr,
|
||||
.update = base507c_update,
|
||||
.blend_modes = BASE_BLEND_MODES,
|
||||
};
|
||||
|
||||
int
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <nvhw/class/cl507a.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_blend.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
|
||||
bool
|
||||
@@ -164,6 +165,8 @@ curs507a_wndw = {
|
||||
.acquire = curs507a_acquire,
|
||||
.release = curs507a_release,
|
||||
.prepare = curs507a_prepare,
|
||||
// TODO: Cursors also support premulti, but we haven't hooked it up anywhere yet.
|
||||
.blend_modes = BIT(DRM_MODE_BLEND_COVERAGE),
|
||||
};
|
||||
|
||||
int
|
||||
|
||||
@@ -2768,6 +2768,7 @@ nv50_display_read_hw_state(struct nouveau_drm *drm)
|
||||
{
|
||||
struct drm_device *dev = drm->dev;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_plane *plane;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct nv50_disp *disp = nv50_disp(dev);
|
||||
int ret;
|
||||
@@ -2781,6 +2782,9 @@ nv50_display_read_hw_state(struct nouveau_drm *drm)
|
||||
nv50_display_read_hw_or_state(dev, disp, nouveau_encoder(encoder));
|
||||
}
|
||||
|
||||
drm_for_each_plane(plane, dev)
|
||||
nv50_wndw_default_state(nv50_wndw(plane));
|
||||
|
||||
DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
|
||||
}
|
||||
|
||||
|
||||
@@ -848,6 +848,28 @@ static const u64 nv50_cursor_format_modifiers[] = {
|
||||
DRM_FORMAT_MOD_INVALID,
|
||||
};
|
||||
|
||||
/*
|
||||
* Setup defaults for the atomic wndw state
|
||||
*/
|
||||
void
|
||||
nv50_wndw_default_state(struct nv50_wndw *wndw)
|
||||
{
|
||||
struct nv50_wndw_atom *armw = nv50_wndw_atom(wndw->plane.state);
|
||||
const unsigned int blend_modes = wndw->func->blend_modes;
|
||||
|
||||
drm_modeset_lock_assert_held(&wndw->plane.mutex);
|
||||
|
||||
/* Ensure the plane's atomic state didn't default to a pixel_blend_mode we don't support */
|
||||
if (blend_modes && (!(BIT(armw->state.pixel_blend_mode) & blend_modes))) {
|
||||
if (blend_modes & BIT(DRM_MODE_BLEND_COVERAGE))
|
||||
armw->state.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE;
|
||||
else if (blend_modes & BIT(DRM_MODE_BLEND_PREMULTI))
|
||||
armw->state.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
|
||||
else if (blend_modes & BIT(DRM_MODE_BLEND_PIXEL_NONE))
|
||||
armw->state.pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
|
||||
enum drm_plane_type type, const char *name, int index,
|
||||
@@ -908,13 +930,6 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
|
||||
ret = drm_plane_create_alpha_property(&wndw->plane);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_plane_create_blend_mode_property(&wndw->plane,
|
||||
BIT(DRM_MODE_BLEND_PIXEL_NONE) |
|
||||
BIT(DRM_MODE_BLEND_PREMULTI) |
|
||||
BIT(DRM_MODE_BLEND_COVERAGE));
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = drm_plane_create_zpos_immutable_property(&wndw->plane,
|
||||
nv50_wndw_zpos_default(&wndw->plane));
|
||||
@@ -922,6 +937,17 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* DRM requires that we have a blend mode property for any type of plane that exposes color
|
||||
* formats with an alpha channel. So do this, even if we don't actually have control for the
|
||||
* blend property hooked up with blend_set.
|
||||
*/
|
||||
if (func->blend_modes) {
|
||||
ret = drm_plane_create_blend_mode_property(&wndw->plane, func->blend_modes);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __NV50_KMS_WNDW_H__
|
||||
#define __NV50_KMS_WNDW_H__
|
||||
#define nv50_wndw(p) container_of((p), struct nv50_wndw, plane)
|
||||
#include <drm/drm_blend.h>
|
||||
|
||||
#include "disp.h"
|
||||
#include "atom.h"
|
||||
#include "lut.h"
|
||||
@@ -44,6 +46,7 @@ void nv50_wndw_flush_clr(struct nv50_wndw *, u32 *interlock, bool flush,
|
||||
struct nv50_wndw_atom *);
|
||||
void nv50_wndw_ntfy_enable(struct nv50_wndw *, struct nv50_wndw_atom *);
|
||||
int nv50_wndw_wait_armed(struct nv50_wndw *, struct nv50_wndw_atom *);
|
||||
void nv50_wndw_default_state(struct nv50_wndw *wndw);
|
||||
|
||||
struct nv50_wndw_func {
|
||||
int (*acquire)(struct nv50_wndw *, struct nv50_wndw_atom *asyw,
|
||||
@@ -74,6 +77,7 @@ struct nv50_wndw_func {
|
||||
int (*image_clr)(struct nv50_wndw *);
|
||||
int (*scale_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
|
||||
int (*blend_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
|
||||
unsigned int blend_modes;
|
||||
|
||||
int (*update)(struct nv50_wndw *, u32 *interlock);
|
||||
};
|
||||
@@ -139,4 +143,11 @@ int wndwca7e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
|
||||
|
||||
int nv50_wndw_new(struct nouveau_drm *, enum drm_plane_type, int index,
|
||||
struct nv50_wndw **);
|
||||
|
||||
/* A set of blend modes supported by all wndws */
|
||||
#define WNDW_BLEND_MODES \
|
||||
( BIT(DRM_MODE_BLEND_COVERAGE) \
|
||||
| BIT(DRM_MODE_BLEND_PREMULTI) \
|
||||
| BIT(DRM_MODE_BLEND_PIXEL_NONE))
|
||||
|
||||
#endif
|
||||
|
||||
@@ -343,6 +343,7 @@ wndwc37e = {
|
||||
.image_set = wndwc37e_image_set,
|
||||
.image_clr = wndwc37e_image_clr,
|
||||
.blend_set = wndwc37e_blend_set,
|
||||
.blend_modes = WNDW_BLEND_MODES,
|
||||
.update = wndwc37e_update,
|
||||
};
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ wndwc57e = {
|
||||
.image_set = wndwc57e_image_set,
|
||||
.image_clr = wndwc37e_image_clr,
|
||||
.blend_set = wndwc37e_blend_set,
|
||||
.blend_modes = WNDW_BLEND_MODES,
|
||||
.update = wndwc37e_update,
|
||||
};
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ wndwc67e = {
|
||||
.image_set = wndwc67e_image_set,
|
||||
.image_clr = wndwc37e_image_clr,
|
||||
.blend_set = wndwc37e_blend_set,
|
||||
.blend_modes = WNDW_BLEND_MODES,
|
||||
.update = wndwc37e_update,
|
||||
};
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ wndwca7e = {
|
||||
.image_set = wndwca7e_image_set,
|
||||
.image_clr = wndwca7e_image_clr,
|
||||
.blend_set = wndwc37e_blend_set,
|
||||
.blend_modes = WNDW_BLEND_MODES,
|
||||
.update = wndwc37e_update,
|
||||
};
|
||||
|
||||
|
||||
@@ -408,6 +408,9 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
|
||||
case NOUVEAU_FIFO_ENGINE_CE:
|
||||
engine = NV_DEVICE_HOST_RUNLIST_ENGINES_CE;
|
||||
break;
|
||||
case NOUVEAU_FIFO_ENGINE_NVDEC:
|
||||
engine = NV_DEVICE_HOST_RUNLIST_ENGINES_NVDEC;
|
||||
break;
|
||||
default:
|
||||
return nouveau_abi16_put(abi16, -ENOSYS);
|
||||
}
|
||||
@@ -485,10 +488,13 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
|
||||
goto done;
|
||||
break;
|
||||
case NV_DEVICE_INFO_V0_TURING:
|
||||
ret = nvif_object_ctor(&chan->chan->user, "abi16CeWar", 0, TURING_DMA_COPY_A,
|
||||
NULL, 0, &chan->ce);
|
||||
if (ret)
|
||||
goto done;
|
||||
if (engine != NV_DEVICE_HOST_RUNLIST_ENGINES_NVDEC) {
|
||||
ret = nvif_object_ctor(&chan->chan->user, "abi16CeWar",
|
||||
0, TURING_DMA_COPY_A, NULL, 0,
|
||||
&chan->ce);
|
||||
if (ret)
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 4
|
||||
#define DRIVER_PATCHLEVEL 2
|
||||
#define DRIVER_PATCHLEVEL 3
|
||||
|
||||
/*
|
||||
* 1.1.1:
|
||||
@@ -39,6 +39,8 @@
|
||||
* - add variable page sizes and compression for Turing+
|
||||
* 1.4.2:
|
||||
* - tell userspace LPTE/SPTE races are fixed.
|
||||
* 1.4.3:
|
||||
* - VDEC contexts can be created.
|
||||
*/
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/cgroup_dmem.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <drm/ttm/ttm_range_manager.h>
|
||||
@@ -188,6 +189,13 @@ nouveau_ttm_init_vram(struct nouveau_drm *drm)
|
||||
|
||||
man->func = &nouveau_vram_manager;
|
||||
|
||||
man->cg = drmm_cgroup_register_region(drm->dev, "vram",
|
||||
&(struct dmem_cgroup_init){
|
||||
.size = drm->gem.vram_available
|
||||
});
|
||||
if (IS_ERR(man->cg))
|
||||
return PTR_ERR(man->cg);
|
||||
|
||||
ttm_resource_manager_init(man, &drm->ttm.bdev,
|
||||
drm->gem.vram_available >> PAGE_SHIFT);
|
||||
ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_VRAM, man);
|
||||
|
||||
@@ -456,15 +456,10 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
|
||||
timer_delete(&dsi->te_timer);
|
||||
#endif
|
||||
|
||||
/* make a copy and unlock, so that isrs can unregister
|
||||
* themselves */
|
||||
memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
|
||||
sizeof(dsi->isr_tables));
|
||||
dsi_handle_isrs(&dsi->isr_tables, irqstatus, vcstatus, ciostatus);
|
||||
|
||||
spin_unlock(&dsi->irq_lock);
|
||||
|
||||
dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
|
||||
|
||||
dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus);
|
||||
|
||||
dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus);
|
||||
|
||||
@@ -379,8 +379,6 @@ struct dsi_data {
|
||||
|
||||
spinlock_t irq_lock;
|
||||
struct dsi_isr_tables isr_tables;
|
||||
/* space for a copy used by the interrupt handler */
|
||||
struct dsi_isr_tables isr_tables_copy;
|
||||
|
||||
int update_vc;
|
||||
#ifdef DSI_PERF_MEASURE
|
||||
|
||||
@@ -144,6 +144,16 @@ struct panel_delay {
|
||||
*/
|
||||
unsigned int disable;
|
||||
|
||||
/**
|
||||
* @pre_unprepare: Time for the end of video data to power off.
|
||||
*
|
||||
* The time (in milliseconds) that it needs to have passed between
|
||||
* the end of valid video data from source and start powering off.
|
||||
*
|
||||
* This is T10-min on eDP timing diagrams. It is not common to set.
|
||||
*/
|
||||
unsigned int pre_unprepare;
|
||||
|
||||
/**
|
||||
* @unprepare: Time to power down completely.
|
||||
*
|
||||
@@ -413,8 +423,12 @@ static int panel_edp_suspend(struct device *dev)
|
||||
|
||||
static int panel_edp_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct panel_edp *p = to_panel_edp(panel);
|
||||
int ret;
|
||||
|
||||
if (p->desc->delay.pre_unprepare)
|
||||
msleep(p->desc->delay.pre_unprepare);
|
||||
|
||||
ret = pm_runtime_put_sync_suspend(panel->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -832,6 +846,13 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void panel_edp_put_adapter(void *_adap)
|
||||
{
|
||||
struct i2c_adapter *adap = _adap;
|
||||
|
||||
i2c_put_adapter(adap);
|
||||
}
|
||||
|
||||
static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
|
||||
struct drm_dp_aux *aux)
|
||||
{
|
||||
@@ -874,11 +895,16 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
|
||||
|
||||
ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
|
||||
if (ddc) {
|
||||
panel->ddc = of_find_i2c_adapter_by_node(ddc);
|
||||
panel->ddc = of_get_i2c_adapter_by_node(ddc);
|
||||
of_node_put(ddc);
|
||||
|
||||
if (!panel->ddc)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
err = devm_add_action_or_reset(dev, panel_edp_put_adapter,
|
||||
panel->ddc);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (aux) {
|
||||
panel->ddc = &aux->ddc;
|
||||
}
|
||||
@@ -890,7 +916,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
|
||||
|
||||
err = drm_panel_of_backlight(&panel->base);
|
||||
if (err)
|
||||
goto err_finished_ddc_init;
|
||||
return err;
|
||||
|
||||
/*
|
||||
* We use runtime PM for prepare / unprepare since those power the panel
|
||||
@@ -937,9 +963,6 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
|
||||
err_finished_pm_runtime:
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
err_finished_ddc_init:
|
||||
if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
|
||||
put_device(&panel->ddc->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -983,8 +1006,6 @@ static void panel_edp_remove(struct device *dev)
|
||||
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc))
|
||||
put_device(&panel->ddc->dev);
|
||||
|
||||
drm_edid_free(panel->drm_edid);
|
||||
panel->drm_edid = NULL;
|
||||
@@ -1803,6 +1824,13 @@ static const struct panel_delay delay_80_500_e80_p2e200 = {
|
||||
.prepare_to_enable = 200,
|
||||
};
|
||||
|
||||
static const struct panel_delay delay_200_500_e80_pu100 = {
|
||||
.hpd_absent = 200,
|
||||
.unprepare = 500,
|
||||
.enable = 80,
|
||||
.pre_unprepare = 100,
|
||||
};
|
||||
|
||||
static const struct panel_delay delay_100_500_e200 = {
|
||||
.hpd_absent = 100,
|
||||
.unprepare = 500,
|
||||
@@ -2029,6 +2057,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a84, &delay_200_500_e50, "NV133WUM-T01"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ae8, &delay_200_500_e50_p2e80, "NV140WUM-N41"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0af2, &delay_200_500_e50_p2e80, "NE140QDM-NX2"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b1e, &delay_200_500_e80, "NE140QDM-N6A"),
|
||||
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80_d50, "NV122WUM-N41"),
|
||||
@@ -2076,6 +2105,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1161, &delay_200_500_e80, "N116BCP-EA2"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1163, &delay_200_500_e80_d50, "N116BCJ-EAK"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1169, &delay_200_500_e80_d50, "N116BCN-EA1"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x116c, &delay_200_500_e80_d50, "N116BCP-EA2"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x116d, &delay_200_500_e80_d50, "N116BCP-EA2"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x117a, &delay_200_500_e80_d50, "N116BCL-EAK"),
|
||||
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"),
|
||||
@@ -2165,6 +2195,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"),
|
||||
EDP_PANEL_ENTRY('S', 'H', 'P', 0x158f, &delay_200_500_p2e100, "LQ134Z1"),
|
||||
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1593, &delay_200_500_p2e100, "LQ134N1"),
|
||||
EDP_PANEL_ENTRY('S', 'H', 'P', 0x15a7, &delay_200_500_e200, "LQ120P1JX51"),
|
||||
|
||||
EDP_PANEL_ENTRY('S', 'T', 'A', 0x0004, &delay_200_500_e200, "116KHD024006"),
|
||||
EDP_PANEL_ENTRY('S', 'T', 'A', 0x0009, &delay_200_500_e250, "116QHD024002"),
|
||||
@@ -2172,7 +2203,7 @@ static const struct edp_panel_entry edp_panels[] = {
|
||||
|
||||
EDP_PANEL_ENTRY('T', 'M', 'A', 0x0811, &delay_200_500_e80_d50, "TM140VDXP01-04"),
|
||||
EDP_PANEL_ENTRY('T', 'M', 'A', 0x2094, &delay_200_500_e50_d100, "TL140VDMS03-01"),
|
||||
EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e50_d100, "TM156VDXP25"),
|
||||
EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e80_pu100, "TM156VDXP25"),
|
||||
|
||||
{ /* sentinal */ }
|
||||
};
|
||||
|
||||
@@ -25,22 +25,8 @@
|
||||
#define ILI9805_SETEXTC_PARAMETER2 (0x98)
|
||||
#define ILI9805_SETEXTC_PARAMETER3 (0x05)
|
||||
|
||||
#define ILI9805_INSTR(_delay, ...) { \
|
||||
.delay = (_delay), \
|
||||
.len = sizeof((u8[]) {__VA_ARGS__}), \
|
||||
.data = (u8[]){__VA_ARGS__} \
|
||||
}
|
||||
|
||||
struct ili9805_instr {
|
||||
size_t len;
|
||||
const u8 *data;
|
||||
u32 delay;
|
||||
};
|
||||
|
||||
struct ili9805_desc {
|
||||
const char *name;
|
||||
const struct ili9805_instr *init;
|
||||
const size_t init_length;
|
||||
void (*init)(struct mipi_dsi_multi_context *ctx);
|
||||
const struct drm_display_mode *mode;
|
||||
u32 width_mm;
|
||||
u32 height_mm;
|
||||
@@ -56,65 +42,80 @@ struct ili9805 {
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static const struct ili9805_instr gpm1780a0_init[] = {
|
||||
ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,
|
||||
ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),
|
||||
ILI9805_INSTR(100, 0xFD, 0x0F, 0x10, 0x44, 0x00),
|
||||
ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00,
|
||||
0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00),
|
||||
ILI9805_INSTR(0, 0xB8, 0x62),
|
||||
ILI9805_INSTR(0, 0xF1, 0x00),
|
||||
ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),
|
||||
ILI9805_INSTR(0, 0xF3, 0x60, 0x83, 0x04),
|
||||
ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),
|
||||
ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),
|
||||
ILI9805_INSTR(0, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,
|
||||
0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),
|
||||
ILI9805_INSTR(0, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04,
|
||||
0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a),
|
||||
ILI9805_INSTR(10, 0xc1, 0x13, 0x39, 0x19, 0x06),
|
||||
ILI9805_INSTR(10, 0xc7, 0xe5),
|
||||
ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),
|
||||
ILI9805_INSTR(10, 0xB4, 0x02),
|
||||
ILI9805_INSTR(0, 0xBB, 0x14, 0x55),
|
||||
ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x08),
|
||||
ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),
|
||||
ILI9805_INSTR(0, 0x20),
|
||||
ILI9805_INSTR(0, 0xB0, 0x01),
|
||||
ILI9805_INSTR(0, 0xB6, 0x31, 0x00, 0xef),
|
||||
ILI9805_INSTR(0, 0xDF, 0x23),
|
||||
ILI9805_INSTR(0, 0xB9, 0x02, 0x00),
|
||||
};
|
||||
static void gpm1780a0_init(struct mipi_dsi_multi_context *ctx)
|
||||
{
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, ILI9805_EXTCMD_CMD_SET_ENABLE_REG,
|
||||
ILI9805_SETEXTC_PARAMETER1, ILI9805_SETEXTC_PARAMETER2,
|
||||
ILI9805_SETEXTC_PARAMETER3);
|
||||
mipi_dsi_msleep(ctx, 100);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xfd, 0x0f, 0x10, 0x44, 0x00);
|
||||
mipi_dsi_msleep(ctx, 100);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00,
|
||||
0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb8, 0x62);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf1, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf2, 0x00, 0x58, 0x40);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf3, 0x60, 0x83, 0x04);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x04, 0x0f, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xeb, 0x08, 0x0f);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08,
|
||||
0x04, 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08,
|
||||
0x04, 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x13, 0x39, 0x19, 0x06);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xc7, 0xe5);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb1, 0x00, 0x12, 0x14);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb4, 0x02);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xbb, 0x14, 0x55);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x08);
|
||||
mipi_dsi_dcs_set_pixel_format_multi(ctx, 0x77);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0x20);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb0, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb6, 0x31, 0x00, 0xef);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xdf, 0x23);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb9, 0x02, 0x00);
|
||||
}
|
||||
|
||||
static const struct ili9805_instr tm041xdhg01_init[] = {
|
||||
ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1,
|
||||
ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3),
|
||||
ILI9805_INSTR(100, 0xFD, 0x0F, 0x13, 0x44, 0x00),
|
||||
ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01,
|
||||
0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01),
|
||||
ILI9805_INSTR(0, 0xB8, 0x74),
|
||||
ILI9805_INSTR(0, 0xF1, 0x00),
|
||||
ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40),
|
||||
ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01),
|
||||
ILI9805_INSTR(0, 0xEB, 0x08, 0x0F),
|
||||
ILI9805_INSTR(0, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,
|
||||
0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),
|
||||
ILI9805_INSTR(0, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04,
|
||||
0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08),
|
||||
ILI9805_INSTR(10, 0xc1, 0x15, 0x03, 0x03, 0x31),
|
||||
ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14),
|
||||
ILI9805_INSTR(10, 0xB4, 0x02),
|
||||
ILI9805_INSTR(0, 0xBB, 0x14, 0x55),
|
||||
ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x0a),
|
||||
ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77),
|
||||
ILI9805_INSTR(0, 0x20),
|
||||
ILI9805_INSTR(0, 0xB0, 0x00),
|
||||
ILI9805_INSTR(0, 0xB6, 0x01),
|
||||
ILI9805_INSTR(0, 0xc2, 0x11),
|
||||
ILI9805_INSTR(0, 0x51, 0xFF),
|
||||
ILI9805_INSTR(0, 0x53, 0x24),
|
||||
ILI9805_INSTR(0, 0x55, 0x00),
|
||||
};
|
||||
static void tm041xdhg01_init(struct mipi_dsi_multi_context *ctx)
|
||||
{
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, ILI9805_EXTCMD_CMD_SET_ENABLE_REG,
|
||||
ILI9805_SETEXTC_PARAMETER1, ILI9805_SETEXTC_PARAMETER2,
|
||||
ILI9805_SETEXTC_PARAMETER3);
|
||||
mipi_dsi_msleep(ctx, 100);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xfd, 0x0f, 0x13, 0x44, 0x00);
|
||||
mipi_dsi_msleep(ctx, 100);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01,
|
||||
0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb8, 0x74);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf1, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xf2, 0x00, 0x58, 0x40);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x04, 0x0f, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xeb, 0x08, 0x0f);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08,
|
||||
0x04, 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08,
|
||||
0x04, 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x15, 0x03, 0x03, 0x31);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb1, 0x00, 0x12, 0x14);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb4, 0x02);
|
||||
mipi_dsi_msleep(ctx, 10);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xbb, 0x14, 0x55);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
|
||||
mipi_dsi_dcs_set_pixel_format_multi(ctx, 0x77);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0x20);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb0, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xb6, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0xc2, 0x11);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0x51, 0xff);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0x53, 0x24);
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, 0x55, 0x00);
|
||||
}
|
||||
|
||||
static inline struct ili9805 *panel_to_ili9805(struct drm_panel *panel)
|
||||
{
|
||||
@@ -148,47 +149,24 @@ static int ili9805_power_on(struct ili9805 *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili9805_power_off(struct ili9805 *ctx)
|
||||
static void ili9805_power_off(struct ili9805 *ctx)
|
||||
{
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
regulator_disable(ctx->dvdd);
|
||||
regulator_disable(ctx->avdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili9805_activate(struct ili9805 *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = ctx->dsi;
|
||||
struct device *dev = &dsi->dev;
|
||||
int i, ret;
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
|
||||
|
||||
for (i = 0; i < ctx->desc->init_length; i++) {
|
||||
const struct ili9805_instr *instr = &ctx->desc->init[i];
|
||||
ctx->desc->init(&dsi_ctx);
|
||||
|
||||
ret = mipi_dsi_dcs_write_buffer(ctx->dsi, instr->data, instr->len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
|
||||
mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000);
|
||||
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
|
||||
|
||||
if (instr->delay > 0)
|
||||
msleep(instr->delay);
|
||||
}
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to exit sleep mode (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set display ON (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static int ili9805_prepare(struct drm_panel *panel)
|
||||
@@ -209,27 +187,13 @@ static int ili9805_prepare(struct drm_panel *panel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili9805_deactivate(struct ili9805 *ctx)
|
||||
static void ili9805_deactivate(struct ili9805 *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = ctx->dsi;
|
||||
struct device *dev = &dsi->dev;
|
||||
int ret;
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set display OFF (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enter sleep mode (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
|
||||
mipi_dsi_usleep_range(&dsi_ctx, 5000, 10000);
|
||||
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
|
||||
}
|
||||
|
||||
static int ili9805_unprepare(struct drm_panel *panel)
|
||||
@@ -271,7 +235,7 @@ static const struct drm_display_mode tm041xdhg01_timing = {
|
||||
};
|
||||
|
||||
static int ili9805_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct ili9805 *ctx = panel_to_ili9805(panel);
|
||||
struct drm_display_mode *mode;
|
||||
@@ -368,7 +332,6 @@ static void ili9805_dsi_remove(struct mipi_dsi_device *dsi)
|
||||
|
||||
static const struct ili9805_desc gpm1780a0_desc = {
|
||||
.init = gpm1780a0_init,
|
||||
.init_length = ARRAY_SIZE(gpm1780a0_init),
|
||||
.mode = &gpm1780a0_timing,
|
||||
.width_mm = 65,
|
||||
.height_mm = 65,
|
||||
@@ -376,7 +339,6 @@ static const struct ili9805_desc gpm1780a0_desc = {
|
||||
|
||||
static const struct ili9805_desc tm041xdhg01_desc = {
|
||||
.init = tm041xdhg01_init,
|
||||
.init_length = ARRAY_SIZE(tm041xdhg01_init),
|
||||
.mode = &tm041xdhg01_timing,
|
||||
.width_mm = 42,
|
||||
.height_mm = 96,
|
||||
|
||||
@@ -2931,6 +2931,248 @@ static const struct jadard_panel_desc waveshare_10_1_inch_b_desc = {
|
||||
MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
|
||||
};
|
||||
|
||||
static int dcltek_dt300250_init_cmds(struct jadard *jadard)
|
||||
{
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
|
||||
|
||||
jd9365da_switch_page(&dsi_ctx, 0x00);
|
||||
jadard_enable_standard_cmds(&dsi_ctx);
|
||||
|
||||
jd9365da_switch_page(&dsi_ctx, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x2B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x2B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xCF);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xCF);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xFE);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x5C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x7F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x02);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xC8);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x66);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x10);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x8D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x29);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x1A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x7F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x65);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x53);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x45);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x3E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x30);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x33);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x3B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x3C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x3F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x5D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x49);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x4D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x3D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x37);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x28);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x15);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x65);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x53);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x45);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x3E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x30);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x33);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x3B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x3C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x3F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x5D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x49);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x4D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x3D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x44);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x28);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x15);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00);
|
||||
|
||||
jd9365da_switch_page(&dsi_ctx, 0x02);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x41);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x4B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x49);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x47);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x4F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x45);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x4D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x51);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x40);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x4A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x5B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x48);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x59);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x46);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x4E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x44);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x4C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x50);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x5F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2C, 0x10);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2D, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2E, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2F, 0x0C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x04);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x0E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x06);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x1B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0x0A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0x1E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x11);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x0D);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x05);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x0F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4A, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x07);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4C, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4D, 0x1A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4E, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4F, 0x09);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x1C);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x0B);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x10);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x70);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x70);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x06);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x4A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x56);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x4F);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xF7);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x01);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x06);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x4A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x10);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88);
|
||||
|
||||
jd9365da_switch_page(&dsi_ctx, 0x04);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x4A);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0F);
|
||||
|
||||
jd9365da_switch_page(&dsi_ctx, 0x05);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x34);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x76);
|
||||
|
||||
jd9365da_switch_page(&dsi_ctx, 0x00);
|
||||
|
||||
mipi_dsi_msleep(&dsi_ctx, 120);
|
||||
|
||||
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
|
||||
|
||||
mipi_dsi_msleep(&dsi_ctx, 120);
|
||||
|
||||
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
|
||||
|
||||
mipi_dsi_msleep(&dsi_ctx, 20);
|
||||
|
||||
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
|
||||
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static const struct jadard_panel_desc dcltek_dt300250_desc = {
|
||||
.mode_4ln = &(const struct drm_display_mode) {
|
||||
.clock = (600 + 48 + 32 + 80) * (1600 + 18 + 6 + 23) * 60 / 1000,
|
||||
|
||||
.hdisplay = 600,
|
||||
.hsync_start = 600 + 48,
|
||||
.hsync_end = 600 + 48 + 32,
|
||||
.htotal = 600 + 48 + 32 + 80,
|
||||
|
||||
.vdisplay = 1600,
|
||||
.vsync_start = 1600 + 18,
|
||||
.vsync_end = 1600 + 18 + 6,
|
||||
.vtotal = 1600 + 18 + 6 + 23,
|
||||
|
||||
.width_mm = 83,
|
||||
.height_mm = 221,
|
||||
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
|
||||
},
|
||||
.lanes = 4,
|
||||
.format = MIPI_DSI_FMT_RGB888,
|
||||
.init = dcltek_dt300250_init_cmds,
|
||||
};
|
||||
|
||||
static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
@@ -2950,7 +3192,8 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
|
||||
else
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
|
||||
MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_MODE_NO_EOT_PACKET;
|
||||
MIPI_DSI_MODE_NO_EOT_PACKET |
|
||||
MIPI_DSI_MODE_LPM;
|
||||
|
||||
dsi->format = desc->format;
|
||||
dsi->lanes = desc->lanes;
|
||||
@@ -3036,6 +3279,10 @@ static const struct of_device_id jadard_of_match[] = {
|
||||
.compatible = "chongzhou,cz101b4001",
|
||||
.data = &cz101b4001_desc
|
||||
},
|
||||
{
|
||||
.compatible = "dcltek,dt300250",
|
||||
.data = &dcltek_dt300250_desc
|
||||
},
|
||||
{
|
||||
.compatible = "kingdisplay,kd101ne3-40ti",
|
||||
.data = &kingdisplay_kd101ne3_40ti_desc
|
||||
|
||||
@@ -429,11 +429,9 @@ static int novatek_probe(struct mipi_dsi_device *dsi)
|
||||
ctx->dsi[i]->mode_flags = desc->mode_flags;
|
||||
ctx->dsi[i]->dsc = &ctx->dsc;
|
||||
ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
|
||||
if (ret < 0) {
|
||||
drm_panel_remove(&ctx->panel);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to attach to DSI host\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (desc->has_dcs_backlight) {
|
||||
|
||||
@@ -912,6 +912,36 @@ static const struct panel_desc ampire_am_800480l1tmqw_t00h = {
|
||||
.connector_type = DRM_MODE_CONNECTOR_DPI,
|
||||
};
|
||||
|
||||
static const struct display_timing ampire_am_800480n3tzqw_00h_timing = {
|
||||
.pixelclock = {23000000, 25000000, 27000000},
|
||||
.hactive = { 800, 800, 800 },
|
||||
.hfront_porch = { 4, 8, 48 },
|
||||
.hback_porch = { 4, 8, 48 },
|
||||
.hsync_len = { 2, 8, 8 },
|
||||
.vactive = { 480, 480, 480 },
|
||||
.vfront_porch = { 4, 8, 12 },
|
||||
.vback_porch = { 4, 8, 12 },
|
||||
.vsync_len = { 2, 4, 8 },
|
||||
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
|
||||
DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
|
||||
DISPLAY_FLAGS_SYNC_POSEDGE,
|
||||
};
|
||||
|
||||
static const struct panel_desc ampire_am_800480n3tzqw_00h = {
|
||||
.timings = &ire_am_800480n3tzqw_00h_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 108,
|
||||
.height = 65,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
|
||||
DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE,
|
||||
.connector_type = DRM_MODE_CONNECTOR_DPI,
|
||||
};
|
||||
|
||||
static const struct panel_desc ampire_am800480r3tmqwa1h = {
|
||||
.modes = &ire_am800480r3tmqwa1h_mode,
|
||||
.num_modes = 1,
|
||||
@@ -5648,6 +5678,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "ampire,am-800480l1tmqw-t00h",
|
||||
.data = &ire_am_800480l1tmqw_t00h,
|
||||
}, {
|
||||
.compatible = "ampire,am-800480n3tzqw-00h",
|
||||
.data = &ire_am_800480n3tzqw_00h,
|
||||
}, {
|
||||
.compatible = "ampire,am800480r3tmqwa1h",
|
||||
.data = &ire_am800480r3tmqwa1h,
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#define ST7703_CMD_SETCABC 0xC8
|
||||
#define ST7703_CMD_SETPANEL 0xCC
|
||||
#define ST7703_CMD_SETGAMMA 0xE0
|
||||
#define ST7703_CMD_UNKNOWN_E1 0xE1
|
||||
#define ST7703_CMD_SETEQ 0xE3
|
||||
#define ST7703_CMD_SETGIP1 0xE9
|
||||
#define ST7703_CMD_SETGIP2 0xEA
|
||||
@@ -673,6 +674,100 @@ static const struct st7703_panel_desc gameforcechi_desc = {
|
||||
.init_sequence = gameforcechi_init_sequence,
|
||||
};
|
||||
|
||||
static void ayaneo_pocket_ds_lower_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
|
||||
{
|
||||
/* Init sequence extracted from the AYANEO Pocket DS BSP kernel. */
|
||||
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12,
|
||||
0x87);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0x00, 0x01,
|
||||
0x78);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x10, 0x10,
|
||||
0x28, 0x28, 0x03, 0xff, 0x00, 0x00, 0x00,
|
||||
0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x0a, 0x0a);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x90, 0x90);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x76,
|
||||
0x22, 0xf0, 0x13);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x33, 0x81,
|
||||
0x05, 0xf9, 0x0e, 0x0e, 0x20, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
|
||||
0x00, 0x91, 0x0a, 0x00, 0x00, 0x01, 0x4f,
|
||||
0x01, 0x00, 0x00, 0x37);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x47);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x10,
|
||||
0x00, 0x80, 0x04);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73,
|
||||
0x50, 0x50, 0x00, 0x00, 0x12, 0x73, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x55, 0x00,
|
||||
0x32, 0x32, 0x77, 0xf1, 0xf7, 0xf7, 0xcc,
|
||||
0xcc, 0x7f, 0x7f, 0x11, 0x11, 0x00, 0x00,
|
||||
0x32);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETIO, 0xb8, 0x00,
|
||||
0x0a, 0x10, 0x01, 0x09, 0x00, 0x00, 0xed,
|
||||
0xc5, 0x00, 0xa5);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCABC, 0x10, 0x40,
|
||||
0x1e, 0x03);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x00,
|
||||
0x00, 0x25, 0x31, 0x3f, 0x26, 0x29, 0x04,
|
||||
0x08, 0x0d, 0x0e, 0x12, 0x0f, 0x11, 0x12,
|
||||
0x18, 0x00, 0x00, 0x00, 0x25, 0x31, 0x3f,
|
||||
0x26, 0x29, 0x04, 0x08, 0x0d, 0x0e, 0x12,
|
||||
0x0f, 0x11, 0x12, 0x18);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_E1, 0x11, 0x11,
|
||||
0x91, 0x00, 0x00, 0x00, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x07, 0x07,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x00, 0x00, 0x00,
|
||||
0x00, 0xff, 0x84, 0xc0, 0x10);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0xc8, 0x10,
|
||||
0x04, 0x04, 0x02, 0x80, 0x81, 0x12, 0x31,
|
||||
0x23, 0x2f, 0x80, 0x80, 0x81, 0x27, 0x20,
|
||||
0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0f, 0x00, 0x00, 0x00, 0x44, 0x00,
|
||||
0x88, 0xff, 0x00, 0x22, 0x44, 0x66, 0x88,
|
||||
0x88, 0x88, 0x55, 0x11, 0x88, 0xff, 0x11,
|
||||
0x33, 0x55, 0x77, 0x88, 0x88, 0x88, 0x00,
|
||||
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x11, 0x55, 0xff, 0x88,
|
||||
0x77, 0x55, 0x33, 0x11, 0x88, 0x88, 0x88,
|
||||
0x00, 0x44, 0xff, 0x88, 0x66, 0x44, 0x22,
|
||||
0x00, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00,
|
||||
0x00, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x20, 0x80, 0x81, 0x00,
|
||||
0x00, 0x00, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff,
|
||||
0x01);
|
||||
}
|
||||
|
||||
static const struct drm_display_mode ayaneo_pocket_ds_lower_mode = {
|
||||
.hdisplay = 768,
|
||||
.hsync_start = 768 + 110,
|
||||
.hsync_end = 768 + 110 + 45,
|
||||
.htotal = 768 + 110 + 45 + 110,
|
||||
.vdisplay = 1024,
|
||||
.vsync_start = 1024 + 18,
|
||||
.vsync_end = 1024 + 18 + 6,
|
||||
.vtotal = 1024 + 18 + 6 + 16,
|
||||
.clock = 65946,
|
||||
.width_mm = 76,
|
||||
.height_mm = 102,
|
||||
};
|
||||
|
||||
static const struct st7703_panel_desc ayaneo_pocket_ds_lower_desc = {
|
||||
.mode = &ayaneo_pocket_ds_lower_mode,
|
||||
.lanes = 4,
|
||||
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
|
||||
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
|
||||
.format = MIPI_DSI_FMT_RGB888,
|
||||
.init_sequence = ayaneo_pocket_ds_lower_init_sequence,
|
||||
};
|
||||
|
||||
static int st7703_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct st7703 *ctx = panel_to_st7703(panel);
|
||||
@@ -916,6 +1011,7 @@ static void st7703_remove(struct mipi_dsi_device *dsi)
|
||||
|
||||
static const struct of_device_id st7703_of_match[] = {
|
||||
{ .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc },
|
||||
{ .compatible = "ayaneo,pocket-ds-lower-panel", .data = &ayaneo_pocket_ds_lower_desc },
|
||||
{ .compatible = "gameforce,chi-panel", .data = &gameforcechi_desc },
|
||||
{ .compatible = "powkiddy,rgb10max3-panel", .data = &rgb10max3_panel_desc },
|
||||
{ .compatible = "powkiddy,rgb30-panel", .data = &rgb30panel_desc },
|
||||
|
||||
@@ -35,6 +35,7 @@ struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel)
|
||||
static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = tdo_tl070wsh30->link };
|
||||
int err;
|
||||
|
||||
err = regulator_enable(tdo_tl070wsh30->supply);
|
||||
@@ -51,44 +52,27 @@ static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel)
|
||||
|
||||
msleep(200);
|
||||
|
||||
err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);
|
||||
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 200);
|
||||
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 20);
|
||||
|
||||
if (dsi_ctx.accum_err)
|
||||
regulator_disable(tdo_tl070wsh30->supply);
|
||||
return err;
|
||||
}
|
||||
|
||||
msleep(200);
|
||||
|
||||
err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev, "failed to set display on: %d\n", err);
|
||||
regulator_disable(tdo_tl070wsh30->supply);
|
||||
return err;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel);
|
||||
int err;
|
||||
|
||||
err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link);
|
||||
if (err < 0)
|
||||
dev_err(panel->dev, "failed to set display off: %d\n", err);
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = tdo_tl070wsh30->link };
|
||||
|
||||
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
|
||||
/* Reset error to continue power-down sequence even if display off failed */
|
||||
dsi_ctx.accum_err = 0;
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link);
|
||||
if (err < 0) {
|
||||
dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
regulator_disable(tdo_tl070wsh30->supply);
|
||||
|
||||
@@ -1824,7 +1824,7 @@ static int panthor_probe(struct platform_device *pdev)
|
||||
ptdev = devm_drm_dev_alloc(&pdev->dev, &panthor_drm_driver,
|
||||
struct panthor_device, base);
|
||||
if (IS_ERR(ptdev))
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(ptdev);
|
||||
|
||||
platform_set_drvdata(pdev, ptdev);
|
||||
|
||||
|
||||
@@ -697,7 +697,8 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev,
|
||||
return ret;
|
||||
|
||||
if (hdr.meta_start > fw->size ||
|
||||
hdr.meta_start + hdr.meta_size > fw->size) {
|
||||
hdr.meta_size > fw->size - hdr.meta_start ||
|
||||
hdr.meta_size <= header_len) {
|
||||
drm_err(&ptdev->base, "Firmware build info corrupt\n");
|
||||
/* We don't need the build info, so continue */
|
||||
return 0;
|
||||
@@ -877,14 +878,15 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev,
|
||||
struct panthor_fw_csg_iface *csg_iface = panthor_fw_get_csg_iface(ptdev, csg_idx);
|
||||
struct panthor_fw_cs_iface *cs_iface = &ptdev->fw->iface.streams[csg_idx][cs_idx];
|
||||
u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
|
||||
u32 iface_offset = CSF_GROUP_CONTROL_OFFSET +
|
||||
(csg_idx * glb_iface->control->group_stride) +
|
||||
u64 iface_offset = CSF_GROUP_CONTROL_OFFSET +
|
||||
((u64)csg_idx * glb_iface->control->group_stride) +
|
||||
CSF_STREAM_CONTROL_OFFSET +
|
||||
(cs_idx * csg_iface->control->stream_stride);
|
||||
((u64)cs_idx * csg_iface->control->stream_stride);
|
||||
struct panthor_fw_cs_iface *first_cs_iface =
|
||||
panthor_fw_get_cs_iface(ptdev, 0, 0);
|
||||
|
||||
if (iface_offset + sizeof(*cs_iface) >= shared_section_sz)
|
||||
if (iface_offset > shared_section_sz ||
|
||||
sizeof(*cs_iface->control) > shared_section_sz - iface_offset)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&cs_iface->lock);
|
||||
@@ -932,10 +934,12 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev,
|
||||
struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
|
||||
struct panthor_fw_csg_iface *csg_iface = &ptdev->fw->iface.groups[csg_idx];
|
||||
u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
|
||||
u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + (csg_idx * glb_iface->control->group_stride);
|
||||
u64 iface_offset = CSF_GROUP_CONTROL_OFFSET +
|
||||
((u64)csg_idx * glb_iface->control->group_stride);
|
||||
unsigned int i;
|
||||
|
||||
if (iface_offset + sizeof(*csg_iface) >= shared_section_sz)
|
||||
if (iface_offset > shared_section_sz ||
|
||||
sizeof(*csg_iface->control) > shared_section_sz - iface_offset)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&csg_iface->lock);
|
||||
@@ -985,11 +989,15 @@ static u32 panthor_get_instr_features(struct panthor_device *ptdev)
|
||||
static int panthor_fw_init_ifaces(struct panthor_device *ptdev)
|
||||
{
|
||||
struct panthor_fw_global_iface *glb_iface = &ptdev->fw->iface.global;
|
||||
u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
|
||||
unsigned int i;
|
||||
|
||||
if (!ptdev->fw->shared_section->mem->kmap)
|
||||
return -EINVAL;
|
||||
|
||||
if (sizeof(*glb_iface->control) > shared_section_sz)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&glb_iface->lock);
|
||||
glb_iface->control = ptdev->fw->shared_section->mem->kmap;
|
||||
|
||||
|
||||
@@ -306,10 +306,13 @@ struct panthor_vm {
|
||||
/** @kernel_auto_va.start: Start of the automatic VA-range for kernel BOs. */
|
||||
u64 start;
|
||||
|
||||
/** @kernel_auto_va.size: Size of the automatic VA-range for kernel BOs. */
|
||||
/** @kernel_auto_va.end: End of the automatic VA-range for kernel BOs. */
|
||||
u64 end;
|
||||
} kernel_auto_va;
|
||||
|
||||
/** @user_va_range: Upper boundary of VAs VM users can map objects against. */
|
||||
u64 user_va_range;
|
||||
|
||||
/** @as: Address space related fields. */
|
||||
struct {
|
||||
/**
|
||||
@@ -2892,6 +2895,8 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
|
||||
va_range = full_va_range;
|
||||
}
|
||||
|
||||
vm->user_va_range = kernel_va_start;
|
||||
|
||||
mutex_init(&vm->mm_lock);
|
||||
drm_mm_init(&vm->mm, kernel_va_start, kernel_va_size);
|
||||
vm->kernel_auto_va.start = auto_kernel_va_start;
|
||||
@@ -2980,6 +2985,10 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file,
|
||||
if (!IS_ALIGNED(op->va | op->size | op->bo_offset, vm_pgsz))
|
||||
return -EINVAL;
|
||||
|
||||
/* We don't allow mappings that overlap with kbo's reserved range */
|
||||
if (range_overflows(op->va, op->size, vm->user_va_range))
|
||||
return -EINVAL;
|
||||
|
||||
switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) {
|
||||
case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP:
|
||||
if (!(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@@ -140,6 +141,11 @@
|
||||
#define SSD133X_SET_PRECHARGE_VOLTAGE 0xbb
|
||||
#define SSD133X_SET_VCOMH_VOLTAGE 0xbe
|
||||
|
||||
/* ssd133x A/B/C channel contrast at full brightness (white balance) */
|
||||
#define SSD133X_DEFAULT_CONTRAST_A 0x91
|
||||
#define SSD133X_DEFAULT_CONTRAST_B 0x50
|
||||
#define SSD133X_DEFAULT_CONTRAST_C 0x7d
|
||||
|
||||
#define MAX_CONTRAST 255
|
||||
|
||||
const struct ssd130x_deviceinfo ssd130x_variants[] = {
|
||||
@@ -582,12 +588,37 @@ static int ssd132x_init(struct ssd130x_device *ssd130x)
|
||||
return ssd130x_run_cmd_seq(ssd130x, cmds);
|
||||
}
|
||||
|
||||
static int ssd133x_init(struct ssd130x_device *ssd130x)
|
||||
/* Scale a channel's white-balance calibration contrast by the requested brightness */
|
||||
static u8 ssd130x_scale_contrast(u8 calibration, u32 brightness)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST(calibration * brightness, MAX_CONTRAST);
|
||||
}
|
||||
|
||||
/*
|
||||
* The A/B/C contrast channels drive sub-pixels whose OLED materials differ
|
||||
* in luminous efficiency, so the per-channel values are a white-balance
|
||||
* calibration. Scale them by the requested brightness instead of
|
||||
* overwriting them, to keep the white point while dimming.
|
||||
*/
|
||||
static int ssd133x_set_contrast(struct ssd130x_device *ssd130x, u32 brightness)
|
||||
{
|
||||
const u8 cmds[] = {
|
||||
2, SSD133X_CONTRAST_A, 0x91,
|
||||
2, SSD133X_CONTRAST_B, 0x50,
|
||||
2, SSD133X_CONTRAST_C, 0x7d,
|
||||
2, SSD133X_CONTRAST_A,
|
||||
ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_A, brightness),
|
||||
2, SSD133X_CONTRAST_B,
|
||||
ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_B, brightness),
|
||||
2, SSD133X_CONTRAST_C,
|
||||
ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_C, brightness),
|
||||
0,
|
||||
};
|
||||
|
||||
return ssd130x_run_cmd_seq(ssd130x, cmds);
|
||||
}
|
||||
|
||||
static int ssd133x_init(struct ssd130x_device *ssd130x)
|
||||
{
|
||||
int ret;
|
||||
const u8 cmds[] = {
|
||||
2, SSD133X_SET_MASTER_CURRENT, 0x06,
|
||||
3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1,
|
||||
3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1,
|
||||
@@ -614,6 +645,10 @@ static int ssd133x_init(struct ssd130x_device *ssd130x)
|
||||
0,
|
||||
};
|
||||
|
||||
ret = ssd133x_set_contrast(ssd130x, ssd130x->contrast);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ssd130x_run_cmd_seq(ssd130x, cmds);
|
||||
}
|
||||
|
||||
@@ -1676,8 +1711,25 @@ static int ssd130x_update_bl(struct backlight_device *bdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops ssd130xfb_bl_ops = {
|
||||
.update_status = ssd130x_update_bl,
|
||||
static int ssd133x_update_bl(struct backlight_device *bdev)
|
||||
{
|
||||
struct ssd130x_device *ssd130x = bl_get_data(bdev);
|
||||
|
||||
ssd130x->contrast = backlight_get_brightness(bdev);
|
||||
|
||||
return ssd133x_set_contrast(ssd130x, ssd130x->contrast);
|
||||
}
|
||||
|
||||
static const struct backlight_ops ssd130xfb_bl_ops[] = {
|
||||
[SSD130X_FAMILY] = {
|
||||
.update_status = ssd130x_update_bl,
|
||||
},
|
||||
[SSD132X_FAMILY] = {
|
||||
.update_status = ssd130x_update_bl,
|
||||
},
|
||||
[SSD133X_FAMILY] = {
|
||||
.update_status = ssd133x_update_bl,
|
||||
},
|
||||
};
|
||||
|
||||
static void ssd130x_parse_properties(struct ssd130x_device *ssd130x)
|
||||
@@ -1884,7 +1936,8 @@ struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x,
|
||||
&ssd130xfb_bl_ops, NULL);
|
||||
&ssd130xfb_bl_ops[ssd130x->device_info->family_id],
|
||||
NULL);
|
||||
if (IS_ERR(bl))
|
||||
return ERR_PTR(dev_err_probe(dev, PTR_ERR(bl),
|
||||
"Unable to register backlight device\n"));
|
||||
|
||||
@@ -208,7 +208,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
|
||||
planes = sunxi_engine_layers_init(drm, engine);
|
||||
if (IS_ERR(planes)) {
|
||||
dev_err(drm->dev, "Couldn't create the planes\n");
|
||||
return NULL;
|
||||
return ERR_CAST(planes);
|
||||
}
|
||||
|
||||
/* find primary and cursor planes for drm_crtc_init_with_planes */
|
||||
|
||||
@@ -289,6 +289,8 @@ static void sun4i_drv_traverse_endpoints(struct endpoint_list *list,
|
||||
|
||||
kfifo_put(&list->fifo, remote);
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
}
|
||||
|
||||
static int sun4i_drv_add_endpoints(struct device *dev,
|
||||
@@ -394,6 +396,7 @@ static int sun4i_drv_probe(struct platform_device *pdev)
|
||||
/* process this endpoint */
|
||||
ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match,
|
||||
endpoint);
|
||||
of_node_put(endpoint);
|
||||
|
||||
/* sun4i_drv_add_endpoints can fail to allocate memory */
|
||||
if (ret < 0)
|
||||
|
||||
@@ -14,24 +14,8 @@
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_framebuffer.h"
|
||||
|
||||
static int sun4i_de_atomic_check(struct drm_device *dev,
|
||||
struct drm_atomic_commit *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return drm_atomic_helper_check_planes(dev, state);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = {
|
||||
.atomic_check = sun4i_de_atomic_check,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
};
|
||||
@@ -46,6 +30,7 @@ void sun4i_framebuffer_init(struct drm_device *drm)
|
||||
|
||||
drm->mode_config.max_width = 8192;
|
||||
drm->mode_config.max_height = 8192;
|
||||
drm->mode_config.normalize_zpos = true;
|
||||
|
||||
drm->mode_config.funcs = &sun4i_de_mode_config_funcs;
|
||||
drm->mode_config.helper_private = &sun4i_de_mode_config_helpers;
|
||||
|
||||
@@ -160,7 +160,7 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
|
||||
|
||||
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
|
||||
|
||||
val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
|
||||
val = SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
|
||||
val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
|
||||
writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
|
||||
|
||||
|
||||
@@ -717,9 +717,11 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
|
||||
case DRM_MODE_ENCODER_DSI:
|
||||
/* DSI is tied to special case of CPU interface */
|
||||
sun4i_tcon0_mode_set_cpu(tcon, encoder, mode);
|
||||
sun4i_tcon_set_mux(tcon, 0, encoder);
|
||||
break;
|
||||
case DRM_MODE_ENCODER_LVDS:
|
||||
sun4i_tcon0_mode_set_lvds(tcon, encoder, mode);
|
||||
sun4i_tcon_set_mux(tcon, 0, encoder);
|
||||
break;
|
||||
case DRM_MODE_ENCODER_NONE:
|
||||
sun4i_tcon0_mode_set_rgb(tcon, encoder, mode);
|
||||
@@ -970,6 +972,7 @@ static int sun4i_tcon_of_get_id_from_port(struct device_node *port)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(remote, "reg", ®);
|
||||
of_node_put(remote);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
@@ -1409,7 +1412,7 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
|
||||
{
|
||||
struct device_node *port, *remote;
|
||||
struct platform_device *pdev;
|
||||
int id, ret;
|
||||
int id, ret = 0;
|
||||
|
||||
/* find TCON TOP platform device and TCON id */
|
||||
|
||||
@@ -1432,21 +1435,20 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon,
|
||||
if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&
|
||||
encoder->encoder_type == DRM_MODE_ENCODER_TMDS) {
|
||||
ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id);
|
||||
if (ret) {
|
||||
put_device(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto out_put_device;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) {
|
||||
ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id);
|
||||
if (ret) {
|
||||
put_device(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto out_put_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_put_device:
|
||||
put_device(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct sun4i_tcon_quirks sun4i_a10_quirks = {
|
||||
|
||||
@@ -86,6 +86,8 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm,
|
||||
of_node_put(remote_port);
|
||||
}
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
} else {
|
||||
crtcs = drm_of_find_possible_crtcs(drm, node);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
|
||||
},
|
||||
}, {
|
||||
594000000, {
|
||||
{ 0x1a40, 0x0003 },
|
||||
{ 0x1a7c, 0x0003 },
|
||||
{ 0x3b4c, 0x0003 },
|
||||
{ 0x5a64, 0x0003 },
|
||||
},
|
||||
|
||||
@@ -785,7 +785,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.vi_scaler_num = 2,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.scanline_yuv = 1024,
|
||||
},
|
||||
.de_type = SUN8I_MIXER_DE2,
|
||||
.mod_rate = 150000000,
|
||||
|
||||
@@ -25,6 +25,49 @@ static bool sun8i_tcon_top_node_is_tcon_top(struct device_node *node)
|
||||
return !!of_match_node(sun8i_tcon_top_of_table, node);
|
||||
}
|
||||
|
||||
static unsigned int sun8i_tcon_top_get_tcon_map(struct device_node *node)
|
||||
{
|
||||
static const u32 out_ports[] = {
|
||||
TCON_TOP_MIXER0_OUT_PORT,
|
||||
TCON_TOP_MIXER1_OUT_PORT,
|
||||
};
|
||||
unsigned int i, map = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(out_ports); i++) {
|
||||
struct device_node *port;
|
||||
|
||||
port = of_graph_get_port_by_id(node, out_ports[i]);
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
for_each_of_graph_port_endpoint(port, ep) {
|
||||
struct of_endpoint endpoint;
|
||||
|
||||
if (of_graph_parse_endpoint(ep, &endpoint))
|
||||
continue;
|
||||
|
||||
if (endpoint.id < TCON_TOP_PORT_TCON_NUM)
|
||||
map |= BIT(endpoint.id);
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static unsigned int sun8i_tcon_top_park_index(struct sun8i_tcon_top *tcon_top,
|
||||
int tcon)
|
||||
{
|
||||
unsigned int candidates;
|
||||
|
||||
candidates = tcon_top->tcon_map & ~BIT(tcon);
|
||||
if (!candidates)
|
||||
candidates = GENMASK(TCON_TOP_PORT_TCON_NUM - 1, 0) & ~BIT(tcon);
|
||||
|
||||
return ffs(candidates) - 1;
|
||||
}
|
||||
|
||||
int sun8i_tcon_top_set_hdmi_src(struct device *dev, int tcon)
|
||||
{
|
||||
struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev);
|
||||
@@ -57,6 +100,7 @@ EXPORT_SYMBOL(sun8i_tcon_top_set_hdmi_src);
|
||||
int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon)
|
||||
{
|
||||
struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev);
|
||||
u32 mixer_msk, other_msk;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
@@ -70,21 +114,27 @@ int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tcon > 3) {
|
||||
dev_err(dev, "TCON index is too high!\n");
|
||||
if (tcon < 0 || tcon >= TCON_TOP_PORT_TCON_NUM) {
|
||||
dev_err(dev, "TCON index is invalid!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mixer_msk = mixer ? TCON_TOP_PORT_DE1_MSK : TCON_TOP_PORT_DE0_MSK;
|
||||
other_msk = mixer ? TCON_TOP_PORT_DE0_MSK : TCON_TOP_PORT_DE1_MSK;
|
||||
|
||||
spin_lock_irqsave(&tcon_top->reg_lock, flags);
|
||||
|
||||
reg = readl(tcon_top->regs + TCON_TOP_PORT_SEL_REG);
|
||||
if (mixer == 0) {
|
||||
reg &= ~TCON_TOP_PORT_DE0_MSK;
|
||||
reg |= FIELD_PREP(TCON_TOP_PORT_DE0_MSK, tcon);
|
||||
} else {
|
||||
reg &= ~TCON_TOP_PORT_DE1_MSK;
|
||||
reg |= FIELD_PREP(TCON_TOP_PORT_DE1_MSK, tcon);
|
||||
|
||||
reg &= ~mixer_msk;
|
||||
reg |= field_prep(mixer_msk, tcon);
|
||||
|
||||
if (field_get(other_msk, reg) == tcon) {
|
||||
reg &= ~other_msk;
|
||||
reg |= field_prep(other_msk,
|
||||
sun8i_tcon_top_park_index(tcon_top, tcon));
|
||||
}
|
||||
|
||||
writel(reg, tcon_top->regs + TCON_TOP_PORT_SEL_REG);
|
||||
|
||||
spin_unlock_irqrestore(&tcon_top->reg_lock, flags);
|
||||
@@ -143,6 +193,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
|
||||
return -ENOMEM;
|
||||
clk_data->num = CLK_NUM;
|
||||
tcon_top->clk_data = clk_data;
|
||||
tcon_top->tcon_map = sun8i_tcon_top_get_tcon_map(dev->of_node);
|
||||
|
||||
spin_lock_init(&tcon_top->reg_lock);
|
||||
|
||||
@@ -175,10 +226,6 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master,
|
||||
goto err_assert_reset;
|
||||
}
|
||||
|
||||
/*
|
||||
* At least on H6, some registers have some bits set by default
|
||||
* which may cause issues. Clear them here.
|
||||
*/
|
||||
writel(0, regs + TCON_TOP_PORT_SEL_REG);
|
||||
writel(0, regs + TCON_TOP_GATE_SRC_REG);
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#define TCON_TOP_PORT_SEL_REG 0x1C
|
||||
#define TCON_TOP_PORT_DE0_MSK GENMASK(1, 0)
|
||||
#define TCON_TOP_PORT_DE1_MSK GENMASK(5, 4)
|
||||
#define TCON_TOP_PORT_TCON_NUM 4
|
||||
|
||||
#define TCON_TOP_MIXER0_OUT_PORT 1
|
||||
#define TCON_TOP_MIXER1_OUT_PORT 3
|
||||
|
||||
#define TCON_TOP_GATE_SRC_REG 0x20
|
||||
#define TCON_TOP_HDMI_SRC_MSK GENMASK(29, 28)
|
||||
@@ -29,6 +33,8 @@ struct sun8i_tcon_top {
|
||||
void __iomem *regs;
|
||||
struct reset_control *rst;
|
||||
|
||||
unsigned int tcon_map;
|
||||
|
||||
/*
|
||||
* spinlock is used to synchronize access to same
|
||||
* register where multiple clock gates can be set.
|
||||
|
||||
@@ -894,20 +894,26 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base,
|
||||
lan3coefftab32_left[offset + i]);
|
||||
regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i),
|
||||
lan3coefftab32_right[offset + i]);
|
||||
}
|
||||
offset = sun8i_vi_scaler_coef_index(vstep) *
|
||||
SUN8I_VI_SCALER_COEFF_COUNT;
|
||||
for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
|
||||
regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
|
||||
lan2coefftab32[offset + i]);
|
||||
|
||||
offset = sun8i_vi_scaler_coef_index(hstep / format->hsub) *
|
||||
SUN8I_VI_SCALER_COEFF_COUNT;
|
||||
for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
|
||||
regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i),
|
||||
ch_left[offset + i]);
|
||||
regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i),
|
||||
ch_right[offset + i]);
|
||||
}
|
||||
|
||||
offset = sun8i_vi_scaler_coef_index(hstep) *
|
||||
offset = sun8i_vi_scaler_coef_index(vstep / format->vsub) *
|
||||
SUN8I_VI_SCALER_COEFF_COUNT;
|
||||
for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) {
|
||||
regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i),
|
||||
lan2coefftab32[offset + i]);
|
||||
for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++)
|
||||
regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i),
|
||||
cy[offset + i]);
|
||||
}
|
||||
}
|
||||
|
||||
void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable)
|
||||
@@ -969,6 +975,8 @@ void sun8i_vi_scaler_setup(struct sun8i_layer *layer,
|
||||
|
||||
regmap_write(layer->regs,
|
||||
SUN50I_SCALER_VSU_SCALE_MODE(base), val);
|
||||
regmap_write(layer->regs,
|
||||
SUN50I_SCALER_VSU_GLB_ALPHA(base), 0xff);
|
||||
}
|
||||
|
||||
regmap_write(layer->regs,
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#define SUN50I_SCALER_VSU_EDSCL_CTRL(base) ((base) + 0x28)
|
||||
#define SUN50I_SCALER_VSU_ANGLE_THR(base) ((base) + 0x2c)
|
||||
#define SUN8I_SCALER_VSU_OUTSIZE(base) ((base) + 0x40)
|
||||
#define SUN50I_SCALER_VSU_GLB_ALPHA(base) ((base) + 0x44)
|
||||
#define SUN8I_SCALER_VSU_YINSIZE(base) ((base) + 0x80)
|
||||
#define SUN8I_SCALER_VSU_YHSTEP(base) ((base) + 0x88)
|
||||
#define SUN8I_SCALER_VSU_YVSTEP(base) ((base) + 0x8c)
|
||||
|
||||
@@ -488,6 +488,118 @@ int ttm_bo_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ttm_bo_alloc_state {
|
||||
/** @charge_pool: The memory pool the resource is charged to */
|
||||
struct dmem_cgroup_pool_state *charge_pool;
|
||||
/** @limit_pool: Which pool limit we should test against */
|
||||
struct dmem_cgroup_pool_state *limit_pool;
|
||||
/** @in_evict: Whether we are currently evicting buffers */
|
||||
bool in_evict;
|
||||
/** @may_try_low: If only unprotected BOs, i.e. BOs whose cgroup
|
||||
* is exceeding its dmem low/min protection, should be considered for eviction
|
||||
*/
|
||||
bool may_try_low;
|
||||
};
|
||||
|
||||
/**
|
||||
* ttm_bo_alloc_at_place - Attempt allocating a BO's backing store in a place
|
||||
*
|
||||
* @bo: The buffer to allocate the backing store of
|
||||
* @place: The place to attempt allocation in
|
||||
* @ctx: ttm_operation_ctx associated with this allocation
|
||||
* @force_space: If we should evict buffers to force space
|
||||
* @res: On allocation success, the resulting struct ttm_resource.
|
||||
* @alloc_state: Object holding allocation state such as charged cgroups.
|
||||
*
|
||||
* Returns:
|
||||
* -EBUSY: No space available, but allocation should be retried with ttm_bo_evict_alloc.
|
||||
* -ENOSPC: No space available, allocation should not be retried.
|
||||
* -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
|
||||
*
|
||||
*/
|
||||
static int ttm_bo_alloc_at_place(struct ttm_buffer_object *bo,
|
||||
const struct ttm_place *place,
|
||||
bool force_space,
|
||||
struct ttm_resource **res,
|
||||
struct ttm_bo_alloc_state *alloc_state)
|
||||
{
|
||||
bool may_evict;
|
||||
int ret;
|
||||
|
||||
may_evict = !alloc_state->in_evict && force_space &&
|
||||
place->mem_type != TTM_PL_SYSTEM;
|
||||
if (!alloc_state->charge_pool) {
|
||||
ret = ttm_resource_try_charge(bo, place, &alloc_state->charge_pool,
|
||||
force_space ? &alloc_state->limit_pool
|
||||
: NULL);
|
||||
if (ret) {
|
||||
/*
|
||||
* -EAGAIN means the charge failed, which we treat
|
||||
* like an allocation failure. Therefore, return an
|
||||
* error code indicating the allocation failed -
|
||||
* either -EBUSY if the allocation should be
|
||||
* retried with eviction, or -ENOSPC if there should
|
||||
* be no second attempt.
|
||||
*/
|
||||
if (!alloc_state->in_evict)
|
||||
alloc_state->may_try_low = may_evict;
|
||||
if (ret == -EAGAIN)
|
||||
ret = may_evict ? -EBUSY : -ENOSPC;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* cgroup protection plays a special role in eviction.
|
||||
* Conceptually, protection of memory via the dmem cgroup controller
|
||||
* entitles the protected cgroup to use a certain amount of memory.
|
||||
* There are two types of protection - the 'low' limit is a
|
||||
* "best-effort" protection, whereas the 'min' limit provides a hard
|
||||
* guarantee that memory within the cgroup's allowance will not be
|
||||
* evicted under any circumstance.
|
||||
*
|
||||
* To faithfully model this concept in TTM, we also need to take cgroup
|
||||
* protection into account when allocating. When allocation in one
|
||||
* place fails, TTM will default to trying other places first before
|
||||
* evicting.
|
||||
* If the allocation is covered by dmem cgroup protection, however,
|
||||
* this prevents the allocation from using the memory it is "entitled"
|
||||
* to. To make sure unprotected allocations cannot push new protected
|
||||
* allocations out of places they are "entitled" to use, we should
|
||||
* evict buffers not covered by any cgroup protection, if this
|
||||
* allocation is covered by cgroup protection.
|
||||
*
|
||||
* Buffers covered by 'min' protection are a special case - the 'min'
|
||||
* limit is a stronger guarantee than 'low', and thus buffers protected
|
||||
* by 'low' but not 'min' should also be considered for eviction.
|
||||
* Buffers protected by 'min' will never be considered for eviction
|
||||
* anyway, so the regular eviction path should be triggered here.
|
||||
* Buffers protected by 'low' but not 'min' will take a special
|
||||
* eviction path that only evicts buffers covered by neither 'low' or
|
||||
* 'min' protections.
|
||||
*/
|
||||
if (!alloc_state->in_evict) {
|
||||
may_evict |= dmem_cgroup_below_min(NULL, alloc_state->charge_pool);
|
||||
alloc_state->may_try_low = may_evict;
|
||||
|
||||
may_evict |= dmem_cgroup_below_low(NULL, alloc_state->charge_pool);
|
||||
}
|
||||
|
||||
ret = ttm_resource_alloc(bo, place, res, alloc_state->charge_pool);
|
||||
if (ret) {
|
||||
if (ret == -ENOSPC && may_evict)
|
||||
ret = -EBUSY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ownership of charge_pool has been transferred to the TTM resource,
|
||||
* don't make the caller think we still hold a reference to it.
|
||||
*/
|
||||
alloc_state->charge_pool = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct ttm_bo_evict_walk - Parameters for the evict walk.
|
||||
*/
|
||||
@@ -503,24 +615,70 @@ struct ttm_bo_evict_walk {
|
||||
/** @evicted: Number of successful evictions. */
|
||||
unsigned long evicted;
|
||||
|
||||
/** @limit_pool: Which pool limit we should test against */
|
||||
struct dmem_cgroup_pool_state *limit_pool;
|
||||
/** @try_low: Whether we should attempt to evict BO's with low watermark threshold */
|
||||
bool try_low;
|
||||
/** @hit_low: If we cannot evict a bo when @try_low is false (first pass) */
|
||||
bool hit_low;
|
||||
|
||||
/** @alloc_state: State associated with the allocation attempt. */
|
||||
struct ttm_bo_alloc_state *alloc_state;
|
||||
};
|
||||
|
||||
static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
|
||||
{
|
||||
struct ttm_bo_evict_walk *evict_walk =
|
||||
container_of(walk, typeof(*evict_walk), walk);
|
||||
struct dmem_cgroup_pool_state *limit_pool, *ancestor = NULL;
|
||||
s64 bo_size = bo->base.size;
|
||||
bool evict_valuable;
|
||||
s64 lret;
|
||||
|
||||
if (!dmem_cgroup_state_evict_valuable(evict_walk->limit_pool, bo->resource->css,
|
||||
evict_walk->try_low, &evict_walk->hit_low))
|
||||
/*
|
||||
* If may_try_low is not set, then we're trying to evict unprotected
|
||||
* buffers in favor of a protected allocation for charge_pool. Explicitly skip
|
||||
* buffers belonging to the same cgroup here - that cgroup is definitely protected,
|
||||
* even though dmem_cgroup_state_evict_valuable would allow the eviction because a
|
||||
* cgroup is always allowed to evict from itself even if it is protected.
|
||||
*/
|
||||
if (!evict_walk->alloc_state->may_try_low &&
|
||||
bo->resource->css == evict_walk->alloc_state->charge_pool)
|
||||
return 0;
|
||||
|
||||
limit_pool = evict_walk->alloc_state->limit_pool;
|
||||
/*
|
||||
* If there is no explicit limit pool, find the root of the shared subtree between
|
||||
* evictor and evictee. This is important so that recursive protection rules can
|
||||
* apply properly: Recursive protection distributes cgroup protection afforded
|
||||
* to a parent cgroup but not used explicitly by a child cgroup between all child
|
||||
* cgroups (see docs of effective_protection in mm/page_counter.c). However, when
|
||||
* direct siblings compete for memory, siblings that were explicitly protected
|
||||
* should get prioritized over siblings that weren't. This only happens correctly
|
||||
* when the root of the shared subtree is passed to
|
||||
* dmem_cgroup_state_evict_valuable. Otherwise, the effective-protection
|
||||
* calculation cannot distinguish direct siblings from unrelated subtrees and the
|
||||
* calculated protection ends up wrong.
|
||||
*/
|
||||
if (!limit_pool) {
|
||||
ancestor = dmem_cgroup_get_common_ancestor(bo->resource->css,
|
||||
evict_walk->alloc_state->charge_pool);
|
||||
limit_pool = ancestor;
|
||||
}
|
||||
|
||||
evict_valuable = dmem_cgroup_state_evict_valuable(limit_pool, bo->resource->css,
|
||||
evict_walk->try_low,
|
||||
&evict_walk->hit_low);
|
||||
if (ancestor)
|
||||
dmem_cgroup_pool_state_put(ancestor);
|
||||
|
||||
if (!evict_valuable)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* evict_walk->place is NULL in cgroup drain mode. Drivers'
|
||||
* eviction_valuable() callbacks must handle a NULL place, treating it
|
||||
* as "any placement": the TTM base implementation already does so via
|
||||
* ttm_resource_intersects().
|
||||
*/
|
||||
if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, evict_walk->place))
|
||||
return 0;
|
||||
|
||||
@@ -536,11 +694,17 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
|
||||
goto out;
|
||||
|
||||
evict_walk->evicted++;
|
||||
if (evict_walk->res)
|
||||
lret = ttm_resource_alloc(evict_walk->evictor, evict_walk->place,
|
||||
evict_walk->res, NULL);
|
||||
if (lret == 0)
|
||||
return 1;
|
||||
if (evict_walk->res) {
|
||||
lret = ttm_bo_alloc_at_place(evict_walk->evictor,
|
||||
evict_walk->place, false,
|
||||
evict_walk->res,
|
||||
evict_walk->alloc_state);
|
||||
if (lret == 0)
|
||||
return 1;
|
||||
} else {
|
||||
/* Cgroup drain: return bytes freed for byte-denominated progress. */
|
||||
return bo_size;
|
||||
}
|
||||
out:
|
||||
/* Errors that should terminate the walk. */
|
||||
if (lret == -ENOSPC)
|
||||
@@ -560,7 +724,7 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
struct ww_acquire_ctx *ticket,
|
||||
struct ttm_resource **res,
|
||||
struct dmem_cgroup_pool_state *limit_pool)
|
||||
struct ttm_bo_alloc_state *state)
|
||||
{
|
||||
struct ttm_bo_evict_walk evict_walk = {
|
||||
.walk = {
|
||||
@@ -573,15 +737,21 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
|
||||
.place = place,
|
||||
.evictor = evictor,
|
||||
.res = res,
|
||||
.limit_pool = limit_pool,
|
||||
.alloc_state = state,
|
||||
};
|
||||
s64 lret;
|
||||
|
||||
state->in_evict = true;
|
||||
|
||||
evict_walk.walk.arg.trylock_only = true;
|
||||
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
|
||||
|
||||
/* One more attempt if we hit low limit? */
|
||||
if (!lret && evict_walk.hit_low) {
|
||||
/* If we failed to find enough BOs to evict, but we skipped over
|
||||
* some BOs because they were covered by dmem low protection, retry
|
||||
* evicting these protected BOs too, except if we're told not to
|
||||
* consider protected BOs at all.
|
||||
*/
|
||||
if (!lret && evict_walk.hit_low && state->may_try_low) {
|
||||
evict_walk.try_low = true;
|
||||
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
|
||||
}
|
||||
@@ -602,11 +772,13 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
|
||||
} while (!lret && evict_walk.evicted);
|
||||
|
||||
/* We hit the low limit? Try once more */
|
||||
if (!lret && evict_walk.hit_low && !evict_walk.try_low) {
|
||||
if (!lret && evict_walk.hit_low && !evict_walk.try_low &&
|
||||
state->may_try_low) {
|
||||
evict_walk.try_low = true;
|
||||
goto retry;
|
||||
}
|
||||
out:
|
||||
state->in_evict = false;
|
||||
if (lret < 0)
|
||||
return lret;
|
||||
if (lret == 0)
|
||||
@@ -614,6 +786,86 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_bo_evict_cgroup() - Evict buffer objects charged to a specific cgroup.
|
||||
* @bdev: The TTM device.
|
||||
* @man: The resource manager whose LRU to walk.
|
||||
* @limit_pool: The cgroup pool state whose members should be evicted.
|
||||
* @target_bytes: Number of bytes to free.
|
||||
* @ctx: The TTM operation context.
|
||||
*
|
||||
* Walk the LRU of @man and evict buffer objects that are charged to the
|
||||
* cgroup identified by @limit_pool, until at least @target_bytes have been
|
||||
* freed. Mirrors the two-pass (trylock -> sleeping-lock, low-watermark)
|
||||
* strategy used by ttm_bo_evict_alloc().
|
||||
*
|
||||
* Return: >= @target_bytes on full success, 0..target_bytes-1 if partial,
|
||||
* negative error code on fatal error.
|
||||
*/
|
||||
s64 ttm_bo_evict_cgroup(struct ttm_device *bdev,
|
||||
struct ttm_resource_manager *man,
|
||||
struct dmem_cgroup_pool_state *limit_pool,
|
||||
s64 target_bytes,
|
||||
struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
struct ttm_bo_evict_walk evict_walk = {
|
||||
.walk = {
|
||||
.ops = &ttm_evict_walk_ops,
|
||||
.arg = { .ctx = ctx },
|
||||
},
|
||||
.alloc_state = &(struct ttm_bo_alloc_state) {
|
||||
.limit_pool = limit_pool,
|
||||
.in_evict = true,
|
||||
},
|
||||
/* place, evictor, res left NULL: selects cgroup drain mode */
|
||||
};
|
||||
s64 lret, pass;
|
||||
|
||||
evict_walk.walk.arg.trylock_only = true;
|
||||
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, target_bytes);
|
||||
if (lret < 0 || lret >= target_bytes)
|
||||
return lret;
|
||||
|
||||
/* Second pass: also evict BOs at the low watermark. */
|
||||
if (evict_walk.hit_low) {
|
||||
evict_walk.try_low = true;
|
||||
pass = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man,
|
||||
target_bytes - lret);
|
||||
if (pass < 0)
|
||||
return pass;
|
||||
lret += pass;
|
||||
if (lret >= target_bytes)
|
||||
return lret;
|
||||
}
|
||||
|
||||
/* Full sleeping-lock pass for remaining target. */
|
||||
evict_walk.try_low = evict_walk.hit_low = false;
|
||||
evict_walk.walk.arg.trylock_only = false;
|
||||
|
||||
retry:
|
||||
evict_walk.walk.arg.sleeping_lock = true;
|
||||
do {
|
||||
evict_walk.evicted = 0;
|
||||
pass = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man,
|
||||
target_bytes - lret);
|
||||
if (pass < 0) {
|
||||
lret = pass;
|
||||
goto out;
|
||||
}
|
||||
lret += pass;
|
||||
} while (lret < target_bytes && evict_walk.evicted);
|
||||
|
||||
/* One more attempt if we hit the low limit during sleeping-lock pass. */
|
||||
if (lret < target_bytes && evict_walk.hit_low && !evict_walk.try_low) {
|
||||
evict_walk.try_low = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
out:
|
||||
return lret;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_evict_cgroup);
|
||||
|
||||
/**
|
||||
* ttm_bo_pin - Pin the buffer object.
|
||||
* @bo: The buffer object to pin
|
||||
@@ -724,9 +976,8 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
|
||||
|
||||
for (i = 0; i < placement->num_placement; ++i) {
|
||||
const struct ttm_place *place = &placement->placement[i];
|
||||
struct dmem_cgroup_pool_state *limit_pool = NULL;
|
||||
struct ttm_bo_alloc_state alloc_state = {};
|
||||
struct ttm_resource_manager *man;
|
||||
bool may_evict;
|
||||
|
||||
man = ttm_manager_type(bdev, place->mem_type);
|
||||
if (!man || !ttm_resource_manager_used(man))
|
||||
@@ -736,25 +987,30 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
|
||||
TTM_PL_FLAG_FALLBACK))
|
||||
continue;
|
||||
|
||||
may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM);
|
||||
ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL);
|
||||
if (ret) {
|
||||
if (ret != -ENOSPC) {
|
||||
dmem_cgroup_pool_state_put(limit_pool);
|
||||
return ret;
|
||||
}
|
||||
if (!may_evict) {
|
||||
dmem_cgroup_pool_state_put(limit_pool);
|
||||
continue;
|
||||
}
|
||||
ret = ttm_bo_alloc_at_place(bo, place, force_space, res,
|
||||
&alloc_state);
|
||||
|
||||
if (ret == -ENOSPC) {
|
||||
dmem_cgroup_uncharge(alloc_state.charge_pool, bo->base.size);
|
||||
dmem_cgroup_pool_state_put(alloc_state.limit_pool);
|
||||
continue;
|
||||
} else if (ret == -EBUSY) {
|
||||
ret = ttm_bo_evict_alloc(bdev, man, place, bo, ctx,
|
||||
ticket, res, limit_pool);
|
||||
dmem_cgroup_pool_state_put(limit_pool);
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
if (ret)
|
||||
ticket, res, &alloc_state);
|
||||
|
||||
dmem_cgroup_pool_state_put(alloc_state.limit_pool);
|
||||
|
||||
if (ret) {
|
||||
dmem_cgroup_uncharge(alloc_state.charge_pool,
|
||||
bo->base.size);
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
return ret;
|
||||
}
|
||||
} else if (ret) {
|
||||
dmem_cgroup_uncharge(alloc_state.charge_pool, bo->base.size);
|
||||
dmem_cgroup_pool_state_put(alloc_state.limit_pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ttm_bo_add_pipelined_eviction_fences(bo, man, ctx->no_wait_gpu);
|
||||
|
||||
@@ -999,7 +999,8 @@ __ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
|
||||
bo = res->bo;
|
||||
if (ttm_lru_walk_trylock(curs, bo))
|
||||
bo_locked = true;
|
||||
else if (!arg->ticket || arg->ctx->no_wait_gpu || arg->trylock_only)
|
||||
else if ((!arg->ticket && !arg->sleeping_lock) || arg->ctx->no_wait_gpu ||
|
||||
arg->trylock_only)
|
||||
continue;
|
||||
|
||||
if (!ttm_bo_get_unless_zero(bo)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user