drm-misc-next for v7.2-rc1:

UAPI Changes:
 - Support medium/low power modes in amdxdna.
 - Support limiting frequency in ivpu.
 - Document license for drm core uAPI headers.
 - Add the following DRM formats: P230, Y7, XYYY2101010, T430,
   XVUY210101010.
 
 Cross-subsystem Changes:
 - Add and improve dt-bindings.
 - Remove unused dma-fence-array's signal_on_any support.
 
 Core Changes:
 - Do not call drop_master on file close if not master.
 - Convert drm-bridge and drm/atomic to use drm_printf_indent.
 - Remove the extra call to drm_connector_attach_encoder after
   drm_bridge_connector_init().
 - Assorted docbook updates.
 
 Driver Changes:
 - Bugfixes in amdxdna, ivpu, mipi-dsi, imagination, nouveau, panthor,
   bridge/analogix_dp, ipv3, lontium-lt8912b, verisilicon, tve200,
   etnaviv, panel/focaltech-ota7290b, panel/jadard-jd9365da-h3,
   bridge/ite-it6263, renesas, xlnx, bridge/cdns-dsi, gma500,
   bridge/microchip-lvds, mgag200.
 - Add support for MStar TSUMU88ADT3-LF-1 bridge.
 - Add support for WaveShare 7, Novatek NT35532, Startek KD070HDFLD092,
   ChipWealth CH13726A AMOLED, Team Source Display TST070WSNE-196C,
   Displaytech DT050BTFT-PTS panels.
 - Improve mipi-dsi shutdown and convert a panasonic panel to use the
   mipi-dsi wrappers.
 - Allowing dumping vbios over debugfs in GSP-RM mode.
 - Update maintainers for ivpu, add reviewer for drm-bridge code
   and update maintainers for LT8912B DRM HDMI bridge.
 - Add test pattern support to bridge/ti-sn65dsi83.
 - Convert vmwgfx to vblank timers.
 - Add power management to sysfb drm drivers to allow suspend/resume.
 - Support the aforementioned new drm formats in xlnx/qynqmp.
 - Fix panel Kconfig dependencies.
 - Add carveout support for debugging and bringup to amxdna.
 - Add support for long command tx via videobuffer in bridge/tc358768.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmn8fBIACgkQ/lWMcqZw
 E8OlSw/+JbotgRTp5I3EwGtOiUoEZF1n20vkwZ5+olbEhP5wVft+Kf5MbD2/Hmja
 GEL/3lxXgbvA1QOXFsED+q5UlKwtmEb3jTj+K83CXZ/YHT8Fk+lFCpNR3dGWWpal
 3CAk+Xe2MYdhBe4Sce04rKYfI7D7R2p1xrvfGDO/4Z1WicI1P2BhgV+6K2buhNJ3
 2FOFRLTQaBdUa/52PgP45lEvQveI5E2UHyjOk2dDEU/uE6JeMHOQzk4d/1VFY+6s
 hjgC5Mzw1eRZeX3LgSsSaCZ4/vKOXUg950oMAbmGeucyPoIm2+dOt6k/bRMTolJt
 wXDGmh69dJliHm5801riqaEBWwMV0JEt3WBcqAiNycIAitsjf9jACkWMfoXz03At
 34qTWxWReXaQXL32ooFD2ZIZhiBSNNbZu50P5xhOAcE2F0siPH8dLSSaamSEKX5P
 T+gklhoXnMljgBz/wRKW5oUC1EQrylsc2C3/vvXHopYDPFhHZ77v1m33x5mXu8jn
 XvUPjMIIx62dBueGtYcODQEOhfvweeg+XWUJ0uJ1SDfqtTDjkw2oEnNqEbYOwaIF
 BR8YjQn3QI5FAFt9VbilQ9lL7XBGgjT4kV8iX4qGedSwKLAzu/b+P3FyTtvNnY71
 vYldKKmhVogzHO97JzVvWq3l6H6dush1REgprGCDAISUesk55g4=
 =kWIv
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2026-05-07' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for v7.2-rc1:

UAPI Changes:
- Support medium/low power modes in amdxdna.
- Support limiting frequency in ivpu.
- Document license for drm core uAPI headers.
- Add the following DRM formats: P230, Y7, XYYY2101010, T430,
  XVUY210101010.

Cross-subsystem Changes:
- Add and improve dt-bindings.
- Remove unused dma-fence-array's signal_on_any support.

Core Changes:
- Do not call drop_master on file close if not master.
- Convert drm-bridge and drm/atomic to use drm_printf_indent.
- Remove the extra call to drm_connector_attach_encoder after
  drm_bridge_connector_init().
- Assorted docbook updates.

Driver Changes:
- Bugfixes in amdxdna, ivpu, mipi-dsi, imagination, nouveau, panthor,
  bridge/analogix_dp, ipv3, lontium-lt8912b, verisilicon, tve200,
  etnaviv, panel/focaltech-ota7290b, panel/jadard-jd9365da-h3,
  bridge/ite-it6263, renesas, xlnx, bridge/cdns-dsi, gma500,
  bridge/microchip-lvds, mgag200.
- Add support for MStar TSUMU88ADT3-LF-1 bridge.
- Add support for WaveShare 7, Novatek NT35532, Startek KD070HDFLD092,
  ChipWealth CH13726A AMOLED, Team Source Display TST070WSNE-196C,
  Displaytech DT050BTFT-PTS panels.
- Improve mipi-dsi shutdown and convert a panasonic panel to use the
  mipi-dsi wrappers.
- Allowing dumping vbios over debugfs in GSP-RM mode.
- Update maintainers for ivpu, add reviewer for drm-bridge code
  and update maintainers for LT8912B DRM HDMI bridge.
- Add test pattern support to bridge/ti-sn65dsi83.
- Convert vmwgfx to vblank timers.
- Add power management to sysfb drm drivers to allow suspend/resume.
- Support the aforementioned new drm formats in xlnx/qynqmp.
- Fix panel Kconfig dependencies.
- Add carveout support for debugging and bringup to amxdna.
- Add support for long command tx via videobuffer in bridge/tc358768.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patch.msgid.link/f73f342d-6efb-416d-81b0-1716bdd98d5f@linux.intel.com
This commit is contained in:
Dave Airlie 2026-05-08 14:14:24 +10:00
commit f96538285c
170 changed files with 4184 additions and 1201 deletions

View File

@ -0,0 +1,30 @@
What: /sys/bus/pci/drivers/intel_vpu/.../sched_mode
Date: October 2024
KernelVersion: 6.12
Contact: dri-devel@lists.freedesktop.org
Description: Current NPU scheduling mode. Returns one of the following strings:
- "HW" - Hardware Scheduler mode
- "OS" - Operating System Scheduler mode
Read-only.
Deprecated since the "OS" scheduling mode is not usable
and will be removed from future versions of the driver.
Will be removed in 2027
What: /sys/bus/pci/drivers/intel_vpu/.../npu_max_frequency_mhz
Date: April 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq.
Shows maximum frequency in MHz of the NPU's data processing unit.
Read-only.
Will be removed in 2027
What: /sys/bus/pci/drivers/intel_vpu/.../npu_current_frequency_mhz
Date: April 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq.
Shows current frequency in MHz of the NPU's data processing unit.
The value is read only when the device is active; otherwise it returns 0.
Read-only.
Will be removed in 2027

View File

@ -0,0 +1,65 @@
What: /sys/bus/pci/drivers/intel_vpu/.../npu_busy_time_us
Date: May 2024
KernelVersion: 6.11
Contact: dri-devel@lists.freedesktop.org
Description: Time in microseconds that the device spent executing jobs. The time is
counted when and only when there are jobs submitted to firmware. This time
can be used to measure the utilization of NPU, either by calculating the
difference between two timepoints or monitoring utilization percentage by
reading periodically. Recommended read period is 1 second to avoid impact
on job submission performance. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../npu_memory_utilization
Date: Jan 2025
KernelVersion: 6.15
Contact: dri-devel@lists.freedesktop.org
Description: Current NPU memory utilization in bytes. Reports the total size of all
resident buffer objects allocated for NPU use. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_min_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Minimum frequency in MHz supported by the NPU hardware. This is a
hardware capability and cannot be changed. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_efficient_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Most efficient operating frequency in MHz for the NPU. This represents
the frequency at which the NPU operates most efficiently in terms of power
and performance. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Maximum frequency in MHz supported by the NPU hardware. This is a
hardware capability and cannot be changed. Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Current operating frequency in MHz of the NPU. The value is valid only
when the device is active; returns 0 when idle. The actual frequency may
be lower than the requested range due to power or thermal constraints.
Read-only.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_min_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Configured minimum operating frequency in MHz (50XX devices and newer).
Values written are clamped to hardware limits (hw_min_freq to hw_max_freq).
If set_min_freq exceeds set_max_freq, the driver clamps set_min_freq to
set_max_freq when selecting the operating frequency. Read-write.
What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_max_freq
Date: April 2026
KernelVersion: 7.2
Contact: dri-devel@lists.freedesktop.org
Description: Configured maximum operating frequency in MHz (50XX devices and newer).
Values written are clamped to hardware limits (hw_min_freq to hw_max_freq).
Read-write.

View File

@ -34,6 +34,7 @@ properties:
- items:
- enum:
- doestek,dtc34lm85am # For the Doestek DTC34LM85AM Flat Panel Display (FPD) Transmitter
- idt,v103 # For the Triple 10-BIT LVDS Transmitter
- onnn,fin3385 # OnSemi FIN3385
- ti,ds90c185 # For the TI DS90C185 FPD-Link Serializer
- ti,ds90c187 # For the TI DS90C187 FPD-Link Serializer

View File

@ -30,6 +30,7 @@ properties:
- algoltek,ag6311
- asl-tek,cs5263
- dumb-vga-dac
- mstar,tsumu88adt3-lf-1
- parade,ps185hdm
- radxa,ra620
- realtek,rtd2171

View File

@ -0,0 +1,67 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/chipwealth,ch13726a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Chip Wealth Technology CH13726A AMOLED driver
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
Chip Wealth Technology CH13726A is a single-chip solution
for AMOLED connected using a MIPI-DSI video interface.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- const: ayntec,thor-panel-bottom
- const: chipwealth,ch13726a
reg:
maxItems: 1
description: DSI virtual channel
vdd-supply: true
vddio-supply: true
vdd1v2-supply: true
avdd-supply: true
port: true
reset-gpios: true
rotation: true
required:
- compatible
- reg
- vdd-supply
- vddio-supply
- vdd1v2-supply
- avdd-supply
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "ayntec,thor-panel-bottom", "chipwealth,ch13726a";
reg = <0>;
vdd1v2-supply = <&vreg_l11b_1p2>;
vddio-supply = <&vdd_disp_1v8>;
vdd-supply = <&vreg_l13b_3p0>;
avdd-supply = <&vdd_disp2_2v8>;
reset-gpios = <&tlmm 133 GPIO_ACTIVE_HIGH>;
};
};
...

View File

@ -24,6 +24,7 @@ properties:
- raspberrypi,dsi-7inch
- startek,kd050hdfia020
- tdo,tl050hdv35
- waveshare,7.0-dsi-touch-a
- wanchanglong,w552946aaa
- wanchanglong,w552946aba
- const: ilitek,ili9881c
@ -34,6 +35,7 @@ properties:
backlight: true
port: true
power-supply: true
iovcc-supply: true
reset-gpios: true
rotation: true

View File

@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/novatek,nt35532.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Novatek NT35532-based DSI display panels
maintainers:
- Cristian Cozzolino <cristian_ci@protonmail.com>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- flipkart,rimob-panel-nt35532-cs
- const: novatek,nt35532
reg:
maxItems: 1
backlight: true
reset-gpios: true
avdd-supply:
description: positive boost supply regulator
avee-supply:
description: negative boost supply regulator
vci-supply:
description: regulator that supplies the analog voltage
vddam-supply:
description: power supply for MIPI interface
vddi-supply:
description: regulator that supplies the I/O voltage
port: true
required:
- compatible
- reg
- reset-gpios
- vddi-supply
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "flipkart,rimob-panel-nt35532-cs", "novatek,nt35532";
reg = <0>;
backlight = <&pmi8950_wled>;
reset-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>;
avdd-supply = <&lab>;
avee-supply = <&ibb>;
vci-supply = <&pm8953_l17>;
vddi-supply = <&pm8953_l6>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
...

View File

@ -63,6 +63,8 @@ properties:
- samsung,s6e3fa7-ams559nk06
# Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel
- tdo,tl070wsh30
# Team Source Display Technology 7" TST070WSBE-196C 1024x600 TFT LCD panel
- team-source-display,tst070wsbe-196c
reg:
maxItems: 1

View File

@ -97,6 +97,8 @@ properties:
- dataimage,fg1001l0dsswmg01
# DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface.
- dataimage,scf0700c48ggu18
# Displaytech DT050BTFT-PTS 5.0" 800x480 TFT LCD Panel
- displaytech,dt050btft-pts
# DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel
- dlc,dlc1010gig
# Emerging Display Technology Corp. 3.5" QVGA TFT LCD panel
@ -304,6 +306,8 @@ properties:
- shelly,sca07010-bfn-lnn
# Starry KR070PE2T 7" WVGA TFT LCD panel
- starry,kr070pe2t
# Startek KD070HDFLD092 7" WSVGA TFT LCD panel
- startek,kd070hdfld092
# Startek KD070WVFPA043-C069A 7" TFT LCD panel
- startek,kd070wvfpa
# Team Source Display Technology TST043015CMHX 4.3" WQVGA TFT LCD panel

View File

@ -437,6 +437,8 @@ patternProperties:
description: Diodes, Inc.
"^dioo,.*":
description: Dioo Microcircuit Co., Ltd
"^displaytech,.*":
description: Displaytech Ltd.
"^djn,.*":
description: Shenzhen DJN Optronics Technology Co., Ltd
"^dlc,.*":

View File

@ -118,6 +118,10 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs
for the same thing co-existing. If we add a few more complete mistakes into the
mix every year it would be entirely unmanageable.
The DRM subsystem has however no concern with independent closed-source
userspace implementations. To officialize that position, the DRM uAPI headers
are covered by the MIT license.
.. _drm_render_node:
Render nodes

View File

@ -7835,11 +7835,13 @@ F: drivers/soc/ti/smartreflex.c
F: include/linux/power/smartreflex.h
DRM ACCEL DRIVERS FOR INTEL VPU
M: Maciej Falkowski <maciej.falkowski@linux.intel.com>
M: Karol Wachowski <karol.wachowski@linux.intel.com>
M: Andrzej Kacprowski <andrzej.kacprowski@linux.intel.com>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/ABI/obsolete/sysfs-driver-ivpu
F: Documentation/ABI/testing/sysfs-driver-ivpu
F: drivers/accel/ivpu/
F: include/uapi/drm/ivpu_accel.h
@ -8148,6 +8150,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/novatek,nt35510.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt35510.c
DRM DRIVER FOR NOVATEK NT35532 PANELS
M: Cristian Cozzolino <cristian_ci@protonmail.com>
S: Maintained
F: Documentation/devicetree/bindings/display/panel/novatek,nt35532.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt35532.c
DRM DRIVER FOR NOVATEK NT35560 PANELS
M: Linus Walleij <linusw@kernel.org>
S: Maintained
@ -8558,6 +8566,7 @@ M: Robert Foss <rfoss@kernel.org>
R: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
R: Jonas Karlman <jonas@kwiboo.se>
R: Jernej Skrabec <jernej.skrabec@gmail.com>
R: Luca Ceresoli <luca.ceresoli@bootlin.com>
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/bridge/
@ -15018,7 +15027,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
F: drivers/hid/hid-lg-g15.c
LONTIUM LT8912B MIPI TO HDMI BRIDGE
M: Adrien Grassein <adrien.grassein@gmail.com>
M: Francesco Dolcini <francesco@dolcini.it>
S: Maintained
F: Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml
F: drivers/gpu/drm/bridge/lontium-lt8912b.c

View File

@ -12,6 +12,7 @@ amdxdna-y := \
aie2_solver.o \
aie4_message.o \
aie4_pci.o \
amdxdna_cbuf.o \
amdxdna_ctx.o \
amdxdna_gem.o \
amdxdna_iommu.o \
@ -28,4 +29,5 @@ amdxdna-y := \
npu6_regs.o
amdxdna-$(CONFIG_PCI_IOV) += aie4_sriov.o
amdxdna-$(CONFIG_DEBUG_FS) += amdxdna_debugfs.o
obj-$(CONFIG_DRM_ACCEL_AMDXDNA) = amdxdna.o

View File

@ -64,6 +64,7 @@ static void aie2_job_release(struct kref *ref)
struct amdxdna_sched_job *job;
job = container_of(ref, struct amdxdna_sched_job, refcnt);
amdxdna_sched_job_cleanup(job);
atomic64_inc(&job->hwctx->job_free_cnt);
wake_up(&job->hwctx->priv->job_free_wq);
@ -195,7 +196,8 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
{
struct dma_fence *fence = job->fence;
trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
trace_xdna_job(&job->base, job->hwctx->name, "signaling fence",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
aie2_tdr_signal(job->hwctx->client->xdna);
job->hwctx->priv->completed++;
@ -366,6 +368,9 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
struct dma_fence *fence;
int ret;
trace_xdna_job(sched_job, hwctx->name, "job run",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
if (!hwctx->priv->mbox_chann)
return NULL;
@ -409,7 +414,8 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
} else {
aie2_tdr_signal(hwctx->client->xdna);
}
trace_xdna_job(sched_job, hwctx->name, "sent to device", job->seq);
trace_xdna_job(sched_job, hwctx->name, "sent to device",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
return fence;
}
@ -419,7 +425,8 @@ static void aie2_sched_job_free(struct drm_sched_job *sched_job)
struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job);
struct amdxdna_hwctx *hwctx = job->hwctx;
trace_xdna_job(sched_job, hwctx->name, "job free", job->seq);
trace_xdna_job(sched_job, hwctx->name, "job free",
job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
if (!job->job_done)
up(&hwctx->priv->job_sem);
@ -437,7 +444,6 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
int ret;
xdna = hwctx->client->xdna;
trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq);
guard(mutex)(&xdna->dev_lock);
@ -540,22 +546,24 @@ static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct alloc_requests *xrs_req;
u32 temporal_only_col = 0;
int ret;
if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) {
hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col;
hwctx->num_col = xdna->dev_handle->total_col;
return aie2_create_context(xdna->dev_handle, hwctx);
}
xrs_req = kzalloc_obj(*xrs_req);
if (!xrs_req)
return -ENOMEM;
xrs_req->cdo.start_cols = hwctx->col_list;
xrs_req->cdo.cols_len = hwctx->col_list_len;
xrs_req->cdo.ncols = hwctx->num_col;
xrs_req->cdo.qos_cap.opc = hwctx->max_opc;
if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) {
xrs_req->cdo.start_cols = &temporal_only_col;
xrs_req->cdo.cols_len = 1;
xrs_req->cdo.ncols = xdna->dev_handle->total_col;
} else {
xrs_req->cdo.start_cols = hwctx->col_list;
xrs_req->cdo.cols_len = hwctx->col_list_len;
xrs_req->cdo.ncols = hwctx->num_col;
}
/* Use platform opc */
xrs_req->cdo.qos_cap.opc = xdna->dev_handle->priv->col_opc * hwctx->num_col;
xrs_req->rqos.gops = hwctx->qos.gops;
xrs_req->rqos.fps = hwctx->qos.fps;
@ -579,15 +587,9 @@ static void aie2_release_resource(struct amdxdna_hwctx *hwctx)
struct amdxdna_dev *xdna = hwctx->client->xdna;
int ret;
if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) {
ret = aie2_destroy_context(xdna->dev_handle, hwctx);
if (ret && ret != -ENODEV)
XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret);
} else {
ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
if (ret)
XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
}
ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
if (ret)
XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
}
static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx)

