drm-misc-next for v6.9:

UAPI Changes:
 
 - changes to fdinfo stats
 
 Cross-subsystem Changes:
 
 agp:
 - remove unused type field from struct agp_bridge_data
 
 Core Changes:
 
 ci:
 - update test names
 - cleanups
 
 gem:
 - add stats for shared buffers plus updates to amdgpu, i915, xe
 
 Documentation:
 - fixes
 
 syncobj:
 - fixes to waiting and sleeping
 
 Driver Changes:
 
 bridge:
 - adv7511: fix crash on irq during probe
 - dw_hdmi: set bridge type
 
 host1x:
 - cleanups
 
 ivpu:
 - updates to firmware API
 - refactor BO allocation
 
 meson:
 - fix error handling in probe
 
 panel:
 - revert "drm/panel-edp: Add auo_b116xa3_mode"
 - add Himax HX83112A plus DT bindings
 - ltk500hd1829: add support for ltk101b4029w and admatec 9904370
 - simple: add BOE BP082WX1-100 8.2" panel plus DT bindungs
 
 renesas:
 - add RZ/G2L DU support plus DT bindings
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmXXUhsACgkQaA3BHVML
 eiNVFQf+IoOXCACGkWEVmVaen50pjEfLq0OjSGHdbTJqhc9wU7Q/kPC+jEpZLyqo
 OUMdXlA55BeLX52O+bvLordDPNETUsYH1QX2BYKDwcNIrvj8ISXcvdbnDcbVmttD
 ZUaaBgZ0g2M6sZQvTVU88/1RtaG64+zuk9VA1dPlh6WnBtXBUeXNtD6YQjH6xY+a
 MjZpB5VafwJTmQxy7qJ4yTLX291Ao8J2YZK8cCSyEr3FQKkAx9sJyp3hPurVIjLM
 f1y1rtoHhxUV/OVg4M559fp6F6tUkFauv4qu5VUvmPPihJTaU0eSQxir0za4VJ4e
 Jr2GOkju0oRRpKfjd0aKvaoWhl+MNg==
 =aaTQ
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2024-02-22' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for v6.9:

UAPI Changes:

- changes to fdinfo stats

Cross-subsystem Changes:

agp:
- remove unused type field from struct agp_bridge_data

Core Changes:

ci:
- update test names
- cleanups

gem:
- add stats for shared buffers plus updates to amdgpu, i915, xe

Documentation:
- fixes

syncobj:
- fixes to waiting and sleeping

Driver Changes:

bridge:
- adv7511: fix crash on irq during probe
- dw_hdmi: set bridge type

host1x:
- cleanups

ivpu:
- updates to firmware API
- refactor BO allocation

meson:
- fix error handling in probe

panel:
- revert "drm/panel-edp: Add auo_b116xa3_mode"
- add Himax HX83112A plus DT bindings
- ltk500hd1829: add support for ltk101b4029w and admatec 9904370
- simple: add BOE BP082WX1-100 8.2" panel plus DT bindungs

renesas:
- add RZ/G2L DU support plus DT bindings

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20240222135841.GA6677@localhost.localdomain
This commit is contained in:
Daniel Vetter 2024-02-26 09:51:48 +01:00
commit 71ab34f72f
70 changed files with 3749 additions and 1280 deletions

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/himax,hx83112a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Himax HX83112A-based DSI display panels
maintainers:
- Luca Weiss <luca.weiss@fairphone.com>
description:
The Himax HX83112A is a generic DSI Panel IC used to control
LCD panels.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
contains:
const: djn,9a-3r063-1102b
vdd1-supply:
description: Digital voltage rail
vsn-supply:
description: Positive source voltage rail
vsp-supply:
description: Negative source voltage rail
reg: true
port: true
required:
- compatible
- reg
- reset-gpios
- vdd1-supply
- vsn-supply
- vsp-supply
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "djn,9a-3r063-1102b";
reg = <0>;
backlight = <&pm6150l_wled>;
reset-gpios = <&pm6150l_gpios 9 GPIO_ACTIVE_LOW>;
vdd1-supply = <&vreg_l1e>;
vsn-supply = <&pm6150l_lcdb_ncp>;
vsp-supply = <&pm6150l_lcdb_ldo>;
port {
panel_in_0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
...

View File

@ -14,7 +14,9 @@ allOf:
properties:
compatible:
const: leadtek,ltk500hd1829
enum:
- leadtek,ltk101b4029w
- leadtek,ltk500hd1829
reg: true
backlight: true
reset-gpios: true

View File

@ -39,6 +39,8 @@ properties:
compatible:
items:
- enum:
# Admatec 9904379 10.1" 1024x600 LVDS panel
- admatec,9904379
- auo,b101ew05
# Chunghwa Picture Tubes Ltd. 7" WXGA (800x1280) TFT LCD LVDS panel
- chunghwa,claa070wp03xg

View File

@ -73,6 +73,8 @@ properties:
- auo,t215hvn01
# Shanghai AVIC Optoelectronics 7" 1024x600 color TFT-LCD panel
- avic,tm070ddh03
# BOE BP082WX1-100 8.2" WXGA (1280x800) LVDS panel
- boe,bp082wx1-100
# BOE BP101WX1-100 10.1" WXGA (1280x800) LVDS panel
- boe,bp101wx1-100
# BOE EV121WXM-N10-1850 12.1" WXGA (1280x800) TFT LCD panel

View File

@ -0,0 +1,126 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/renesas,rzg2l-du.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G2L Display Unit (DU)
maintainers:
- Biju Das <biju.das.jz@bp.renesas.com>
- Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
description: |
These DT bindings describe the Display Unit embedded in the Renesas RZ/G2L
and RZ/V2L SoCs.
properties:
compatible:
oneOf:
- enum:
- renesas,r9a07g044-du # RZ/G2{L,LC}
- items:
- enum:
- renesas,r9a07g054-du # RZ/V2L
- const: renesas,r9a07g044-du # RZ/G2L fallback
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: Main clock
- description: Register access clock
- description: Video clock
clock-names:
items:
- const: aclk
- const: pclk
- const: vclk
resets:
maxItems: 1
power-domains:
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports
description: |
The connections to the DU output video ports are modeled using the OF
graph bindings. The number of ports and their assignment are
model-dependent. Each port shall have a single endpoint.
patternProperties:
"^port@[0-1]$":
$ref: /schemas/graph.yaml#/properties/port
unevaluatedProperties: false
required:
- port@0
unevaluatedProperties: false
renesas,vsps:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
items:
- description: phandle to VSP instance that serves the DU channel
- description: Channel index identifying the LIF instance in that VSP
description:
A list of phandle and channel index tuples to the VSPs that handle the
memory interfaces for the DU channels.
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- power-domains
- ports
- renesas,vsps
additionalProperties: false
examples:
# RZ/G2L DU
- |
#include <dt-bindings/clock/r9a07g044-cpg.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
display@10890000 {
compatible = "renesas,r9a07g044-du";
reg = <0x10890000 0x10000>;
interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD R9A07G044_LCDC_CLK_A>,
<&cpg CPG_MOD R9A07G044_LCDC_CLK_P>,
<&cpg CPG_MOD R9A07G044_LCDC_CLK_D>;
clock-names = "aclk", "pclk", "vclk";
resets = <&cpg R9A07G044_LCDC_RESET_N>;
power-domains = <&cpg>;
renesas,vsps = <&vspd0 0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
endpoint {
remote-endpoint = <&dsi0_in>;
};
};
port@1 {
reg = <1>;
};
};
};
...

View File

@ -37,6 +37,7 @@ properties:
- description: OVR2 overlay manager for vp2
- description: VP1 video port 1
- description: VP2 video port 2
- description: common1 DSS register area
reg-names:
items:
@ -47,6 +48,7 @@ properties:
- const: ovr2
- const: vp1
- const: vp2
- const: common1
clocks:
items:
@ -147,9 +149,10 @@ examples:
<0x04a07000 0x1000>, /* ovr1 */
<0x04a08000 0x1000>, /* ovr2 */
<0x04a0a000 0x1000>, /* vp1 */
<0x04a0b000 0x1000>; /* vp2 */
<0x04a0b000 0x1000>, /* vp2 */
<0x04a01000 0x1000>; /* common1 */
reg-names = "common", "vidl1", "vid",
"ovr1", "ovr2", "vp1", "vp2";
"ovr1", "ovr2", "vp1", "vp2", "common1";
ti,am65x-oldi-io-ctrl = <&dss_oldi_io_ctrl>;
power-domains = <&k3_pds 67 TI_SCI_PD_EXCLUSIVE>;
clocks = <&k3_clks 67 1>,

View File

@ -61,6 +61,8 @@ patternProperties:
description: Analog Devices, Inc.
"^adieng,.*":
description: ADI Engineering, Inc.
"^admatec,.*":
description: admatec GmbH
"^advantech,.*":
description: Advantech Corporation
"^aeroflexgaisler,.*":

View File

@ -138,7 +138,7 @@ indicating kibi- or mebi-bytes.
- drm-shared-<region>: <uint> [KiB|MiB]
The total size of buffers that are shared with another file (ie. have more
The total size of buffers that are shared with another file (e.g., have more
than a single handle).
- drm-total-<region>: <uint> [KiB|MiB]

View File

@ -7025,7 +7025,7 @@ X: drivers/gpu/drm/mediatek/
X: drivers/gpu/drm/msm/
X: drivers/gpu/drm/nouveau/
X: drivers/gpu/drm/radeon/
X: drivers/gpu/drm/renesas/
X: drivers/gpu/drm/renesas/rcar-du/
X: drivers/gpu/drm/tegra/
DRM DRIVERS FOR ALLWINNER A10
@ -7193,12 +7193,22 @@ F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
F: Documentation/devicetree/bindings/display/renesas,du.yaml
F: drivers/gpu/drm/renesas/rcar-du/
DRM DRIVERS FOR RENESAS RZ
M: Biju Das <biju.das.jz@bp.renesas.com>
L: dri-devel@lists.freedesktop.org
L: linux-renesas-soc@vger.kernel.org
S: Maintained
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
F: drivers/gpu/drm/renesas/rz-du/
DRM DRIVERS FOR RENESAS SHMOBILE
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
M: Geert Uytterhoeven <geert+renesas@glider.be>
L: dri-devel@lists.freedesktop.org
L: linux-renesas-soc@vger.kernel.org
S: Supported
T: git git://anongit.freedesktop.org/drm/drm-misc
F: Documentation/devicetree/bindings/display/renesas,shmobile-lcdc.yaml
F: drivers/gpu/drm/renesas/shmobile/
F: include/linux/platform_data/shmob_drm.h

View File

@ -286,22 +286,6 @@ static const struct file_operations fw_trace_level_fops = {
.write = fw_trace_level_fops_write,
};
static ssize_t
ivpu_reset_engine_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
{
struct ivpu_device *vdev = file->private_data;
if (!size)
return -EINVAL;
if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COMPUTE))
return -ENODEV;
if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COPY))
return -ENODEV;
return size;
}
static ssize_t
ivpu_force_recovery_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
{
@ -327,6 +311,22 @@ static const struct file_operations ivpu_force_recovery_fops = {
.write = ivpu_force_recovery_fn,
};
static ssize_t
ivpu_reset_engine_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
{
struct ivpu_device *vdev = file->private_data;
if (!size)
return -EINVAL;
if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COMPUTE))
return -ENODEV;
if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COPY))
return -ENODEV;
return size;
}
static const struct file_operations ivpu_reset_engine_fops = {
.owner = THIS_MODULE,
.open = simple_open,

View File

@ -45,11 +45,11 @@ MODULE_PARM_DESC(test_mode, "Test mode mask. See IVPU_TEST_MODE_* macros.");
u8 ivpu_pll_min_ratio;
module_param_named(pll_min_ratio, ivpu_pll_min_ratio, byte, 0644);
MODULE_PARM_DESC(pll_min_ratio, "Minimum PLL ratio used to set VPU frequency");
MODULE_PARM_DESC(pll_min_ratio, "Minimum PLL ratio used to set NPU frequency");
u8 ivpu_pll_max_ratio = U8_MAX;
module_param_named(pll_max_ratio, ivpu_pll_max_ratio, byte, 0644);
MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set VPU frequency");
MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set NPU frequency");
bool ivpu_disable_mmu_cont_pages;
module_param_named(disable_mmu_cont_pages, ivpu_disable_mmu_cont_pages, bool, 0644);
@ -328,13 +328,13 @@ static int ivpu_wait_for_ready(struct ivpu_device *vdev)
ivpu_ipc_consumer_del(vdev, &cons);
if (!ret && ipc_hdr.data_addr != IVPU_IPC_BOOT_MSG_DATA_ADDR) {
ivpu_err(vdev, "Invalid VPU ready message: 0x%x\n",
ivpu_err(vdev, "Invalid NPU ready message: 0x%x\n",
ipc_hdr.data_addr);
return -EIO;
}
if (!ret)
ivpu_dbg(vdev, PM, "VPU ready message received successfully\n");
ivpu_dbg(vdev, PM, "NPU ready message received successfully\n");
return ret;
}
@ -533,6 +533,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
atomic64_set(&vdev->unique_id_counter, 0);
xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC);
xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);
lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key);
INIT_LIST_HEAD(&vdev->bo_list);
@ -606,6 +607,7 @@ err_power_down:
if (IVPU_WA(d3hot_after_power_off))
pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot);
err_xa_destroy:
xa_destroy(&vdev->db_xa);
xa_destroy(&vdev->submitted_jobs_xa);
xa_destroy(&vdev->context_xa);
return ret;
@ -641,6 +643,8 @@ static void ivpu_dev_fini(struct ivpu_device *vdev)
ivpu_mmu_reserved_context_fini(vdev);
ivpu_mmu_global_context_fini(vdev);
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->db_xa));
xa_destroy(&vdev->db_xa);
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
xa_destroy(&vdev->submitted_jobs_xa);
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->context_xa));

View File

@ -36,6 +36,9 @@
#define IVPU_USER_CONTEXT_MIN_SSID 2
#define IVPU_USER_CONTEXT_MAX_SSID (IVPU_USER_CONTEXT_MIN_SSID + 63)
#define IVPU_MIN_DB 1
#define IVPU_MAX_DB 255
#define IVPU_NUM_ENGINES 2
#define IVPU_PLATFORM_SILICON 0
@ -119,6 +122,8 @@ struct ivpu_device {
struct xarray context_xa;
struct xa_limit context_xa_limit;
struct xarray db_xa;
struct mutex bo_list_lock; /* Protects bo_list */
struct list_head bo_list;
@ -189,7 +194,7 @@ static inline int ivpu_hw_gen(struct ivpu_device *vdev)
case PCI_DEVICE_ID_LNL:
return IVPU_HW_40XX;
default:
ivpu_err(vdev, "Unknown VPU device\n");
ivpu_err(vdev, "Unknown NPU device\n");
return 0;
}
}

View File

