mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
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:
commit
71ab34f72f
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -14,7 +14,9 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: leadtek,ltk500hd1829
|
||||
enum:
|
||||
- leadtek,ltk101b4029w
|
||||
- leadtek,ltk500hd1829
|
||||
reg: true
|
||||
backlight: true
|
||||
reset-gpios: true
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
126
Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
Normal file
126
Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -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>,
|
||||
|
@ -61,6 +61,8 @@ patternProperties:
|
||||
description: Analog Devices, Inc.
|
||||
"^adieng,.*":
|
||||
description: ADI Engineering, Inc.
|
||||
"^admatec,.*":
|
||||
description: admatec GmbH
|
||||
"^advantech,.*":
|
||||
description: Advantech Corporation
|
||||
"^aeroflexgaisler,.*":
|
||||
|
@ -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]
|
||||
|
12
MAINTAINERS
12
MAINTAINERS
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Suspend to RAM seems to be broken on this machine
|
||||
.*suspend.*
|
@ -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
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Suspend to RAM seems to be broken on this machine
|
||||
.*suspend.*
|
@ -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;
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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"),
|
||||
|
372
drivers/gpu/drm/panel/panel-himax-hx83112a.c
Normal file
372
drivers/gpu/drm/panel/panel-himax-hx83112a.c
Normal 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");
|
@ -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 = <k101b4029w_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 = <k500hd1829_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 = <k101b4029w_data,
|
||||
},
|
||||
{
|
||||
.compatible = "leadtek,ltk500hd1829",
|
||||
.data = <k500hd1829_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltk500hd1829_of_match);
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -1,4 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-y += rcar-du/
|
||||
obj-y += rz-du/
|
||||
obj-$(CONFIG_DRM_SHMOBILE) += shmobile/
|
||||
|
12
drivers/gpu/drm/renesas/rz-du/Kconfig
Normal file
12
drivers/gpu/drm/renesas/rz-du/Kconfig
Normal 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.
|
8
drivers/gpu/drm/renesas/rz-du/Makefile
Normal file
8
drivers/gpu/drm/renesas/rz-du/Makefile
Normal 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
|
422
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
Normal file
422
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
Normal 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, ©->state);
|
||||
|
||||
return ©->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;
|
||||
}
|
89
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.h
Normal file
89
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.h
Normal 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__ */
|
175
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
Normal file
175
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
Normal 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");
|
78
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
Normal file
78
drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
Normal 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__ */
|
72
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
Normal file
72
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
Normal 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);
|
||||
}
|
32
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.h
Normal file
32
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.h
Normal 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__ */
|
371
drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
Normal file
371
drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.c
Normal 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;
|
||||
}
|
43
drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.h
Normal file
43
drivers/gpu/drm/renesas/rz-du/rzg2l_du_kms.h
Normal 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__ */
|
349
drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
Normal file
349
drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
Normal 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, ©->state);
|
||||
|
||||
return ©->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;
|
||||
}
|
82
drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h
Normal file
82
drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h
Normal 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__ */
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user