View File

@ -246,6 +246,7 @@ static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action)
xdna = hwctx->client->xdna;
hwctx->start_col = action->part.start_col;
hwctx->num_unused_col = action->part.ncols - hwctx->num_col;
hwctx->num_col = action->part.ncols;
ret = aie2_create_context(xdna->dev_handle, hwctx);
if (ret)
@ -594,7 +595,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1;
for (i = 0; i < xrs_cfg.clk_list.num_levels; i++)
xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk;
xrs_cfg.sys_eff_factor = 1;
xrs_cfg.sys_eff_factor = 2;
xrs_cfg.ddev = &xdna->ddev;
xrs_cfg.actions = &aie2_xrs_actions;
xrs_cfg.total_col = ndev->total_col;

View File

@ -237,6 +237,7 @@ struct amdxdna_dev_priv {
#define COL_ALIGN_NONE 0
#define COL_ALIGN_NATURE 1
u32 col_align;
u32 col_opc;
u32 mbox_dev_addr;
/* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */
u32 mbox_size;

View File

@ -74,7 +74,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
return ret;
ndev->pw_mode = POWER_MODE_DEFAULT;
ndev->dft_dpm_level = ndev->max_dpm_level;
ndev->dft_dpm_level = 0;
return 0;
}
@ -108,6 +108,14 @@ int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = ndev->dft_dpm_level;
break;
case POWER_MODE_LOW:
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = 0;
break;
case POWER_MODE_MEDIUM:
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = ndev->max_dpm_level / 2;
break;
default:
return -EOPNOTSUPP;
}

View File

@ -52,7 +52,7 @@ static u32 calculate_gops(struct aie_qos *rqos)
u32 service_rate = 0;
if (rqos->latency)
service_rate = (1000 / rqos->latency);
service_rate = max_t(u32, 1000 / rqos->latency, 1);
if (rqos->fps > service_rate)
return rqos->fps * rqos->gops;
@ -348,6 +348,7 @@ int xrs_release_resource(void *hdl, u64 rid)
{
struct solver_state *xrs = hdl;
struct solver_node *node;
u32 level = 0;
node = rg_search_node(&xrs->rgp, rid);
if (!node) {
@ -358,6 +359,13 @@ int xrs_release_resource(void *hdl, u64 rid)
xrs->cfg.actions->unload(node->cb_arg);
remove_solver_node(&xrs->rgp, node);
/* set the dpm level which fits all the sessions */
list_for_each_entry(node, &xrs->rgp.node_list, list) {
if (node->dpm_level > level)
level = node->dpm_level;
}
xrs->cfg.actions->set_dft_dpm_level(xrs->cfg.ddev, level);
return 0;
}

View File

@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include <drm/drm_mm.h>
#include <drm/drm_prime.h>
#include "amdxdna_cbuf.h"
#include "amdxdna_pci_drv.h"
/*
* Carveout memory is a chunk of memory which is physically contiguous and
* is reserved during early boot time. There is only one chunk of such memory
* per device. Once available, all BOs accessible from device should be
* allocated from this memory. This is a platform debug/bringup feature.
*/
struct amdxdna_carveout {
u64 addr;
u64 size;
struct drm_mm mm;
struct mutex lock; /* protect mm */
};
bool amdxdna_use_carveout(struct amdxdna_dev *xdna)
{
return !!xdna->carveout;
}
void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size)
{
if (amdxdna_use_carveout(xdna)) {
*addr = xdna->carveout->addr;
*size = xdna->carveout->size;
} else {
*addr = 0;
*size = 0;
}
}
int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size)
{
struct amdxdna_carveout *carveout;
/* Only allow carveout memory to be set up once. */
if (amdxdna_use_carveout(xdna)) {
XDNA_ERR(xdna, "Carveout memory has already been set up.");
return -EBUSY;
}
carveout = kzalloc_obj(*carveout);
if (!carveout)
return -ENOMEM;
carveout->addr = carveout_addr;
carveout->size = carveout_size;
mutex_init(&carveout->lock);
drm_mm_init(&carveout->mm, carveout->addr, carveout->size);
xdna->carveout = carveout;
XDNA_INFO(xdna, "Use carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr);
return 0;
}
void amdxdna_carveout_fini(struct amdxdna_dev *xdna)
{
struct amdxdna_carveout *carveout = xdna->carveout;
if (!amdxdna_use_carveout(xdna))
return;
XDNA_INFO(xdna, "Cleanup carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr);
drm_mm_takedown(&carveout->mm);
mutex_destroy(&carveout->lock);
kfree(carveout);
xdna->carveout = NULL;
}
struct amdxdna_cbuf_priv {
struct amdxdna_dev *xdna;
struct drm_mm_node node;
};
static struct sg_table *amdxdna_cbuf_map(struct dma_buf_attachment *attach,
enum dma_data_direction direction)
{
struct amdxdna_cbuf_priv *cbuf = attach->dmabuf->priv;
struct device *dev = attach->dev;
struct scatterlist *sgl, *sg;
int ret, n_entries, i;
struct sg_table *sgt;
dma_addr_t dma_addr;
size_t dma_size;
size_t max_seg;
sgt = kzalloc_obj(*sgt);
if (!sgt)
return ERR_PTR(-ENOMEM);
max_seg = min_t(size_t, UINT_MAX, dma_max_mapping_size(dev));
n_entries = (cbuf->node.size + max_seg - 1) / max_seg;
sgl = kzalloc_objs(*sg, n_entries);
if (!sgl) {
ret = -ENOMEM;
goto free_sgt;
}
sg_init_table(sgl, n_entries);
sgt->orig_nents = n_entries;
sgt->nents = n_entries;
sgt->sgl = sgl;
dma_size = cbuf->node.size;
dma_addr = dma_map_resource(dev, cbuf->node.start, dma_size,
direction, DMA_ATTR_SKIP_CPU_SYNC);
ret = dma_mapping_error(dev, dma_addr);
if (ret) {
pr_err("Failed to dma_map_resource carveout dma buf, ret %d\n", ret);
goto free_sgl;
}
for_each_sgtable_dma_sg(sgt, sg, i) {
size_t len = min_t(size_t, max_seg, dma_size);
sg_dma_address(sg) = dma_addr;
sg_dma_len(sg) = len;
dma_addr += len;
dma_size -= len;
}
return sgt;
free_sgl:
kfree(sgl);
free_sgt:
kfree(sgt);
return ERR_PTR(ret);
}
static void amdxdna_cbuf_unmap(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction direction)
{
dma_unmap_resource(attach->dev, sg_dma_address(sgt->sgl),
drm_prime_get_contiguous_size(sgt), direction,
DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sgt);
kfree(sgt);
}
static void amdxdna_cbuf_release(struct dma_buf *dbuf)
{
struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
struct amdxdna_carveout *carveout;
carveout = cbuf->xdna->carveout;
mutex_lock(&carveout->lock);
drm_mm_remove_node(&cbuf->node);
mutex_unlock(&carveout->lock);
kfree(cbuf);
}
static vm_fault_t amdxdna_cbuf_vm_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct amdxdna_cbuf_priv *cbuf;
unsigned long pfn;
pgoff_t pgoff;
cbuf = vma->vm_private_data;
pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
pfn = (cbuf->node.start >> PAGE_SHIFT) + pgoff;
return vmf_insert_pfn(vma, vmf->address, pfn);
}
static const struct vm_operations_struct amdxdna_cbuf_vm_ops = {
.fault = amdxdna_cbuf_vm_fault,
};
static int amdxdna_cbuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
{
struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
vma->vm_ops = &amdxdna_cbuf_vm_ops;
vma->vm_private_data = cbuf;
vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
return 0;
}
static int amdxdna_cbuf_vmap(struct dma_buf *dbuf, struct iosys_map *map)
{
struct amdxdna_cbuf_priv *cbuf = dbuf->priv;
void *kva;
kva = memremap(cbuf->node.start, cbuf->node.size, MEMREMAP_WB);
if (!kva) {
pr_err("Failed to vmap carveout dma buf\n");
return -ENOMEM;
}
iosys_map_set_vaddr(map, kva);
return 0;
}
static void amdxdna_cbuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map)
{
memunmap(map->vaddr);
}
static const struct dma_buf_ops amdxdna_cbuf_dmabuf_ops = {
.map_dma_buf = amdxdna_cbuf_map,
.unmap_dma_buf = amdxdna_cbuf_unmap,
.release = amdxdna_cbuf_release,
.mmap = amdxdna_cbuf_mmap,
.vmap = amdxdna_cbuf_vmap,
.vunmap = amdxdna_cbuf_vunmap,
};
static int amdxdna_cbuf_clear(struct dma_buf *dbuf)
{
struct iosys_map vmap = IOSYS_MAP_INIT_VADDR(NULL);
dma_buf_vmap(dbuf, &vmap);
if (!vmap.vaddr)
return -EFAULT;
memset(vmap.vaddr, 0, dbuf->size);
dma_buf_vunmap(dbuf, &vmap);
return 0;
}
struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment)
{
struct amdxdna_dev *xdna = to_xdna_dev(dev);
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct amdxdna_carveout *carveout;
struct amdxdna_cbuf_priv *cbuf;
struct dma_buf *dbuf;
int ret;
cbuf = kzalloc_obj(*cbuf);
if (!cbuf)
return ERR_PTR(-ENOMEM);
cbuf->xdna = xdna;
carveout = xdna->carveout;
mutex_lock(&carveout->lock);
ret = drm_mm_insert_node_generic(&carveout->mm, &cbuf->node, size,
alignment, 0, DRM_MM_INSERT_BEST);
mutex_unlock(&carveout->lock);
if (ret)
goto free_cbuf;
exp_info.size = size;
exp_info.ops = &amdxdna_cbuf_dmabuf_ops;
exp_info.priv = cbuf;
exp_info.flags = O_RDWR;
dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) {
ret = PTR_ERR(dbuf);
goto remove_node;
}
ret = amdxdna_cbuf_clear(dbuf);
if (ret) {
dma_buf_put(dbuf);
goto out;
}
return dbuf;
remove_node:
drm_mm_remove_node(&cbuf->node);
free_cbuf:
kfree(cbuf);
out:
return ERR_PTR(ret);
}

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AMDXDNA_CBUF_H_
#define _AMDXDNA_CBUF_H_
#include "amdxdna_pci_drv.h"
#include <drm/drm_device.h>
#include <linux/dma-buf.h>
bool amdxdna_use_carveout(struct amdxdna_dev *xdna);
int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size);
void amdxdna_carveout_fini(struct amdxdna_dev *xdna);
void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size);
struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment);
#endif

View File

@ -514,7 +514,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
goto unlock_srcu;
}
job->hwctx = hwctx;
job->mm = current->mm;
@ -612,6 +611,8 @@ int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_
if (args->ext || args->ext_flags)
return -EINVAL;
trace_amdxdna_debug_point(current->comm, args->type, "job received");
switch (args->type) {
case AMDXDNA_CMD_SUBMIT_EXEC_BUF:
return amdxdna_drm_submit_execbuf(client, args);

View File

@ -119,6 +119,7 @@ struct amdxdna_hwctx {
container_of(j, struct amdxdna_sched_job, base)
enum amdxdna_job_opcode {
DEFAULT_IO,
SYNC_DEBUG_BO,
ATTACH_DEBUG_BO,
DETACH_DEBUG_BO,

View File

@ -0,0 +1,129 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#include "amdxdna_cbuf.h"
#include "amdxdna_debugfs.h"
#include <drm/drm_file.h>
#include <linux/debugfs.h>
#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#define _DBGFS_FOPS(_open, _release, _write) \
{ \
.owner = THIS_MODULE, \
.open = _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = _release, \
.write = _write, \
}
#define AMDXDNA_DBGFS_FOPS(_name, _show, _write) \
static int amdxdna_dbgfs_##_name##_open(struct inode *inode, struct file *file) \
{ \
return single_open(file, _show, inode->i_private); \
} \
static int amdxdna_dbgfs_##_name##_release(struct inode *inode, struct file *file) \
{ \
return single_release(inode, file); \
} \
static const struct file_operations amdxdna_fops_##_name = \
_DBGFS_FOPS(amdxdna_dbgfs_##_name##_open, amdxdna_dbgfs_##_name##_release, _write)
#define AMDXDNA_DBGFS_FILE(_name, _mode) { #_name, &amdxdna_fops_##_name, _mode }
#define file_to_xdna(file) (((struct seq_file *)(file)->private_data)->private)
static ssize_t amdxdna_carveout_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct amdxdna_dev *xdna = file_to_xdna(file);
char kbuf[128];
u64 size, addr;
char *sep;
int ret;
if (count == 0 || count >= sizeof(kbuf))
return -EINVAL;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
kbuf[count] = '\0';
strim(kbuf);
XDNA_DBG(xdna, "Trying to set carveout to %s", kbuf);
sep = strchr(kbuf, '@');
if (!sep)
return -EINVAL;
*sep = '\0';
sep++;
ret = kstrtou64(kbuf, 0, &size);
if (ret)
return ret;
ret = kstrtou64(sep, 0, &addr);
if (ret)
return ret;
/* Sanity check the addr and size. */
if (!size)
return -EINVAL;
if (!IS_ALIGNED(addr, PAGE_SIZE) || !IS_ALIGNED(size, PAGE_SIZE))
return -EINVAL;
guard(mutex)(&xdna->dev_lock);
ret = amdxdna_carveout_init(xdna, addr, size);
if (ret)
return ret;
return count;
}
static int amdxdna_carveout_show(struct seq_file *m, void *unused)
{
struct amdxdna_dev *xdna = m->private;
u64 addr, size;
guard(mutex)(&xdna->dev_lock);
amdxdna_get_carveout_conf(xdna, &addr, &size);
seq_printf(m, "0x%llx@0x%llx\n", size, addr);
return 0;
}
/*
* Input/output format: <carveout_size>@<carveout_address>
*/
AMDXDNA_DBGFS_FOPS(carveout, amdxdna_carveout_show, amdxdna_carveout_write);
static const struct {
const char *name;
const struct file_operations *fops;
umode_t mode;
} amdxdna_dbgfs_files[] = {
AMDXDNA_DBGFS_FILE(carveout, 0600),
};
void amdxdna_debugfs_init(struct amdxdna_dev *xdna)
{
struct drm_minor *minor = xdna->ddev.accel;
int i;
/*
* It should be okay that debugfs fails to init.
* We rely on DRM framework to finish debugfs.
*/
for (i = 0; i < ARRAY_SIZE(amdxdna_dbgfs_files); i++) {
debugfs_create_file(amdxdna_dbgfs_files[i].name,
amdxdna_dbgfs_files[i].mode,
minor->debugfs_root,
xdna,
amdxdna_dbgfs_files[i].fops);
}
}

View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026, Advanced Micro Devices, Inc.
*/
#ifndef _AMDXDNA_DEBUGFS_H_
#define _AMDXDNA_DEBUGFS_H_
#include "amdxdna_pci_drv.h"
#if defined(CONFIG_DEBUG_FS)
void amdxdna_debugfs_init(struct amdxdna_dev *xdna);
#else
static inline void amdxdna_debugfs_init(struct amdxdna_dev *xdna)
{
}
#endif /* CONFIG_DEBUG_FS */
#endif

View File

@ -16,6 +16,7 @@
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include "amdxdna_cbuf.h"
#include "amdxdna_ctx.h"
#include "amdxdna_gem.h"
#include "amdxdna_pci_drv.h"
@ -516,10 +517,6 @@ static void amdxdna_imported_obj_free(struct amdxdna_gem_obj *abo)
static inline bool
amdxdna_gem_skip_bo_usage(struct amdxdna_gem_obj *abo)
{
/* Do not count imported BOs since the buffer is not allocated by us. */
if (is_import_bo(abo))
return true;
/* Already counted as part of HEAP BO */
if (abo->type == AMDXDNA_BO_DEV)
return true;
@ -571,9 +568,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
if (abo->type == AMDXDNA_BO_DEV_HEAP)
drm_mm_takedown(&abo->mm);
if (amdxdna_iova_on(xdna))
amdxdna_iommu_unmap_bo(xdna, abo);
amdxdna_dma_unmap_bo(xdna, abo);
amdxdna_gem_vunmap(abo);
mutex_destroy(&abo->lock);
@ -591,18 +586,20 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi
guard(mutex)(&abo->lock);
abo->open_ref++;
if (abo->open_ref > 1)
return 0;
if (abo->open_ref == 1) {
/* Attached to the client when first opened by it. */
abo->client = filp->driver_priv;
amdxdna_gem_add_bo_usage(abo);
}
if (amdxdna_iova_on(xdna)) {
ret = amdxdna_iommu_map_bo(xdna, abo);
/* Attached to the client when first opened by it. */
abo->client = filp->driver_priv;
/* No need to set up dma addr mapping in PASID mode. */
if (!amdxdna_pasid_on(abo->client)) {
ret = amdxdna_dma_map_bo(xdna, abo);
if (ret)
return ret;
}
amdxdna_gem_add_bo_usage(abo);
return 0;
}
@ -620,6 +617,39 @@ static void amdxdna_gem_obj_close(struct drm_gem_object *gobj, struct drm_file *
}
}
static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
int ret;
iosys_map_clear(map);
dma_resv_assert_held(obj->resv);
if (is_import_bo(abo))
ret = dma_buf_vmap(abo->dma_buf, map);
else
ret = drm_gem_shmem_object_vmap(obj, map);
if (ret)
return ret;
if (!map->vaddr)
return -ENOMEM;
return 0;
}
static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
dma_resv_assert_held(obj->resv);
if (is_import_bo(abo))
dma_buf_vunmap(abo->dma_buf, map);
else
drm_gem_shmem_object_vunmap(obj, map);
}
static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
@ -645,8 +675,8 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
.vmap = drm_gem_shmem_object_vmap,
.vunmap = drm_gem_shmem_object_vunmap,
.vmap = amdxdna_gem_obj_vmap,
.vunmap = amdxdna_gem_obj_vunmap,
.mmap = amdxdna_gem_obj_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
.export = amdxdna_gem_prime_export,
@ -714,6 +744,36 @@ amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create
return to_xdna_obj(gobj);
}
static struct amdxdna_gem_obj *
amdxdna_gem_create_cbuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args)
{
struct amdxdna_dev *xdna = to_xdna_dev(dev);
size_t size = PAGE_ALIGN(args->size);
struct drm_gem_object *gobj;
struct amdxdna_gem_obj *ret;
struct dma_buf *dma_buf;
u64 align;
if (!size) {
XDNA_ERR(xdna, "Invalid BO size 0x%llx", args->size);
return ERR_PTR(-EINVAL);
}
align = (args->type == AMDXDNA_BO_DEV_HEAP) ? xdna->dev_info->dev_mem_size : 0;
dma_buf = amdxdna_get_cbuf(dev, size, align);
if (IS_ERR(dma_buf))
return ERR_CAST(dma_buf);
gobj = amdxdna_gem_prime_import(dev, dma_buf);
if (IS_ERR(gobj))
ret = ERR_CAST(gobj);
else
ret = to_xdna_obj(gobj);
dma_buf_put(dma_buf);
return ret;
}
struct drm_gem_object *
amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf)
{
@ -769,6 +829,8 @@ amdxdna_drm_create_share_bo(struct drm_device *dev,
if (args->vaddr)
abo = amdxdna_gem_create_ubuf_object(dev, args);
else if (amdxdna_use_carveout(to_xdna_dev(dev)))
abo = amdxdna_gem_create_cbuf_object(dev, args);
else
abo = amdxdna_gem_create_shmem_object(dev, args);
if (IS_ERR(abo))
@ -884,7 +946,6 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
args->type, args->vaddr, args->size, args->flags);
switch (args->type) {
case AMDXDNA_BO_CMD:
fallthrough;
case AMDXDNA_BO_SHARE:
abo = amdxdna_drm_create_share_bo(dev, args, filp);
break;

View File

@ -35,14 +35,15 @@ static struct iova *amdxdna_iommu_alloc_iova(struct amdxdna_dev *xdna,
return iova;
}
int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
int amdxdna_dma_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
{
unsigned long contig_sz;
struct sg_table *sgt;
dma_addr_t dma_addr;
struct iova *iova;
ssize_t size;
if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM)
if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHARE)
return 0;
sgt = drm_gem_shmem_get_pages_sgt(&abo->base);
@ -51,47 +52,63 @@ int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
return PTR_ERR(sgt);
}
if (!sgt->orig_nents || !sg_page(sgt->sgl)) {
XDNA_ERR(xdna, "sgl is zero length or not page backed");
if (!sgt->orig_nents) {
XDNA_ERR(xdna, "sgl is zero length");
return -EOPNOTSUPP;
}
iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr,
(abo->type == AMDXDNA_BO_DEV_HEAP));
if (IS_ERR(iova)) {
XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova));
return PTR_ERR(iova);
if (amdxdna_iova_on(xdna)) {
if (!sg_page(sgt->sgl)) {
XDNA_ERR(xdna, "sgl is not page backed");
return -EOPNOTSUPP;
}
iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr,
(abo->type == AMDXDNA_BO_DEV_HEAP));
if (IS_ERR(iova)) {
XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova));
return PTR_ERR(iova);
}
size = iommu_map_sgtable(xdna->domain, dma_addr, sgt,
IOMMU_READ | IOMMU_WRITE);
if (size < 0) {
XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size);
__free_iova(&xdna->iovad, iova);
return size;
}
if (size < abo->mem.size) {
iommu_unmap(xdna->domain, dma_addr, size);
__free_iova(&xdna->iovad, iova);
return -ENXIO;
}
abo->mem.dma_addr = dma_addr;
} else {
/* Device doesn't support scatter/gather list, fail non-contiguous mapping. */
contig_sz = drm_prime_get_contiguous_size(sgt);
if (contig_sz < abo->mem.size) {
XDNA_ERR(xdna,
"noncontiguous dma addr, contig size:%ld, expected size:%ld",
contig_sz, abo->mem.size);
return -EINVAL;
}
abo->mem.dma_addr = sg_dma_address(sgt->sgl);
}
size = iommu_map_sgtable(xdna->domain, dma_addr, sgt,
IOMMU_READ | IOMMU_WRITE);
if (size < 0) {
XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size);
__free_iova(&xdna->iovad, iova);
return size;
}
if (size < abo->mem.size) {
iommu_unmap(xdna->domain, dma_addr, size);
__free_iova(&xdna->iovad, iova);
return -ENXIO;
}
abo->mem.dma_addr = dma_addr;
return 0;
}
void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
void amdxdna_dma_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
{
size_t size;
if (abo->mem.dma_addr == AMDXDNA_INVALID_ADDR)
return;
size = iova_align(&xdna->iovad, abo->mem.size);
iommu_unmap(xdna->domain, abo->mem.dma_addr, size);
free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr));
if (amdxdna_iova_on(xdna)) {
size = iova_align(&xdna->iovad, abo->mem.size);
iommu_unmap(xdna->domain, abo->mem.dma_addr, size);
free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr));
}
abo->mem.dma_addr = AMDXDNA_INVALID_ADDR;
}