@ -46,15 +46,13 @@
static char *ivpu_firmware;
module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644);
MODULE_PARM_DESC(firmware, "VPU firmware binary in /lib/firmware/..");
MODULE_PARM_DESC(firmware, "NPU firmware binary in /lib/firmware/..");
/* TODO: Remove mtl_vpu.bin from names after transition to generation based FW names */
static struct {
int gen;
const char *name;
} fw_names[] = {
{ IVPU_HW_37XX, "vpu_37xx.bin" },
{ IVPU_HW_37XX, "mtl_vpu.bin" },
{ IVPU_HW_37XX, "intel/vpu/vpu_37xx_v0.0.bin" },
{ IVPU_HW_40XX, "vpu_40xx.bin" },
{ IVPU_HW_40XX, "intel/vpu/vpu_40xx_v0.0.bin" },
@ -251,6 +249,7 @@ static int ivpu_fw_update_global_range(struct ivpu_device *vdev)
static int ivpu_fw_mem_init(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
struct ivpu_addr_range fw_range;
int log_verb_size;
int ret;
@ -258,16 +257,19 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
if (ret)
return ret;
fw->mem = ivpu_bo_alloc_internal(vdev, fw->runtime_addr, fw->runtime_size, DRM_IVPU_BO_WC);
fw_range.start = fw->runtime_addr;
fw_range.end = fw->runtime_addr + fw->runtime_size;
fw->mem = ivpu_bo_create(vdev, &vdev->gctx, &fw_range, fw->runtime_size,
DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!fw->mem) {
ivpu_err(vdev, "Failed to allocate firmware runtime memory\n");
ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n");
return -ENOMEM;
}
fw->mem_log_crit = ivpu_bo_alloc_internal(vdev, 0, IVPU_FW_CRITICAL_BUFFER_SIZE,
DRM_IVPU_BO_CACHED);
fw->mem_log_crit = ivpu_bo_create_global(vdev, IVPU_FW_CRITICAL_BUFFER_SIZE,
DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
if (!fw->mem_log_crit) {
ivpu_err(vdev, "Failed to allocate critical log buffer\n");
ivpu_err(vdev, "Failed to create critical log buffer\n");
ret = -ENOMEM;
goto err_free_fw_mem;
}
@ -277,18 +279,19 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
else
log_verb_size = IVPU_FW_VERBOSE_BUFFER_SMALL_SIZE;
fw->mem_log_verb = ivpu_bo_alloc_internal(vdev, 0, log_verb_size, DRM_IVPU_BO_CACHED);
fw->mem_log_verb = ivpu_bo_create_global(vdev, log_verb_size,
DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
if (!fw->mem_log_verb) {
ivpu_err(vdev, "Failed to allocate verbose log buffer\n");
ivpu_err(vdev, "Failed to create verbose log buffer\n");
ret = -ENOMEM;
goto err_free_log_crit;
}
if (fw->shave_nn_size) {
fw->mem_shave_nn = ivpu_bo_alloc_internal(vdev, vdev->hw->ranges.shave.start,
fw->shave_nn_size, DRM_IVPU_BO_WC);
fw->mem_shave_nn = ivpu_bo_create(vdev, &vdev->gctx, &vdev->hw->ranges.shave,
fw->shave_nn_size, DRM_IVPU_BO_WC);
if (!fw->mem_shave_nn) {
ivpu_err(vdev, "Failed to allocate shavenn buffer\n");
ivpu_err(vdev, "Failed to create shavenn buffer\n");
ret = -ENOMEM;
goto err_free_log_verb;
}
@ -297,11 +300,11 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
return 0;
err_free_log_verb:
ivpu_bo_free_internal(fw->mem_log_verb);
ivpu_bo_free(fw->mem_log_verb);
err_free_log_crit:
ivpu_bo_free_internal(fw->mem_log_crit);
ivpu_bo_free(fw->mem_log_crit);
err_free_fw_mem:
ivpu_bo_free_internal(fw->mem);
ivpu_bo_free(fw->mem);
return ret;
}
@ -310,13 +313,13 @@ static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
struct ivpu_fw_info *fw = vdev->fw;
if (fw->mem_shave_nn) {
ivpu_bo_free_internal(fw->mem_shave_nn);
ivpu_bo_free(fw->mem_shave_nn);
fw->mem_shave_nn = NULL;
}
ivpu_bo_free_internal(fw->mem_log_verb);
ivpu_bo_free_internal(fw->mem_log_crit);
ivpu_bo_free_internal(fw->mem);
ivpu_bo_free(fw->mem_log_verb);
ivpu_bo_free(fw->mem_log_crit);
ivpu_bo_free(fw->mem);
fw->mem_log_verb = NULL;
fw->mem_log_crit = NULL;
@ -470,6 +473,8 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
boot_params->d0i3_residency_time_us);
ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n",
boot_params->d0i3_entry_vpu_ts);
ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n",
boot_params->system_time_us);
}
void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params)
@ -481,11 +486,14 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
boot_params->d0i3_residency_time_us =
ktime_us_delta(ktime_get_boottime(), vdev->hw->d0i3_entry_host_ts);
boot_params->d0i3_entry_vpu_ts = vdev->hw->d0i3_entry_vpu_ts;
boot_params->system_time_us = ktime_to_us(ktime_get_real());
ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n",
boot_params->d0i3_residency_time_us);
ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n",
boot_params->d0i3_entry_vpu_ts);
ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n",
boot_params->system_time_us);
boot_params->save_restore_ret_address = 0;
vdev->pm->is_warmboot = true;
@ -563,6 +571,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
boot_params->d0i3_residency_time_us = 0;
boot_params->d0i3_entry_vpu_ts = 0;
boot_params->system_time_us = ktime_to_us(ktime_get_real());
wmb(); /* Flush WC buffers after writing bootparams */
ivpu_fw_boot_params_print(vdev, boot_params);

View File

@ -20,7 +20,7 @@
unsigned int ivpu_log_level = IVPU_FW_LOG_ERROR;
module_param(ivpu_log_level, uint, 0444);
MODULE_PARM_DESC(ivpu_log_level,
"VPU firmware default trace level: debug=" __stringify(IVPU_FW_LOG_DEBUG)
"NPU firmware default trace level: debug=" __stringify(IVPU_FW_LOG_DEBUG)
" info=" __stringify(IVPU_FW_LOG_INFO)
" warn=" __stringify(IVPU_FW_LOG_WARN)
" error=" __stringify(IVPU_FW_LOG_ERROR)
@ -121,11 +121,11 @@ void ivpu_fw_log_print(struct ivpu_device *vdev, bool only_new_msgs, struct drm_
u32 next = 0;
while (fw_log_ptr(vdev, vdev->fw->mem_log_crit, &next, &log_header) == 0)
fw_log_print_buffer(vdev, log_header, "VPU critical", only_new_msgs, p);
fw_log_print_buffer(vdev, log_header, "NPU critical", only_new_msgs, p);
next = 0;
while (fw_log_ptr(vdev, vdev->fw->mem_log_verb, &next, &log_header) == 0)
fw_log_print_buffer(vdev, log_header, "VPU verbose", only_new_msgs, p);
fw_log_print_buffer(vdev, log_header, "NPU verbose", only_new_msgs, p);
}
void ivpu_fw_log_clear(struct ivpu_device *vdev)

View File

@ -172,8 +172,7 @@ struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t siz
return &bo->base.base;
}
static struct ivpu_bo *
ivpu_bo_create(struct ivpu_device *vdev, u64 size, u32 flags)
static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags)
{
struct drm_gem_shmem_object *shmem;
struct ivpu_bo *bo;
@ -201,7 +200,7 @@ ivpu_bo_create(struct ivpu_device *vdev, u64 size, u32 flags)
return bo;
}
static int ivpu_bo_open(struct drm_gem_object *obj, struct drm_file *file)
static int ivpu_gem_bo_open(struct drm_gem_object *obj, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct ivpu_device *vdev = file_priv->vdev;
@ -224,7 +223,7 @@ static int ivpu_bo_open(struct drm_gem_object *obj, struct drm_file *file)
return ivpu_bo_alloc_vpu_addr(bo, &file_priv->ctx, range);
}
static void ivpu_bo_free(struct drm_gem_object *obj)
static void ivpu_gem_bo_free(struct drm_gem_object *obj)
{
struct ivpu_device *vdev = to_ivpu_device(obj->dev);
struct ivpu_bo *bo = to_ivpu_bo(obj);
@ -245,8 +244,8 @@ static void ivpu_bo_free(struct drm_gem_object *obj)
}
static const struct drm_gem_object_funcs ivpu_gem_funcs = {
.free = ivpu_bo_free,
.open = ivpu_bo_open,
.free = ivpu_gem_bo_free,
.open = ivpu_gem_bo_open,
.print_info = drm_gem_shmem_object_print_info,
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
@ -272,9 +271,9 @@ int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *fi
if (size == 0)
return -EINVAL;
bo = ivpu_bo_create(vdev, size, args->flags);
bo = ivpu_bo_alloc(vdev, size, args->flags);
if (IS_ERR(bo)) {
ivpu_err(vdev, "Failed to create BO: %pe (ctx %u size %llu flags 0x%x)",
ivpu_err(vdev, "Failed to allocate BO: %pe (ctx %u size %llu flags 0x%x)",
bo, file_priv->ctx.id, args->size, args->flags);
return PTR_ERR(bo);
}
@ -289,33 +288,28 @@ int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *fi
}
struct ivpu_bo *
ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 flags)
ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
struct ivpu_addr_range *range, u64 size, u32 flags)
{
const struct ivpu_addr_range *range;
struct ivpu_addr_range fixed_range;
struct iosys_map map;
struct ivpu_bo *bo;
int ret;
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(vpu_addr));
if (drm_WARN_ON(&vdev->drm, !range))
return NULL;
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(range->start));
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(range->end));
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(size));
if (vpu_addr) {
fixed_range.start = vpu_addr;
fixed_range.end = vpu_addr + size;
range = &fixed_range;
} else {
range = &vdev->hw->ranges.global;
}
bo = ivpu_bo_create(vdev, size, flags);
bo = ivpu_bo_alloc(vdev, size, flags);
if (IS_ERR(bo)) {
ivpu_err(vdev, "Failed to create BO: %pe (vpu_addr 0x%llx size %llu flags 0x%x)",
bo, vpu_addr, size, flags);
ivpu_err(vdev, "Failed to allocate BO: %pe (vpu_addr 0x%llx size %llu flags 0x%x)",
bo, range->start, size, flags);
return NULL;
}
ret = ivpu_bo_alloc_vpu_addr(bo, &vdev->gctx, range);
ret = ivpu_bo_alloc_vpu_addr(bo, ctx, range);
if (ret)
goto err_put;
@ -323,11 +317,14 @@ ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 fla
if (ret)
goto err_put;
dma_resv_lock(bo->base.base.resv, NULL);
ret = drm_gem_shmem_vmap(&bo->base, &map);
dma_resv_unlock(bo->base.base.resv);
if (ret)
goto err_put;
if (flags & DRM_IVPU_BO_MAPPABLE) {
dma_resv_lock(bo->base.base.resv, NULL);
ret = drm_gem_shmem_vmap(&bo->base, &map);
dma_resv_unlock(bo->base.base.resv);
if (ret)
goto err_put;
}
return bo;
@ -336,13 +333,20 @@ err_put:
return NULL;
}
void ivpu_bo_free_internal(struct ivpu_bo *bo)
struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags)
{
return ivpu_bo_create(vdev, &vdev->gctx, &vdev->hw->ranges.global, size, flags);
}
void ivpu_bo_free(struct ivpu_bo *bo)
{
struct iosys_map map = IOSYS_MAP_INIT_VADDR(bo->base.vaddr);
dma_resv_lock(bo->base.base.resv, NULL);
drm_gem_shmem_vunmap(&bo->base, &map);
dma_resv_unlock(bo->base.base.resv);
if (bo->flags & DRM_IVPU_BO_MAPPABLE) {
dma_resv_lock(bo->base.base.resv, NULL);
drm_gem_shmem_vunmap(&bo->base, &map);
dma_resv_unlock(bo->base.base.resv);
}
drm_gem_object_put(&bo->base.base);
}

View File

@ -28,8 +28,10 @@ int ivpu_bo_pin(struct ivpu_bo *bo);
void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size);
struct ivpu_bo *ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 flags);
void ivpu_bo_free_internal(struct ivpu_bo *bo);
struct ivpu_bo *ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
struct ivpu_addr_range *range, u64 size, u32 flags);
struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags);
void ivpu_bo_free(struct ivpu_bo *bo);
int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file);

View File

@ -13,7 +13,7 @@
#include "ivpu_pm.h"
#define TILE_FUSE_ENABLE_BOTH 0x0
#define TILE_SKU_BOTH_MTL 0x3630
#define TILE_SKU_BOTH 0x3630
/* Work point configuration values */
#define CONFIG_1_TILE 0x01
@ -228,7 +228,7 @@ static int ivpu_pll_drive(struct ivpu_device *vdev, bool enable)
ret = ivpu_hw_37xx_wait_for_vpuip_bar(vdev);
if (ret) {
ivpu_err(vdev, "Timed out waiting for VPUIP bar\n");
ivpu_err(vdev, "Timed out waiting for NPU IP bar\n");
return ret;
}
}
@ -599,7 +599,7 @@ static int ivpu_hw_37xx_info_init(struct ivpu_device *vdev)
struct ivpu_hw_info *hw = vdev->hw;
hw->tile_fuse = TILE_FUSE_ENABLE_BOTH;
hw->sku = TILE_SKU_BOTH_MTL;
hw->sku = TILE_SKU_BOTH;
hw->config = WP_CONFIG_2_TILE_4_3_RATIO;
ivpu_pll_init_frequency_ratios(vdev);
@ -742,10 +742,10 @@ static int ivpu_hw_37xx_power_down(struct ivpu_device *vdev)
ivpu_hw_37xx_save_d0i3_entry_timestamp(vdev);
if (!ivpu_hw_37xx_is_idle(vdev))
ivpu_warn(vdev, "VPU not idle during power down\n");
ivpu_warn(vdev, "NPU not idle during power down\n");
if (ivpu_hw_37xx_reset(vdev)) {
ivpu_err(vdev, "Failed to reset VPU\n");
ivpu_err(vdev, "Failed to reset NPU\n");
ret = -EIO;
}

View File

@ -80,11 +80,11 @@ static char *ivpu_platform_to_str(u32 platform)
{
switch (platform) {
case IVPU_PLATFORM_SILICON:
return "IVPU_PLATFORM_SILICON";
return "SILICON";
case IVPU_PLATFORM_SIMICS:
return "IVPU_PLATFORM_SIMICS";
return "SIMICS";
case IVPU_PLATFORM_FPGA:
return "IVPU_PLATFORM_FPGA";
return "FPGA";
default:
return "Invalid platform";
}
@ -773,7 +773,7 @@ static int ivpu_hw_40xx_reset(struct ivpu_device *vdev)
int ret = 0;
if (ivpu_hw_40xx_ip_reset(vdev)) {
ivpu_err(vdev, "Failed to reset VPU IP\n");
ivpu_err(vdev, "Failed to reset NPU IP\n");
ret = -EIO;
}
@ -931,7 +931,7 @@ static int ivpu_hw_40xx_power_down(struct ivpu_device *vdev)
ivpu_hw_40xx_save_d0i3_entry_timestamp(vdev);
if (!ivpu_hw_40xx_is_idle(vdev) && ivpu_hw_40xx_ip_reset(vdev))
ivpu_warn(vdev, "Failed to reset the VPU\n");
ivpu_warn(vdev, "Failed to reset the NPU\n");
if (ivpu_pll_disable(vdev)) {
ivpu_err(vdev, "Failed to disable PLL\n");

View File

@ -58,8 +58,8 @@ static void ivpu_ipc_mem_fini(struct ivpu_device *vdev)
{
struct ivpu_ipc_info *ipc = vdev->ipc;
ivpu_bo_free_internal(ipc->mem_rx);
ivpu_bo_free_internal(ipc->mem_tx);
ivpu_bo_free(ipc->mem_rx);
ivpu_bo_free(ipc->mem_tx);
}
static int
@ -471,13 +471,13 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
struct ivpu_ipc_info *ipc = vdev->ipc;
int ret;
ipc->mem_tx = ivpu_bo_alloc_internal(vdev, 0, SZ_16K, DRM_IVPU_BO_WC);
ipc->mem_tx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!ipc->mem_tx) {
ivpu_err(vdev, "Failed to allocate mem_tx\n");
return -ENOMEM;
}
ipc->mem_rx = ivpu_bo_alloc_internal(vdev, 0, SZ_16K, DRM_IVPU_BO_WC);
ipc->mem_rx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!ipc->mem_rx) {
ivpu_err(vdev, "Failed to allocate mem_rx\n");
ret = -ENOMEM;
@ -506,9 +506,9 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
return 0;
err_free_rx:
ivpu_bo_free_internal(ipc->mem_rx);
ivpu_bo_free(ipc->mem_rx);
err_free_tx:
ivpu_bo_free_internal(ipc->mem_tx);
ivpu_bo_free(ipc->mem_tx);
return ret;
}

View File

@ -30,19 +30,26 @@ static void ivpu_cmdq_ring_db(struct ivpu_device *vdev, struct ivpu_cmdq *cmdq)
static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv, u16 engine)
{
struct xa_limit db_xa_limit = {.max = IVPU_MAX_DB, .min = IVPU_MIN_DB};
struct ivpu_device *vdev = file_priv->vdev;
struct vpu_job_queue_header *jobq_header;
struct ivpu_cmdq *cmdq;
int ret;
cmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL);
if (!cmdq)
return NULL;
cmdq->mem = ivpu_bo_alloc_internal(vdev, 0, SZ_4K, DRM_IVPU_BO_WC);
if (!cmdq->mem)
goto cmdq_free;
ret = xa_alloc(&vdev->db_xa, &cmdq->db_id, NULL, db_xa_limit, GFP_KERNEL);
if (ret) {
ivpu_err(vdev, "Failed to allocate doorbell id: %d\n", ret);
goto err_free_cmdq;
}
cmdq->mem = ivpu_bo_create_global(vdev, SZ_4K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!cmdq->mem)
goto err_erase_xa;
cmdq->db_id = file_priv->ctx.id + engine * ivpu_get_context_count(vdev);
cmdq->entry_count = (u32)((ivpu_bo_size(cmdq->mem) - sizeof(struct vpu_job_queue_header)) /
sizeof(struct vpu_job_queue_entry));
@ -55,7 +62,9 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv, u16 e
return cmdq;
cmdq_free:
err_erase_xa:
xa_erase(&vdev->db_xa, cmdq->db_id);
err_free_cmdq:
kfree(cmdq);
return NULL;
}
@ -65,7 +74,8 @@ static void ivpu_cmdq_free(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *c
if (!cmdq)
return;
ivpu_bo_free_internal(cmdq->mem);
ivpu_bo_free(cmdq->mem);
xa_erase(&file_priv->vdev->db_xa, cmdq->db_id);
kfree(cmdq);
}

View File

@ -22,7 +22,7 @@
static bool ivpu_disable_recovery;
module_param_named_unsafe(disable_recovery, ivpu_disable_recovery, bool, 0644);
MODULE_PARM_DESC(disable_recovery, "Disables recovery when VPU hang is detected");
MODULE_PARM_DESC(disable_recovery, "Disables recovery when NPU hang is detected");
static unsigned long ivpu_tdr_timeout_ms;
module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, ulong, 0644);
@ -112,11 +112,11 @@ static void ivpu_pm_recovery_work(struct work_struct *work)
char *evt[2] = {"IVPU_PM_EVENT=IVPU_RECOVER", NULL};
int ret;
ivpu_err(vdev, "Recovering the VPU (reset #%d)\n", atomic_read(&vdev->pm->reset_counter));
ivpu_err(vdev, "Recovering the NPU (reset #%d)\n", atomic_read(&vdev->pm->reset_counter));
ret = pm_runtime_resume_and_get(vdev->drm.dev);
if (ret)
ivpu_err(vdev, "Failed to resume VPU: %d\n", ret);
ivpu_err(vdev, "Failed to resume NPU: %d\n", ret);
ivpu_fw_log_dump(vdev);
@ -255,10 +255,10 @@ int ivpu_pm_runtime_suspend_cb(struct device *dev)
ret = ivpu_suspend(vdev);
if (ret)
ivpu_err(vdev, "Failed to set suspend VPU: %d\n", ret);
ivpu_err(vdev, "Failed to suspend NPU: %d\n", ret);
if (!hw_is_idle) {
ivpu_err(vdev, "VPU failed to enter idle, force suspended.\n");
ivpu_err(vdev, "NPU failed to enter idle, force suspended.\n");
ivpu_fw_log_dump(vdev);
ivpu_pm_prepare_cold_boot(vdev);
} else {

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2020-2023 Intel Corporation
* Copyright (c) 2020-2023, Intel Corporation.
*/
#ifndef VPU_BOOT_API_H
@ -27,12 +27,12 @@
* Minor version changes when API backward compatibility is preserved.
* Resets to 0 if Major version is incremented.
*/
#define VPU_BOOT_API_VER_MINOR 20
#define VPU_BOOT_API_VER_MINOR 22
/*
* API header changed (field names, documentation, formatting) but API itself has not been changed
*/
#define VPU_BOOT_API_VER_PATCH 4
#define VPU_BOOT_API_VER_PATCH 0
/*
* Index in the API version table
@ -41,7 +41,7 @@
#define VPU_BOOT_API_VER_INDEX 0
/* ------------ FW API version information end ---------------------*/
#pragma pack(push, 1)
#pragma pack(push, 4)
/*
* Firmware image header format
@ -66,9 +66,17 @@ struct vpu_firmware_header {
/* Size of memory require for firmware execution */
u32 runtime_size;
u32 shave_nn_fw_size;
/* Size of primary preemption buffer. */
/*
* Size of primary preemption buffer, assuming a 2-job submission queue.
* NOTE: host driver is expected to adapt size accordingly to actual
* submission queue size and device capabilities.
*/
u32 preemption_buffer_1_size;
/* Size of secondary preemption buffer. */
/*
* Size of secondary preemption buffer, assuming a 2-job submission queue.
* NOTE: host driver is expected to adapt size accordingly to actual
* submission queue size and device capabilities.
*/
u32 preemption_buffer_2_size;
/* Space reserved for future preemption-related fields. */
u32 preemption_reserved[6];
@ -181,10 +189,10 @@ struct vpu_warm_boot_section {
#define VPU_PRESENT_CALL_PERIOD_MS_MAX 10000
/**
* Macros to enable various operation modes within the VPU.
* Macros to enable various power profiles within the NPU.
* To be defined as part of 32 bit mask.
*/
#define VPU_OP_MODE_SURVIVABILITY 0x1
#define POWER_PROFILE_SURVIVABILITY 0x1
struct vpu_boot_params {
u32 magic;
@ -317,7 +325,15 @@ struct vpu_boot_params {
u64 d0i3_residency_time_us;
/* Value of VPU perf counter at the time of entering D0i3 state . */
u64 d0i3_entry_vpu_ts;
u32 pad4[20];
/*
* The system time of the host operating system in microseconds.
* E.g the number of microseconds since 1st of January 1970, or whatever date the
* host operating system uses to maintain system time.
* This value will be used to track system time on the VPU.
* The KMD is required to update this value on every VPU reset.
*/
u64 system_time_us;
u32 pad4[18];
/* Warm boot information: 0x400 - 0x43F */
u32 warm_boot_sections_count;
u32 warm_boot_start_address_reference;
@ -344,10 +360,14 @@ struct vpu_boot_params {
u32 vpu_focus_present_timer_ms;
/* VPU ECC Signaling */
u32 vpu_uses_ecc_mca_signal;
/* Values defined by VPU_OP_MODE* macros */
u32 vpu_operation_mode;
/* Unused/reserved: 0x480 - 0xFFF */
u32 pad6[736];
/* Values defined by POWER_PROFILE* macros */
u32 power_profile;
/* Microsecond value for DCT active cycle */
u32 dct_active_us;
/* Microsecond value for DCT inactive cycle */
u32 dct_inactive_us;
/* Unused/reserved: 0x488 - 0xFFF */
u32 pad6[734];
};
/*

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2020-2023 Intel Corporation
* Copyright (c) 2020-2023, Intel Corporation.
*/
/**
@ -27,7 +27,7 @@
/*
* API header changed (field names, documentation, formatting) but API itself has not been changed
*/
#define VPU_JSM_API_VER_PATCH 0
#define VPU_JSM_API_VER_PATCH 6
/*
* Index in the API version table
@ -43,8 +43,11 @@
/* Max number of impacted contexts that can be dealt with the engine reset command */
#define VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS 3
/** Pack the API structures for now, once alignment issues are fixed this can be removed */
#pragma pack(push, 1)
/*
* Pack the API structures to enforce binary compatibility
* Align to 8 bytes for optimal performance
*/
#pragma pack(push, 8)
/*
* Engine indexes.
@ -124,6 +127,19 @@
*/
#define VPU_HWS_MAX_REALTIME_PRIORITY_LEVEL 31U
/*
* vpu_jsm_engine_reset_context flag definitions
*/
#define VPU_ENGINE_RESET_CONTEXT_FLAG_COLLATERAL_DAMAGE_MASK BIT(0)
#define VPU_ENGINE_RESET_CONTEXT_HANG_PRIMARY_CAUSE 0
#define VPU_ENGINE_RESET_CONTEXT_COLLATERAL_DAMAGE 1
/*
* Invalid command queue handle identifier. Applies to cmdq_id and cmdq_group
* in this API.
*/
#define VPU_HWS_INVALID_CMDQ_HANDLE 0ULL
/*
* Job format.
*/
@ -613,7 +629,7 @@ struct vpu_jsm_engine_reset_context {
u32 reserved_0;
/* Command queue id */
u64 cmdq_id;
/* Flags: 0: cause of hang; 1: collateral damage of reset */
/* See VPU_ENGINE_RESET_CONTEXT_* defines */
u64 flags;
};
@ -730,11 +746,7 @@ struct vpu_ipc_msg_payload_hws_create_cmdq {
u32 host_ssid;
/* Engine for which queue is being created */
u32 engine_idx;
/*
* Cmdq group may be set to 0 or equal to
* cmdq_id while each priority band contains
* only single engine instances.
*/
/* Cmdq group: only used for HWS logging of state changes */
u64 cmdq_group;
/* Command queue id */
u64 cmdq_id;

View File

@ -138,7 +138,6 @@ struct agp_bridge_data {
unsigned long gart_bus_addr;
unsigned long gatt_bus_addr;
u32 mode;
enum chipset_type type;
unsigned long *key_list;
atomic_t current_memory_agp;
atomic_t agp_in_use;

View File

@ -97,6 +97,10 @@ void amdgpu_show_fdinfo(struct drm_printer *p, struct drm_file *file)
stats.requested_visible_vram/1024UL);
drm_printf(p, "amd-requested-gtt:\t%llu KiB\n",
stats.requested_gtt/1024UL);
drm_printf(p, "drm-shared-vram:\t%llu KiB\n", stats.vram_shared/1024UL);
drm_printf(p, "drm-shared-gtt:\t%llu KiB\n", stats.gtt_shared/1024UL);
drm_printf(p, "drm-shared-cpu:\t%llu KiB\n", stats.cpu_shared/1024UL);
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
if (!usage[hw_ip])
continue;

View File

@ -1273,25 +1273,36 @@ void amdgpu_bo_get_memory(struct amdgpu_bo *bo,
struct amdgpu_mem_stats *stats)
{
uint64_t size = amdgpu_bo_size(bo);
struct drm_gem_object *obj;
unsigned int domain;
bool shared;
/* Abort if the BO doesn't currently have a backing store */
if (!bo->tbo.resource)
return;
obj = &bo->tbo.base;
shared = drm_gem_object_is_shared_for_memory_stats(obj);
domain = amdgpu_mem_type_to_domain(bo->tbo.resource->mem_type);
switch (domain) {
case AMDGPU_GEM_DOMAIN_VRAM:
stats->vram += size;
if (amdgpu_bo_in_cpu_visible_vram(bo))
stats->visible_vram += size;
if (shared)
stats->vram_shared += size;
break;
case AMDGPU_GEM_DOMAIN_GTT:
stats->gtt += size;
if (shared)
stats->gtt_shared += size;
break;
case AMDGPU_GEM_DOMAIN_CPU:
default:
stats->cpu += size;
if (shared)
stats->cpu_shared += size;
break;
}

View File

@ -138,12 +138,18 @@ struct amdgpu_bo_vm {
struct amdgpu_mem_stats {
/* current VRAM usage, includes visible VRAM */
uint64_t vram;
/* current shared VRAM usage, includes visible VRAM */
uint64_t vram_shared;
/* current visible VRAM usage */
uint64_t visible_vram;
/* current GTT usage */
uint64_t gtt;
/* current shared GTT usage */
uint64_t gtt_shared;
/* current system memory usage */
uint64_t cpu;
/* current shared system memory usage */
uint64_t cpu_shared;
/* sum of evicted buffers, includes visible VRAM */
uint64_t evicted_vram;
/* sum of evicted buffers due to CPU access */

View File

@ -1290,17 +1290,6 @@ static int adv7511_probe(struct i2c_client *i2c)
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
if (i2c->irq) {
init_waitqueue_head(&adv7511->wq);
ret = devm_request_threaded_irq(dev, i2c->irq, NULL,
adv7511_irq_handler,
IRQF_ONESHOT, dev_name(dev),
adv7511);
if (ret)
goto err_unregister_cec;
}
adv7511_power_off(adv7511);
i2c_set_clientdata(i2c, adv7511);
@ -1324,6 +1313,17 @@ static int adv7511_probe(struct i2c_client *i2c)
adv7511_audio_init(dev, adv7511);
if (i2c->irq) {
init_waitqueue_head(&adv7511->wq);
ret = devm_request_threaded_irq(dev, i2c->irq, NULL,
adv7511_irq_handler,
IRQF_ONESHOT, dev_name(dev),
adv7511);
if (ret)
goto err_unregister_audio;
}
if (adv7511->info->has_dsi) {
ret = adv7533_attach_dsi(adv7511);
if (ret)

View File

@ -3549,6 +3549,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
hdmi->bridge.interlace_allowed = true;
hdmi->bridge.ddc = hdmi->ddc;
hdmi->bridge.of_node = pdev->dev.of_node;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,2 @@
kms_3d,Fail
kms_addfb_basic@addfb25-bad-modifier,Fail
kms_force_connector_basic@force-edid,Fail
kms_hdmi_inject@inject-4k,Fail

View File

@ -15,3 +15,4 @@ kms_plane_alpha_blend@alpha-7efc,Fail
kms_plane_alpha_blend@coverage-7efc,Fail
kms_plane_alpha_blend@coverage-vs-premult-vs-constant,Fail
kms_rmfb@close-fd,Fail
kms_universal_plane@universal-plane-sanity,Fail

View File

@ -0,0 +1,2 @@
# Suspend to RAM seems to be broken on this machine
.*suspend.*

View File

@ -15,3 +15,4 @@ kms_plane_alpha_blend@alpha-7efc,Fail
kms_plane_alpha_blend@coverage-7efc,Fail
kms_plane_alpha_blend@coverage-vs-premult-vs-constant,Fail
kms_rmfb@close-fd,Fail
kms_universal_plane@universal-plane-sanity,Fail

View File

@ -0,0 +1,2 @@
# Suspend to RAM seems to be broken on this machine
.*suspend.*

View File

@ -913,7 +913,7 @@ void drm_show_memory_stats(struct drm_printer *p, struct drm_file *file)
DRM_GEM_OBJECT_PURGEABLE;
}
if (obj->handle_count > 1) {
if (drm_gem_object_is_shared_for_memory_stats(obj)) {
status.shared += obj->size;
} else {
status.private += obj->size;

View File

@ -441,6 +441,9 @@ int drm_syncobj_find_fence(struct drm_file *file_private,
u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
int ret;
if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)
return -EINVAL;
if (!syncobj)
return -ENOENT;
@ -1040,8 +1043,11 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
uint64_t *points;
uint32_t signaled_count, i;
if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)
if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
might_sleep();
lockdep_assert_none_held_once();
}
points = kmalloc_array(count, sizeof(*points), GFP_KERNEL);
if (points == NULL)
@ -1109,7 +1115,8 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs,
* fallthough and try a 0 timeout wait!
*/
if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
if (flags & (DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT |
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE)) {
for (i = 0; i < count; ++i)
drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]);
}

View File

@ -53,7 +53,7 @@ obj_meminfo(struct drm_i915_gem_object *obj,
obj->mm.region->id : INTEL_REGION_SMEM;
const u64 sz = obj->base.size;
if (obj->base.handle_count > 1)
if (drm_gem_object_is_shared_for_memory_stats(&obj->base))
stats[id].shared += sz;
else
stats[id].private += sz;

View File

@ -312,7 +312,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
/* Encoder Initialization */
ret = meson_encoder_cvbs_init(priv);
ret = meson_encoder_cvbs_probe(priv);
if (ret)
goto exit_afbcd;
@ -326,12 +326,12 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
}
}
ret = meson_encoder_hdmi_init(priv);
ret = meson_encoder_hdmi_probe(priv);
if (ret)
goto exit_afbcd;
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
ret = meson_encoder_dsi_init(priv);
ret = meson_encoder_dsi_probe(priv);
if (ret)
goto exit_afbcd;
}

View File

@ -219,7 +219,7 @@ static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
};
int meson_encoder_cvbs_init(struct meson_drm *priv)
int meson_encoder_cvbs_probe(struct meson_drm *priv)
{
struct drm_device *drm = priv->drm;
struct meson_encoder_cvbs *meson_encoder_cvbs;
@ -240,10 +240,9 @@ int meson_encoder_cvbs_init(struct meson_drm *priv)
meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote);
of_node_put(remote);
if (!meson_encoder_cvbs->next_bridge) {
dev_err(priv->dev, "Failed to find CVBS Connector bridge\n");
return -EPROBE_DEFER;
}
if (!meson_encoder_cvbs->next_bridge)
return dev_err_probe(priv->dev, -EPROBE_DEFER,
"Failed to find CVBS Connector bridge\n");
/* CVBS Encoder Bridge */
meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs;
@ -259,10 +258,9 @@ int meson_encoder_cvbs_init(struct meson_drm *priv)
/* Encoder */
ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder,
DRM_MODE_ENCODER_TVDAC);
if (ret) {
dev_err(priv->dev, "Failed to init CVBS encoder: %d\n", ret);
return ret;
}
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to init CVBS encoder\n");
meson_encoder_cvbs->encoder.possible_crtcs = BIT(0);
@ -276,10 +274,10 @@ int meson_encoder_cvbs_init(struct meson_drm *priv)
/* Initialize & attach Bridge Connector */
connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder);
if (IS_ERR(connector)) {
dev_err(priv->dev, "Unable to create CVBS bridge connector\n");
return PTR_ERR(connector);
}
if (IS_ERR(connector))
return dev_err_probe(priv->dev, PTR_ERR(connector),
"Unable to create CVBS bridge connector\n");
drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder);
priv->encoders[MESON_ENC_CVBS] = meson_encoder_cvbs;