View File

@ -361,6 +361,7 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
int ret;
mb_chann = container_of(rx_work, struct mailbox_channel, rx_work);
trace_mbox_rx_worker(MAILBOX_NAME, mb_chann->msix_irq);
if (READ_ONCE(mb_chann->bad_state)) {
MB_ERR(mb_chann, "Channel in bad state, work aborted");

View File

@ -14,7 +14,9 @@
#include <linux/iommu.h>
#include <linux/pci.h>
#include "amdxdna_cbuf.h"
#include "amdxdna_ctx.h"
#include "amdxdna_debugfs.h"
#include "amdxdna_gem.h"
#include "amdxdna_pci_drv.h"
#include "amdxdna_pm.h"
@ -67,11 +69,40 @@ static const struct amdxdna_device_id amdxdna_ids[] = {
{0}
};
static int amdxdna_sva_init(struct amdxdna_client *client)
{
struct amdxdna_dev *xdna = client->xdna;
client->sva = iommu_sva_bind_device(xdna->ddev.dev, client->mm);
if (IS_ERR(client->sva)) {
XDNA_ERR(xdna, "SVA bind device failed, ret %ld", PTR_ERR(client->sva));
return PTR_ERR(client->sva);
}
client->pasid = iommu_sva_get_pasid(client->sva);
if (client->pasid == IOMMU_PASID_INVALID) {
iommu_sva_unbind_device(client->sva);
XDNA_ERR(xdna, "SVA get pasid failed");
return -ENODEV;
}
return 0;
}
static void amdxdna_sva_fini(struct amdxdna_client *client)
{
if (IS_ERR_OR_NULL(client->sva))
return;
iommu_sva_unbind_device(client->sva);
client->sva = NULL;
client->pasid = IOMMU_PASID_INVALID;
}
static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
{
struct amdxdna_dev *xdna = to_xdna_dev(ddev);
struct amdxdna_client *client;
int ret;
client = kzalloc_obj(*client);
if (!client)
@ -80,22 +111,13 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
client->pid = pid_nr(rcu_access_pointer(filp->pid));
client->xdna = xdna;
client->pasid = IOMMU_PASID_INVALID;
client->mm = current->mm;
if (!amdxdna_iova_on(xdna)) {
client->sva = iommu_sva_bind_device(xdna->ddev.dev, current->mm);
if (IS_ERR(client->sva)) {
ret = PTR_ERR(client->sva);
XDNA_ERR(xdna, "SVA bind device failed, ret %d", ret);
goto failed;
}
client->pasid = iommu_sva_get_pasid(client->sva);
if (client->pasid == IOMMU_PASID_INVALID) {
XDNA_ERR(xdna, "SVA get pasid failed");
ret = -ENODEV;
goto unbind_sva;
}
/* No need to fail open since user may use pa + carveout later. */
if (amdxdna_sva_init(client))
XDNA_WARN(xdna, "PASID not available for pid %d", client->pid);
}
client->mm = current->mm;
mmgrab(client->mm);
init_srcu_struct(&client->hwctx_srcu);
xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC);
@ -110,14 +132,6 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
XDNA_DBG(xdna, "pid %d opened", client->pid);
return 0;
unbind_sva:
if (!IS_ERR_OR_NULL(client->sva))
iommu_sva_unbind_device(client->sva);
failed:
kfree(client);
return ret;
}
static void amdxdna_client_cleanup(struct amdxdna_client *client)
@ -131,11 +145,8 @@ static void amdxdna_client_cleanup(struct amdxdna_client *client)
drm_gem_object_put(to_gobj(client->dev_heap));
mutex_destroy(&client->mm_lock);
if (!IS_ERR_OR_NULL(client->sva))
iommu_sva_unbind_device(client->sva);
mmdrop(client->mm);
amdxdna_sva_fini(client);
kfree(client);
}
@ -242,15 +253,17 @@ static void amdxdna_show_fdinfo(struct drm_printer *p, struct drm_file *filp)
/*
* Note for driver specific BO memory usage stat.
* Total memory alloc = amdxdna-internal-alloc + amdxdna-external-alloc
* Total memory in use = amdxdna-internal-alloc + amdxdna-external-alloc, which
* includes both imported and created BOs. To avoid double counts, it includes
* HEAP BO, but not DEV BO. DEV BO is counted by amdxdna-heap-alloc.
*/
drm_fdinfo_print_size(p, drv_name, "heap", "alloc", heap_usage);
drm_fdinfo_print_size(p, drv_name, "internal", "alloc", internal_usage);
drm_fdinfo_print_size(p, drv_name, "external", "alloc", external_usage);
/*
* Note for DRM standard BO memory stat.
* drm-total-memory counts both DEV BO and HEAP BO
* drm-shared-memory counts BO imported
* drm-total-memory counts both DEV BO and HEAP BO. The DEV BO size is double counted.
* drm-shared-memory counts BO shared with other processes/devices.
*/
drm_show_memory_stats(p, filp);
}
@ -299,25 +312,38 @@ amdxdna_get_dev_info(struct pci_dev *pdev)
return NULL;
}
static void amdxdna_xdna_drm_release(struct drm_device *drm, void *res)
{
struct amdxdna_dev *xdna = res;
amdxdna_carveout_fini(xdna);
}
static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct amdxdna_dev *xdna;
struct drm_device *ddev;
int ret;
xdna = devm_drm_dev_alloc(dev, &amdxdna_drm_drv, typeof(*xdna), ddev);
if (IS_ERR(xdna))
return PTR_ERR(xdna);
ddev = &xdna->ddev;
xdna->dev_info = amdxdna_get_dev_info(pdev);
if (!xdna->dev_info)
return -ENODEV;
drmm_mutex_init(&xdna->ddev, &xdna->dev_lock);
drmm_mutex_init(ddev, &xdna->dev_lock);
init_rwsem(&xdna->notifier_lock);
INIT_LIST_HEAD(&xdna->client_list);
pci_set_drvdata(pdev, xdna);
ret = drmm_add_action(ddev, amdxdna_xdna_drm_release, xdna);
if (ret)
return ret;
if (IS_ENABLED(CONFIG_LOCKDEP)) {
fs_reclaim_acquire(GFP_KERNEL);
might_lock(&xdna->notifier_lock);
@ -348,12 +374,13 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto failed_dev_fini;
}
ret = drm_dev_register(&xdna->ddev, 0);
ret = drm_dev_register(ddev, 0);
if (ret) {
XDNA_ERR(xdna, "DRM register failed, ret %d", ret);
goto failed_sysfs_fini;
}
amdxdna_debugfs_init(xdna);
return 0;
failed_sysfs_fini:

View File

@ -104,6 +104,8 @@ struct amdxdna_fw_ver {
u32 build;
};
struct amdxdna_carveout;
struct amdxdna_dev {
struct drm_device ddev;
struct amdxdna_dev_hdl *dev_handle;
@ -121,6 +123,8 @@ struct amdxdna_dev {
struct iova_domain iovad;
/* Accurate board name queried from firmware, or default_vbnv as fallback */
const char *vbnv;
struct amdxdna_carveout *carveout;
};
/*
@ -172,11 +176,11 @@ void amdxdna_sysfs_fini(struct amdxdna_dev *xdna);
int amdxdna_iommu_init(struct amdxdna_dev *xdna);
void amdxdna_iommu_fini(struct amdxdna_dev *xdna);
int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo);
void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo);
void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr);
void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size,
void *cpu_addr, dma_addr_t dma_addr);
int amdxdna_dma_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo);
void amdxdna_dma_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo);
static inline bool amdxdna_iova_on(struct amdxdna_dev *xdna)
{

View File

@ -97,6 +97,7 @@ static const struct amdxdna_dev_priv npu1_dev_priv = {
.rt_config = npu1_default_rt_cfg,
.dpm_clk_tbl = npu1_dpm_clk_table,
.col_align = COL_ALIGN_NONE,
.col_opc = 2048,
.mbox_dev_addr = NPU1_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU1_SRAM_BAR_BASE,

View File

@ -160,6 +160,7 @@ static const struct amdxdna_dev_priv npu4_dev_priv = {
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
.col_align = COL_ALIGN_NATURE,
.col_opc = 4096,
.mbox_dev_addr = NPU4_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU4_SRAM_BAR_BASE,

View File

@ -67,6 +67,7 @@ static const struct amdxdna_dev_priv npu5_dev_priv = {
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
.col_align = COL_ALIGN_NATURE,
.col_opc = 4096,
.mbox_dev_addr = NPU5_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU5_SRAM_BAR_BASE,

View File

@ -67,6 +67,7 @@ static const struct amdxdna_dev_priv npu6_dev_priv = {
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
.col_align = COL_ALIGN_NATURE,
.col_opc = 4096,
.mbox_dev_addr = NPU6_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU6_SRAM_BAR_BASE,

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#include <linux/firmware.h>
@ -234,7 +234,7 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f
args->value = vdev->platform;
break;
case DRM_IVPU_PARAM_CORE_CLOCK_RATE:
args->value = ivpu_hw_dpu_max_freq_get(vdev);
args->value = ivpu_hw_btrs_pll_ratio_to_hz(vdev, vdev->hw->pll.max_ratio);
break;
case DRM_IVPU_PARAM_NUM_CONTEXTS:
args->value = file_priv->user_limits->max_ctx_count;
@ -487,6 +487,10 @@ int ivpu_boot(struct ivpu_device *vdev)
ret = ivpu_hw_sched_init(vdev);
if (ret)
goto err_disable_ipc;
ret = ivpu_hw_btrs_cfg_freq_init(vdev);
if (ret)
goto err_disable_ipc;
}
return 0;

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#ifndef __IVPU_HW_H__
@ -28,6 +28,7 @@ struct ivpu_hw_info {
struct ivpu_addr_range dma;
} ranges;
struct {
/* Hardware min and max pll ratio */
u8 min_ratio;
u8 max_ratio;
/*
@ -35,6 +36,9 @@ struct ivpu_hw_info {
* performance to power ratio at this frequency.
*/
u8 pn_ratio;
/* Pll ratios configured via sysfs interface */
u8 cfg_min_ratio;
u8 cfg_max_ratio;
u32 profiling_freq;
} pll;
struct {
@ -80,16 +84,6 @@ static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range)
return range->end - range->start;
}
static inline u32 ivpu_hw_dpu_max_freq_get(struct ivpu_device *vdev)
{
return ivpu_hw_btrs_dpu_max_freq_get(vdev);
}
static inline u32 ivpu_hw_dpu_freq_get(struct ivpu_device *vdev)
{
return ivpu_hw_btrs_dpu_freq_get(vdev);
}
static inline void ivpu_hw_irq_clear(struct ivpu_device *vdev)
{
ivpu_hw_ip_irq_clear(vdev);

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#include <linux/units.h>
@ -11,6 +11,7 @@
#include "ivpu_hw_btrs_lnl_reg.h"
#include "ivpu_hw_btrs_mtl_reg.h"
#include "ivpu_hw_reg_io.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_pm.h"
#define BTRS_MTL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR)) | \
@ -33,8 +34,7 @@
#define PLL_CDYN_DEFAULT 0x80
#define PLL_EPP_DEFAULT 0x80
#define PLL_REF_CLK_FREQ 50000000ull
#define PLL_RATIO_TO_FREQ(x) ((x) * PLL_REF_CLK_FREQ)
#define PLL_REF_CLK_FREQ_MHZ 50
#define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC)
#define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC)
@ -59,8 +59,6 @@
#define DCT_ENABLE 0x1
#define DCT_DISABLE 0x0
static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio);
int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev)
{
REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, BTRS_MTL_ALL_IRQ_MASK);
@ -111,6 +109,8 @@ void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev)
hw->pll.min_ratio = clamp_t(u8, ivpu_pll_min_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
hw->pll.max_ratio = clamp_t(u8, ivpu_pll_max_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
hw->pll.pn_ratio = clamp_t(u8, hw->pll.pn_ratio, hw->pll.min_ratio, hw->pll.max_ratio);
hw->pll.cfg_max_ratio = hw->pll.max_ratio;
hw->pll.cfg_min_ratio = hw->pll.min_ratio;
}
static bool tile_disable_check(u32 config)
@ -341,8 +341,8 @@ int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
prepare_wp_request(vdev, &wp, enable);
ivpu_dbg(vdev, PM, "PLL workpoint request: %lu MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n",
pll_ratio_to_dpu_freq(vdev, wp.target) / HZ_PER_MHZ, wp.cfg, wp.epp, wp.cdyn);
ivpu_dbg(vdev, PM, "PLL workpoint request: %u MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n",
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, wp.target), wp.cfg, wp.epp, wp.cdyn);
ret = wp_request_send(vdev, &wp);
if (ret) {
@ -598,35 +598,101 @@ static u32 pll_config_get_lnl(struct ivpu_device *vdev)
return REGB_RD32(VPU_HW_BTRS_LNL_PLL_FREQ);
}
static u32 pll_ratio_to_dpu_freq_mtl(u16 ratio)
static u32 pll_ratio_to_mhz_mtl(u8 pll_ratio)
{
return (PLL_RATIO_TO_FREQ(ratio) * 2) / 3;
return (pll_ratio * PLL_REF_CLK_FREQ_MHZ * 2) / 3;
}
static u32 pll_ratio_to_dpu_freq_lnl(u16 ratio)
static u32 pll_ratio_to_mhz_lnl(u8 pll_ratio)
{
return PLL_RATIO_TO_FREQ(ratio) / 2;
return (pll_ratio * PLL_REF_CLK_FREQ_MHZ) / 2;
}
static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio)
u32 ivpu_hw_btrs_pll_ratio_to_mhz(struct ivpu_device *vdev, u8 pll_ratio)
{
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
return pll_ratio_to_dpu_freq_mtl(ratio);
return pll_ratio_to_mhz_mtl(pll_ratio);
else
return pll_ratio_to_dpu_freq_lnl(ratio);
return pll_ratio_to_mhz_lnl(pll_ratio);
}
u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev)
u32 ivpu_hw_btrs_pll_ratio_to_hz(struct ivpu_device *vdev, u8 pll_ratio)
{
return pll_ratio_to_dpu_freq(vdev, vdev->hw->pll.max_ratio);
return ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pll_ratio) * HZ_PER_MHZ;
}
u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev)
u32 ivpu_hw_btrs_current_freq_get(struct ivpu_device *vdev)
{
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
return pll_ratio_to_dpu_freq_mtl(pll_config_get_mtl(vdev));
return pll_ratio_to_mhz_mtl(pll_config_get_mtl(vdev));
else
return pll_ratio_to_dpu_freq_lnl(pll_config_get_lnl(vdev));
return pll_ratio_to_mhz_lnl(pll_config_get_lnl(vdev));
}
static int ivpu_hw_btrs_cfg_freq_set(struct ivpu_device *vdev, u8 cfg_min_ratio, u8 cfg_max_ratio)
{
u8 min_ratio = clamp_t(u8, cfg_min_ratio, vdev->hw->pll.min_ratio, cfg_max_ratio);
u8 pn_ratio = clamp_t(u8, vdev->hw->pll.pn_ratio, min_ratio, cfg_max_ratio);
int ret;
ivpu_dbg(vdev, PM, "Set frequency range to min: %u, pn: %u, max: %u MHz\n",
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, min_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pn_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, cfg_max_ratio));
ret = ivpu_rpm_get(vdev);
if (ret < 0)
return ret;
ret = ivpu_jsm_msg_freq_config(vdev, min_ratio, pn_ratio, cfg_max_ratio);
ivpu_rpm_put(vdev);
if (ret) {
ivpu_warn(vdev,
"Failed to set frequency to min: %u, pn: %u, max: %u MHz, ret %d\n",
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, min_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pn_ratio),
ivpu_hw_btrs_pll_ratio_to_mhz(vdev, cfg_max_ratio),
ret);
return ret;
}
vdev->hw->pll.cfg_min_ratio = cfg_min_ratio;
vdev->hw->pll.cfg_max_ratio = cfg_max_ratio;
return 0;
}
static u8 dpu_mhz_to_pll_ratio_lnl(u32 freq_mhz)
{
return clamp_t(u32, freq_mhz / (PLL_REF_CLK_FREQ_MHZ / 2), 0, U8_MAX);
}
int ivpu_hw_btrs_cfg_max_freq_set(struct ivpu_device *vdev, u32 max_freq_mhz)
{
u8 ratio = dpu_mhz_to_pll_ratio_lnl(max_freq_mhz);
u8 cfg_max_ratio = clamp_t(u8, ratio, vdev->hw->pll.min_ratio, vdev->hw->pll.max_ratio);
return ivpu_hw_btrs_cfg_freq_set(vdev, vdev->hw->pll.cfg_min_ratio, cfg_max_ratio);
}
int ivpu_hw_btrs_cfg_min_freq_set(struct ivpu_device *vdev, u32 min_freq_mhz)
{
u8 ratio = dpu_mhz_to_pll_ratio_lnl(min_freq_mhz);
u8 cfg_min_ratio = clamp_t(u8, ratio, vdev->hw->pll.min_ratio, vdev->hw->pll.max_ratio);
return ivpu_hw_btrs_cfg_freq_set(vdev, cfg_min_ratio, vdev->hw->pll.cfg_max_ratio);
}
int ivpu_hw_btrs_cfg_freq_init(struct ivpu_device *vdev)
{
if (vdev->hw->pll.min_ratio == vdev->hw->pll.cfg_min_ratio &&
vdev->hw->pll.max_ratio == vdev->hw->pll.cfg_max_ratio)
return 0;
return ivpu_hw_btrs_cfg_freq_set(vdev,
vdev->hw->pll.cfg_min_ratio,
vdev->hw->pll.cfg_max_ratio);
}
/* Handler for IRQs from Buttress core (irqB) */
@ -641,8 +707,8 @@ bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq)
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
u32 pll = pll_config_get_mtl(vdev);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz",
pll, pll_ratio_to_dpu_freq_mtl(pll) / HZ_PER_MHZ);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %u MHz",
pll, pll_ratio_to_mhz_mtl(pll));
}
if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, status)) {
@ -695,8 +761,8 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
u32 pll = pll_config_get_lnl(vdev);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz",
pll, pll_ratio_to_dpu_freq_lnl(pll) / HZ_PER_MHZ);
ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %u MHz",
pll, pll_ratio_to_mhz_lnl(pll));
}
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, status)) {

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2025 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#ifndef __IVPU_HW_BTRS_H__
@ -31,8 +31,12 @@ int ivpu_hw_btrs_ip_reset(struct ivpu_device *vdev);
void ivpu_hw_btrs_profiling_freq_reg_set_lnl(struct ivpu_device *vdev);
void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev);
void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_pll_ratio_to_mhz(struct ivpu_device *vdev, u8 pll_ratio);
u32 ivpu_hw_btrs_pll_ratio_to_hz(struct ivpu_device *vdev, u8 pll_ratio);
u32 ivpu_hw_btrs_current_freq_get(struct ivpu_device *vdev);
int ivpu_hw_btrs_cfg_min_freq_set(struct ivpu_device *vdev, u32 freq_mhz);
int ivpu_hw_btrs_cfg_max_freq_set(struct ivpu_device *vdev, u32 freq_mhz);
int ivpu_hw_btrs_cfg_freq_init(struct ivpu_device *vdev);
bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq);
bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq);
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);

View File

@ -307,18 +307,6 @@ static void pwr_island_trickle_drive_40xx(struct ivpu_device *vdev, bool enable)
}
static void pwr_island_drive_37xx(struct ivpu_device *vdev, bool enable)
{
u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0);
if (enable)
val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
else
val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
}
static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable)
{
u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0);
@ -330,6 +318,18 @@ static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable)
REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
}
static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable)
{
u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0);
if (enable)
val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
else
val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
}
static void pwr_island_enable(struct ivpu_device *vdev)
{
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020-2024 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#include "ivpu_drv.h"
@ -86,6 +86,9 @@ const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type)
IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE_DONE);
IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE);
IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE_DONE);
IVPU_CASE_TO_STR(VPU_JSM_MSG_FREQ_CONFIG);
IVPU_CASE_TO_STR(VPU_JSM_MSG_FREQ_CONFIG_RSP);
IVPU_CASE_TO_STR(VPU_JSM_MSG_RESERVED_111E);
}
#undef IVPU_CASE_TO_STR
@ -571,3 +574,16 @@ int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev)
return ivpu_ipc_send_and_wait(vdev, &req, VPU_IPC_CHAN_ASYNC_CMD,
vdev->timeout.state_dump_msg);
}
int ivpu_jsm_msg_freq_config(struct ivpu_device *vdev, u16 min_ratio, u16 pn_ratio, u16 max_ratio)
{
struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_FREQ_CONFIG};
struct vpu_jsm_msg resp;
req.payload.freq_config.min_freq_pll_ratio = min_ratio;
req.payload.freq_config.pn_freq_pll_ratio = pn_ratio;
req.payload.freq_config.max_freq_pll_ratio = max_ratio;
return ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_FREQ_CONFIG_RSP, &resp,
VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);
}

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2020-2024 Intel Corporation
* Copyright (C) 2020-2026 Intel Corporation
*/
#ifndef __IVPU_JSM_MSG_H__
@ -45,5 +45,6 @@ int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us
int ivpu_jsm_dct_disable(struct ivpu_device *vdev);
int ivpu_jsm_state_dump(struct ivpu_device *vdev);
int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev);
int ivpu_jsm_msg_freq_config(struct ivpu_device *vdev, u16 min_ratio, u16 pn_ratio, u16 max_ratio);
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#include <linux/device.h>
@ -82,8 +82,7 @@ static DEVICE_ATTR_RO(npu_memory_utilization);
* - "OS" - Operating System Scheduler mode
*
*/
static ssize_t
sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
@ -94,47 +93,162 @@ sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR_RO(sched_mode);
/**
* DOC: npu_max_frequency
* DOC: NPU frequency control and information
*
* The npu_max_frequency shows maximum frequency in MHz of the NPU's data
* processing unit
* Hardware frequency capabilities:
* freq/hw_min_freq - Minimum frequency supported by the NPU hardware.
* freq/hw_max_freq - Maximum frequency supported by the NPU hardware.
* freq/hw_efficient_freq - Most efficient operating frequency for the NPU.
*
* Configurable frequency limits (50XX devices and newer):
* freq/set_min_freq - Configured minimum operating frequency.
* freq/set_max_freq - Configured maximum operating frequency.
*
* Clamping behavior: Values written to set_min_freq and set_max_freq are
* clamped to hardware limits. If set_min_freq exceeds set_max_freq, the driver
* clamps set_min_freq to set_max_freq when selecting the operating frequency.
*
* Current operating frequency:
* freq/current_freq - Current frequency in MHz. Valid only when the device is
* active; returns 0 when idle. May be lower than the requested range due to
* power or thermal constraints.
*
* Legacy attributes (backward compatibility):
* npu_max_frequency_mhz - Alias for freq/hw_max_freq.
* npu_current_frequency_mhz - Alias for freq/current_freq.
*/
static ssize_t
npu_max_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t hw_min_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq = ivpu_hw_dpu_max_freq_get(vdev);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.min_ratio);
return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(npu_max_frequency_mhz);
static DEVICE_ATTR_RO(hw_min_freq);
/**
* DOC: npu_current_frequency_mhz
*
* The npu_current_frequency_mhz shows current frequency in MHz of the NPU's
* data processing unit
*/
static ssize_t
npu_current_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t hw_efficient_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq = 0;
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.pn_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(hw_efficient_freq);
static ssize_t hw_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.max_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(hw_max_freq);
static ssize_t set_min_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.cfg_min_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static ssize_t
set_min_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz;
int ret;
ret = kstrtou32(buf, 10, &freq_mhz);
if (ret)
return ret;
ret = ivpu_hw_btrs_cfg_min_freq_set(vdev, freq_mhz);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(set_min_freq);
static ssize_t set_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.cfg_max_ratio);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static ssize_t
set_max_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz;
int ret;
ret = kstrtou32(buf, 10, &freq_mhz);
if (ret)
return ret;
/* Convert MHz to Hz and set max frequency */
ret = ivpu_hw_btrs_cfg_max_freq_set(vdev, freq_mhz);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(set_max_freq);
static ssize_t current_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
u32 freq_mhz = 0;
/* Read frequency only if device is active, otherwise frequency is 0 */
if (pm_runtime_get_if_active(vdev->drm.dev) > 0) {
freq = ivpu_hw_dpu_freq_get(vdev);
freq_mhz = ivpu_hw_btrs_current_freq_get(vdev);
pm_runtime_put_autosuspend(vdev->drm.dev);
}
return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ);
return sysfs_emit(buf, "%u\n", freq_mhz);
}
static DEVICE_ATTR_RO(npu_current_frequency_mhz);
static DEVICE_ATTR_RO(current_freq);
/* Alias to current_freq for legacy compat */
static struct device_attribute dev_attr_npu_max_frequency_mhz =
__ATTR(npu_max_frequency_mhz, 0444, hw_max_freq_show, NULL);
static struct device_attribute dev_attr_npu_current_frequency_mhz =
__ATTR(npu_current_frequency_mhz, 0444, current_freq_show, NULL);
static struct attribute *ivpu_freq_attrs[] = {
&dev_attr_hw_min_freq.attr,
&dev_attr_hw_efficient_freq.attr,
&dev_attr_hw_max_freq.attr,
&dev_attr_current_freq.attr,
NULL,
};
static struct attribute_group ivpu_freq_attr_group = {
.name = "freq",
.attrs = ivpu_freq_attrs,
};
static struct attribute *ivpu_dev_attrs[] = {
&dev_attr_npu_busy_time_us.attr,
@ -154,6 +268,32 @@ void ivpu_sysfs_init(struct ivpu_device *vdev)
int ret;
ret = devm_device_add_group(vdev->drm.dev, &ivpu_dev_attr_group);
if (ret)
ivpu_warn(vdev, "Failed to add group to device, ret %d", ret);
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs group to device, ret %d", ret);
return;
}
ret = devm_device_add_group(vdev->drm.dev, &ivpu_freq_attr_group);
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs freq group, ret %d", ret);
return;
}
if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_50XX) {
ret = sysfs_add_file_to_group(&vdev->drm.dev->kobj,
&dev_attr_set_min_freq.attr,
"freq");
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs set_min_freq to device, ret %d", ret);
return;
}
ret = sysfs_add_file_to_group(&vdev->drm.dev->kobj,
&dev_attr_set_max_freq.attr,
"freq");
if (ret) {
ivpu_warn(vdev, "Failed to add sysfs set_max_freq to device, ret %d", ret);
return;
}
}
}

View File

@ -23,7 +23,7 @@
/*
* Minor version changes when API backward compatibility is preserved.
*/
#define VPU_JSM_API_VER_MINOR 33
#define VPU_JSM_API_VER_MINOR 34
/*
* API header changed (field names, documentation, formatting) but API itself has not been changed
@ -604,6 +604,16 @@ enum vpu_ipc_msg_type {
* This command has no payload
*/
VPU_JSM_MSG_DCT_DISABLE = 0x111d,
/**
* Reserved command ID to ensure that the following command requests /
* responses have the same lower byte value.
*/
VPU_JSM_MSG_RESERVED_111E = 0x111e,
/**
* Control command: Configure VPU frequency scaling parameters.
* @see vpu_ipc_msg_payload_freq_config
*/
VPU_JSM_MSG_FREQ_CONFIG = 0x111f,
/**
* Dump VPU state. To be used for debug purposes only.
* This command has no payload.
@ -766,6 +776,11 @@ enum vpu_ipc_msg_type {
* This command has no payload
*/
VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e,
/**
* Response to control command: Configure VPU frequency scaling parameters.
* @see vpu_ipc_msg_payload_freq_config
*/
VPU_JSM_MSG_FREQ_CONFIG_RSP = 0x221f,
/**
* Response to state dump control command.
* This command has no payload.
@ -1650,6 +1665,22 @@ struct vpu_ipc_msg_payload_pwr_dct_control {
u32 dct_inactive_us;
};
/**
* Payload for @ref VPU_JSM_MSG_FREQ_CONFIG message.
*
* This payload allows the host to configure the VPU frequency scaling parameters.
*/
struct vpu_ipc_msg_payload_freq_config {
/** Minimum frequency PLL ratio */
u32 min_freq_pll_ratio;
/** Efficiency frequency PLL ratio */
u32 pn_freq_pll_ratio;
/** Maximum frequency PLL ratio */
u32 max_freq_pll_ratio;
/** Reserved for 64-bit alignment */
u32 reserved_0;
};
/*
* Payloads union, used to define complete message format.
*/
@ -1692,6 +1723,7 @@ union vpu_ipc_msg_payload {
struct vpu_ipc_msg_payload_hws_resume_engine hws_resume_engine;
struct vpu_ipc_msg_payload_pwr_d0i3_enter pwr_d0i3_enter;
struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control;
struct vpu_ipc_msg_payload_freq_config freq_config;
};
/**

View File

@ -190,15 +190,13 @@ EXPORT_SYMBOL(dma_fence_array_alloc);
* @fences: [in] array containing the fences
* @context: [in] fence context to use
* @seqno: [in] sequence number to use
* @signal_on_any: [in] signal on any fence in the array
*
* Implementation of @dma_fence_array_create without allocation. Useful to init
* a preallocated dma fence array in the path of reclaim or dma fence signaling.
*/
void dma_fence_array_init(struct dma_fence_array *array,
int num_fences, struct dma_fence **fences,
u64 context, unsigned seqno,
bool signal_on_any)
u64 context, unsigned seqno)
{
static struct lock_class_key dma_fence_array_lock_key;
@ -222,7 +220,7 @@ void dma_fence_array_init(struct dma_fence_array *array,
*/
lockdep_set_class(&array->base.inline_lock, &dma_fence_array_lock_key);
atomic_set(&array->num_pending, signal_on_any ? 1 : num_fences);
atomic_set(&array->num_pending, num_fences);
array->fences = fences;
array->base.error = PENDING_ERROR;
@ -249,7 +247,6 @@ EXPORT_SYMBOL(dma_fence_array_init);
* @fences: [in] array containing the fences
* @context: [in] fence context to use
* @seqno: [in] sequence number to use
* @signal_on_any: [in] signal on any fence in the array
*
* Allocate a dma_fence_array object and initialize the base fence with
* dma_fence_init().
@ -264,8 +261,7 @@ EXPORT_SYMBOL(dma_fence_array_init);
*/
struct dma_fence_array *dma_fence_array_create(int num_fences,
struct dma_fence **fences,
u64 context, unsigned seqno,
bool signal_on_any)
u64 context, unsigned seqno)
{
struct dma_fence_array *array;
@ -273,8 +269,7 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
if (!array)
return NULL;
dma_fence_array_init(array, num_fences, fences,
context, seqno, signal_on_any);
dma_fence_array_init(array, num_fences, fences, context, seqno);
return array;
}

View File

@ -180,8 +180,7 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
if (count > 1) {
result = dma_fence_array_create(count, array,
dma_fence_context_alloc(1),
1, false);
dma_fence_context_alloc(1), 1);
if (!result) {
for (i = 0; i < count; i++)
dma_fence_put(array[i]);

View File

@ -648,8 +648,7 @@ int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage,
}
array = dma_fence_array_create(count, fences,
dma_fence_context_alloc(1),
1, false);
dma_fence_context_alloc(1), 1);
if (!array) {
while (count--)
dma_fence_put(fences[count]);

View File

@ -64,7 +64,7 @@ static struct dma_fence *mock_array(unsigned int num_fences, ...)
array = dma_fence_array_create(num_fences, fences,
dma_fence_context_alloc(1),
1, false);
1);
if (!array)
goto error_free;
return &array->base;

View File

@ -444,8 +444,6 @@ static int adp_setup_mode_config(struct adp_drv_private *adp)
if (IS_ERR(adp->connector))
return PTR_ERR(adp->connector);
drm_connector_attach_encoder(adp->connector, adp->encoder);
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0) {
drm_err(drm, "failed to initialize vblank");

View File

@ -768,8 +768,6 @@ static int adv7511_connector_init(struct adv7511 *adv)
return PTR_ERR(connector);
}
drm_connector_attach_encoder(connector, bridge->encoder);
return 0;
}