View File

@ -24,7 +24,7 @@ struct meson_cvbs_mode {
/* Modes supported by the CVBS output */
extern struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT];
int meson_encoder_cvbs_init(struct meson_drm *priv);
int meson_encoder_cvbs_probe(struct meson_drm *priv);
void meson_encoder_cvbs_remove(struct meson_drm *priv);
#endif /* __MESON_VENC_CVBS_H */

View File

@ -100,7 +100,7 @@ static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
};
int meson_encoder_dsi_init(struct meson_drm *priv)
int meson_encoder_dsi_probe(struct meson_drm *priv)
{
struct meson_encoder_dsi *meson_encoder_dsi;
struct device_node *remote;
@ -118,10 +118,9 @@ int meson_encoder_dsi_init(struct meson_drm *priv)
}
meson_encoder_dsi->next_bridge = of_drm_find_bridge(remote);
if (!meson_encoder_dsi->next_bridge) {
dev_dbg(priv->dev, "Failed to find DSI transceiver bridge\n");
return -EPROBE_DEFER;
}
if (!meson_encoder_dsi->next_bridge)
return dev_err_probe(priv->dev, -EPROBE_DEFER,
"Failed to find DSI transceiver bridge\n");
/* DSI Encoder Bridge */
meson_encoder_dsi->bridge.funcs = &meson_encoder_dsi_bridge_funcs;
@ -135,19 +134,17 @@ int meson_encoder_dsi_init(struct meson_drm *priv)
/* Encoder */
ret = drm_simple_encoder_init(priv->drm, &meson_encoder_dsi->encoder,
DRM_MODE_ENCODER_DSI);
if (ret) {
dev_err(priv->dev, "Failed to init DSI encoder: %d\n", ret);
return ret;
}
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to init DSI encoder\n");
meson_encoder_dsi->encoder.possible_crtcs = BIT(0);
/* Attach DSI Encoder Bridge to Encoder */
ret = drm_bridge_attach(&meson_encoder_dsi->encoder, &meson_encoder_dsi->bridge, NULL, 0);
if (ret) {
dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
return ret;
}
if (ret)
return dev_err_probe(priv->dev, ret,
"Failed to attach bridge\n");
/*
* We should have now in place:

View File

@ -7,7 +7,7 @@
#ifndef __MESON_ENCODER_DSI_H
#define __MESON_ENCODER_DSI_H
int meson_encoder_dsi_init(struct meson_drm *priv);
int meson_encoder_dsi_probe(struct meson_drm *priv);
void meson_encoder_dsi_remove(struct meson_drm *priv);
#endif /* __MESON_ENCODER_DSI_H */

View File

@ -366,7 +366,7 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
};
int meson_encoder_hdmi_init(struct meson_drm *priv)
int meson_encoder_hdmi_probe(struct meson_drm *priv)
{
struct meson_encoder_hdmi *meson_encoder_hdmi;
struct platform_device *pdev;
@ -386,8 +386,8 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote);
if (!meson_encoder_hdmi->next_bridge) {
dev_err(priv->dev, "Failed to find HDMI transceiver bridge\n");
ret = -EPROBE_DEFER;
ret = dev_err_probe(priv->dev, -EPROBE_DEFER,
"Failed to find HDMI transceiver bridge\n");
goto err_put_node;
}
@ -405,7 +405,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder,
DRM_MODE_ENCODER_TMDS);
if (ret) {
dev_err(priv->dev, "Failed to init HDMI encoder: %d\n", ret);
dev_err_probe(priv->dev, ret, "Failed to init HDMI encoder\n");
goto err_put_node;
}
@ -415,7 +415,7 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
dev_err_probe(priv->dev, ret, "Failed to attach bridge\n");
goto err_put_node;
}
@ -423,8 +423,9 @@ int meson_encoder_hdmi_init(struct meson_drm *priv)
meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm,
&meson_encoder_hdmi->encoder);
if (IS_ERR(meson_encoder_hdmi->connector)) {
dev_err(priv->dev, "Unable to create HDMI bridge connector\n");
ret = PTR_ERR(meson_encoder_hdmi->connector);
ret = dev_err_probe(priv->dev,
PTR_ERR(meson_encoder_hdmi->connector),
"Unable to create HDMI bridge connector\n");
goto err_put_node;
}
drm_connector_attach_encoder(meson_encoder_hdmi->connector,

View File

@ -7,7 +7,7 @@
#ifndef __MESON_ENCODER_HDMI_H
#define __MESON_ENCODER_HDMI_H
int meson_encoder_hdmi_init(struct meson_drm *priv);
int meson_encoder_hdmi_probe(struct meson_drm *priv);
void meson_encoder_hdmi_remove(struct meson_drm *priv);
#endif /* __MESON_ENCODER_HDMI_H */

View File

@ -145,6 +145,16 @@ config DRM_PANEL_LVDS
handling of power supplies or control signals. It implements automatic
backlight handling if the panel is attached to a backlight controller.
config DRM_PANEL_HIMAX_HX83112A
tristate "Himax HX83112A-based DSI panel"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_KMS_HELPER
help
Say Y here if you want to enable support for Himax HX83112A-based
display panels, such as the one found in the Fairphone 4 smartphone.
config DRM_PANEL_HIMAX_HX8394
tristate "HIMAX HX8394 MIPI-DSI LCD panels"
depends on OF

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) += panel-ebbg-ft8719.o
obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o

View File

@ -1871,6 +1871,8 @@ static int boe_panel_add(struct boe_panel *boe)
gpiod_set_value(boe->enable_gpio, 0);
boe->base.prepare_prev_first = true;
drm_panel_init(&boe->base, dev, &boe_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
err = of_drm_get_panel_orientation(dev->of_node, &boe->orientation);

View File

@ -1002,19 +1002,6 @@ static const struct panel_desc auo_b101ean01 = {
},
};
static const struct drm_display_mode auo_b116xa3_mode = {
.clock = 70589,
.hdisplay = 1366,
.hsync_start = 1366 + 40,
.hsync_end = 1366 + 40 + 40,
.htotal = 1366 + 40 + 40 + 32,
.vdisplay = 768,
.vsync_start = 768 + 10,
.vsync_end = 768 + 10 + 12,
.vtotal = 768 + 10 + 12 + 6,
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
};
static const struct drm_display_mode auo_b116xak01_mode = {
.clock = 69300,
.hdisplay = 1366,
@ -1963,12 +1950,10 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x255c, &delay_200_500_e50, "B116XTN02.5"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"),
EDP_PANEL_ENTRY2('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01.0",
&auo_b116xa3_mode),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x435c, &delay_200_500_e50, "Unknown"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x582d, &delay_200_500_e50, "B133UAN01.0"),
EDP_PANEL_ENTRY2('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1",
&auo_b116xa3_mode),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x723c, &delay_200_500_e50, "B140XTN07.2"),

View File

@ -0,0 +1,372 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree.
* Copyright (c) 2024 Luca Weiss <luca.weiss@fairphone.com>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
/* Manufacturer specific DSI commands */
#define HX83112A_SETPOWER1 0xb1
#define HX83112A_SETDISP 0xb2
#define HX83112A_SETDRV 0xb4
#define HX83112A_SETEXTC 0xb9
#define HX83112A_SETBANK 0xbd
#define HX83112A_SETPTBA 0xbf
#define HX83112A_SETDGCLUT 0xc1
#define HX83112A_SETTCON 0xc7
#define HX83112A_SETCLOCK 0xcb
#define HX83112A_SETPANEL 0xcc
#define HX83112A_SETPOWER2 0xd2
#define HX83112A_SETGIP0 0xd3
#define HX83112A_SETGIP1 0xd5
#define HX83112A_SETGIP2 0xd6
#define HX83112A_SETGIP3 0xd8
#define HX83112A_SETTP1 0xe7
#define HX83112A_UNKNOWN1 0xe9
struct hx83112a_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator_bulk_data supplies[3];
struct gpio_desc *reset_gpio;
};
static inline struct hx83112a_panel *to_hx83112a_panel(struct drm_panel *panel)
{
return container_of(panel, struct hx83112a_panel, panel);
}
static void hx83112a_reset(struct hx83112a_panel *ctx)
{
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
msleep(20);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
msleep(20);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
msleep(50);
}
static int hx83112a_on(struct hx83112a_panel *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETEXTC, 0x83, 0x11, 0x2a);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPOWER1,
0x08, 0x28, 0x28, 0x83, 0x83, 0x4c, 0x4f, 0x33);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDISP,
0x00, 0x02, 0x00, 0x90, 0x24, 0x00, 0x08, 0x19,
0xea, 0x11, 0x11, 0x00, 0x11, 0xa3);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDRV,
0x58, 0x68, 0x58, 0x68, 0x0f, 0xef, 0x0b, 0xc0,
0x0b, 0xc0, 0x0b, 0xc0, 0x00, 0xff, 0x00, 0xff,
0x00, 0x00, 0x14, 0x15, 0x00, 0x29, 0x11, 0x07,
0x12, 0x00, 0x29);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDRV,
0x00, 0x12, 0x12, 0x11, 0x88, 0x12, 0x12, 0x00,
0x53);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x03);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT,
0xff, 0xfe, 0xfb, 0xf8, 0xf4, 0xf1, 0xed, 0xe6,
0xe2, 0xde, 0xdb, 0xd6, 0xd3, 0xcf, 0xca, 0xc6,
0xc2, 0xbe, 0xb9, 0xb0, 0xa7, 0x9e, 0x96, 0x8d,
0x84, 0x7c, 0x74, 0x6b, 0x62, 0x5a, 0x51, 0x49,
0x41, 0x39, 0x31, 0x29, 0x21, 0x19, 0x12, 0x0a,
0x06, 0x05, 0x02, 0x01, 0x00, 0x00, 0xc9, 0xb3,
0x08, 0x0e, 0xf2, 0xe1, 0x59, 0xf4, 0x22, 0xad,
0x40);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETDGCLUT, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTCON,
0x70, 0x00, 0x04, 0xe0, 0x33, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPANEL, 0x08);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPOWER2, 0x2b, 0x2b);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP0,
0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08,
0x08, 0x03, 0x03, 0x22, 0x18, 0x07, 0x07, 0x07,
0x07, 0x32, 0x10, 0x06, 0x00, 0x06, 0x32, 0x10,
0x07, 0x00, 0x07, 0x32, 0x19, 0x31, 0x09, 0x31,
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x08,
0x09, 0x30, 0x00, 0x00, 0x00, 0x06, 0x0d, 0x00,
0x0f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP0,
0x00, 0x00, 0x19, 0x10, 0x00, 0x0a, 0x00, 0x81);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP1,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0xc0, 0xc0, 0x18, 0x18, 0x19, 0x19, 0x18, 0x18,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
0x28, 0x28, 0x24, 0x24, 0x02, 0x03, 0x02, 0x03,
0x00, 0x01, 0x00, 0x01, 0x31, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP2,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19,
0x40, 0x40, 0x18, 0x18, 0x18, 0x18, 0x3f, 0x3f,
0x24, 0x24, 0x28, 0x28, 0x01, 0x00, 0x01, 0x00,
0x03, 0x02, 0x03, 0x02, 0x31, 0x31, 0x31, 0x31,
0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa,
0xaa, 0xaa, 0xaa, 0xea, 0xab, 0xaa, 0xaa, 0xaa);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0x2e, 0x28, 0x00, 0x00, 0x00, 0xaa, 0x2e,
0x28, 0x00, 0x00, 0x00, 0xaa, 0xee, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xee, 0xaa, 0xaa, 0xaa, 0xaa);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xff,
0xff, 0xff, 0xff, 0xff);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x03);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETGIP3,
0xaa, 0xaa, 0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xea, 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff,
0xff, 0xff, 0xaa, 0xff, 0xff, 0xff, 0xff, 0xff);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
0x0e, 0x0e, 0x1e, 0x65, 0x1c, 0x65, 0x00, 0x50,
0x20, 0x20, 0x00, 0x00, 0x02, 0x02, 0x02, 0x05,
0x14, 0x14, 0x32, 0xb9, 0x23, 0xb9, 0x08);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x01);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
0x02, 0x00, 0xa8, 0x01, 0xa8, 0x0d, 0xa4, 0x0e);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x02);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETTP1,
0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETBANK, 0x00);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0xc3);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETCLOCK, 0xd1, 0xd6);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0x3f);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0xc6);
mipi_dsi_dcs_write_seq(dsi, HX83112A_SETPTBA, 0x37);
mipi_dsi_dcs_write_seq(dsi, HX83112A_UNKNOWN1, 0x3f);
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
return ret;
}
msleep(150);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display on: %d\n", ret);
return ret;
}
msleep(50);
return 0;
}
static int hx83112a_disable(struct drm_panel *panel)
{
struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display off: %d\n", ret);
return ret;
}
msleep(20);
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
return ret;
}
msleep(120);
return 0;
}
static int hx83112a_prepare(struct drm_panel *panel)
{
struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
hx83112a_reset(ctx);
ret = hx83112a_on(ctx);
if (ret < 0) {
dev_err(dev, "Failed to initialize panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
return ret;
}
return 0;
}
static int hx83112a_unprepare(struct drm_panel *panel)
{
struct hx83112a_panel *ctx = to_hx83112a_panel(panel);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
return 0;
}
static const struct drm_display_mode hx83112a_mode = {
.clock = (1080 + 28 + 8 + 8) * (2340 + 27 + 5 + 5) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 28,
.hsync_end = 1080 + 28 + 8,
.htotal = 1080 + 28 + 8 + 8,
.vdisplay = 2340,
.vsync_start = 2340 + 27,
.vsync_end = 2340 + 27 + 5,
.vtotal = 2340 + 27 + 5 + 5,
.width_mm = 67,
.height_mm = 145,
.type = DRM_MODE_TYPE_DRIVER,
};
static int hx83112a_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
return drm_connector_helper_get_modes_fixed(connector, &hx83112a_mode);
}
static const struct drm_panel_funcs hx83112a_panel_funcs = {
.prepare = hx83112a_prepare,
.unprepare = hx83112a_unprepare,
.disable = hx83112a_disable,
.get_modes = hx83112a_get_modes,
};
static int hx83112a_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct hx83112a_panel *ctx;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->supplies[0].supply = "vdd1";
ctx->supplies[1].supply = "vsn";
ctx->supplies[2].supply = "vsp";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
ctx->supplies);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get regulators\n");
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio))
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset-gpios\n");
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_VIDEO_HSE |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
ctx->panel.prepare_prev_first = true;
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
drm_panel_add(&ctx->panel);
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
static void hx83112a_remove(struct mipi_dsi_device *dsi)
{
struct hx83112a_panel *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
}
static const struct of_device_id hx83112a_of_match[] = {
{ .compatible = "djn,9a-3r063-1102b" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hx83112a_of_match);
static struct mipi_dsi_driver hx83112a_driver = {
.probe = hx83112a_probe,
.remove = hx83112a_remove,
.driver = {
.name = "panel-himax-hx83112a",
.of_match_table = hx83112a_of_match,
},
};
module_mipi_dsi_driver(hx83112a_driver);
MODULE_DESCRIPTION("DRM driver for hx83112a-equipped DSI panels");
MODULE_LICENSE("GPL");

View File

@ -11,6 +11,7 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
@ -21,25 +22,224 @@
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
struct ltk500hd1829_cmd {
char cmd;
char data;
};
struct ltk500hd1829_desc {
const struct drm_display_mode *mode;
const struct ltk500hd1829_cmd *init;
unsigned int num_init;
};
struct ltk500hd1829 {
struct device *dev;
struct drm_panel panel;
struct gpio_desc *reset_gpio;
struct regulator *vcc;
struct regulator *iovcc;
const struct ltk500hd1829_desc *panel_desc;
bool prepared;
};
struct ltk500hd1829_cmd {
char cmd;
char data;
static const struct ltk500hd1829_cmd ltk101b4029w_init[] = {
/* Page0 */
{ 0xE0, 0x00 },
/* PASSWORD */
{ 0xE1, 0x93 },
{ 0xE2, 0x65 },
{ 0xE3, 0xF8 },
{ 0x80, 0x03 }, /* 0X03:4-LANE; 0X02:3-LANE; 0X01:2-LANE */
/* Page1 */
{ 0xE0, 0x01 },
/* Set VCOM */
{ 0x00, 0x00 },
{ 0x01, 0x6F },
/* Set Gamma Power, VGMP,VGMN,VGSP,VGSN */
{ 0x17, 0x00 },
{ 0x18, 0xAF }, /* 4.3V */
{ 0x19, 0x01 }, /* 0.3V */
{ 0x1A, 0x00 },
{ 0x1B, 0xAF }, /* 4.3V */
{ 0x1C, 0x01 }, /* 0.3V */
/* Set Gate Power */
{ 0x1F, 0x3E }, /* VGH_R = 15V */
{ 0x20, 0x28 }, /* VGL_R = -12V */
{ 0x21, 0x28 }, /* VGL_R2 = -12V */
{ 0x22, 0x7E },
/* SETPANEL */
{ 0x35, 0x26 },
{ 0x37, 0x09 },
/* SET RGBCYC */
{ 0x38, 0x04 },
{ 0x39, 0x00 },
{ 0x3A, 0x01 },
{ 0x3C, 0x7C },
{ 0x3D, 0xFF },
{ 0x3E, 0xFF },
{ 0x3F, 0x7F },
/* Set TCON */
{ 0x40, 0x06 }, /* RSO = 800 RGB */
{ 0x41, 0xA0 }, /* LN = 640->1280 line */
{ 0x42, 0x81 },
{ 0x43, 0x08 }, /* VFP = 8 */
{ 0x44, 0x0B }, /* VBP = 12 */
{ 0x45, 0x28 }, /* HBP = 40 */
/* power voltage */
{ 0x55, 0x0F }, /* DCDCM = 0001, JD PWR_IC */
{ 0x57, 0x69 },
{ 0x59, 0x0A }, /* VCL = -2.9V */
{ 0x5A, 0x28 }, /* VGH = 15V */
{ 0x5B, 0x14 }, /* VGL = -11V */
/* Gamma */
{ 0x5D, 0x7C },
{ 0x5E, 0x65 },
{ 0x5F, 0x55 },
{ 0x60, 0x47 },
{ 0x61, 0x43 },
{ 0x62, 0x32 },
{ 0x63, 0x34 },
{ 0x64, 0x1C },
{ 0x65, 0x33 },
{ 0x66, 0x31 },
{ 0x67, 0x30 },
{ 0x68, 0x4E },
{ 0x69, 0x3C },
{ 0x6A, 0x44 },
{ 0x6B, 0x35 },
{ 0x6C, 0x31 },
{ 0x6D, 0x23 },
{ 0x6E, 0x11 },
{ 0x6F, 0x00 },
{ 0x70, 0x7C },
{ 0x71, 0x65 },
{ 0x72, 0x55 },
{ 0x73, 0x47 },
{ 0x74, 0x43 },
{ 0x75, 0x32 },
{ 0x76, 0x34 },
{ 0x77, 0x1C },
{ 0x78, 0x33 },
{ 0x79, 0x31 },
{ 0x7A, 0x30 },
{ 0x7B, 0x4E },
{ 0x7C, 0x3C },
{ 0x7D, 0x44 },
{ 0x7E, 0x35 },
{ 0x7F, 0x31 },
{ 0x80, 0x23 },
{ 0x81, 0x11 },
{ 0x82, 0x00 },
/* Page2, for GIP */
{ 0xE0, 0x02 },
/* GIP_L Pin mapping */
{ 0x00, 0x1E },
{ 0x01, 0x1E },
{ 0x02, 0x41 },
{ 0x03, 0x41 },
{ 0x04, 0x43 },
{ 0x05, 0x43 },
{ 0x06, 0x1F },
{ 0x07, 0x1F },
{ 0x08, 0x35 },
{ 0x09, 0x1F },
{ 0x0A, 0x15 },
{ 0x0B, 0x15 },
{ 0x0C, 0x1F },
{ 0x0D, 0x47 },
{ 0x0E, 0x47 },
{ 0x0F, 0x45 },
{ 0x10, 0x45 },
{ 0x11, 0x4B },
{ 0x12, 0x4B },
{ 0x13, 0x49 },
{ 0x14, 0x49 },
{ 0x15, 0x1F },
/* GIP_R Pin mapping */
{ 0x16, 0x1E },
{ 0x17, 0x1E },
{ 0x18, 0x40 },
{ 0x19, 0x40 },
{ 0x1A, 0x42 },
{ 0x1B, 0x42 },
{ 0x1C, 0x1F },
{ 0x1D, 0x1F },
{ 0x1E, 0x35 },
{ 0x1F, 0x1F },
{ 0x20, 0x15 },
{ 0x21, 0x15 },
{ 0x22, 0x1f },
{ 0x23, 0x46 },
{ 0x24, 0x46 },
{ 0x25, 0x44 },
{ 0x26, 0x44 },
{ 0x27, 0x4A },
{ 0x28, 0x4A },
{ 0x29, 0x48 },
{ 0x2A, 0x48 },
{ 0x2B, 0x1F },
/* GIP Timing */
{ 0x58, 0x40 },
{ 0x5B, 0x30 },
{ 0x5C, 0x03 },
{ 0x5D, 0x30 },
{ 0x5E, 0x01 },
{ 0x5F, 0x02 },
{ 0x63, 0x14 },
{ 0x64, 0x6A },
{ 0x67, 0x73 },
{ 0x68, 0x05 },
{ 0x69, 0x14 },
{ 0x6A, 0x6A },
{ 0x6B, 0x08 },
{ 0x6C, 0x00 },
{ 0x6D, 0x00 },
{ 0x6E, 0x00 },
{ 0x6F, 0x88 },
{ 0x77, 0xDD },
{ 0x79, 0x0E },
{ 0x7A, 0x03 },
{ 0x7D, 0x14 },
{ 0x7E, 0x6A },
/* Page4 */
{ 0xE0, 0x04 },
{ 0x09, 0x11 },
{ 0x0E, 0x48 },
{ 0x2B, 0x2B },
{ 0x2D, 0x03 },
{ 0x2E, 0x44 },
/* Page0 */
{ 0xE0, 0x00 },
{ 0xE6, 0x02 },
{ 0xE7, 0x0C },
};
static const struct drm_display_mode ltk101b4029w_mode = {
.hdisplay = 800,
.hsync_start = 800 + 18,
.hsync_end = 800 + 18 + 18,
.htotal = 800 + 18 + 18 + 18,
.vdisplay = 1280,
.vsync_start = 1280 + 24,
.vsync_end = 1280 + 24 + 4,
.vtotal = 1280 + 24 + 4 + 8,
.clock = 67330,
.width_mm = 136,
.height_mm = 218,
};
static const struct ltk500hd1829_desc ltk101b4029w_data = {
.mode = &ltk101b4029w_mode,
.init = ltk101b4029w_init,
.num_init = ARRAY_SIZE(ltk101b4029w_init),
};
/*
* There is no description in the Reference Manual about these commands.
* We received them from the vendor, so just use them as is.
*/
static const struct ltk500hd1829_cmd init_code[] = {
static const struct ltk500hd1829_cmd ltk500hd1829_init[] = {
{ 0xE0, 0x00 },
{ 0xE1, 0x93 },
{ 0xE2, 0x65 },
@ -260,6 +460,26 @@ static const struct ltk500hd1829_cmd init_code[] = {
{ 0x35, 0x00 },
};
static const struct drm_display_mode ltk500hd1829_mode = {
.hdisplay = 720,
.hsync_start = 720 + 50,
.hsync_end = 720 + 50 + 50,
.htotal = 720 + 50 + 50 + 50,
.vdisplay = 1280,
.vsync_start = 1280 + 30,
.vsync_end = 1280 + 30 + 4,
.vtotal = 1280 + 30 + 4 + 12,
.clock = 69217,
.width_mm = 62,
.height_mm = 110,
};
static const struct ltk500hd1829_desc ltk500hd1829_data = {
.mode = &ltk500hd1829_mode,
.init = ltk500hd1829_init,
.num_init = ARRAY_SIZE(ltk500hd1829_init),
};
static inline
struct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel)
{
@ -324,8 +544,8 @@ static int ltk500hd1829_prepare(struct drm_panel *panel)
/* tRT: >= 5ms */
usleep_range(5000, 6000);
for (i = 0; i < ARRAY_SIZE(init_code); i++) {
ret = mipi_dsi_generic_write(dsi, &init_code[i],
for (i = 0; i < ctx->panel_desc->num_init; i++) {
ret = mipi_dsi_generic_write(dsi, &ctx->panel_desc->init[i],
sizeof(struct ltk500hd1829_cmd));
if (ret < 0) {
dev_err(panel->dev, "failed to write init cmds: %d\n", ret);
@ -359,31 +579,17 @@ disable_vcc:
return ret;
}
static const struct drm_display_mode default_mode = {
.hdisplay = 720,
.hsync_start = 720 + 50,
.hsync_end = 720 + 50 + 50,
.htotal = 720 + 50 + 50 + 50,
.vdisplay = 1280,
.vsync_start = 1280 + 30,
.vsync_end = 1280 + 30 + 4,
.vtotal = 1280 + 30 + 4 + 12,
.clock = 69217,
.width_mm = 62,
.height_mm = 110,
};
static int ltk500hd1829_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel);
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, &default_mode);
mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
if (!mode) {
dev_err(ctx->dev, "failed to add mode %ux%u@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
drm_mode_vrefresh(&default_mode));
ctx->panel_desc->mode->hdisplay, ctx->panel_desc->mode->vdisplay,
drm_mode_vrefresh(ctx->panel_desc->mode));
return -ENOMEM;
}
@ -413,6 +619,10 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi)
if (!ctx)
return -ENOMEM;
ctx->panel_desc = of_device_get_match_data(dev);
if (!ctx->panel_desc)
return -EINVAL;
ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) {
dev_err(dev, "cannot get reset gpio\n");
@ -492,7 +702,14 @@ static void ltk500hd1829_remove(struct mipi_dsi_device *dsi)
}
static const struct of_device_id ltk500hd1829_of_match[] = {
{ .compatible = "leadtek,ltk500hd1829", },
{
.compatible = "leadtek,ltk101b4029w",
.data = &ltk101b4029w_data,
},
{
.compatible = "leadtek,ltk500hd1829",
.data = &ltk500hd1829_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match);

View File

@ -1367,6 +1367,23 @@ static const struct drm_display_mode boe_bp101wx1_100_mode = {
.vtotal = 800 + 6 + 8 + 2,
};
static const struct panel_desc boe_bp082wx1_100 = {
.modes = &boe_bp101wx1_100_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 177,
.height = 110,
},
.delay = {
.enable = 50,
.disable = 50,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
.bus_flags = DRM_BUS_FLAG_DE_HIGH,
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
static const struct panel_desc boe_bp101wx1_100 = {
.modes = &boe_bp101wx1_100_mode,
.num_modes = 1,
@ -4373,6 +4390,9 @@ static const struct of_device_id platform_of_match[] = {
}, {
.compatible = "bananapi,s070wv20-ct16",
.data = &bananapi_s070wv20_ct16,
}, {
.compatible = "boe,bp082wx1-100",
.data = &boe_bp082wx1_100,
}, {
.compatible = "boe,bp101wx1-100",
.data = &boe_bp101wx1_100,

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/gpu/drm/renesas/rcar-du/Kconfig"
source "drivers/gpu/drm/renesas/rz-du/Kconfig"
source "drivers/gpu/drm/renesas/shmobile/Kconfig"

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += rcar-du/
obj-y += rz-du/
obj-$(CONFIG_DRM_SHMOBILE) += shmobile/

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
config DRM_RZG2L_DU
tristate "DRM Support for RZ/G2L Display Unit"
depends on ARCH_RZG2L || COMPILE_TEST
depends on DRM && OF
depends on VIDEO_RENESAS_VSP1
select DRM_GEM_DMA_HELPER
select DRM_KMS_HELPER
select VIDEOMODE_HELPERS
help
Choose this option if you have an RZ/G2L alike chipset.
If M is selected the module will be called rzg2l-du-drm.

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
rzg2l-du-drm-y := rzg2l_du_crtc.o \
rzg2l_du_drv.o \
rzg2l_du_encoder.o \
rzg2l_du_kms.o \
rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1) += rzg2l_du_vsp.o
obj-$(CONFIG_DRM_RZG2L_DU) += rzg2l-du-drm.o

View File

@ -0,0 +1,422 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RZ/G2L Display Unit CRTCs
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_crtc.c
*/
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_vblank.h>
#include "rzg2l_du_crtc.h"
#include "rzg2l_du_drv.h"
#include "rzg2l_du_encoder.h"
#include "rzg2l_du_kms.h"
#include "rzg2l_du_vsp.h"
#define DU_MCR0 0x00
#define DU_MCR0_DI_EN BIT(8)
#define DU_DITR0 0x10
#define DU_DITR0_DEMD_HIGH (BIT(8) | BIT(9))
#define DU_DITR0_VSPOL BIT(16)
#define DU_DITR0_HSPOL BIT(17)
#define DU_DITR1 0x14
#define DU_DITR1_VSA(x) ((x) << 0)
#define DU_DITR1_VACTIVE(x) ((x) << 16)
#define DU_DITR2 0x18
#define DU_DITR2_VBP(x) ((x) << 0)
#define DU_DITR2_VFP(x) ((x) << 16)
#define DU_DITR3 0x1c
#define DU_DITR3_HSA(x) ((x) << 0)
#define DU_DITR3_HACTIVE(x) ((x) << 16)
#define DU_DITR4 0x20
#define DU_DITR4_HBP(x) ((x) << 0)
#define DU_DITR4_HFP(x) ((x) << 16)
#define DU_MCR1 0x40
#define DU_MCR1_PB_AUTOCLR BIT(16)
#define DU_PBCR0 0x4c
#define DU_PBCR0_PB_DEP(x) ((x) << 0)
/* -----------------------------------------------------------------------------
* Hardware Setup
*/
static void rzg2l_du_crtc_set_display_timing(struct rzg2l_du_crtc *rcrtc)
{
const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
unsigned long mode_clock = mode->clock * 1000;
u32 ditr0, ditr1, ditr2, ditr3, ditr4, pbcr0;
struct rzg2l_du_device *rcdu = rcrtc->dev;
clk_prepare_enable(rcrtc->rzg2l_clocks.dclk);
clk_set_rate(rcrtc->rzg2l_clocks.dclk, mode_clock);
ditr0 = (DU_DITR0_DEMD_HIGH
| ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DU_DITR0_VSPOL : 0)
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DU_DITR0_HSPOL : 0));
ditr1 = DU_DITR1_VSA(mode->vsync_end - mode->vsync_start)
| DU_DITR1_VACTIVE(mode->vdisplay);
ditr2 = DU_DITR2_VBP(mode->vtotal - mode->vsync_end)
| DU_DITR2_VFP(mode->vsync_start - mode->vdisplay);
ditr3 = DU_DITR3_HSA(mode->hsync_end - mode->hsync_start)
| DU_DITR3_HACTIVE(mode->hdisplay);
ditr4 = DU_DITR4_HBP(mode->htotal - mode->hsync_end)
| DU_DITR4_HFP(mode->hsync_start - mode->hdisplay);
pbcr0 = DU_PBCR0_PB_DEP(0x1f);
writel(ditr0, rcdu->mmio + DU_DITR0);
writel(ditr1, rcdu->mmio + DU_DITR1);
writel(ditr2, rcdu->mmio + DU_DITR2);
writel(ditr3, rcdu->mmio + DU_DITR3);
writel(ditr4, rcdu->mmio + DU_DITR4);
writel(pbcr0, rcdu->mmio + DU_PBCR0);
/* Enable auto clear */
writel(DU_MCR1_PB_AUTOCLR, rcdu->mmio + DU_MCR1);
}
/* -----------------------------------------------------------------------------
* Page Flip
*/
void rzg2l_du_crtc_finish_page_flip(struct rzg2l_du_crtc *rcrtc)
{
struct drm_pending_vblank_event *event;
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
event = rcrtc->event;
rcrtc->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
if (!event)
return;
spin_lock_irqsave(&dev->event_lock, flags);
drm_crtc_send_vblank_event(&rcrtc->crtc, event);
wake_up(&rcrtc->flip_wait);
spin_unlock_irqrestore(&dev->event_lock, flags);
drm_crtc_vblank_put(&rcrtc->crtc);
}
static bool rzg2l_du_crtc_page_flip_pending(struct rzg2l_du_crtc *rcrtc)
{
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
bool pending;
spin_lock_irqsave(&dev->event_lock, flags);
pending = rcrtc->event;
spin_unlock_irqrestore(&dev->event_lock, flags);
return pending;
}
static void rzg2l_du_crtc_wait_page_flip(struct rzg2l_du_crtc *rcrtc)
{
struct rzg2l_du_device *rcdu = rcrtc->dev;
if (wait_event_timeout(rcrtc->flip_wait,
!rzg2l_du_crtc_page_flip_pending(rcrtc),
msecs_to_jiffies(50)))
return;
dev_warn(rcdu->dev, "page flip timeout\n");
rzg2l_du_crtc_finish_page_flip(rcrtc);
}
/* -----------------------------------------------------------------------------
* Start/Stop and Suspend/Resume
*/
static void rzg2l_du_crtc_setup(struct rzg2l_du_crtc *rcrtc)
{
/* Configure display timings and output routing */
rzg2l_du_crtc_set_display_timing(rcrtc);
/* Enable the VSP compositor. */
rzg2l_du_vsp_enable(rcrtc);
/* Turn vertical blanking interrupt reporting on. */
drm_crtc_vblank_on(&rcrtc->crtc);
}
static int rzg2l_du_crtc_get(struct rzg2l_du_crtc *rcrtc)
{
int ret;
/*
* Guard against double-get, as the function is called from both the
* .atomic_enable() and .atomic_flush() handlers.
*/
if (rcrtc->initialized)
return 0;
ret = clk_prepare_enable(rcrtc->rzg2l_clocks.aclk);
if (ret < 0)
return ret;
ret = clk_prepare_enable(rcrtc->rzg2l_clocks.pclk);
if (ret < 0)
goto error_bus_clock;
ret = reset_control_deassert(rcrtc->rstc);
if (ret < 0)
goto error_peri_clock;
rzg2l_du_crtc_setup(rcrtc);
rcrtc->initialized = true;
return 0;
error_peri_clock:
clk_disable_unprepare(rcrtc->rzg2l_clocks.pclk);
error_bus_clock:
clk_disable_unprepare(rcrtc->rzg2l_clocks.aclk);
return ret;
}
static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc)
{
clk_disable_unprepare(rcrtc->rzg2l_clocks.dclk);
reset_control_assert(rcrtc->rstc);
clk_disable_unprepare(rcrtc->rzg2l_clocks.pclk);
clk_disable_unprepare(rcrtc->rzg2l_clocks.aclk);
rcrtc->initialized = false;
}
static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start)
{
struct rzg2l_du_device *rcdu = rcrtc->dev;
writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0);
}
static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc)
{
rzg2l_du_start_stop(rcrtc, true);
}
static void rzg2l_du_crtc_stop(struct rzg2l_du_crtc *rcrtc)
{
struct drm_crtc *crtc = &rcrtc->crtc;
/*
* Disable vertical blanking interrupt reporting. We first need to wait
* for page flip completion before stopping the CRTC as userspace
* expects page flips to eventually complete.
*/
rzg2l_du_crtc_wait_page_flip(rcrtc);
drm_crtc_vblank_off(crtc);
/* Disable the VSP compositor. */
rzg2l_du_vsp_disable(rcrtc);
rzg2l_du_start_stop(rcrtc, false);
}
/* -----------------------------------------------------------------------------
* CRTC Functions
*/
static void rzg2l_du_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);
rzg2l_du_crtc_get(rcrtc);
rzg2l_du_crtc_start(rcrtc);
}
static void rzg2l_du_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);
rzg2l_du_crtc_stop(rcrtc);
rzg2l_du_crtc_put(rcrtc);
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
drm_crtc_send_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);
}
static void rzg2l_du_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);
struct drm_device *dev = rcrtc->crtc.dev;
unsigned long flags;
WARN_ON(!crtc->state->enable);
if (crtc->state->event) {
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irqsave(&dev->event_lock, flags);
rcrtc->event = crtc->state->event;
crtc->state->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
}
rzg2l_du_vsp_atomic_flush(rcrtc);
}
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
.atomic_flush = rzg2l_du_crtc_atomic_flush,
.atomic_enable = rzg2l_du_crtc_atomic_enable,
.atomic_disable = rzg2l_du_crtc_atomic_disable,
};
static struct drm_crtc_state *
rzg2l_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
struct rzg2l_du_crtc_state *state;
struct rzg2l_du_crtc_state *copy;
if (WARN_ON(!crtc->state))
return NULL;
state = to_rzg2l_crtc_state(crtc->state);
copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
if (!copy)
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->state);
return &copy->state;
}
static void rzg2l_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
__drm_atomic_helper_crtc_destroy_state(state);
kfree(to_rzg2l_crtc_state(state));
}
static void rzg2l_du_crtc_reset(struct drm_crtc *crtc)
{
struct rzg2l_du_crtc_state *state;
if (crtc->state) {
rzg2l_du_crtc_atomic_destroy_state(crtc, crtc->state);
crtc->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return;
__drm_atomic_helper_crtc_reset(crtc, &state->state);
}
static int rzg2l_du_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);
rcrtc->vblank_enable = true;
return 0;
}
static void rzg2l_du_crtc_disable_vblank(struct drm_crtc *crtc)
{
struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc);
rcrtc->vblank_enable = false;
}
static const struct drm_crtc_funcs crtc_funcs_rz = {
.reset = rzg2l_du_crtc_reset,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = rzg2l_du_crtc_atomic_duplicate_state,
.atomic_destroy_state = rzg2l_du_crtc_atomic_destroy_state,
.enable_vblank = rzg2l_du_crtc_enable_vblank,
.disable_vblank = rzg2l_du_crtc_disable_vblank,
};
/* -----------------------------------------------------------------------------
* Initialization
*/
int rzg2l_du_crtc_create(struct rzg2l_du_device *rcdu)
{
struct rzg2l_du_crtc *rcrtc = &rcdu->crtcs[0];
struct drm_crtc *crtc = &rcrtc->crtc;
struct drm_plane *primary;
int ret;
rcrtc->rstc = devm_reset_control_get_shared(rcdu->dev, NULL);
if (IS_ERR(rcrtc->rstc)) {
dev_err(rcdu->dev, "can't get cpg reset\n");
return PTR_ERR(rcrtc->rstc);
}
rcrtc->rzg2l_clocks.aclk = devm_clk_get(rcdu->dev, "aclk");
if (IS_ERR(rcrtc->rzg2l_clocks.aclk)) {
dev_err(rcdu->dev, "no axi clock for DU\n");
return PTR_ERR(rcrtc->rzg2l_clocks.aclk);
}
rcrtc->rzg2l_clocks.pclk = devm_clk_get(rcdu->dev, "pclk");
if (IS_ERR(rcrtc->rzg2l_clocks.pclk)) {
dev_err(rcdu->dev, "no peripheral clock for DU\n");
return PTR_ERR(rcrtc->rzg2l_clocks.pclk);
}
rcrtc->rzg2l_clocks.dclk = devm_clk_get(rcdu->dev, "vclk");
if (IS_ERR(rcrtc->rzg2l_clocks.dclk)) {
dev_err(rcdu->dev, "no video clock for DU\n");
return PTR_ERR(rcrtc->rzg2l_clocks.dclk);
}
init_waitqueue_head(&rcrtc->flip_wait);
rcrtc->dev = rcdu;
primary = rzg2l_du_vsp_get_drm_plane(rcrtc, rcrtc->vsp_pipe);
if (IS_ERR(primary))
return PTR_ERR(primary);
ret = drmm_crtc_init_with_planes(&rcdu->ddev, crtc, primary, NULL,
&crtc_funcs_rz, NULL);
if (ret < 0)
return ret;
drm_crtc_helper_add(crtc, &crtc_helper_funcs);
return 0;
}