View File

@ -1486,7 +1486,7 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev)
dp->plat_data->next_bridge = devm_drm_panel_bridge_add(dp->dev,
dp->plat_data->panel);
if (IS_ERR(dp->plat_data->next_bridge)) {
ret = PTR_ERR(bridge);
ret = PTR_ERR(dp->plat_data->next_bridge);
goto err_unregister_aux;
}
}

View File

@ -1230,7 +1230,7 @@ static const struct mipi_dsi_host_ops cdns_dsi_ops = {
.transfer = cdns_dsi_transfer,
};
static int __maybe_unused cdns_dsi_resume(struct device *dev)
static int cdns_dsi_resume(struct device *dev)
{
struct cdns_dsi *dsi = dev_get_drvdata(dev);
@ -1241,7 +1241,7 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev)
return 0;
}
static int __maybe_unused cdns_dsi_suspend(struct device *dev)
static int cdns_dsi_suspend(struct device *dev)
{
struct cdns_dsi *dsi = dev_get_drvdata(dev);
@ -1251,8 +1251,9 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
return 0;
}
static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume,
NULL);
static const struct dev_pm_ops cdns_dsi_pm_ops = {
RUNTIME_PM_OPS(cdns_dsi_suspend, cdns_dsi_resume, NULL)
};
static int cdns_dsi_drm_probe(struct platform_device *pdev)
{
@ -1399,7 +1400,7 @@ static struct platform_driver cdns_dsi_platform_driver = {
.driver = {
.name = "cdns-dsi",
.of_match_table = cdns_dsi_of_match,
.pm = &cdns_dsi_pm_ops,
.pm = pm_ptr(&cdns_dsi_pm_ops),
},
};
module_platform_driver(cdns_dsi_platform_driver);

View File

@ -200,6 +200,7 @@ struct it6263 {
struct regmap *lvds_regmap;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
struct gpio_desc *reset_gpio;
int lvds_data_mapping;
bool lvds_dual_link;
bool lvds_link12_swap;
@ -603,6 +604,15 @@ static void it6263_bridge_atomic_enable(struct drm_bridge *bridge,
bool pclk_high;
int i, ret;
it6263_hw_reset(it->reset_gpio);
ret = it6263_lvds_set_i2c_addr(it);
if (ret)
dev_err(it->dev, "failed to set I2C addr\n");
it6263_lvds_config(it);
it6263_hdmi_config(it);
connector = drm_atomic_get_new_connector_for_encoder(state,
bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
@ -697,8 +707,6 @@ static int it6263_bridge_attach(struct drm_bridge *bridge,
return ret;
}
drm_connector_attach_encoder(connector, encoder);
return 0;
}
@ -815,7 +823,6 @@ static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL,
ENABLE_PKT | REPEAT_PKT);
return 0;
}
@ -840,7 +847,6 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = {
static int it6263_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct gpio_desc *reset_gpio;
struct it6263 *it;
int ret;
@ -858,9 +864,9 @@ static int it6263_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(it->hdmi_regmap),
"failed to init I2C regmap for HDMI\n");
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset_gpio))
return dev_err_probe(dev, PTR_ERR(reset_gpio),
it->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(it->reset_gpio))
return dev_err_probe(dev, PTR_ERR(it->reset_gpio),
"failed to get reset gpio\n");
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it6263_supplies),
@ -872,12 +878,6 @@ static int it6263_probe(struct i2c_client *client)
if (ret)
return ret;
it6263_hw_reset(reset_gpio);
ret = it6263_lvds_set_i2c_addr(it);
if (ret)
return dev_err_probe(dev, ret, "failed to set I2C addr\n");
it->lvds_i2c = devm_i2c_new_dummy_device(dev, client->adapter,
LVDS_INPUT_CTRL_I2C_ADDR);
if (IS_ERR(it->lvds_i2c))
@ -890,9 +890,6 @@ static int it6263_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(it->lvds_regmap),
"failed to init I2C regmap for LVDS\n");
it6263_lvds_config(it);
it6263_hdmi_config(it);
i2c_set_clientdata(client, it);
it->bridge.of_node = dev->of_node;

View File

@ -2048,7 +2048,7 @@ static void it6505_start_hdcp(struct it6505 *it6505)
DRM_DEV_DEBUG_DRIVER(dev, "start");
it6505_reset_hdcp(it6505);
queue_delayed_work(system_wq, &it6505->hdcp_work,
queue_delayed_work(system_percpu_wq, &it6505->hdcp_work,
msecs_to_jiffies(2400));
}

View File

@ -493,6 +493,7 @@ static int lt8912_attach_dsi(struct lt8912 *lt)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_LPM |
MIPI_DSI_MODE_VIDEO_NO_HFP |
MIPI_DSI_MODE_NO_EOT_PACKET;
ret = devm_mipi_dsi_attach(dev, dsi);

View File

@ -11,6 +11,7 @@
#include <linux/component.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/media-bus-format.h>
#include <linux/mfd/syscon.h>
#include <linux/of_graph.h>
#include <linux/pinctrl/devinfo.h>
@ -23,7 +24,6 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@ -41,9 +41,11 @@
/* Bitfields in LVDSC_CFGR (Configuration Register) */
#define LVDSC_CFGR_PIXSIZE_24BITS 0
#define LVDSC_CFGR_PIXSIZE_18BITS BIT(0)
#define LVDSC_CFGR_DEN_POL_HIGH 0
#define LVDSC_CFGR_DC_UNBALANCED 0
#define LVDSC_CFGR_MAPPING_JEIDA BIT(6)
#define LVDSC_CFGR_MAPPING_VESA 0
/*Bitfields in LVDSC_SR */
#define LVDSC_SR_CS BIT(0)
@ -56,7 +58,6 @@ struct mchp_lvds {
struct device *dev;
void __iomem *regs;
struct clk *pclk;
struct drm_panel *panel;
struct drm_bridge bridge;
struct drm_bridge *panel_bridge;
};
@ -76,9 +77,10 @@ static inline void lvds_writel(struct mchp_lvds *lvds, u32 offset, u32 val)
writel_relaxed(val, lvds->regs + offset);
}
static void lvds_serialiser_on(struct mchp_lvds *lvds)
static void lvds_serialiser_on(struct mchp_lvds *lvds, u32 bus_format)
{
unsigned long timeout = jiffies + msecs_to_jiffies(LVDS_POLL_TIMEOUT_MS);
u8 map, pix_size;
/* The LVDSC registers can only be written if WPEN is cleared */
lvds_writel(lvds, LVDSC_WPMR, (LVDSC_WPMR_WPKEY_PSSWD &
@ -93,11 +95,24 @@ static void lvds_serialiser_on(struct mchp_lvds *lvds)
usleep_range(1000, 2000);
}
switch (bus_format) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
map = LVDSC_CFGR_MAPPING_JEIDA;
pix_size = LVDSC_CFGR_PIXSIZE_18BITS;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
map = LVDSC_CFGR_MAPPING_VESA;
pix_size = LVDSC_CFGR_PIXSIZE_24BITS;
break;
default:
map = LVDSC_CFGR_MAPPING_JEIDA;
pix_size = LVDSC_CFGR_PIXSIZE_24BITS;
break;
}
/* Configure the LVDSC */
lvds_writel(lvds, LVDSC_CFGR, (LVDSC_CFGR_MAPPING_JEIDA |
LVDSC_CFGR_DC_UNBALANCED |
LVDSC_CFGR_DEN_POL_HIGH |
LVDSC_CFGR_PIXSIZE_24BITS));
lvds_writel(lvds, LVDSC_CFGR, map | LVDSC_CFGR_DC_UNBALANCED |
LVDSC_CFGR_DEN_POL_HIGH | pix_size);
/* Enable the LVDS serializer */
lvds_writel(lvds, LVDSC_CR, LVDSC_CR_SER_EN);
@ -113,9 +128,11 @@ static int mchp_lvds_attach(struct drm_bridge *bridge,
bridge, flags);
}
static void mchp_lvds_enable(struct drm_bridge *bridge)
static void mchp_lvds_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct mchp_lvds *lvds = bridge_to_lvds(bridge);
struct drm_connector *connector;
int ret;
ret = clk_prepare_enable(lvds->pclk);
@ -130,10 +147,18 @@ static void mchp_lvds_enable(struct drm_bridge *bridge)
return;
}
lvds_serialiser_on(lvds);
/* default to jeida-24 */
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
if (connector && connector->display_info.num_bus_formats)
bus_format = connector->display_info.bus_formats[0];
lvds_serialiser_on(lvds, bus_format);
}
static void mchp_lvds_disable(struct drm_bridge *bridge)
static void mchp_lvds_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct mchp_lvds *lvds = bridge_to_lvds(bridge);
@ -143,15 +168,14 @@ static void mchp_lvds_disable(struct drm_bridge *bridge)
static const struct drm_bridge_funcs mchp_lvds_bridge_funcs = {
.attach = mchp_lvds_attach,
.enable = mchp_lvds_enable,
.disable = mchp_lvds_disable,
.atomic_enable = mchp_lvds_atomic_enable,
.atomic_disable = mchp_lvds_atomic_disable,
};
static int mchp_lvds_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mchp_lvds *lvds;
struct device_node *port;
int ret;
if (!dev->of_node)
@ -173,19 +197,6 @@ static int mchp_lvds_probe(struct platform_device *pdev)
return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk),
"could not get pclk_lvds\n");
port = of_graph_get_remote_node(dev->of_node, 1, 0);
if (!port) {
dev_err(dev,
"can't find port point, please init lvds panel port!\n");
return -ENODEV;
}
lvds->panel = of_drm_find_panel(port);
of_node_put(port);
if (IS_ERR(lvds->panel))
return -EPROBE_DEFER;
lvds->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
if (IS_ERR(lvds->panel_bridge))

View File

@ -270,6 +270,11 @@ static const struct of_device_id simple_bridge_match[] = {
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
},
}, {
.compatible = "mstar,tsumu88adt3-lf-1",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
},
}, {
.compatible = "parade,ps185hdm",
.data = &(const struct simple_bridge_info) {

View File

@ -45,6 +45,9 @@
/* Debug (16-bit addressable) */
#define TC358768_VBUFCTRL 0x00E0
#define TC358768_VBUFCTRL_VBUF_EN BIT(15)
#define TC358768_VBUFCTRL_TX_EN BIT(14)
#define TC358768_VBUFCTRL_MASK BIT(13)
#define TC358768_DBG_WIDTH 0x00E2
#define TC358768_DBG_VBLANK 0x00E4
#define TC358768_DBG_DATA 0x00E8
@ -115,7 +118,7 @@
#define TC358768_DSI_HACT 0x062C
/* TC358768_DSI_CONTROL (0x040C) register */
#define TC358768_DSI_CONTROL_DIS_MODE BIT(15)
#define TC358768_DSI_CONTROL_DSI_MODE BIT(15)
#define TC358768_DSI_CONTROL_TXMD BIT(7)
#define TC358768_DSI_CONTROL_HSCKMD BIT(5)
#define TC358768_DSI_CONTROL_EOTDIS BIT(0)
@ -123,7 +126,7 @@
/* TC358768_DSI_CONFW (0x0500) register */
#define TC358768_DSI_CONFW_MODE_SET (5 << 29)
#define TC358768_DSI_CONFW_MODE_CLR (6 << 29)
#define TC358768_DSI_CONFW_ADDR_DSI_CONTROL (0x3 << 24)
#define TC358768_DSI_CONFW_ADDR(x) ((x) << 24)
/* TC358768_DSICMD_TX (0x0600) register */
#define TC358768_DSI_CMDTX_DC_START BIT(0)
@ -232,6 +235,36 @@ static void tc358768_update_bits(struct tc358768_priv *priv, u32 reg, u32 mask,
tc358768_write(priv, reg, tmp);
}
static void tc358768_confw_update_bits(struct tc358768_priv *priv, u16 reg,
u16 mask, u16 val)
{
u8 confw_addr;
u32 confw_val;
switch (reg) {
case TC358768_DSI_CONTROL:
confw_addr = 0x3;
break;
default:
priv->error = -EINVAL;
return;
}
if (mask != val) {
confw_val = TC358768_DSI_CONFW_MODE_CLR |
TC358768_DSI_CONFW_ADDR(confw_addr) |
mask;
tc358768_write(priv, TC358768_DSI_CONFW, confw_val);
}
if (val & mask) {
confw_val = TC358768_DSI_CONFW_MODE_SET |
TC358768_DSI_CONFW_ADDR(confw_addr) |
(val & mask);
tc358768_write(priv, TC358768_DSI_CONFW, confw_val);
}
}
static void tc358768_dsicmd_tx(struct tc358768_priv *priv)
{
u32 val;
@ -448,6 +481,8 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
bridge->pre_enable_prev_first = true;
}
priv->output.dev = dev;
@ -505,9 +540,21 @@ static ssize_t tc358768_dsi_host_transfer(struct mipi_dsi_host *host,
return -ENOTSUPP;
}
if (msg->tx_len > 1024) {
dev_warn(priv->dev, "Maximum 1024 byte MIPI tx is supported\n");
return -EINVAL;
}
if (msg->tx_len > 8) {
dev_warn(priv->dev, "Maximum 8 byte MIPI tx is supported\n");
return -ENOTSUPP;
u32 confctl;
tc358768_read(priv, TC358768_CONFCTL, &confctl);
if (confctl & BIT(6)) {
dev_warn(priv->dev,
"Video is currently active. Unable to transmit long command\n");
return -EBUSY;
}
}
ret = mipi_dsi_create_packet(&packet, msg);
@ -520,23 +567,66 @@ static ssize_t tc358768_dsi_host_transfer(struct mipi_dsi_host *host,
tc358768_write(priv, TC358768_DSICMD_WC, 0);
tc358768_write(priv, TC358768_DSICMD_WD0,
(packet.header[2] << 8) | packet.header[1]);
} else {
int i;
tc358768_dsicmd_tx(priv);
} else if (packet.payload_length <= 8) {
tc358768_write(priv, TC358768_DSICMD_TYPE,
(0x40 << 8) | (packet.header[0] & 0x3f));
tc358768_write(priv, TC358768_DSICMD_WC, packet.payload_length);
for (i = 0; i < packet.payload_length; i += 2) {
for (int i = 0; i < packet.payload_length; i += 2) {
u16 val = packet.payload[i];
if (i + 1 < packet.payload_length)
val |= packet.payload[i + 1] << 8;
tc358768_write(priv, TC358768_DSICMD_WD0 + i, val);
}
}
tc358768_dsicmd_tx(priv);
tc358768_dsicmd_tx(priv);
} else {
unsigned long tx_sleep_us;
size_t len;
/* For packets over 8 bytes we need to use the video buffer */
tc358768_write(priv, TC358768_DATAFMT, BIT(0)); /* txdt_en */
tc358768_write(priv, TC358768_DSITX_DT, packet.header[0] & 0x3f);
tc358768_write(priv, TC358768_CMDBYTE, packet.payload_length);
tc358768_write(priv, TC358768_VBUFCTRL, TC358768_VBUFCTRL_VBUF_EN);
/*
* Write the payload in 2-byte chunks, and pad with zeroes to
* align to 4 bytes.
*/
len = ALIGN(packet.payload_length, 4);
for (int i = 0; i < len; i += 2) {
u16 val = 0;
if (i < packet.payload_length)
val |= packet.payload[i];
if (i + 1 < packet.payload_length)
val |= packet.payload[i + 1] << 8;
tc358768_write(priv, TC358768_DBG_DATA, val);
}
/* Start transmission */
tc358768_write(priv, TC358768_VBUFCTRL,
TC358768_VBUFCTRL_VBUF_EN |
TC358768_VBUFCTRL_TX_EN |
TC358768_VBUFCTRL_MASK);
/*
* The TC358768 spec says to wait until the transmission has
* been finished, estimating the sleep time based on the payload
* and clock rates. We use a simple safe estimate of 2us per
* byte (LP mode transmission).
*/
tx_sleep_us = packet.payload_length * 2;
usleep_range(tx_sleep_us, tx_sleep_us * 2);
tc358768_write(priv, TC358768_VBUFCTRL, TC358768_VBUFCTRL_MASK);
tc358768_write(priv, TC358768_VBUFCTRL, 0); /* Stop transmission */
}
ret = tc358768_clear_error(priv);
if (ret)
@ -690,8 +780,7 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
{
struct tc358768_priv *priv = bridge_to_tc358768(bridge);
struct mipi_dsi_device *dsi_dev = priv->output.dev;
unsigned long mode_flags = dsi_dev->mode_flags;
u32 val, val2, lptxcnt, hact, data_type;
u32 val, mask, val2, lptxcnt, hact;
s32 raw_val;
struct drm_crtc_state *crtc_state;
struct drm_connector_state *conn_state;
@ -712,11 +801,6 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
u32 dsi_vsdly;
const u32 internal_dly = 40;
if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
dev_warn_once(dev, "Non-continuous mode unimplemented, falling back to continuous\n");
mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS;
}
tc358768_hw_enable(priv);
ret = tc358768_sw_reset(priv);
@ -726,6 +810,9 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
return;
}
/* Release RstPtr so that the video buffer can be used for DSI commands */
tc358768_update_bits(priv, TC358768_PP_MISC, BIT(14), 0);
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
conn_state = drm_atomic_get_new_connector_state(state, connector);
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
@ -742,30 +829,20 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
dsiclk = priv->dsiclk;
hsbyteclk = dsiclk / 4;
/* Data Format Control Register */
val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */
switch (dsi_dev->format) {
case MIPI_DSI_FMT_RGB888:
val |= (0x3 << 4);
hact = vm.hactive * 3;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
break;
case MIPI_DSI_FMT_RGB666:
val |= (0x4 << 4);
hact = vm.hactive * 3;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
val |= (0x4 << 4) | BIT(3);
hact = vm.hactive * 18 / 8;
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
break;
case MIPI_DSI_FMT_RGB565:
val |= (0x5 << 4);
hact = vm.hactive * 2;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
dev_err(dev, "Invalid data format (%u)\n",
@ -921,9 +998,6 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
/* VSDly[9:0] */
tc358768_write(priv, TC358768_VSDLY, dsi_vsdly - internal_dly);
tc358768_write(priv, TC358768_DATAFMT, val);
tc358768_write(priv, TC358768_DSITX_DT, data_type);
/* Enable D-PHY (HiZ->LP11) */
tc358768_write(priv, TC358768_CLW_CNTRL, 0x0000);
/* Enable lanes */
@ -1000,7 +1074,7 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
tc358768_write(priv, TC358768_HSTXVREGEN, val);
tc358768_write(priv, TC358768_TXOPTIONCNTRL,
(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0));
(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0));
/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
val = tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps * 4);
@ -1063,33 +1137,63 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge,
tc358768_write(priv, TC358768_DSI_START, 0x1);
/* Configure DSI_Control register */
val = TC358768_DSI_CONFW_MODE_CLR | TC358768_DSI_CONFW_ADDR_DSI_CONTROL;
val |= TC358768_DSI_CONTROL_TXMD | TC358768_DSI_CONTROL_HSCKMD |
0x3 << 1 | TC358768_DSI_CONTROL_EOTDIS;
tc358768_write(priv, TC358768_DSI_CONFW, val);
val = (dsi_dev->lanes - 1) << 1;
val = TC358768_DSI_CONFW_MODE_SET | TC358768_DSI_CONFW_ADDR_DSI_CONTROL;
val |= (dsi_dev->lanes - 1) << 1;
if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_LPM))
val |= TC358768_DSI_CONTROL_TXMD;
val |= TC358768_DSI_CONTROL_TXMD;
if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
val |= TC358768_DSI_CONTROL_HSCKMD;
if (dsi_dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)
val |= TC358768_DSI_CONTROL_EOTDIS;
tc358768_write(priv, TC358768_DSI_CONFW, val);
mask = TC358768_DSI_CONTROL_TXMD | TC358768_DSI_CONTROL_HSCKMD |
0x3 << 1 | TC358768_DSI_CONTROL_EOTDIS;
val = TC358768_DSI_CONFW_MODE_CLR | TC358768_DSI_CONFW_ADDR_DSI_CONTROL;
val |= TC358768_DSI_CONTROL_DIS_MODE; /* DSI mode */
tc358768_write(priv, TC358768_DSI_CONFW, val);
tc358768_confw_update_bits(priv, TC358768_DSI_CONTROL, mask, val);
tc358768_confw_update_bits(priv, TC358768_DSI_CONTROL,
TC358768_DSI_CONTROL_DSI_MODE, 0);
ret = tc358768_clear_error(priv);
if (ret)
dev_err(dev, "Bridge pre_enable failed: %d\n", ret);
}
static void tc358768_config_video_format(struct tc358768_priv *priv)
{
struct mipi_dsi_device *dsi_dev = priv->output.dev;
u32 val, data_type;
/* Data Format Control Register */
val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */
switch (dsi_dev->format) {
case MIPI_DSI_FMT_RGB888:
val |= (0x3 << 4);
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
break;
case MIPI_DSI_FMT_RGB666:
val |= (0x4 << 4);
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
val |= (0x4 << 4) | BIT(3);
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
break;
case MIPI_DSI_FMT_RGB565:
val |= (0x5 << 4);
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
dev_err(priv->dev, "Invalid data format (%u)\n", dsi_dev->format);
return;
}
tc358768_write(priv, TC358768_DATAFMT, val);
tc358768_write(priv, TC358768_DSITX_DT, data_type);
}
static void tc358768_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
@ -1101,6 +1205,14 @@ static void tc358768_bridge_atomic_enable(struct drm_bridge *bridge,
return;
}
/* Configure video format registers */
tc358768_config_video_format(priv);
/* Enable HS mode for video TX */
tc358768_confw_update_bits(priv, TC358768_DSI_CONTROL,
TC358768_DSI_CONTROL_TXMD,
TC358768_DSI_CONTROL_TXMD);
/* clear FrmStop and RstPtr */
tc358768_update_bits(priv, TC358768_PP_MISC, 0x3 << 14, 0);