View File

@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RZ/G2L Display Unit CRTCs
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_crtc.h
*/
#ifndef __RZG2L_DU_CRTC_H__
#define __RZG2L_DU_CRTC_H__
#include <linux/container_of.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <drm/drm_crtc.h>
#include <drm/drm_writeback.h>
#include <media/vsp1.h>
struct clk;
struct reset_control;
struct rzg2l_du_vsp;
struct rzg2l_du_format_info;
/**
* struct rzg2l_du_crtc - the CRTC, representing a DU superposition processor
* @crtc: base DRM CRTC
* @dev: the DU device
* @initialized: whether the CRTC has been initialized and clocks enabled
* @vblank_enable: whether vblank events are enabled on this CRTC
* @event: event to post when the pending page flip completes
* @flip_wait: wait queue used to signal page flip completion
* @vsp: VSP feeding video to this CRTC
* @vsp_pipe: index of the VSP pipeline feeding video to this CRTC
* @rstc: reset controller
* @rzg2l_clocks: the bus, main and video clock
*/
struct rzg2l_du_crtc {
struct drm_crtc crtc;
struct rzg2l_du_device *dev;
bool initialized;
bool vblank_enable;
struct drm_pending_vblank_event *event;
wait_queue_head_t flip_wait;
struct rzg2l_du_vsp *vsp;
unsigned int vsp_pipe;
const char *const *sources;
unsigned int sources_count;
struct reset_control *rstc;
struct {
struct clk *aclk;
struct clk *pclk;
struct clk *dclk;
} rzg2l_clocks;
};
static inline struct rzg2l_du_crtc *to_rzg2l_crtc(struct drm_crtc *c)
{
return container_of(c, struct rzg2l_du_crtc, crtc);
}
/**
* struct rzg2l_du_crtc_state - Driver-specific CRTC state
* @state: base DRM CRTC state
* @outputs: bitmask of the outputs (enum rzg2l_du_output) driven by this CRTC
*/
struct rzg2l_du_crtc_state {
struct drm_crtc_state state;
unsigned int outputs;
};
static inline struct rzg2l_du_crtc_state *to_rzg2l_crtc_state(struct drm_crtc_state *s)
{
return container_of(s, struct rzg2l_du_crtc_state, state);
}
int rzg2l_du_crtc_create(struct rzg2l_du_device *rcdu);
void rzg2l_du_crtc_finish_page_flip(struct rzg2l_du_crtc *rcrtc);
#endif /* __RZG2L_DU_CRTC_H__ */

View File

@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RZ/G2L Display Unit DRM driver
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_drv.c
*/
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_probe_helper.h>
#include "rzg2l_du_drv.h"
#include "rzg2l_du_kms.h"
/* -----------------------------------------------------------------------------
* Device Information
*/
static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
.channels_mask = BIT(0),
.routes = {
[RZG2L_DU_OUTPUT_DSI0] = {
.possible_outputs = BIT(0),
.port = 0,
},
[RZG2L_DU_OUTPUT_DPAD0] = {
.possible_outputs = BIT(0),
.port = 1,
}
}
};
static const struct of_device_id rzg2l_du_of_table[] = {
{ .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg2l_du_of_table);
const char *rzg2l_du_output_name(enum rzg2l_du_output output)
{
static const char * const names[] = {
[RZG2L_DU_OUTPUT_DSI0] = "DSI0",
[RZG2L_DU_OUTPUT_DPAD0] = "DPAD0"
};
if (output >= ARRAY_SIZE(names))
return "UNKNOWN";
return names[output];
}
/* -----------------------------------------------------------------------------
* DRM operations
*/
DEFINE_DRM_GEM_DMA_FOPS(rzg2l_du_fops);
static const struct drm_driver rzg2l_du_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.dumb_create = rzg2l_du_dumb_create,
.fops = &rzg2l_du_fops,
.name = "rzg2l-du",
.desc = "Renesas RZ/G2L Display Unit",
.date = "20230410",
.major = 1,
.minor = 0,
};
/* -----------------------------------------------------------------------------
* Platform driver
*/
static void rzg2l_du_remove(struct platform_device *pdev)
{
struct rzg2l_du_device *rcdu = platform_get_drvdata(pdev);
struct drm_device *ddev = &rcdu->ddev;
drm_dev_unregister(ddev);
drm_atomic_helper_shutdown(ddev);
drm_kms_helper_poll_fini(ddev);
}
static void rzg2l_du_shutdown(struct platform_device *pdev)
{
struct rzg2l_du_device *rcdu = platform_get_drvdata(pdev);
drm_atomic_helper_shutdown(&rcdu->ddev);
}
static int rzg2l_du_probe(struct platform_device *pdev)
{
struct rzg2l_du_device *rcdu;
int ret;
if (drm_firmware_drivers_only())
return -ENODEV;
/* Allocate and initialize the RZ/G2L device structure. */
rcdu = devm_drm_dev_alloc(&pdev->dev, &rzg2l_du_driver,
struct rzg2l_du_device, ddev);
if (IS_ERR(rcdu))
return PTR_ERR(rcdu);
rcdu->dev = &pdev->dev;
rcdu->info = of_device_get_match_data(rcdu->dev);
platform_set_drvdata(pdev, rcdu);
/* I/O resources */
rcdu->mmio = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio);
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
/* DRM/KMS objects */
ret = rzg2l_du_modeset_init(rcdu);
if (ret < 0) {
/*
* Don't use dev_err_probe(), as it would overwrite the probe
* deferral reason recorded in rzg2l_du_modeset_init().
*/
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to initialize DRM/KMS (%d)\n", ret);
goto error;
}
/*
* Register the DRM device with the core and the connectors with
* sysfs.
*/
ret = drm_dev_register(&rcdu->ddev, 0);
if (ret)
goto error;
drm_info(&rcdu->ddev, "Device %s probed\n", dev_name(&pdev->dev));
drm_fbdev_generic_setup(&rcdu->ddev, 32);
return 0;
error:
drm_kms_helper_poll_fini(&rcdu->ddev);
return ret;
}
static struct platform_driver rzg2l_du_platform_driver = {
.probe = rzg2l_du_probe,
.remove_new = rzg2l_du_remove,
.shutdown = rzg2l_du_shutdown,
.driver = {
.name = "rzg2l-du",
.of_match_table = rzg2l_du_of_table,
},
};
module_platform_driver(rzg2l_du_platform_driver);
MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
MODULE_DESCRIPTION("Renesas RZ/G2L Display Unit DRM Driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RZ/G2L Display Unit DRM driver
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_drv.h
*/
#ifndef __RZG2L_DU_DRV_H__
#define __RZG2L_DU_DRV_H__
#include <linux/kernel.h>
#include <drm/drm_device.h>
#include "rzg2l_du_crtc.h"
#include "rzg2l_du_vsp.h"
struct device;
struct drm_property;
enum rzg2l_du_output {
RZG2L_DU_OUTPUT_DSI0,
RZG2L_DU_OUTPUT_DPAD0,
RZG2L_DU_OUTPUT_MAX,
};
/*
* struct rzg2l_du_output_routing - Output routing specification
* @possible_outputs: bitmask of possible outputs
* @port: device tree port number corresponding to this output route
*
* The DU has 2 possible outputs (DPAD0, DSI0). Output routing data
* specify the valid SoC outputs, which CRTC can drive the output, and the type
* of in-SoC encoder for the output.
*/
struct rzg2l_du_output_routing {
unsigned int possible_outputs;
unsigned int port;
};
/*
* struct rzg2l_du_device_info - DU model-specific information
* @channels_mask: bit mask of available DU channels
* @routes: array of CRTC to output routes, indexed by output (RZG2L_DU_OUTPUT_*)
*/
struct rzg2l_du_device_info {
unsigned int channels_mask;
struct rzg2l_du_output_routing routes[RZG2L_DU_OUTPUT_MAX];
};
#define RZG2L_DU_MAX_CRTCS 1
#define RZG2L_DU_MAX_VSPS 1
#define RZG2L_DU_MAX_DSI 1
struct rzg2l_du_device {
struct device *dev;
const struct rzg2l_du_device_info *info;
void __iomem *mmio;
struct drm_device ddev;
struct rzg2l_du_crtc crtcs[RZG2L_DU_MAX_CRTCS];
unsigned int num_crtcs;
struct rzg2l_du_vsp vsps[RZG2L_DU_MAX_VSPS];
};
static inline struct rzg2l_du_device *to_rzg2l_du_device(struct drm_device *dev)
{
return container_of(dev, struct rzg2l_du_device, ddev);
}
const char *rzg2l_du_output_name(enum rzg2l_du_output output);
#endif /* __RZG2L_DU_DRV_H__ */

View File

@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RZ/G2L Display Unit Encoder
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_encoder.c
*/
#include <linux/export.h>
#include <linux/of.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_panel.h>
#include "rzg2l_du_drv.h"
#include "rzg2l_du_encoder.h"
/* -----------------------------------------------------------------------------
* Encoder
*/
static const struct drm_encoder_funcs rzg2l_du_encoder_funcs = {
};
int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
enum rzg2l_du_output output,
struct device_node *enc_node)
{
struct rzg2l_du_encoder *renc;
struct drm_connector *connector;
struct drm_bridge *bridge;
int ret;
/* Locate the DRM bridge from the DT node. */
bridge = of_drm_find_bridge(enc_node);
if (!bridge)
return -EPROBE_DEFER;
dev_dbg(rcdu->dev, "initializing encoder %pOF for output %s\n",
enc_node, rzg2l_du_output_name(output));
renc = drmm_encoder_alloc(&rcdu->ddev, struct rzg2l_du_encoder, base,
&rzg2l_du_encoder_funcs, DRM_MODE_ENCODER_NONE,
NULL);
if (IS_ERR(renc))
return PTR_ERR(renc);
renc->output = output;
/* Attach the bridge to the encoder. */
ret = drm_bridge_attach(&renc->base, bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
dev_err(rcdu->dev,
"failed to attach bridge %pOF for output %s (%d)\n",
bridge->of_node, rzg2l_du_output_name(output), ret);
return ret;
}
/* Create the connector for the chain of bridges. */
connector = drm_bridge_connector_init(&rcdu->ddev, &renc->base);
if (IS_ERR(connector)) {
dev_err(rcdu->dev,
"failed to created connector for output %s (%ld)\n",
rzg2l_du_output_name(output), PTR_ERR(connector));
return PTR_ERR(connector);
}
return drm_connector_attach_encoder(connector, &renc->base);
}

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RZ/G2L Display Unit Encoder
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_encoder.h
*/
#ifndef __RZG2L_DU_ENCODER_H__
#define __RZG2L_DU_ENCODER_H__
#include <drm/drm_encoder.h>
#include <linux/container_of.h>
struct rzg2l_du_device;
struct rzg2l_du_encoder {
struct drm_encoder base;
enum rzg2l_du_output output;
};
static inline struct rzg2l_du_encoder *to_rzg2l_encoder(struct drm_encoder *e)
{
return container_of(e, struct rzg2l_du_encoder, base);
}
int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
enum rzg2l_du_output output,
struct device_node *enc_node);
#endif /* __RZG2L_DU_ENCODER_H__ */

View File

@ -0,0 +1,371 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RZ/G2L Display Unit Mode Setting
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_kms.c
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include "rzg2l_du_crtc.h"
#include "rzg2l_du_drv.h"
#include "rzg2l_du_encoder.h"
#include "rzg2l_du_kms.h"
#include "rzg2l_du_vsp.h"
/* -----------------------------------------------------------------------------
* Format helpers
*/
static const struct rzg2l_du_format_info rzg2l_du_format_infos[] = {
{
.fourcc = DRM_FORMAT_XRGB8888,
.v4l2 = V4L2_PIX_FMT_XBGR32,
.bpp = 32,
.planes = 1,
.hsub = 1,
}, {
.fourcc = DRM_FORMAT_ARGB8888,
.v4l2 = V4L2_PIX_FMT_ABGR32,
.bpp = 32,
.planes = 1,
.hsub = 1,
}, {
.fourcc = DRM_FORMAT_RGB888,
.v4l2 = V4L2_PIX_FMT_BGR24,
.bpp = 24,
.planes = 1,
.hsub = 1,
}
};
const struct rzg2l_du_format_info *rzg2l_du_format_info(u32 fourcc)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rzg2l_du_format_infos); ++i) {
if (rzg2l_du_format_infos[i].fourcc == fourcc)
return &rzg2l_du_format_infos[i];
}
return NULL;
}
/* -----------------------------------------------------------------------------
* Frame buffer
*/
int rzg2l_du_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
unsigned int align = 16 * args->bpp / 8;
args->pitch = roundup(min_pitch, align);
return drm_gem_dma_dumb_create_internal(file, dev, args);
}
static struct drm_framebuffer *
rzg2l_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
const struct rzg2l_du_format_info *format;
unsigned int max_pitch;
format = rzg2l_du_format_info(mode_cmd->pixel_format);
if (!format) {
dev_dbg(dev->dev, "unsupported pixel format %p4cc\n",
&mode_cmd->pixel_format);
return ERR_PTR(-EINVAL);
}
/*
* On RZ/G2L the memory interface is handled by the VSP that limits the
* pitch to 65535 bytes.
*/
max_pitch = 65535;
if (mode_cmd->pitches[0] > max_pitch) {
dev_dbg(dev->dev, "invalid pitch value %u\n",
mode_cmd->pitches[0]);
return ERR_PTR(-EINVAL);
}
return drm_gem_fb_create(dev, file_priv, mode_cmd);
}
/* -----------------------------------------------------------------------------
* Initialization
*/
static const struct drm_mode_config_helper_funcs rzg2l_du_mode_config_helper = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
static const struct drm_mode_config_funcs rzg2l_du_mode_config_funcs = {
.fb_create = rzg2l_du_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static int rzg2l_du_encoders_init_one(struct rzg2l_du_device *rcdu,
enum rzg2l_du_output output,
struct of_endpoint *ep)
{
struct device_node *entity;
int ret;
/* Locate the connected entity and initialize the encoder. */
entity = of_graph_get_remote_port_parent(ep->local_node);
if (!entity) {
dev_dbg(rcdu->dev, "unconnected endpoint %pOF, skipping\n",
ep->local_node);
return -ENODEV;
}
if (!of_device_is_available(entity)) {
dev_dbg(rcdu->dev,
"connected entity %pOF is disabled, skipping\n",
entity);
of_node_put(entity);
return -ENODEV;
}
ret = rzg2l_du_encoder_init(rcdu, output, entity);
if (ret && ret != -EPROBE_DEFER && ret != -ENOLINK)
dev_warn(rcdu->dev,
"failed to initialize encoder %pOF on output %s (%d), skipping\n",
entity, rzg2l_du_output_name(output), ret);
of_node_put(entity);
return ret;
}
static int rzg2l_du_encoders_init(struct rzg2l_du_device *rcdu)
{
struct device_node *np = rcdu->dev->of_node;
struct device_node *ep_node;
unsigned int num_encoders = 0;
/*
* Iterate over the endpoints and create one encoder for each output
* pipeline.
*/
for_each_endpoint_of_node(np, ep_node) {
enum rzg2l_du_output output;
struct of_endpoint ep;
unsigned int i;
int ret;
ret = of_graph_parse_endpoint(ep_node, &ep);
if (ret < 0) {
of_node_put(ep_node);
return ret;
}
/* Find the output route corresponding to the port number. */
for (i = 0; i < RZG2L_DU_OUTPUT_MAX; ++i) {
if (rcdu->info->routes[i].port == ep.port) {
output = i;
break;
}
}
if (i == RZG2L_DU_OUTPUT_MAX) {
dev_warn(rcdu->dev,
"port %u references unexisting output, skipping\n",
ep.port);
continue;
}
/* Process the output pipeline. */
ret = rzg2l_du_encoders_init_one(rcdu, output, &ep);
if (ret < 0) {
if (ret == -EPROBE_DEFER) {
of_node_put(ep_node);
return ret;
}
continue;
}
num_encoders++;
}
return num_encoders;
}
static int rzg2l_du_vsps_init(struct rzg2l_du_device *rcdu)
{
const struct device_node *np = rcdu->dev->of_node;
const char *vsps_prop_name = "renesas,vsps";
struct of_phandle_args args;
struct {
struct device_node *np;
unsigned int crtcs_mask;
} vsps[RZG2L_DU_MAX_VSPS] = { { NULL, }, };
unsigned int vsps_count = 0;
unsigned int cells;
unsigned int i;
int ret;
/*
* First parse the DT vsps property to populate the list of VSPs. Each
* entry contains a pointer to the VSP DT node and a bitmask of the
* connected DU CRTCs.
*/
ret = of_property_count_u32_elems(np, vsps_prop_name);
cells = ret / rcdu->num_crtcs - 1;
if (cells != 1)
return -EINVAL;
for (i = 0; i < rcdu->num_crtcs; ++i) {
unsigned int j;
ret = of_parse_phandle_with_fixed_args(np, vsps_prop_name,
cells, i, &args);
if (ret < 0)
goto done;
/*
* Add the VSP to the list or update the corresponding existing
* entry if the VSP has already been added.
*/
for (j = 0; j < vsps_count; ++j) {
if (vsps[j].np == args.np)
break;
}
if (j < vsps_count)
of_node_put(args.np);
else
vsps[vsps_count++].np = args.np;
vsps[j].crtcs_mask |= BIT(i);
/*
* Store the VSP pointer and pipe index in the CRTC. If the
* second cell of the 'renesas,vsps' specifier isn't present,
* default to 0.
*/
rcdu->crtcs[i].vsp = &rcdu->vsps[j];
rcdu->crtcs[i].vsp_pipe = cells >= 1 ? args.args[0] : 0;
}
/*
* Then initialize all the VSPs from the node pointers and CRTCs bitmask
* computed previously.
*/
for (i = 0; i < vsps_count; ++i) {
struct rzg2l_du_vsp *vsp = &rcdu->vsps[i];
vsp->index = i;
vsp->dev = rcdu;
ret = rzg2l_du_vsp_init(vsp, vsps[i].np, vsps[i].crtcs_mask);
if (ret)
goto done;
}
done:
for (i = 0; i < ARRAY_SIZE(vsps); ++i)
of_node_put(vsps[i].np);
return ret;
}
int rzg2l_du_modeset_init(struct rzg2l_du_device *rcdu)
{
struct drm_device *dev = &rcdu->ddev;
struct drm_encoder *encoder;
unsigned int num_encoders;
int ret;
ret = drmm_mode_config_init(dev);
if (ret)
return ret;
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.normalize_zpos = true;
dev->mode_config.funcs = &rzg2l_du_mode_config_funcs;
dev->mode_config.helper_private = &rzg2l_du_mode_config_helper;
/*
* The RZ DU uses the VSP1 for memory access, and is limited
* to frame sizes of 1920x1080.
*/
dev->mode_config.max_width = 1920;
dev->mode_config.max_height = 1080;
rcdu->num_crtcs = hweight8(rcdu->info->channels_mask);
/*
* Initialize vertical blanking interrupts handling. Start with vblank
* disabled for all CRTCs.
*/
ret = drm_vblank_init(dev, rcdu->num_crtcs);
if (ret < 0)
return ret;
/* Initialize the compositors. */
ret = rzg2l_du_vsps_init(rcdu);
if (ret < 0)
return ret;
/* Create the CRTCs. */
ret = rzg2l_du_crtc_create(rcdu);
if (ret < 0)
return ret;
/* Initialize the encoders. */
ret = rzg2l_du_encoders_init(rcdu);
if (ret < 0)
return dev_err_probe(rcdu->dev, ret,
"failed to initialize encoders\n");
if (ret == 0) {
dev_err(rcdu->dev, "error: no encoder could be initialized\n");
return -EINVAL;
}
num_encoders = ret;
/*
* Set the possible CRTCs and possible clones. There's always at least
* one way for all encoders to clone each other, set all bits in the
* possible clones field.
*/
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct rzg2l_du_encoder *renc = to_rzg2l_encoder(encoder);
const struct rzg2l_du_output_routing *route =
&rcdu->info->routes[renc->output];
encoder->possible_crtcs = route->possible_outputs;
encoder->possible_clones = (1 << num_encoders) - 1;
}
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return 0;
}