View File

@ -114,6 +114,7 @@
#define REG_VID_CHA_HORIZONTAL_FRONT_PORCH 0x38
#define REG_VID_CHA_VERTICAL_FRONT_PORCH 0x3a
#define REG_VID_CHA_TEST_PATTERN 0x3c
#define REG_VID_CHA_TEST_PATTERN_EN BIT(4)
/* IRQ registers */
#define REG_IRQ_GLOBAL 0xe0
#define REG_IRQ_GLOBAL_IRQ_EN BIT(0)
@ -134,6 +135,9 @@
#define REG_IRQ_STAT_CHA_SOT_BIT_ERR BIT(2)
#define REG_IRQ_STAT_CHA_PLL_UNLOCK BIT(0)
static bool sn65dsi83_test_pattern;
module_param_named(test_pattern, sn65dsi83_test_pattern, bool, 0644);
enum sn65dsi83_channel {
CHANNEL_A,
CHANNEL_B
@ -523,6 +527,7 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
const struct drm_display_mode *mode;
struct drm_connector *connector;
struct drm_crtc *crtc;
bool test_pattern = sn65dsi83_test_pattern;
bool lvds_format_24bpp;
bool lvds_format_jeida;
unsigned int pval;
@ -645,7 +650,11 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
REG_LVDS_LANE_CHB_LVDS_TERM : 0));
regmap_write(ctx->regmap, REG_LVDS_CM, 0x00);
le16val = cpu_to_le16(mode->hdisplay);
/*
* Active line length needs to be halved for test pattern
* generation in dual LVDS output.
*/
le16val = cpu_to_le16(mode->hdisplay / (test_pattern ? dual_factor : 1));
regmap_bulk_write(ctx->regmap, REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW,
&le16val, 2);
le16val = cpu_to_le16(mode->vdisplay);
@ -668,7 +677,8 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
(mode->hsync_start - mode->hdisplay) / dual_factor);
regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH,
mode->vsync_start - mode->vdisplay);
regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00);
regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN,
test_pattern ? REG_VID_CHA_TEST_PATTERN_EN : 0);
/* Enable PLL */
regmap_write(ctx->regmap, REG_RC_PLL_EN, REG_RC_PLL_EN_PLL_EN);

View File

@ -776,8 +776,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge,
goto err_initted_aux;
}
drm_connector_attach_encoder(pdata->connector, pdata->bridge.encoder);
return 0;
err_initted_aux:

View File

@ -114,7 +114,7 @@ static void tfp410_hpd_callback(void *arg, enum drm_connector_status status)
{
struct tfp410 *dvi = arg;
mod_delayed_work(system_wq, &dvi->hpd_work,
mod_delayed_work(system_percpu_wq, &dvi->hpd_work,
msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
}

View File

@ -773,8 +773,11 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
* @drm: the DRM device
* @encoder: the encoder where the bridge chain starts
*
* Allocate, initialise and register a &drm_bridge_connector with the @drm
* device. The connector is associated with a chain of bridges that starts at
* Create a new &drm_bridge_connector for the @drm device. The connector is
* allocated, initialised, registered with the @drm device and attached to
* @encoder.
*
* The connector is associated with a chain of bridges that starts at
* the @encoder. All bridges in the chain shall report bridge operation flags
* (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of
* them may create a DRM connector directly.
@ -1055,6 +1058,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER))
drm_connector_attach_content_protection_property(connector, true);
ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
return ERR_PTR(ret);
return connector;
}
EXPORT_SYMBOL_GPL(drm_bridge_connector_init);

View File

@ -463,19 +463,19 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p,
struct drm_crtc *crtc = state->crtc;
drm_printf(p, "crtc[%u]: %s\n", crtc->base.id, crtc->name);
drm_printf(p, "\tenable=%d\n", state->enable);
drm_printf(p, "\tactive=%d\n", state->active);
drm_printf(p, "\tself_refresh_active=%d\n", state->self_refresh_active);
drm_printf(p, "\tplanes_changed=%d\n", state->planes_changed);
drm_printf(p, "\tmode_changed=%d\n", state->mode_changed);
drm_printf(p, "\tactive_changed=%d\n", state->active_changed);
drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed);
drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed);
drm_printf(p, "\tplane_mask=%x\n", state->plane_mask);
drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask);
drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask);
drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode));
drm_printf(p, "\tbackground_color=%llx\n", state->background_color);
drm_printf_indent(p, 1, "enable=%d\n", state->enable);
drm_printf_indent(p, 1, "active=%d\n", state->active);
drm_printf_indent(p, 1, "self_refresh_active=%d\n", state->self_refresh_active);
drm_printf_indent(p, 1, "planes_changed=%d\n", state->planes_changed);
drm_printf_indent(p, 1, "mode_changed=%d\n", state->mode_changed);
drm_printf_indent(p, 1, "active_changed=%d\n", state->active_changed);
drm_printf_indent(p, 1, "connectors_changed=%d\n", state->connectors_changed);
drm_printf_indent(p, 1, "color_mgmt_changed=%d\n", state->color_mgmt_changed);
drm_printf_indent(p, 1, "plane_mask=%x\n", state->plane_mask);
drm_printf_indent(p, 1, "connector_mask=%x\n", state->connector_mask);
drm_printf_indent(p, 1, "encoder_mask=%x\n", state->encoder_mask);
drm_printf_indent(p, 1, "mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode));
drm_printf_indent(p, 1, "background_color=%llx\n", state->background_color);
if (crtc->funcs->atomic_print_state)
crtc->funcs->atomic_print_state(p, state);
@ -818,38 +818,38 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p,
struct drm_colorop *colorop = state->colorop;
drm_printf(p, "colorop[%u]:\n", colorop->base.id);
drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
drm_printf_indent(p, 1, "type=%s\n", drm_get_colorop_type_name(colorop->type));
if (colorop->bypass_property)
drm_printf(p, "\tbypass=%u\n", state->bypass);
drm_printf_indent(p, 1, "bypass=%u\n", state->bypass);
switch (colorop->type) {
case DRM_COLOROP_1D_CURVE:
drm_printf(p, "\tcurve_1d_type=%s\n",
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
drm_printf_indent(p, 1, "curve_1d_type=%s\n",
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
break;
case DRM_COLOROP_1D_LUT:
drm_printf(p, "\tsize=%d\n", colorop->size);
drm_printf(p, "\tinterpolation=%s\n",
drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation));
drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
drm_printf_indent(p, 1, "size=%d\n", colorop->size);
drm_printf_indent(p, 1, "interpolation=%s\n",
drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation));
drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0);
break;
case DRM_COLOROP_CTM_3X4:
drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0);
break;
case DRM_COLOROP_MULTIPLIER:
drm_printf(p, "\tmultiplier=%llu\n", state->multiplier);
drm_printf_indent(p, 1, "multiplier=%llu\n", state->multiplier);
break;
case DRM_COLOROP_3D_LUT:
drm_printf(p, "\tsize=%d\n", colorop->size);
drm_printf(p, "\tinterpolation=%s\n",
drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation));
drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
drm_printf_indent(p, 1, "size=%d\n", colorop->size);
drm_printf_indent(p, 1, "interpolation=%s\n",
drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation));
drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0);
break;
default:
break;
}
drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 0);
drm_printf_indent(p, 1, "next=%d\n", colorop->next ? colorop->next->base.id : 0);
}
static void drm_atomic_plane_print_state(struct drm_printer *p,
@ -860,21 +860,21 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
struct drm_rect dest = drm_plane_state_dest(state);
drm_printf(p, "plane[%u]: %s\n", plane->base.id, plane->name);
drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)");
drm_printf(p, "\tfb=%u\n", state->fb ? state->fb->base.id : 0);
drm_printf_indent(p, 1, "crtc=%s\n", state->crtc ? state->crtc->name : "(null)");
drm_printf_indent(p, 1, "fb=%u\n", state->fb ? state->fb->base.id : 0);
if (state->fb)
drm_framebuffer_print_info(p, 2, state->fb);
drm_printf(p, "\tcrtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest));
drm_printf(p, "\tsrc-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src));
drm_printf(p, "\trotation=%x\n", state->rotation);
drm_printf(p, "\tnormalized-zpos=%x\n", state->normalized_zpos);
drm_printf(p, "\tcolor-encoding=%s\n",
drm_get_color_encoding_name(state->color_encoding));
drm_printf(p, "\tcolor-range=%s\n",
drm_get_color_range_name(state->color_range));
drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed);
drm_printf(p, "\tcolor-pipeline=%d\n",
state->color_pipeline ? state->color_pipeline->base.id : 0);
drm_printf_indent(p, 1, "crtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest));
drm_printf_indent(p, 1, "src-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src));
drm_printf_indent(p, 1, "rotation=%x\n", state->rotation);
drm_printf_indent(p, 1, "normalized-zpos=%x\n", state->normalized_zpos);
drm_printf_indent(p, 1, "color-encoding=%s\n",
drm_get_color_encoding_name(state->color_encoding));
drm_printf_indent(p, 1, "color-range=%s\n",
drm_get_color_range_name(state->color_range));
drm_printf_indent(p, 1, "color_mgmt_changed=%d\n", state->color_mgmt_changed);
drm_printf_indent(p, 1, "color-pipeline=%d\n",
state->color_pipeline ? state->color_pipeline->base.id : 0);
if (plane->funcs->atomic_print_state)
plane->funcs->atomic_print_state(p, state);
}
@ -1347,27 +1347,27 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
struct drm_connector *connector = state->connector;
drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name);
drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)");
drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware);
drm_printf(p, "\tinterlace_allowed=%d\n", connector->interlace_allowed);
drm_printf(p, "\tycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed);
drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc);
drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace));
drm_printf_indent(p, 1, "crtc=%s\n", state->crtc ? state->crtc->name : "(null)");
drm_printf_indent(p, 1, "self_refresh_aware=%d\n", state->self_refresh_aware);
drm_printf_indent(p, 1, "interlace_allowed=%d\n", connector->interlace_allowed);
drm_printf_indent(p, 1, "ycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed);
drm_printf_indent(p, 1, "max_requested_bpc=%d\n", state->max_requested_bpc);
drm_printf_indent(p, 1, "colorspace=%s\n", drm_get_colorspace_name(state->colorspace));
if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) {
drm_printf(p, "\tbroadcast_rgb=%s\n",
drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb));
drm_printf(p, "\tis_limited_range=%c\n", state->hdmi.is_limited_range ? 'y' : 'n');
drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc);
drm_printf(p, "\toutput_format=%s\n",
drm_hdmi_connector_get_output_format_name(state->hdmi.output_format));
drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate);
drm_printf_indent(p, 1, "broadcast_rgb=%s\n",
drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb));
drm_printf_indent(p, 1, "is_limited_range=%c\n", state->hdmi.is_limited_range ? 'y' : 'n');
drm_printf_indent(p, 1, "output_bpc=%u\n", state->hdmi.output_bpc);
drm_printf_indent(p, 1, "output_format=%s\n",
drm_hdmi_connector_get_output_format_name(state->hdmi.output_format));
drm_printf_indent(p, 1, "tmds_char_rate=%llu\n", state->hdmi.tmds_char_rate);
}
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
if (state->writeback_job && state->writeback_job->fb)
drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id);
drm_printf_indent(p, 1, "fb=%d\n", state->writeback_job->fb->base.id);
if (connector->funcs->atomic_print_state)
connector->funcs->atomic_print_state(p, state);

View File

@ -347,7 +347,7 @@ void drm_master_release(struct drm_file *file_priv)
if (!drm_is_current_master_locked(file_priv))
goto out;
if (dev->master == file_priv->master)
if (dev->master && dev->master == file_priv->master)
drm_drop_master(dev, file_priv);
out:
if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {

View File

@ -1614,18 +1614,17 @@ static void drm_bridge_debugfs_show_bridge(struct drm_printer *p,
drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
drm_printf(p, "\trefcount: %u%s\n", refcount,
lingering ? " [lingering]" : "");
drm_printf_indent(p, 1, "refcount: %u%s\n", refcount,
lingering ? " [lingering]" : "");
drm_printf(p, "\ttype: [%d] %s\n",
bridge->type,
drm_get_connector_type_name(bridge->type));
drm_printf_indent(p, 1, "type: [%d] %s\n", bridge->type,
drm_get_connector_type_name(bridge->type));
/* The OF node could be freed after drm_bridge_remove() */
if (bridge->of_node && !lingering)
drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node);
drm_printf_indent(p, 1, "OF: %pOFfc\n", bridge->of_node);
drm_printf(p, "\tops: [0x%x]", bridge->ops);
drm_printf_indent(p, 1, "ops: [0x%x]", bridge->ops);
if (bridge->ops & DRM_BRIDGE_OP_DETECT)
drm_puts(p, " detect");
if (bridge->ops & DRM_BRIDGE_OP_EDID)

View File

@ -275,6 +275,7 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_YVU422, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_YVU444, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_Y8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true },
{ .format = DRM_FORMAT_NV21, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true },
{ .format = DRM_FORMAT_NV16, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
@ -287,6 +288,7 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_XYUV8888, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_VUY888, .depth = 0, .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_XVUY2101010, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
{ .format = DRM_FORMAT_Y210, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_Y212, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
@ -354,6 +356,9 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_P030, .depth = 0, .num_planes = 2,
.char_per_block = { 4, 8, 0 }, .block_w = { 3, 3, 0 }, .block_h = { 1, 1, 0 },
.hsub = 2, .vsub = 2, .is_yuv = true},
{ .format = DRM_FORMAT_P230, .depth = 0, .num_planes = 2,
.char_per_block = { 4, 8, 0 }, .block_w = { 3, 3, 0 }, .block_h = { 1, 1, 0 },
.hsub = 2, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_S010, .depth = 0, .num_planes = 3,
.char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 },
.hsub = 2, .vsub = 2, .is_yuv = true},
@ -381,6 +386,12 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_S416, .depth = 0, .num_planes = 3,
.char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 },
.hsub = 1, .vsub = 1, .is_yuv = true},
{ .format = DRM_FORMAT_XYYY2101010, .depth = 0, .num_planes = 1,
.char_per_block = { 4, 0, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 },
.hsub = 1, .vsub = 1, .is_yuv = true },
{ .format = DRM_FORMAT_T430, .depth = 0, .num_planes = 3,
.char_per_block = { 4, 4, 4 }, .block_w = { 3, 3, 3 }, .block_h = { 1, 1, 1 },
.hsub = 1, .vsub = 1, .is_yuv = true },
};
unsigned int i;
@ -491,12 +502,20 @@ EXPORT_SYMBOL(drm_format_info_block_height);
*/
unsigned int drm_format_info_bpp(const struct drm_format_info *info, int plane)
{
unsigned int block_size;
if (!info || plane < 0 || plane >= info->num_planes)
return 0;
return info->char_per_block[plane] * 8 /
(drm_format_info_block_width(info, plane) *
drm_format_info_block_height(info, plane));
block_size = drm_format_info_block_width(info, plane) *
drm_format_info_block_height(info, plane);
if (info->char_per_block[plane] * 8 % block_size) {
pr_warn("unable to return an integer bpp\n");
return 0;
}
return info->char_per_block[plane] * 8 / block_size;
}
EXPORT_SYMBOL(drm_format_info_bpp);

View File