View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RZ/G2L Display Unit Mode Setting
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_kms.h
*/
#ifndef __RZG2L_DU_KMS_H__
#define __RZG2L_DU_KMS_H__
#include <linux/types.h>
struct dma_buf_attachment;
struct drm_file;
struct drm_device;
struct drm_gem_object;
struct drm_mode_create_dumb;
struct rzg2l_du_device;
struct sg_table;
struct rzg2l_du_format_info {
u32 fourcc;
u32 v4l2;
unsigned int bpp;
unsigned int planes;
unsigned int hsub;
};
const struct rzg2l_du_format_info *rzg2l_du_format_info(u32 fourcc);
int rzg2l_du_modeset_init(struct rzg2l_du_device *rcdu);
int rzg2l_du_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args);
struct drm_gem_object *
rzg2l_du_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach,
struct sg_table *sgt);
#endif /* __RZG2L_DU_KMS_H__ */

View File

@ -0,0 +1,349 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* RZ/G2L Display Unit VSP-Based Compositor
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_vsp.c
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_vblank.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <media/vsp1.h>
#include "rzg2l_du_drv.h"
#include "rzg2l_du_kms.h"
#include "rzg2l_du_vsp.h"
static void rzg2l_du_vsp_complete(void *private, unsigned int status, u32 crc)
{
struct rzg2l_du_crtc *crtc = private;
if (crtc->vblank_enable)
drm_crtc_handle_vblank(&crtc->crtc);
if (status & VSP1_DU_STATUS_COMPLETE)
rzg2l_du_crtc_finish_page_flip(crtc);
drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
}
void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc)
{
const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
struct vsp1_du_lif_config cfg = {
.width = mode->hdisplay,
.height = mode->vdisplay,
.interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
.callback = rzg2l_du_vsp_complete,
.callback_data = crtc,
};
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc)
{
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
}
void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc)
{
struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
struct rzg2l_du_crtc_state *state;
state = to_rzg2l_crtc_state(crtc->crtc.state);
vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
struct drm_plane *rzg2l_du_vsp_get_drm_plane(struct rzg2l_du_crtc *crtc,
unsigned int pipe_index)
{
struct rzg2l_du_device *rcdu = crtc->vsp->dev;
struct drm_plane *plane = NULL;
drm_for_each_plane(plane, &rcdu->ddev) {
struct rzg2l_du_vsp_plane *vsp_plane = to_rzg2l_vsp_plane(plane);
if (vsp_plane->index == pipe_index)
break;
}
return plane ? plane : ERR_PTR(-EINVAL);
}
static const u32 rzg2l_du_vsp_formats[] = {
DRM_FORMAT_RGB332,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_XRGB4444,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
DRM_FORMAT_YVYU,
DRM_FORMAT_NV12,
DRM_FORMAT_NV21,
DRM_FORMAT_NV16,
DRM_FORMAT_NV61,
DRM_FORMAT_YUV420,
DRM_FORMAT_YVU420,
DRM_FORMAT_YUV422,
DRM_FORMAT_YVU422,
DRM_FORMAT_YUV444,
DRM_FORMAT_YVU444,
};
static void rzg2l_du_vsp_plane_setup(struct rzg2l_du_vsp_plane *plane)
{
struct rzg2l_du_vsp_plane_state *state =
to_rzg2l_vsp_plane_state(plane->plane.state);
struct rzg2l_du_crtc *crtc = to_rzg2l_crtc(state->state.crtc);
struct drm_framebuffer *fb = plane->plane.state->fb;
const struct rzg2l_du_format_info *format;
struct vsp1_du_atomic_config cfg = {
.pixelformat = 0,
.pitch = fb->pitches[0],
.alpha = state->state.alpha >> 8,
.zpos = state->state.zpos,
};
u32 fourcc = state->format->fourcc;
unsigned int i;
cfg.src.left = state->state.src.x1 >> 16;
cfg.src.top = state->state.src.y1 >> 16;
cfg.src.width = drm_rect_width(&state->state.src) >> 16;
cfg.src.height = drm_rect_height(&state->state.src) >> 16;
cfg.dst.left = state->state.dst.x1;
cfg.dst.top = state->state.dst.y1;
cfg.dst.width = drm_rect_width(&state->state.dst);
cfg.dst.height = drm_rect_height(&state->state.dst);
for (i = 0; i < state->format->planes; ++i) {
struct drm_gem_dma_object *gem;
gem = drm_fb_dma_get_gem_obj(fb, i);
cfg.mem[i] = gem->dma_addr + fb->offsets[i];
}
if (state->state.pixel_blend_mode == DRM_MODE_BLEND_PIXEL_NONE) {
switch (fourcc) {
case DRM_FORMAT_ARGB1555:
fourcc = DRM_FORMAT_XRGB1555;
break;
case DRM_FORMAT_ARGB4444:
fourcc = DRM_FORMAT_XRGB4444;
break;
case DRM_FORMAT_ARGB8888:
fourcc = DRM_FORMAT_XRGB8888;
break;
}
}
format = rzg2l_du_format_info(fourcc);
cfg.pixelformat = format->v4l2;
cfg.premult = state->state.pixel_blend_mode == DRM_MODE_BLEND_PREMULTI;
vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
plane->index, &cfg);
}
static int __rzg2l_du_vsp_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state,
const struct rzg2l_du_format_info **format)
{
struct drm_crtc_state *crtc_state;
int ret;
if (!state->crtc) {
/*
* The visible field is not reset by the DRM core but only
* updated by drm_atomic_helper_check_plane_state, set it
* manually.
*/
state->visible = false;
*format = NULL;
return 0;
}
crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
ret = drm_atomic_helper_check_plane_state(state, crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
true, true);
if (ret < 0)
return ret;
if (!state->visible) {
*format = NULL;
return 0;
}
*format = rzg2l_du_format_info(state->fb->format->format);
return 0;
}
static int rzg2l_du_vsp_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct rzg2l_du_vsp_plane_state *rstate = to_rzg2l_vsp_plane_state(new_plane_state);
return __rzg2l_du_vsp_plane_atomic_check(plane, new_plane_state, &rstate->format);
}
static void rzg2l_du_vsp_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
struct rzg2l_du_vsp_plane *rplane = to_rzg2l_vsp_plane(plane);
struct rzg2l_du_crtc *crtc = to_rzg2l_crtc(old_state->crtc);
if (new_state->visible)
rzg2l_du_vsp_plane_setup(rplane);
else if (old_state->crtc)
vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
rplane->index, NULL);
}
static const struct drm_plane_helper_funcs rzg2l_du_vsp_plane_helper_funcs = {
.atomic_check = rzg2l_du_vsp_plane_atomic_check,
.atomic_update = rzg2l_du_vsp_plane_atomic_update,
};
static struct drm_plane_state *
rzg2l_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
{
struct rzg2l_du_vsp_plane_state *copy;
if (WARN_ON(!plane->state))
return NULL;
copy = kzalloc(sizeof(*copy), GFP_KERNEL);
if (!copy)
return NULL;
__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
return &copy->state;
}
static void rzg2l_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
__drm_atomic_helper_plane_destroy_state(state);
kfree(to_rzg2l_vsp_plane_state(state));
}
static void rzg2l_du_vsp_plane_reset(struct drm_plane *plane)
{
struct rzg2l_du_vsp_plane_state *state;
if (plane->state) {
rzg2l_du_vsp_plane_atomic_destroy_state(plane, plane->state);
plane->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return;
__drm_atomic_helper_plane_reset(plane, &state->state);
}
static const struct drm_plane_funcs rzg2l_du_vsp_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.reset = rzg2l_du_vsp_plane_reset,
.atomic_duplicate_state = rzg2l_du_vsp_plane_atomic_duplicate_state,
.atomic_destroy_state = rzg2l_du_vsp_plane_atomic_destroy_state,
};
static void rzg2l_du_vsp_cleanup(struct drm_device *dev, void *res)
{
struct rzg2l_du_vsp *vsp = res;
put_device(vsp->vsp);
}
int rzg2l_du_vsp_init(struct rzg2l_du_vsp *vsp, struct device_node *np,
unsigned int crtcs)
{
struct rzg2l_du_device *rcdu = vsp->dev;
struct platform_device *pdev;
unsigned int num_crtcs = hweight32(crtcs);
unsigned int num_planes = 2;
unsigned int i;
int ret;
/* Find the VSP device and initialize it. */
pdev = of_find_device_by_node(np);
if (!pdev)
return -ENXIO;
vsp->vsp = &pdev->dev;
ret = drmm_add_action_or_reset(&rcdu->ddev, rzg2l_du_vsp_cleanup, vsp);
if (ret < 0)
return ret;
ret = vsp1_du_init(vsp->vsp);
if (ret < 0)
return ret;
for (i = 0; i < num_planes; ++i) {
enum drm_plane_type type = i < num_crtcs
? DRM_PLANE_TYPE_PRIMARY
: DRM_PLANE_TYPE_OVERLAY;
struct rzg2l_du_vsp_plane *plane;
plane = drmm_universal_plane_alloc(&rcdu->ddev, struct rzg2l_du_vsp_plane,
plane, crtcs, &rzg2l_du_vsp_plane_funcs,
rzg2l_du_vsp_formats,
ARRAY_SIZE(rzg2l_du_vsp_formats),
NULL, type, NULL);
if (IS_ERR(plane))
return PTR_ERR(plane);
plane->vsp = vsp;
plane->index = i;
drm_plane_helper_add(&plane->plane,
&rzg2l_du_vsp_plane_helper_funcs);
}
return 0;
}

View File

@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* RZ/G2L Display Unit VSP-Based Compositor
*
* Copyright (C) 2023 Renesas Electronics Corporation
*
* Based on rcar_du_vsp.h
*/
#ifndef __RZG2L_DU_VSP_H__
#define __RZG2L_DU_VSP_H__
#include <drm/drm_plane.h>
#include <linux/container_of.h>
#include <linux/scatterlist.h>
struct device;
struct drm_framebuffer;
struct rzg2l_du_device;
struct rzg2l_du_format_info;
struct rzg2l_du_vsp;
struct rzg2l_du_vsp_plane {
struct drm_plane plane;
struct rzg2l_du_vsp *vsp;
unsigned int index;
};
struct rzg2l_du_vsp {
unsigned int index;
struct device *vsp;
struct rzg2l_du_device *dev;
};
static inline struct rzg2l_du_vsp_plane *to_rzg2l_vsp_plane(struct drm_plane *p)
{
return container_of(p, struct rzg2l_du_vsp_plane, plane);
}
/**
* struct rzg2l_du_vsp_plane_state - Driver-specific plane state
* @state: base DRM plane state
* @format: information about the pixel format used by the plane
*/
struct rzg2l_du_vsp_plane_state {
struct drm_plane_state state;
const struct rzg2l_du_format_info *format;
};
static inline struct rzg2l_du_vsp_plane_state *
to_rzg2l_vsp_plane_state(struct drm_plane_state *state)
{
return container_of(state, struct rzg2l_du_vsp_plane_state, state);
}
#if IS_ENABLED(CONFIG_VIDEO_RENESAS_VSP1)
int rzg2l_du_vsp_init(struct rzg2l_du_vsp *vsp, struct device_node *np,
unsigned int crtcs);
void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc);
void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc);
void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc);
struct drm_plane *rzg2l_du_vsp_get_drm_plane(struct rzg2l_du_crtc *crtc,
unsigned int pipe_index);
#else
static inline int rzg2l_du_vsp_init(struct rzg2l_du_vsp *vsp, struct device_node *np,
unsigned int crtcs)
{
return -ENXIO;
}
static inline void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc) { };
static inline void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc) { };
static inline void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc) { };
static inline struct drm_plane *rzg2l_du_vsp_get_drm_plane(struct rzg2l_du_crtc *crtc,
unsigned int pipe_index)
{
return ERR_PTR(-ENXIO);
}
#endif
#endif /* __RZG2L_DU_VSP_H__ */

View File

@ -113,7 +113,7 @@ static void bo_meminfo(struct xe_bo *bo,
else
mem_type = XE_PL_TT;
if (bo->ttm.base.handle_count > 1)
if (drm_gem_object_is_shared_for_memory_stats(&bo->ttm.base))
stats[mem_type].shared += sz;
else
stats[mem_type].private += sz;

View File

@ -625,8 +625,7 @@ void host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2,
struct host1x_channel *channel = cdma_to_channel(cdma);
struct host1x *host1x = cdma_to_host1x(cdma);
struct push_buffer *pb = &cdma->push_buffer;
unsigned int space = cdma->slots_free;
unsigned int needed = 2, extra = 0;
unsigned int space, needed = 2, extra = 0;
if (host1x_debug_trace_cmdbuf)
trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2,

View File

@ -107,7 +107,7 @@ struct drm_bridge_funcs {
* Since this function is both called from the check phase of an atomic
* commit, and the mode validation in the probe paths it is not allowed
* to look at anything else but the passed-in mode, and validate it
* against configuration-invariant hardward constraints. Any further
* against configuration-invariant hardware constraints. Any further
* limits which depend upon the configuration can only be checked in
* @mode_fixup.
*

View File

@ -553,6 +553,19 @@ unsigned long drm_gem_lru_scan(struct drm_gem_lru *lru,
int drm_gem_evict(struct drm_gem_object *obj);
/**
* drm_gem_object_is_shared_for_memory_stats - helper for shared memory stats
*
* This helper should only be used for fdinfo shared memory stats to determine
* if a GEM object is shared.
*
* @obj: obj in question
*/
static inline bool drm_gem_object_is_shared_for_memory_stats(struct drm_gem_object *obj)
{
return (obj->handle_count > 1) || obj->dma_buf;
}
#ifdef CONFIG_LOCKDEP
/**
* drm_gem_gpuva_set_lock() - Set the lock protecting accesses to the gpuva list.