@ -587,6 +587,9 @@ EXPORT_SYMBOL(mipi_dsi_create_packet);
* mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
* @dsi: DSI peripheral device
*
* This function is deprecated. Use mipi_dsi_shutdown_peripheral_multi()
* instead.
*
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
@ -1980,6 +1983,31 @@ void mipi_dsi_dcs_set_tear_scanline_multi(struct mipi_dsi_multi_context *ctx,
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline_multi);
/**
* mipi_dsi_shutdown_peripheral_multi() - sends a Shutdown Peripheral command
* @ctx: Context for multiple DSI transactions
*
* Like mipi_dsi_shutdown_peripheral() but deals with errors in a way that
* makes it convenient to make several calls in a row.
*/
void mipi_dsi_shutdown_peripheral_multi(struct mipi_dsi_multi_context *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
if (ctx->accum_err)
return;
ret = mipi_dsi_shutdown_peripheral(dsi);
if (ret < 0) {
ctx->accum_err = ret;
dev_err(dev, "Failed to shutdown peripheral: %d\n",
ctx->accum_err);
}
}
EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral_multi);
static int mipi_dsi_drv_probe(struct device *dev)
{
struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);

View File

@ -93,18 +93,6 @@ void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv,
#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
/*
* Return the storage size of a structure with a variable length array.
* The array is nelem elements of elem_size, where the base structure
* is defined by base. If the size overflows size_t, return zero.
*/
static inline size_t size_vstruct(size_t nelem, size_t elem_size, size_t base)
{
if (elem_size && nelem > (SIZE_MAX - base) / elem_size)
return 0;
return base + nelem * elem_size;
}
/*
* Etnaviv timeouts are specified wrt CLOCK_MONOTONIC, not jiffies.
* We need to calculate the timeout in terms of number of jiffies

View File

@ -32,9 +32,8 @@ static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
struct etnaviv_gpu *gpu, size_t nr_bos, size_t nr_pmrs)
{
struct etnaviv_gem_submit *submit;
size_t sz = size_vstruct(nr_bos, sizeof(submit->bos[0]), sizeof(*submit));
submit = kzalloc(sz, GFP_KERNEL);
submit = kzalloc_flex(*submit, bos, nr_bos);
if (!submit)
return NULL;

View File

@ -118,7 +118,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
return ret;
}
return drm_connector_attach_encoder(connector, dp->plat_data.encoder);
return 0;
}
static void exynos_dp_unbind(struct device *dev, struct device *master,

View File

@ -24,7 +24,6 @@
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_pciids.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>

View File

@ -10,7 +10,7 @@
#include "hyperv_drm.h"
#define VMBUS_RING_BUFSIZE (256 * 1024)
#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024)
#define VMBUS_VSP_TIMEOUT (10 * HZ)
#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))

View File

@ -3205,8 +3205,7 @@ eb_composite_fence_create(struct i915_execbuffer *eb, int out_fence_fd)
fence_array = dma_fence_array_create(eb->num_batches,
fences,
eb->context->parallel.fence_context,
eb->context->parallel.seqno++,
false);
eb->context->parallel.seqno++);
if (!fence_array) {
kfree(fences);
return ERR_PTR(-ENOMEM);

View File

@ -77,7 +77,7 @@ const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
};
param_check_hexint(init_fw_trace_mask, &pvr_fw_trace_init_mask);
module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0600);
module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0400);
__MODULE_PARM_TYPE(init_fw_trace_mask, "hexint");
MODULE_PARM_DESC(init_fw_trace_mask,
"Enable FW trace for the specified groups at device init time");

View File

@ -81,13 +81,7 @@ static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm,
return ret;
}
ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
dev_err(dev,
"failed to attach encoder to connector for CRTC%u: %d\n",
crtc->index, ret);
return ret;
return 0;
}
int dc_kms_init(struct dc_drm_device *dc_drm)

View File

@ -103,8 +103,6 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms)
return PTR_ERR(kms->connector);
}
drm_connector_attach_encoder(kms->connector, encoder);
return 0;
}

View File

@ -13,6 +13,7 @@ config DRM_IMX
config DRM_IMX_PARALLEL_DISPLAY
tristate "Support for parallel displays"
depends on DRM_IMX
depends on OF
select DRM_BRIDGE
select DRM_BRIDGE_CONNECTOR
select DRM_DISPLAY_HELPER
@ -33,6 +34,7 @@ config DRM_IMX_LDB
tristate "Support for LVDS displays"
depends on DRM_IMX
depends on COMMON_CLK
depends on OF
select MFD_SYSCON
select DRM_BRIDGE
select DRM_BRIDGE_CONNECTOR

View File

@ -407,8 +407,6 @@ static int imx_ldb_register(struct drm_device *drm,
if (IS_ERR(connector))
return PTR_ERR(connector);
drm_connector_attach_encoder(connector, encoder);
return 0;
}

View File

@ -216,8 +216,6 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(connector))
return PTR_ERR(connector);
drm_connector_attach_encoder(connector, encoder);
return 0;
}

View File

@ -448,8 +448,6 @@ static int imx_lcdc_probe(struct platform_device *pdev)
if (IS_ERR(lcdc->connector))
return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n");
drm_connector_attach_encoder(lcdc->connector, &lcdc->pipe.encoder);
/*
* The LCDC controller does not have an enable bit. The
* controller starts directly when the clocks are enabled.

View File

@ -1330,8 +1330,6 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
ret = PTR_ERR(connector);
goto err_drvdata;
}
drm_connector_attach_encoder(connector, encoder);
}
drm_for_each_encoder(encoder, drm) {

View File

@ -1457,7 +1457,7 @@ int kmb_dsi_encoder_init(struct drm_device *dev, struct kmb_dsi *kmb_dsi)
drm_encoder_cleanup(encoder);
return PTR_ERR(connector);
}
drm_connector_attach_encoder(connector, encoder);
return 0;
}

View File

@ -1049,7 +1049,6 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
ret = PTR_ERR(dpi->connector);
goto err_cleanup;
}
drm_connector_attach_encoder(dpi->connector, &dpi->encoder);
return 0;

View File

@ -936,7 +936,6 @@ static int mtk_dsi_encoder_init(struct drm_device *drm, struct mtk_dsi *dsi)
ret = PTR_ERR(dsi->connector);
goto err_cleanup_encoder;
}
drm_connector_attach_encoder(dsi->connector, &dsi->encoder);
return 0;

View File

@ -279,8 +279,6 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv)
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;
return 0;

View File

@ -433,8 +433,6 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv)
"Unable to create HDMI bridge connector\n");
goto err_put_node;
}
drm_connector_attach_encoder(meson_encoder_hdmi->connector,
&meson_encoder_hdmi->encoder);
/*
* We should have now in place:

View File

@ -19,7 +19,6 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
#include <drm/drm_pciids.h>
#include <drm/drm_print.h>
#include "mgag200_drv.h"

View File

@ -232,13 +232,6 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
return PTR_ERR(connector);
}
ret = drm_connector_attach_encoder(connector, encoder);
if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to attach LVDS connector: %d\n", ret);
return ret;
}
break;
case DRM_MODE_ENCODER_TMDS:
encoder = mdp4_dtv_encoder_init(dev);

View File

@ -376,7 +376,5 @@ struct drm_connector *msm_dp_drm_connector_init(struct msm_dp *msm_dp_display,
if (!msm_dp_display->is_edp)
drm_connector_attach_dp_subconnector_property(connector);
drm_connector_attach_encoder(connector, encoder);
return connector;
}

View File

@ -485,10 +485,6 @@ int msm_dsi_manager_connector_init(struct msm_dsi *msm_dsi,
return PTR_ERR(connector);
}
ret = drm_connector_attach_encoder(connector, encoder);
if (ret < 0)
return ret;
return 0;
}

View File

@ -190,8 +190,6 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi,
goto fail;
}
drm_connector_attach_encoder(hdmi->connector, hdmi->encoder);
ret = devm_request_irq(dev->dev, hdmi->irq,
msm_hdmi_irq, IRQF_TRIGGER_HIGH,
"hdmi_isr", hdmi);

View File

@ -99,12 +99,6 @@ static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
return dev_err_probe(dev, PTR_ERR(connector),
"Failed to init bridge_connector for endpoint%u\n",
of_ep.id);
ret = drm_connector_attach_encoder(connector, encoder);
if (ret)
return dev_err_probe(dev, ret,
"Failed to attach connector for endpoint%u\n",
of_ep.id);
}
return 0;

View File

@ -2474,7 +2474,7 @@ nv50_disp_atomic_commit(struct drm_device *dev,
pm_runtime_get_noresume(dev->dev);
if (nonblock)
queue_work(system_unbound_wq, &state->commit_work);
queue_work(system_dfl_wq, &state->commit_work);
else
nv50_disp_atomic_commit_tail(state);

View File

@ -2085,10 +2085,27 @@ nouveau_bios_init(struct drm_device *dev)
int ret;
/* only relevant for PCI devices */
if (!dev_is_pci(dev->dev) ||
nvkm_gsp_rm(nvxx_device(drm)->gsp))
if (!dev_is_pci(dev->dev))
return 0;
if (nvkm_gsp_rm(nvxx_device(drm)->gsp)) {
struct nvkm_bios *nvkm_bios = nvxx_bios(drm);
/*
* If this GPU has an nvkm_device_chip.bios entry, then the VBIOS
* data was already read by the nvkm layer during nvkm_bios_new().
* Point the legacy DRM-level VBIOS structure at the same buffer
* so that any remaining legacy code can access it. This exposes
* the VBIOS via the DRM debugfs entries.
*/
if (nvkm_bios) {
bios->data = nvkm_bios->data;
bios->length = nvkm_bios->size;
}
return 0;
}
if (!NVInitVBIOS(dev))
return -ENODEV;

View File

@ -632,7 +632,7 @@ nouveau_drm_device_init(struct nouveau_drm *drm)
struct drm_device *dev = drm->dev;
int ret;
drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", 0,
drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", WQ_PERCPU,
WQ_MAX_ACTIVE);
if (!drm->sched_wq)
return -ENOMEM;

View File

@ -413,7 +413,8 @@ nouveau_sched_init(struct nouveau_sched *sched, struct nouveau_drm *drm,
int ret;
if (!wq) {
wq = alloc_workqueue("nouveau_sched_wq_%d", 0, WQ_MAX_ACTIVE,
wq = alloc_workqueue("nouveau_sched_wq_%d", WQ_PERCPU,
WQ_MAX_ACTIVE,
current->pid);
if (!wq)
return -ENOMEM;

View File

@ -507,8 +507,6 @@ static int omap_modeset_init(struct drm_device *dev)
return PTR_ERR(pipe->connector);
}
drm_connector_attach_encoder(pipe->connector, encoder);
crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
if (IS_ERR(crtc))
return PTR_ERR(crtc);

View File

@ -105,8 +105,20 @@ config DRM_PANEL_BOE_TV101WUM_LL2
Say Y here if you want to support for BOE TV101WUM-LL2
WUXGA PANEL DSI Video Mode panel
config DRM_PANEL_CHIPWEALTH_CH13726A
tristate "CHIPWEALTH CH13726A-based DSI panel"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_HELPER
help
Say Y here if you want to enable support for ChipWealth
CH13726A-based display panels.
config DRM_PANEL_EBBG_FT8719
tristate "EBBG FT8719 panel driver"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
@ -357,7 +369,8 @@ config DRM_PANEL_JADARD_JD9365DA_H3
config DRM_PANEL_JDI_LPM102A188A
tristate "JDI LPM102A188A DSI panel"
depends on OF && GPIOLIB
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
@ -472,6 +485,7 @@ config DRM_PANEL_LG_LG4573
config DRM_PANEL_LG_SW43408
tristate "LG SW43408 panel"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
@ -562,6 +576,16 @@ config DRM_PANEL_NOVATEK_NT35510
around the Novatek NT35510 display controller, such as some
Hydis panels.
config DRM_PANEL_NOVATEK_NT35532
tristate "Novatek NT35532-based DSI video mode panel"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_KMS_HELPER
help
Say Y or M here if you want to enable support for Novatek
NT35532-based 1080p video mode DSI panels.
config DRM_PANEL_NOVATEK_NT35560
tristate "Novatek NT35560 DSI command mode panel"
depends on OF
@ -597,6 +621,7 @@ config DRM_PANEL_NOVATEK_NT36523
config DRM_PANEL_NOVATEK_NT36672A
tristate "Novatek NT36672A DSI panel"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
@ -607,6 +632,7 @@ config DRM_PANEL_NOVATEK_NT36672A
config DRM_PANEL_NOVATEK_NT36672E
tristate "Novatek NT36672E DSI panel"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
@ -744,7 +770,8 @@ config DRM_PANEL_RAYDIUM_RM692E5
config DRM_PANEL_RAYDIUM_RM69380
tristate "Raydium RM69380-based DSI panel"
depends on OF && GPIOLIB
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
@ -902,10 +929,10 @@ config DRM_PANEL_SAMSUNG_S6D7AA0
config DRM_PANEL_SAMSUNG_S6E3FC2X01
tristate "Samsung S6E3FC2X01 DSI panel controller"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS
help
Say Y or M here if you want to enable support for the
Samsung S6E3FC2 DDIC and connected MIPI DSI panel.
@ -922,11 +949,18 @@ config DRM_PANEL_SAMSUNG_S6E3HA2
config DRM_PANEL_SAMSUNG_S6E3HA8
tristate "Samsung S6E3HA8 DSI video mode panel"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_DSC_HELPER
select VIDEOMODE_HELPERS
help
Say Y or M here if you want to enable support for the
Samsung S6E3HA8 DDIC and connected MIPI DSI panel.
Currently supported panels:
Samsung AMB577PX01 (found in the Samsung S9 smartphone)
config DRM_PANEL_SAMSUNG_S6E63J0X03
tristate "Samsung S6E63J0X03 DSI command mode panel"
@ -994,10 +1028,10 @@ config DRM_PANEL_SAMSUNG_S6E8FC0
config DRM_PANEL_SAMSUNG_SOFEF00
tristate "Samsung SOFEF00 DSI panel controller"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS
help
Say Y or M here if you want to enable support for the Samsung AMOLED
panel SOFEF00 DDIC and connected panel.
@ -1262,6 +1296,7 @@ config DRM_PANEL_VISIONOX_R66451
config DRM_PANEL_VISIONOX_RM69299
tristate "Visionox RM69299"
depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o
obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o
obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o
obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o
obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o
obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
@ -55,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3052C) += panel-newvision-nv3052c.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35532) += panel-novatek-nt35532.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o

View File

@ -0,0 +1,333 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ChipWealth CH13726A MIPI-DSI panel driver
* Copyright (c) 2024, Teguh Sobirin <teguh@sobir.in>.
*/
#include <linux/backlight.h>
#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 <video/mipi_display.h>
static const struct regulator_bulk_data ch13726a_supplies[] = {
{ .supply = "vdd1v2", },
{ .supply = "vddio", },
{ .supply = "vdd", },
{ .supply = "avdd", },
};
struct ch13726a_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator_bulk_data *supplies;
struct gpio_desc *reset_gpio;
struct ch13726a_desc *desc;
enum drm_panel_orientation orientation;
};
struct ch13726a_desc {
unsigned int width_mm;
unsigned int height_mm;
unsigned int bpc;
const struct drm_display_mode *modes;
unsigned int num_modes;
};
static inline struct ch13726a_panel *to_ch13726a_panel(struct drm_panel *panel)
{
return container_of(panel, struct ch13726a_panel, panel);
}
static void ch13726a_reset(struct ch13726a_panel *ctx)
{
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(10000, 11000);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
usleep_range(10000, 11000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(10000, 11000);
}
static int ch13726a_on(struct ch13726a_panel *ctx)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x50);
mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb9, 0x00);
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
return dsi_ctx.accum_err;
}
static int ch13726a_disable(struct drm_panel *panel)
{
struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 50);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
return dsi_ctx.accum_err;
}
static int ch13726a_prepare(struct drm_panel *panel)
{
struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
struct device *dev = &ctx->dsi->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
ch13726a_reset(ctx);
ret = ch13726a_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(ch13726a_supplies), ctx->supplies);
return ret;
}
msleep(28);
return 0;
}
static int ch13726a_unprepare(struct drm_panel *panel)
{
struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ch13726a_supplies), ctx->supplies);
return 0;
}
static const struct drm_display_mode thor_bottom_modes[] = {
{
/* 120Hz */
.clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 120 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 28,
.hsync_end = 1080 + 28 + 4,
.htotal = 1080 + 28 + 4 + 36,
.vdisplay = 1240,
.vsync_start = 1240 + 16,
.vsync_end = 1240 + 16 + 4,
.vtotal = 1240 + 16 + 4 + 8,
},
{
/* 60Hz */
.clock = (1080 + 28 + 4 + 36) * (1240 + 16 + 4 + 8) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 28,
.hsync_end = 1080 + 28 + 4,
.htotal = 1080 + 28 + 4 + 36,
.vdisplay = 1240,
.vsync_start = 1240 + 16,
.vsync_end = 1240 + 16 + 4,
.vtotal = 1240 + 16 + 4 + 8,
}
};
static struct ch13726a_desc thor_bottom_desc = {
.modes = thor_bottom_modes,
.num_modes = ARRAY_SIZE(thor_bottom_modes),
.width_mm = 65,
.height_mm = 75,
.bpc = 8,
};
static int ch13726a_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
for (uint8_t i = 0; i < ctx->desc->num_modes; i++) {
const struct drm_display_mode *m = &ctx->desc->modes[i];
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, m);
if (!mode) {
dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
return -ENOMEM;
}
mode->type = DRM_MODE_TYPE_DRIVER;
if (i == 0)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
}
connector->display_info.width_mm = ctx->desc->width_mm;
connector->display_info.height_mm = ctx->desc->height_mm;
connector->display_info.bpc = ctx->desc->bpc;
return ctx->desc->num_modes;
}
static enum drm_panel_orientation ch13726a_get_orientation(struct drm_panel *panel)
{
struct ch13726a_panel *ctx = to_ch13726a_panel(panel);
return ctx->orientation;
}
static const struct drm_panel_funcs ch13726a_panel_funcs = {
.prepare = ch13726a_prepare,
.unprepare = ch13726a_unprepare,
.disable = ch13726a_disable,
.get_modes = ch13726a_get_modes,
.get_orientation = ch13726a_get_orientation,
};
static int ch13726a_bl_update_status(struct backlight_device *bl)
{
struct mipi_dsi_device *dsi = bl_get_data(bl);
u16 brightness = backlight_get_brightness(bl);
int ret;
dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
if (ret < 0)
return ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
return 0;
}
static const struct backlight_ops ch13726a_bl_ops = {
.update_status = ch13726a_bl_update_status,
};
static struct backlight_device *
ch13726a_create_backlight(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.brightness = 255,
.max_brightness = 255,
};
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
&ch13726a_bl_ops, &props);
}
static int ch13726a_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct ch13726a_panel *ctx;
int ret;
ctx = devm_drm_panel_alloc(dev, __typeof(*ctx), panel,
&ch13726a_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ctx->desc = (struct ch13726a_desc *)of_device_get_match_data(dev);
if (!ctx->desc)
return -ENODEV;
ret = devm_regulator_bulk_get_const(dev,
ARRAY_SIZE(ch13726a_supplies),
ch13726a_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");
ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
if (ret < 0) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
return ret;
}
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_CLOCK_NON_CONTINUOUS;
ctx->panel.prepare_prev_first = true;
ctx->panel.backlight = ch13726a_create_backlight(dsi);
if (IS_ERR(ctx->panel.backlight))
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"Failed to create backlight\n");
drm_panel_add(&ctx->panel);
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
drm_panel_remove(&ctx->panel);
return ret;
}
return 0;
}
static void ch13726a_remove(struct mipi_dsi_device *dsi)
{
struct ch13726a_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 ch13726a_of_match[] = {
{ .compatible = "ayntec,thor-panel-bottom", .data = &thor_bottom_desc },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ch13726a_of_match);
static struct mipi_dsi_driver ch13726a_driver = {
.probe = ch13726a_probe,
.remove = ch13726a_remove,
.driver = {
.name = "panel-ch13726a-amoled",
.of_match_table = ch13726a_of_match,
},
};
module_mipi_dsi_driver(ch13726a_driver);
MODULE_DESCRIPTION("DRM driver for CH13726A DSI panels");
MODULE_LICENSE("GPL");

View File

@ -156,8 +156,8 @@ static int ota7290b_probe(struct mipi_dsi_device *dsi)
ctx = devm_drm_panel_alloc(&dsi->dev, struct ota7290b, panel,
&ota7290b_funcs,
DRM_MODE_CONNECTOR_DSI);
if (!ctx)
return -ENOMEM;
if (IS_ERR(ctx))
return PTR_ERR(ctx);
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;

View File

@ -52,6 +52,7 @@ struct ili9881c {
const struct ili9881c_desc *desc;
struct regulator *power;
struct regulator *iovcc;
struct gpio_desc *reset;
enum drm_panel_orientation orientation;
@ -1997,6 +1998,205 @@ static const struct ili9881c_instr bsd1218_a101kl68_init[] = {
ILI9881C_COMMAND_INSTR(0xd3, 0x3f),
};
static const struct ili9881c_instr waveshare_7inch_a_init[] = {
ILI9881C_SWITCH_PAGE_INSTR(3),
ILI9881C_COMMAND_INSTR(0x01, 0x00),
ILI9881C_COMMAND_INSTR(0x02, 0x00),
ILI9881C_COMMAND_INSTR(0x03, 0x73),
ILI9881C_COMMAND_INSTR(0x04, 0x00),
ILI9881C_COMMAND_INSTR(0x05, 0x00),
ILI9881C_COMMAND_INSTR(0x06, 0x0a),
ILI9881C_COMMAND_INSTR(0x07, 0x00),
ILI9881C_COMMAND_INSTR(0x08, 0x00),
ILI9881C_COMMAND_INSTR(0x09, 0x61),
ILI9881C_COMMAND_INSTR(0x0a, 0x00),
ILI9881C_COMMAND_INSTR(0x0b, 0x00),
ILI9881C_COMMAND_INSTR(0x0c, 0x01),
ILI9881C_COMMAND_INSTR(0x0d, 0x00),
ILI9881C_COMMAND_INSTR(0x0e, 0x00),
ILI9881C_COMMAND_INSTR(0x0f, 0x61),
ILI9881C_COMMAND_INSTR(0x10, 0x61),
ILI9881C_COMMAND_INSTR(0x11, 0x00),
ILI9881C_COMMAND_INSTR(0x12, 0x00),
ILI9881C_COMMAND_INSTR(0x13, 0x00),
ILI9881C_COMMAND_INSTR(0x14, 0x00),
ILI9881C_COMMAND_INSTR(0x15, 0x00),
ILI9881C_COMMAND_INSTR(0x16, 0x00),
ILI9881C_COMMAND_INSTR(0x17, 0x00),
ILI9881C_COMMAND_INSTR(0x18, 0x00),
ILI9881C_COMMAND_INSTR(0x19, 0x00),
ILI9881C_COMMAND_INSTR(0x1a, 0x00),
ILI9881C_COMMAND_INSTR(0x1b, 0x00),
ILI9881C_COMMAND_INSTR(0x1c, 0x00),
ILI9881C_COMMAND_INSTR(0x1d, 0x00),
ILI9881C_COMMAND_INSTR(0x1e, 0x40),
ILI9881C_COMMAND_INSTR(0x1f, 0x80),
ILI9881C_COMMAND_INSTR(0x20, 0x06),
ILI9881C_COMMAND_INSTR(0x21, 0x01),
ILI9881C_COMMAND_INSTR(0x22, 0x00),
ILI9881C_COMMAND_INSTR(0x23, 0x00),
ILI9881C_COMMAND_INSTR(0x24, 0x00),
ILI9881C_COMMAND_INSTR(0x25, 0x00),
ILI9881C_COMMAND_INSTR(0x26, 0x00),
ILI9881C_COMMAND_INSTR(0x27, 0x00),
ILI9881C_COMMAND_INSTR(0x28, 0x33),
ILI9881C_COMMAND_INSTR(0x29, 0x03),
ILI9881C_COMMAND_INSTR(0x2a, 0x00),
ILI9881C_COMMAND_INSTR(0x2b, 0x00),
ILI9881C_COMMAND_INSTR(0x2c, 0x00),
ILI9881C_COMMAND_INSTR(0x2d, 0x00),
ILI9881C_COMMAND_INSTR(0x2e, 0x00),
ILI9881C_COMMAND_INSTR(0x2f, 0x00),
ILI9881C_COMMAND_INSTR(0x30, 0x00),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
ILI9881C_COMMAND_INSTR(0x32, 0x00),
ILI9881C_COMMAND_INSTR(0x33, 0x00),
ILI9881C_COMMAND_INSTR(0x34, 0x04),
ILI9881C_COMMAND_INSTR(0x35, 0x00),
ILI9881C_COMMAND_INSTR(0x36, 0x00),
ILI9881C_COMMAND_INSTR(0x37, 0x00),
ILI9881C_COMMAND_INSTR(0x38, 0x3c),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
ILI9881C_COMMAND_INSTR(0x3a, 0x00),
ILI9881C_COMMAND_INSTR(0x3b, 0x00),
ILI9881C_COMMAND_INSTR(0x3c, 0x00),
ILI9881C_COMMAND_INSTR(0x3d, 0x00),
ILI9881C_COMMAND_INSTR(0x3e, 0x00),
ILI9881C_COMMAND_INSTR(0x3f, 0x00),
ILI9881C_COMMAND_INSTR(0x40, 0x00),
ILI9881C_COMMAND_INSTR(0x41, 0x00),
ILI9881C_COMMAND_INSTR(0x42, 0x00),
ILI9881C_COMMAND_INSTR(0x43, 0x00),
ILI9881C_COMMAND_INSTR(0x44, 0x00),
ILI9881C_COMMAND_INSTR(0x50, 0x10),
ILI9881C_COMMAND_INSTR(0x51, 0x32),
ILI9881C_COMMAND_INSTR(0x52, 0x54),
ILI9881C_COMMAND_INSTR(0x53, 0x76),
ILI9881C_COMMAND_INSTR(0x54, 0x98),
ILI9881C_COMMAND_INSTR(0x55, 0xba),
ILI9881C_COMMAND_INSTR(0x56, 0x10),
ILI9881C_COMMAND_INSTR(0x57, 0x32),
ILI9881C_COMMAND_INSTR(0x58, 0x54),
ILI9881C_COMMAND_INSTR(0x59, 0x76),
ILI9881C_COMMAND_INSTR(0x5a, 0x98),
ILI9881C_COMMAND_INSTR(0x5b, 0xba),
ILI9881C_COMMAND_INSTR(0x5c, 0xdc),
ILI9881C_COMMAND_INSTR(0x5d, 0xfe),
ILI9881C_COMMAND_INSTR(0x5e, 0x00),
ILI9881C_COMMAND_INSTR(0x5f, 0x0e),
ILI9881C_COMMAND_INSTR(0x60, 0x0f),
ILI9881C_COMMAND_INSTR(0x61, 0x0c),
ILI9881C_COMMAND_INSTR(0x62, 0x0d),
ILI9881C_COMMAND_INSTR(0x63, 0x06),
ILI9881C_COMMAND_INSTR(0x64, 0x07),
ILI9881C_COMMAND_INSTR(0x65, 0x02),
ILI9881C_COMMAND_INSTR(0x66, 0x02),
ILI9881C_COMMAND_INSTR(0x67, 0x02),
ILI9881C_COMMAND_INSTR(0x68, 0x02),
ILI9881C_COMMAND_INSTR(0x69, 0x01),
ILI9881C_COMMAND_INSTR(0x6a, 0x00),
ILI9881C_COMMAND_INSTR(0x6b, 0x02),
ILI9881C_COMMAND_INSTR(0x6c, 0x15),
ILI9881C_COMMAND_INSTR(0x6d, 0x14),
ILI9881C_COMMAND_INSTR(0x6e, 0x02),
ILI9881C_COMMAND_INSTR(0x6f, 0x02),
ILI9881C_COMMAND_INSTR(0x70, 0x02),
ILI9881C_COMMAND_INSTR(0x71, 0x02),
ILI9881C_COMMAND_INSTR(0x72, 0x02),
ILI9881C_COMMAND_INSTR(0x73, 0x02),
ILI9881C_COMMAND_INSTR(0x74, 0x02),
ILI9881C_COMMAND_INSTR(0x75, 0x0e),
ILI9881C_COMMAND_INSTR(0x76, 0x0f),
ILI9881C_COMMAND_INSTR(0x77, 0x0c),
ILI9881C_COMMAND_INSTR(0x78, 0x0d),
ILI9881C_COMMAND_INSTR(0x79, 0x06),
ILI9881C_COMMAND_INSTR(0x7a, 0x07),
ILI9881C_COMMAND_INSTR(0x7b, 0x02),
ILI9881C_COMMAND_INSTR(0x7c, 0x02),
ILI9881C_COMMAND_INSTR(0x7d, 0x02),
ILI9881C_COMMAND_INSTR(0x7e, 0x02),
ILI9881C_COMMAND_INSTR(0x7f, 0x01),
ILI9881C_COMMAND_INSTR(0x80, 0x00),
ILI9881C_COMMAND_INSTR(0x81, 0x02),
ILI9881C_COMMAND_INSTR(0x82, 0x14),
ILI9881C_COMMAND_INSTR(0x83, 0x15),
ILI9881C_COMMAND_INSTR(0x84, 0x02),
ILI9881C_COMMAND_INSTR(0x85, 0x02),
ILI9881C_COMMAND_INSTR(0x86, 0x02),
ILI9881C_COMMAND_INSTR(0x87, 0x02),
ILI9881C_COMMAND_INSTR(0x88, 0x02),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
ILI9881C_COMMAND_INSTR(0x38, 0x01),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
ILI9881C_COMMAND_INSTR(0x6c, 0x15),
ILI9881C_COMMAND_INSTR(0x6e, 0x2a),
ILI9881C_COMMAND_INSTR(0x6f, 0x33),
ILI9881C_COMMAND_INSTR(0x3a, 0x94),
ILI9881C_COMMAND_INSTR(0x8d, 0x14),
ILI9881C_COMMAND_INSTR(0x87, 0xba),
ILI9881C_COMMAND_INSTR(0x26, 0x76),
ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
ILI9881C_COMMAND_INSTR(0xb5, 0x06),
ILI9881C_COMMAND_INSTR(0x3b, 0x98),
ILI9881C_SWITCH_PAGE_INSTR(1),
ILI9881C_COMMAND_INSTR(0x22, 0x0a),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
ILI9881C_COMMAND_INSTR(0x53, 0x71),
ILI9881C_COMMAND_INSTR(0x55, 0x8f),
ILI9881C_COMMAND_INSTR(0x40, 0x33),
ILI9881C_COMMAND_INSTR(0x50, 0x96),
ILI9881C_COMMAND_INSTR(0x51, 0x96),
ILI9881C_COMMAND_INSTR(0x60, 0x23),
ILI9881C_COMMAND_INSTR(0xa0, 0x08),
ILI9881C_COMMAND_INSTR(0xa1, 0x1d),
ILI9881C_COMMAND_INSTR(0xa2, 0x2a),
ILI9881C_COMMAND_INSTR(0xa3, 0x10),
ILI9881C_COMMAND_INSTR(0xa4, 0x15),
ILI9881C_COMMAND_INSTR(0xa5, 0x28),
ILI9881C_COMMAND_INSTR(0xa6, 0x1c),
ILI9881C_COMMAND_INSTR(0xa7, 0x1d),
ILI9881C_COMMAND_INSTR(0xa8, 0x7e),
ILI9881C_COMMAND_INSTR(0xa9, 0x1d),
ILI9881C_COMMAND_INSTR(0xaa, 0x29),
ILI9881C_COMMAND_INSTR(0xab, 0x6b),
ILI9881C_COMMAND_INSTR(0xac, 0x1a),
ILI9881C_COMMAND_INSTR(0xad, 0x18),
ILI9881C_COMMAND_INSTR(0xae, 0x4b),
ILI9881C_COMMAND_INSTR(0xaf, 0x20),
ILI9881C_COMMAND_INSTR(0xb0, 0x27),
ILI9881C_COMMAND_INSTR(0xb1, 0x50),
ILI9881C_COMMAND_INSTR(0xb2, 0x64),
ILI9881C_COMMAND_INSTR(0xb3, 0x39),
ILI9881C_COMMAND_INSTR(0xc0, 0x08),
ILI9881C_COMMAND_INSTR(0xc1, 0x1d),
ILI9881C_COMMAND_INSTR(0xc2, 0x2a),
ILI9881C_COMMAND_INSTR(0xc3, 0x10),
ILI9881C_COMMAND_INSTR(0xc4, 0x15),
ILI9881C_COMMAND_INSTR(0xc5, 0x28),
ILI9881C_COMMAND_INSTR(0xc6, 0x1c),
ILI9881C_COMMAND_INSTR(0xc7, 0x1d),
ILI9881C_COMMAND_INSTR(0xc8, 0x7e),
ILI9881C_COMMAND_INSTR(0xc9, 0x1d),
ILI9881C_COMMAND_INSTR(0xca, 0x29),
ILI9881C_COMMAND_INSTR(0xcb, 0x6b),
ILI9881C_COMMAND_INSTR(0xcc, 0x1a),
ILI9881C_COMMAND_INSTR(0xcd, 0x18),
ILI9881C_COMMAND_INSTR(0xce, 0x4b),
ILI9881C_COMMAND_INSTR(0xcf, 0x20),
ILI9881C_COMMAND_INSTR(0xd0, 0x27),
ILI9881C_COMMAND_INSTR(0xd1, 0x50),
ILI9881C_COMMAND_INSTR(0xd2, 0x64),
ILI9881C_COMMAND_INSTR(0xd3, 0x39),
ILI9881C_SWITCH_PAGE_INSTR(0),
ILI9881C_COMMAND_INSTR(0x3a, 0x77),
ILI9881C_COMMAND_INSTR(0x36, 0x00),
};
static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel)
{
return container_of(panel, struct ili9881c, panel);
@ -2035,9 +2235,19 @@ static int ili9881c_prepare(struct drm_panel *panel)
int ret;
/* Power the panel */
if (ctx->iovcc) {
ret = regulator_enable(ctx->iovcc);
if (ret)
return ret;
}
msleep(5);
ret = regulator_enable(ctx->power);
if (ret)
return ret;
if (ret) {
mctx.accum_err = ret;
goto disable_iovcc;
}
msleep(5);
/* And reset it */
@ -2074,6 +2284,9 @@ static int ili9881c_prepare(struct drm_panel *panel)
disable_power:
regulator_disable(ctx->power);
disable_iovcc:
if (ctx->iovcc)
regulator_disable(ctx->iovcc);
return mctx.accum_err;
}
@ -2085,6 +2298,8 @@ static int ili9881c_unprepare(struct drm_panel *panel)
mipi_dsi_dcs_set_display_off_multi(&mctx);
mipi_dsi_dcs_enter_sleep_mode_multi(&mctx);
regulator_disable(ctx->power);
if (ctx->iovcc)
regulator_disable(ctx->iovcc);
gpiod_set_value_cansleep(ctx->reset, 1);
return 0;
@ -2260,6 +2475,23 @@ static const struct drm_display_mode bsd1218_a101kl68_default_mode = {
.height_mm = 170,
};
static const struct drm_display_mode waveshare_7inch_a_mode = {
.clock = 83333,
.hdisplay = 720,
.hsync_start = 720 + 120,
.hsync_end = 720 + 120 + 100,
.htotal = 720 + 120 + 100 + 100,
.vdisplay = 1280,
.vsync_start = 1280 + 10,
.vsync_end = 1280 + 10 + 10,
.vtotal = 1280 + 10 + 10 + 10,
.width_mm = 85,
.height_mm = 154,
};
static int ili9881c_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
@ -2329,6 +2561,11 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power),
"Couldn't get our power regulator\n");
ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc");
if (IS_ERR(ctx->iovcc))
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc),
"Couldn't get our iovcc regulator\n");
ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset))
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
@ -2454,6 +2691,15 @@ static const struct ili9881c_desc bsd1218_a101kl68_desc = {
.lanes = 4,
};
static const struct ili9881c_desc waveshare_7inch_a_desc = {
.init = waveshare_7inch_a_init,
.init_length = ARRAY_SIZE(waveshare_7inch_a_init),
.mode = &waveshare_7inch_a_mode,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE |
MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
.lanes = 2,
};
static const struct of_device_id ili9881c_of_match[] = {
{ .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc },
{ .compatible = "bestar,bsd1218-a101kl68", .data = &bsd1218_a101kl68_desc },
@ -2462,6 +2708,7 @@ static const struct of_device_id ili9881c_of_match[] = {
{ .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc },
{ .compatible = "wanchanglong,w552946aaa", .data = &w552946aaa_desc },
{ .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc },
{ .compatible = "waveshare,7.0-dsi-touch-a", .data = &waveshare_7inch_a_desc },
{ .compatible = "ampire,am8001280g", .data = &am8001280g_desc },
{ .compatible = "raspberrypi,dsi-5inch", &rpi_5inch_desc },
{ .compatible = "raspberrypi,dsi-7inch", &rpi_7inch_desc },

Some files were not shown because too many files have changed in this diff Show More