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

drm-misc-next for 7.3:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
- Switch to kmalloc_objs where relevant
- Create drm_of_get_panel_orientation(), and switch to it where relevant

Driver Changes:

- rockchip: Switch away from simple helpers
- rocket: error handling and null pointer dereference fixes
- ssd130x: Addressing fixes
- sun4i: Switch away from simple helpers, refcount fix
- v3d: validate CPU-job query writes boundaries, misc perfmon fixes

- bridges:
  - analogix_dp: Switch to DP link training helpers

- panels:
  - visionox-vtdr6130: Enable DSC
  - New panels: Ilitek ILI7807S, Renesas R63419, MNE001BS6-2, MNF601BS4-1

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

From: Maxime Ripard <mripard@redhat.com>
Link: https://patch.msgid.link/20260709-calm-ibis-of-storm-4aefe4@houat
This commit is contained in:
Dave Airlie
2026-07-10 10:22:38 +10:00
69 changed files with 1502 additions and 302 deletions

View File

@@ -0,0 +1,71 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/ilitek,ili7807s.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ilitek ILI7807S-based DSI panels
maintainers:
- Arpit Saini <arpit.saini@oss.qualcomm.com>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- dlc,dlc0697
- const: ilitek,ili7807s
reg:
maxItems: 1
description: DSI virtual channel
vddi-supply:
description: I/O voltage supply (1.8V)
avdd-supply:
description: Positive LCD bias supply (AVDD), typically +5.5V
(range 4.5V to 6.3V)
avee-supply:
description: Negative LCD bias supply (AVEE), typically -5.5V
(range -6.3V to -4.5V)
required:
- compatible
- reg
- reset-gpios
- vddi-supply
- avdd-supply
- avee-supply
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "dlc,dlc0697", "ilitek,ili7807s";
reg = <0>;
reset-gpios = <&tlmm 3 GPIO_ACTIVE_LOW>;
vddi-supply = <&pm4125_l15>;
avdd-supply = <&avdd>;
avee-supply = <&avee>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};

View File

@@ -0,0 +1,98 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/renesas,r63419.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas R63419 based dual-DSI video mode Display Panel
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
The Renesas R63419 is a generic DDIC used to control dual-DSI LCD panels.
allOf:
- $ref: panel-common-dual.yaml#
properties:
compatible:
items:
- enum:
- ayaneo,wt0600-2k
- ayaneo,wt0630-2k
- const: renesas,r63419
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
vsp-supply: true
vsn-supply: true
vci-supply: true
backlight: true
reset-gpios: true
rotation: true
ports: true
required:
- compatible
- reg
- vdd-supply
- vddio-supply
- vsp-supply
- vsn-supply
- vci-supply
- backlight
- reset-gpios
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "ayaneo,wt0600-2k", "renesas,r63419";
reg = <0>;
reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>;
vdd-supply = <&vdd_3v0_lcd>;
vddio-supply = <&vdd_1v8_io>;
vsn-supply = <&vdd_5v0_neg>;
vsp-supply = <&vdd_5v0_pos>;
vci-supply = <&vdd_3v0_vci>;
backlight = <&backlight>;
rotation = <90>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel_in0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1 {
reg = <1>;
panel_in1: endpoint {
remote-endpoint = <&dsi1_out>;
};
};
};
};
};
...

View File

@@ -8858,11 +8858,14 @@ M: Sandy Huang <hjc@rock-chips.com>
M: Heiko Stübner <heiko@sntech.de>
M: Andy Yan <andy.yan@rock-chips.com>
L: dri-devel@lists.freedesktop.org
L: linux-rockchip@lists.infradead.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/rockchip/
F: drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.*
F: drivers/gpu/drm/ci/xfails/rockchip*
F: drivers/gpu/drm/rockchip/
F: include/drm/bridge/dw_hdmi_qp.h
DRM DRIVERS FOR STI
M: Alain Volmat <alain.volmat@foss.st.com>

View File

@@ -8,6 +8,7 @@
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
#include <linux/interrupt.h>
#include <linux/overflow.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -102,6 +103,7 @@ rocket_copy_tasks(struct drm_device *dev,
fail:
kvfree(rjob->tasks);
rjob->tasks = NULL;
return ret;
}
@@ -188,14 +190,19 @@ static int rocket_job_push(struct rocket_job *job)
struct rocket_device *rdev = job->rdev;
struct drm_gem_object **bos;
struct ww_acquire_ctx acquire_ctx;
u32 bo_count;
int ret = 0;
bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
GFP_KERNEL);
if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count))
return -EINVAL;
bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL);
if (!bos)
return -ENOMEM;
memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx);
if (ret)
goto err;
@@ -220,7 +227,7 @@ static int rocket_job_push(struct rocket_job *job)
rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
err_unlock:
drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx);
err:
kvfree(bos);
@@ -310,13 +317,13 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
dma_fence_put(job->done_fence);
job->done_fence = dma_fence_get(fence);
ret = pm_runtime_get_sync(core->dev);
ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
return fence;
goto err_put_fences;
ret = iommu_attach_group(job->domain->domain, core->iommu_group);
if (ret < 0)
return fence;
goto err_put_pm;
scoped_guard(mutex, &core->job_lock) {
core->in_flight_job = job;
@@ -324,6 +331,14 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
}
return fence;
err_put_pm:
pm_runtime_put(core->dev);
err_put_fences:
dma_fence_put(job->done_fence);
job->done_fence = NULL;
dma_fence_put(fence);
return ERR_PTR(ret);
}
static void rocket_job_handle_irq(struct rocket_core *core)
@@ -548,6 +563,7 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
kref_init(&rjob->refcount);
rjob->rdev = rdev;
rjob->domain = rocket_iommu_domain_get(file_priv);
ret = drm_sched_job_init(&rjob->base,
&file_priv->sched_entity,
@@ -573,8 +589,6 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
rjob->out_bo_count = job->out_bo_handle_count;
rjob->domain = rocket_iommu_domain_get(file_priv);
ret = rocket_job_push(rjob);
if (ret)
goto out_cleanup_job;

View File

@@ -93,9 +93,9 @@ static int fence_cmp(const void *_a, const void *_b)
*
* Return: Number of unique fences remaining in the array.
*/
int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
size_t dma_fence_dedup_array(struct dma_fence **fences, size_t num_fences)
{
int i, j;
size_t i, j;
if (!num_fences)
return 0;
@@ -118,14 +118,14 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
EXPORT_SYMBOL_GPL(dma_fence_dedup_array);
/* Implementation for the dma_fence_merge() marco, don't use directly */
struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences,
struct dma_fence **fences,
struct dma_fence_unwrap *iter)
{
struct dma_fence *tmp, *unsignaled = NULL, **array;
struct dma_fence_array *result;
ktime_t timestamp;
int i, count;
size_t i, count;
count = 0;
timestamp = ns_to_ktime(0);

View File

@@ -328,7 +328,7 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
u8 voltage_swing, pre_emphasis, training_lane;
u8 link_status[DP_LINK_STATUS_SIZE];
usleep_range(100, 101);
drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd);
lane_count = dp->link_train.lane_count;
@@ -389,7 +389,7 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
u32 reg;
u8 link_status[DP_LINK_STATUS_SIZE];
usleep_range(400, 401);
drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd);
lane_count = dp->link_train.lane_count;
@@ -750,6 +750,12 @@ static int analogix_dp_commit(struct analogix_dp_device *dp)
{
int ret;
ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd);
if (ret < 0) {
dev_err(dp->dev, "failed to read dpcd caps: %d\n", ret);
return ret;
}
ret = analogix_dp_train_link(dp);
if (ret) {
dev_err(dp->dev, "unable to do link train, ret=%d\n", ret);

View File

@@ -169,6 +169,8 @@ struct analogix_dp_device {
bool fast_train_enable;
bool psr_supported;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
struct analogix_dp_plat_data *plat_data;
};

View File

@@ -79,7 +79,7 @@ void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr)
nr = PAGE_SIZE / sizeof(void *);
exec->flags = flags;
exec->objects = kvmalloc_array(nr, sizeof(void *), GFP_KERNEL);
exec->objects = kvmalloc_objs(*exec->objects, nr, GFP_KERNEL);
/* If allocation here fails, just delay that till the first use */
exec->max_objects = exec->objects ? nr : 0;

View File

@@ -2229,50 +2229,6 @@ drm_gpuva_find(struct drm_gpuvm *gpuvm,
}
EXPORT_SYMBOL_GPL(drm_gpuva_find);
/**
* drm_gpuva_find_prev() - find the &drm_gpuva before the given address
* @gpuvm: the &drm_gpuvm to search in
* @start: the given GPU VA's start address
*
* Find the adjacent &drm_gpuva before the GPU VA with given &start address.
*
* Note that if there is any free space between the GPU VA mappings no mapping
* is returned.
*
* Returns: a pointer to the found &drm_gpuva or NULL if none was found
*/
struct drm_gpuva *
drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start)
{
if (!drm_gpuvm_range_valid(gpuvm, start - 1, 1))
return NULL;
return drm_gpuva_it_iter_first(&gpuvm->rb.tree, start - 1, start);
}
EXPORT_SYMBOL_GPL(drm_gpuva_find_prev);
/**
* drm_gpuva_find_next() - find the &drm_gpuva after the given address
* @gpuvm: the &drm_gpuvm to search in
* @end: the given GPU VA's end address
*
* Find the adjacent &drm_gpuva after the GPU VA with given &end address.
*
* Note that if there is any free space between the GPU VA mappings no mapping
* is returned.
*
* Returns: a pointer to the found &drm_gpuva or NULL if none was found
*/
struct drm_gpuva *
drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end)
{
if (!drm_gpuvm_range_valid(gpuvm, end, 1))
return NULL;
return drm_gpuva_it_iter_first(&gpuvm->rb.tree, end, end + 1);
}
EXPORT_SYMBOL_GPL(drm_gpuva_find_next);
/**
* drm_gpuvm_interval_empty() - indicate whether a given interval of the VA space
* is empty

View File

@@ -7,6 +7,7 @@
#include <linux/of_graph.h>
#include <drm/drm_bridge.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_encoder.h>
@@ -220,6 +221,49 @@ int drm_of_encoder_active_endpoint(struct device_node *node,
}
EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
/**
* drm_of_get_panel_orientation - look up the orientation of the panel through
* the "rotation" binding from a device tree node
* @np: device tree node of the panel
* @orientation: orientation enum to be filled in
*
* Looks up the rotation of a panel in the device tree. The orientation of the
* panel is expressed as a property name "rotation" in the device tree. The
* rotation in the device tree is counter clockwise.
*
* Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
* rotation property doesn't exist. Return a negative error code on failure.
*/
int drm_of_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation)
{
int rotation, ret;
ret = of_property_read_u32(np, "rotation", &rotation);
if (ret == -EINVAL) {
/* Don't return an error if there's no rotation property. */
*orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
return 0;
}
if (ret < 0)
return ret;
if (rotation == 0)
*orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
else if (rotation == 90)
*orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
else if (rotation == 180)
*orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
else if (rotation == 270)
*orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
else
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation);
/**
* drm_of_find_panel_or_bridge - return connected panel or bridge device
* @np: device tree node containing encoder output ports

View File

@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <drm/drm_crtc.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
@@ -491,49 +492,6 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np)
return ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(of_drm_find_panel);
/**
* of_drm_get_panel_orientation - look up the orientation of the panel through
* the "rotation" binding from a device tree node
* @np: device tree node of the panel
* @orientation: orientation enum to be filled in
*
* Looks up the rotation of a panel in the device tree. The orientation of the
* panel is expressed as a property name "rotation" in the device tree. The
* rotation in the device tree is counter clockwise.
*
* Return: 0 when a valid rotation value (0, 90, 180, or 270) is read or the
* rotation property doesn't exist. Return a negative error code on failure.
*/
int of_drm_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation)
{
int rotation, ret;
ret = of_property_read_u32(np, "rotation", &rotation);
if (ret == -EINVAL) {
/* Don't return an error if there's no rotation property. */
*orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
return 0;
}
if (ret < 0)
return ret;
if (rotation == 0)
*orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
else if (rotation == 90)
*orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
else if (rotation == 180)
*orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
else if (rotation == 270)
*orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
else
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(of_drm_get_panel_orientation);
#endif
/* Find panel by fwnode. This should be identical to of_drm_find_panel(). */

View File

@@ -1619,7 +1619,7 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data,
goto err_points;
}
chains = kmalloc_array(args->count_handles, sizeof(void *), GFP_KERNEL);
chains = kmalloc_objs(*chains, args->count_handles, GFP_KERNEL);
if (!chains) {
ret = -ENOMEM;
goto err_points;

View File

@@ -285,6 +285,18 @@ config DRM_PANEL_HYDIS_HV101HD1
If M is selected the module will be called panel-hydis-hv101hd1
config DRM_PANEL_ILITEK_ILI7807S
tristate "Ilitek ILI7807S-based panels"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
Say Y if you want to enable support for panels based on the
Ilitek ILI7807S display controller, such as the DLC DLC0697
1080x1920 MIPI DSI panel.
If M is selected the module will be called panel-ilitek-ili7807s.
config DRM_PANEL_ILITEK_IL9322
tristate "Ilitek ILI9322 320x240 QVGA panels"
depends on OF && SPI
@@ -826,6 +838,18 @@ config DRM_PANEL_RENESAS_R61307
This panel controller can be found in LG Optimus Vu P895 smartphone
in combination with LCD panel.
config DRM_PANEL_RENESAS_R63419
tristate "Renesas R63419 dual-DSI video mode panels"
depends on OF && GPIOLIB
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
help
Say Y here if you want to enable support for Ayaneo WT0600 and WT0630
1440x2560 60Hz dual-DSI video mode display panels with Renesas
R63419 IC.
These panels are used in Ayaneo handheld gaming devices.
config DRM_PANEL_RENESAS_R69328
tristate "Renesas R69328 720x1280 DSI video mode panel"
depends on OF
@@ -1352,6 +1376,8 @@ config DRM_PANEL_VISIONOX_VTDR6130
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select DRM_DISPLAY_DSC_HELPER
select DRM_DISPLAY_HELPER
help
Say Y here if you want to enable support for Visionox
VTDR6130 1080x2400 AMOLED DSI panel.

View File

@@ -28,6 +28,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9488) += panel-ilitek-ili9488.o
@@ -81,6 +82,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o
obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o
obj-$(CONFIG_DRM_PANEL_RENESAS_R63419) += panel-renesas-r63419.o
obj-$(CONFIG_DRM_PANEL_RENESAS_R69328) += panel-renesas-r69328.o
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o

View File

@@ -12,6 +12,7 @@
#include <linux/regulator/consumer.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -165,7 +166,7 @@ static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi)
if (!ctx->panel_info)
return -EINVAL;
ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get panel orientation\n");

View File

@@ -15,6 +15,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -380,7 +381,7 @@ static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
"Failed to get reset GPIO\n");
ret = of_drm_get_panel_orientation(dsi->dev.of_node,
ret = drm_of_get_panel_orientation(dsi->dev.of_node,
&ctx->orientation);
if (ret)
return dev_err_probe(&dsi->dev, ret,

View File

@@ -13,6 +13,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
@@ -1722,7 +1723,7 @@ static int boe_panel_add(struct boe_panel *boe)
boe->base.prepare_prev_first = true;
err = of_drm_get_panel_orientation(dev->of_node, &boe->orientation);
err = drm_of_get_panel_orientation(dev->of_node, &boe->orientation);
if (err < 0) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err);
return err;

View File

@@ -21,6 +21,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -374,7 +375,7 @@ static int icna35xx_probe(struct mipi_dsi_device *dsi)
pinfo->dsi = dsi;
mipi_dsi_set_drvdata(dsi, pinfo);
ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &pinfo->orientation);
if (ret < 0) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
return ret;

View File

@@ -13,6 +13,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
@@ -268,7 +269,7 @@ static int ch13726a_probe(struct mipi_dsi_device *dsi)
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);
ret = drm_of_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;

View File

@@ -40,6 +40,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
/**
@@ -865,7 +866,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc,
return dev_err_probe(dev, PTR_ERR(panel->enable_gpio),
"failed to request GPIO\n");
err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation);
err = drm_of_get_panel_orientation(dev->of_node, &panel->orientation);
if (err) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err);
return err;
@@ -2111,8 +2112,10 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1468, &delay_200_500_e50, "MNE007QB2-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x147c, &delay_200_500_e50_d100, "MNE007QB3-1"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1486, &delay_200_500_e50_d100, "MNE001BS6-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1529, &delay_200_500_e80_d50, "MNF307QS3-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x153f, &delay_200_500_e200_d50, "MNF601BS4-1"),
EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"),

View File

@@ -21,6 +21,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
/* Manufacturer specific Commands send via DSI */
@@ -233,7 +234,7 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi)
return ret;
}
ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
ret = drm_of_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;

View File

@@ -17,6 +17,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -181,7 +182,7 @@ static int ota7290b_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->vdd),
"Couldn't get our VDD supply\n");
ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
if (ret) {
dev_err(&dsi->dev, "%pOF: failed to get orientation: %d\n",
dsi->dev.of_node, ret);

View File

@@ -17,6 +17,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
@@ -1264,7 +1265,7 @@ static int hx83102_panel_add(struct hx83102 *ctx)
ctx->base.prepare_prev_first = true;
err = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
err = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
if (err < 0)
return dev_err_probe(dev, err, "failed to get orientation\n");

View File

@@ -21,6 +21,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#define DRV_NAME "panel-himax-hx8394"
@@ -991,7 +992,7 @@ static int hx8394_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
"Failed to get reset gpio\n");
ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
ret = drm_of_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;

View File

@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#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 <video/mipi_display.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
struct panel_desc {
const struct drm_display_mode *mode;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
void (*init)(struct mipi_dsi_multi_context *dsi_ctx);
};
struct ili7807s {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
const struct panel_desc *desc;
struct regulator_bulk_data *supplies;
struct gpio_desc *reset_gpio;
};
static const struct regulator_bulk_data ili7807s_supplies[] = {
{ .supply = "vddi" },
{ .supply = "avdd" },
{ .supply = "avee" },
};
static inline struct ili7807s *to_ili7807s(struct drm_panel *panel)
{
return container_of(panel, struct ili7807s, panel);
}
static void ili7807s_reset(struct ili7807s *ctx)
{
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 void dlc0697_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
{
mipi_dsi_dcs_soft_reset_multi(dsi_ctx);
mipi_dsi_msleep(dsi_ctx, 120);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x78, 0x07, 0x00);
mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x09, 0x99);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01);
mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x1fff);
mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx);
mipi_dsi_msleep(dsi_ctx, 120);
mipi_dsi_dcs_set_display_on_multi(dsi_ctx);
mipi_dsi_msleep(dsi_ctx, 20);
}
static int ili7807s_on(struct ili7807s *ctx)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
ctx->desc->init(&dsi_ctx);
return dsi_ctx.accum_err;
}
static int ili7807s_off(struct ili7807s *ctx)
{
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 120);
return dsi_ctx.accum_err;
}
static int ili7807s_prepare(struct drm_panel *panel)
{
struct ili7807s *ctx = to_ili7807s(panel);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
if (ret < 0) {
dev_err(ctx->panel.dev, "failed to enable regulators: %d\n", ret);
return ret;
}
msleep(20);
ili7807s_reset(ctx);
ret = ili7807s_on(ctx);
if (ret < 0) {
dev_err(ctx->panel.dev, "failed to initialise panel: %d\n", ret);
goto err;
}
return 0;
err:
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
return ret;
}
static int ili7807s_unprepare(struct drm_panel *panel)
{
struct ili7807s *ctx = to_ili7807s(panel);
int ret;
ret = ili7807s_off(ctx);
if (ret < 0)
dev_err(ctx->panel.dev, "failed to disable panel: %d\n", ret);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies);
return 0;
}
static int ili7807s_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct ili7807s *ctx = to_ili7807s(panel);
return drm_connector_helper_get_modes_fixed(connector, ctx->desc->mode);
}
static const struct drm_panel_funcs ili7807s_panel_funcs = {
.prepare = ili7807s_prepare,
.unprepare = ili7807s_unprepare,
.get_modes = ili7807s_get_modes,
};
static int ili7807s_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_large(dsi, brightness);
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
return ret;
}
static const struct backlight_ops ili7807s_bl_ops = {
.update_status = ili7807s_bl_update_status,
};
static struct backlight_device *ili7807s_create_backlight(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.brightness = 0x3fff,
.max_brightness = 0x3fff,
};
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
&ili7807s_bl_ops, &props);
}
static const struct drm_display_mode dlc0697_mode = {
.clock = (1080 + 18 + 2 + 16) * (1920 + 26 + 4 + 20) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 18,
.hsync_end = 1080 + 18 + 2,
.htotal = 1080 + 18 + 2 + 16,
.vdisplay = 1920,
.vsync_start = 1920 + 26,
.vsync_end = 1920 + 26 + 4,
.vtotal = 1920 + 26 + 4 + 20,
.width_mm = 87,
.height_mm = 154,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
static const struct panel_desc dlc0697_desc = {
.mode = &dlc0697_mode,
.lanes = 4,
.format = MIPI_DSI_FMT_RGB888,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST,
.init = dlc0697_init_sequence,
};
static int ili7807s_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
const struct panel_desc *desc;
struct ili7807s *ctx;
int ret;
ctx = devm_drm_panel_alloc(dev, struct ili7807s, panel,
&ili7807s_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
desc = of_device_get_match_data(dev);
ctx->desc = desc;
ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(ili7807s_supplies),
ili7807s_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 gpio\n");
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
dsi->lanes = desc->lanes;
dsi->format = desc->format;
dsi->mode_flags = desc->mode_flags | MIPI_DSI_MODE_LPM;
ctx->panel.prepare_prev_first = true;
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return ret;
if (!ctx->panel.backlight) {
ctx->panel.backlight = ili7807s_create_backlight(dsi);
if (IS_ERR(ctx->panel.backlight))
return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
"failed to create backlight\n");
}
ret = devm_drm_panel_add(dev, &ctx->panel);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to add panel\n");
return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id ili7807s_of_match[] = {
{ .compatible = "dlc,dlc0697", .data = &dlc0697_desc },
{ }
};
MODULE_DEVICE_TABLE(of, ili7807s_of_match);
static struct mipi_dsi_driver ili7807s_dsi_driver = {
.probe = ili7807s_probe,
.driver = {
.name = "panel-ilitek-ili7807s",
.of_match_table = ili7807s_of_match,
},
};
module_mipi_dsi_driver(ili7807s_dsi_driver);
MODULE_AUTHOR("Arpit Saini <arpit.saini@oss.qualcomm.com>");
MODULE_DESCRIPTION("Panel driver for Ilitek ILI7807S LCD DSI panel");
MODULE_LICENSE("GPL");

View File

@@ -13,6 +13,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -237,7 +238,7 @@ static int ili9488_dsi_probe(struct mipi_dsi_device *dsi)
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get regulators\n");
ret = of_drm_get_panel_orientation(dev->of_node, &ili->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &ili->orientation);
if (ret)
return dev_err_probe(dev, ret, "failed to get orientation\n");

View File

@@ -9,6 +9,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
@@ -131,7 +132,7 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
dsi->format = ctx->desc->format;
dsi->lanes = ctx->desc->lanes;
ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
if (ret)
return dev_err_probe(dev, ret, "Failed to get orientation\n");

View File

@@ -16,6 +16,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
@@ -2571,7 +2572,7 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
"Couldn't get our reset GPIO\n");
ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
if (ret) {
dev_err(&dsi->dev, "%pOF: failed to get orientation: %d\n",
dsi->dev.of_node, ret);

View File

@@ -13,6 +13,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <video/mipi_display.h>
@@ -795,7 +796,7 @@ static int ili9882t_add(struct ili9882t *ili)
gpiod_set_value_cansleep(ili->enable_gpio, 0);
err = of_drm_get_panel_orientation(dev->of_node, &ili->orientation);
err = drm_of_get_panel_orientation(dev->of_node, &ili->orientation);
if (err < 0) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err);
return err;

View File

@@ -2992,7 +2992,7 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(jadard->vccio),
"failed to get vccio regulator\n");
ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &jadard->orientation);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get orientation\n");

View File

@@ -126,7 +126,7 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
struct device_node *np = lvds->dev->of_node;
int ret;
ret = of_drm_get_panel_orientation(np, &lvds->orientation);
ret = drm_of_get_panel_orientation(np, &lvds->orientation);
if (ret < 0) {
dev_err(lvds->dev, "%pOF: failed to get orientation %d\n", np, ret);
return ret;

View File

@@ -19,6 +19,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#define DSI_NUM_MIN 1
@@ -1206,7 +1207,7 @@ static int nt36523_probe(struct mipi_dsi_device *dsi)
pinfo->dsi[0] = dsi;
mipi_dsi_set_drvdata(dsi, pinfo);
ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &pinfo->orientation);
if (ret < 0) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret);
return ret;

View File

@@ -0,0 +1,351 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* DRM driver for Renesas R63419 based dual-DSI video mode panels
*
* Copyright (c) 2025, Kancy Joe <kancy2333@outlook.com>
* Copyright (C) 2026 Linaro Limited
* Author: Neil Armstrong <neil.armstrong@linaro.org>
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <video/mipi_display.h>
#include <drm/drm_connector.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
struct renesas_r63419_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi[2];
const struct panel_desc *desc;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data *vdd_supplies;
struct regulator_bulk_data *vcc_supplies;
enum drm_panel_orientation orientation;
};
/* VDDIO/VDD Supplies */
static const struct regulator_bulk_data renesas_r63419_vdd_supplies[] = {
{ .supply = "vddio" },
{ .supply = "vdd" },
};
/* VSP/VSN/VCI Supplies */
static const struct regulator_bulk_data renesas_r63419_vcc_supplies[] = {
{ .supply = "vsp" },
{ .supply = "vsn" },
{ .supply = "vci" },
};
struct panel_desc {
const struct drm_display_mode *mode;
unsigned int lanes;
unsigned long mode_flags;
enum mipi_dsi_pixel_format format;
const struct mipi_dsi_device_info dsi_info;
};
static const struct drm_display_mode wt0600_mode = {
/* Dual dsi */
.clock = 2 * (720 + 100 + 8 + 40) * (2560 + 15 + 2 + 8) * 60 / 1000,
.hdisplay = 2 * 720,
.hsync_start = 2 * (720 + 100),
.hsync_end = 2 * (720 + 100 + 8),
.htotal = 2 * (720 + 100 + 8 + 40),
.vdisplay = 2560,
.vsync_start = 2560 + 15,
.vsync_end = 2560 + 15 + 2,
.vtotal = 2560 + 15 + 2 + 8,
.type = DRM_MODE_TYPE_DRIVER,
.width_mm = 74,
.height_mm = 131,
};
static const struct drm_display_mode wt0630_mode = {
/* Dual dsi */
.clock = 2 * (720 + 100 + 8 + 40) * (2560 + 15 + 2 + 8) * 60 / 1000,
.hdisplay = 2 * 720,
.hsync_start = 2 * (720 + 100),
.hsync_end = 2 * (720 + 100 + 8),
.htotal = 2 * (720 + 100 + 8 + 40),
.vdisplay = 2560,
.vsync_start = 2560 + 15,
.vsync_end = 2560 + 15 + 2,
.vtotal = 2560 + 15 + 2 + 8,
.type = DRM_MODE_TYPE_DRIVER,
.width_mm = 78,
.height_mm = 140,
};
static struct panel_desc wt0600_desc = {
.lanes = 4,
.mode = &wt0600_mode,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
.format = MIPI_DSI_FMT_RGB888,
};
static struct panel_desc wt0630_desc = {
.lanes = 4,
.mode = &wt0630_mode, /* wt0600 only has different screen size */
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
.format = MIPI_DSI_FMT_RGB888,
};
static inline struct renesas_r63419_panel *
to_renesas_r63419_panel(struct drm_panel *panel)
{
return container_of(panel, struct renesas_r63419_panel, panel);
}
static int renesas_r63419_on(struct renesas_r63419_panel *ctx)
{
struct mipi_dsi_multi_context dsi_ctx = { 0 };
/*
* Panel registers are loaded from DDIC Non Volatile Memory
*
* The DDIC expects this sequence to get out of sleep and enable display
*/
mipi_dsi_dual(mipi_dsi_dcs_set_display_on_multi,
&dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
mipi_dsi_msleep(&dsi_ctx, 50);
mipi_dsi_dual(mipi_dsi_dcs_exit_sleep_mode_multi,
&dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
mipi_dsi_msleep(&dsi_ctx, 150);
return dsi_ctx.accum_err;
}
static int renesas_r63419_disable(struct drm_panel *panel)
{
struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
struct mipi_dsi_multi_context dsi_ctx = { 0 };
mipi_dsi_dual(mipi_dsi_dcs_set_display_off_multi,
&dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
mipi_dsi_msleep(&dsi_ctx, 50);
mipi_dsi_dual(mipi_dsi_dcs_enter_sleep_mode_multi,
&dsi_ctx, ctx->dsi[0], ctx->dsi[1]);
mipi_dsi_msleep(&dsi_ctx, 120);
return 0;
}
static int renesas_r63419_prepare(struct drm_panel *panel)
{
struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(renesas_r63419_vdd_supplies),
ctx->vdd_supplies);
if (ret < 0)
return ret;
usleep_range(1000, 2000);
ret = regulator_bulk_enable(ARRAY_SIZE(renesas_r63419_vcc_supplies),
ctx->vcc_supplies);
if (ret < 0) {
regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies),
ctx->vdd_supplies);
return ret;
}
usleep_range(1000, 2000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(3000, 4000);
ret = renesas_r63419_on(ctx);
if (ret < 0) {
dev_err(panel->dev, "Failed to initialize panel: %d\n", ret);
/* Power off sequence from the r63419 datasheet */
regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies),
ctx->vdd_supplies);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vcc_supplies),
ctx->vcc_supplies);
return ret;
}
return 0;
}
static int renesas_r63419_unprepare(struct drm_panel *panel)
{
struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
/* Power off sequence from the r63419 datasheet */
regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies), ctx->vdd_supplies);
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vcc_supplies), ctx->vcc_supplies);
return 0;
}
static int renesas_r63419_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
const struct drm_display_mode *mode = ctx->desc->mode;
return drm_connector_helper_get_modes_fixed(connector, mode);
}
static enum drm_panel_orientation
renesas_r63419_get_orientation(struct drm_panel *panel)
{
struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel);
return ctx->orientation;
}
static const struct drm_panel_funcs renesas_r63419_panel_funcs = {
.disable = renesas_r63419_disable,
.prepare = renesas_r63419_prepare,
.unprepare = renesas_r63419_unprepare,
.get_modes = renesas_r63419_get_modes,
.get_orientation = renesas_r63419_get_orientation,
};
static int renesas_r63419_probe(struct mipi_dsi_device *dsi)
{
struct mipi_dsi_device_info info = { 0 };
struct device *dev = &dsi->dev;
struct renesas_r63419_panel *ctx;
struct device_node *dsi1_node;
struct mipi_dsi_host *dsi1_host;
int ret, i;
ctx = devm_drm_panel_alloc(dev, struct renesas_r63419_panel, panel,
&renesas_r63419_panel_funcs, DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
ctx->desc = of_device_get_match_data(dev);
if (!ctx->desc)
return dev_err_probe(dev, -ENODEV,
"Failed to get panel description\n");
ret = devm_regulator_bulk_get_const(&dsi->dev,
ARRAY_SIZE(renesas_r63419_vdd_supplies),
renesas_r63419_vdd_supplies, &ctx->vdd_supplies);
if (ret < 0)
return ret;
ret = devm_regulator_bulk_get_const(&dsi->dev,
ARRAY_SIZE(renesas_r63419_vcc_supplies),
renesas_r63419_vcc_supplies, &ctx->vcc_supplies);
if (ret < 0)
return ret;
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 gpio\n");
/* Get second DSI host */
dsi1_node = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
if (!dsi1_node)
return dev_err_probe(dev, -ENODEV,
"Failed to get remote node for second DSI\n");
dsi1_host = of_find_mipi_dsi_host_by_node(dsi1_node);
of_node_put(dsi1_node);
if (!dsi1_host)
return dev_err_probe(dev, -EPROBE_DEFER,
"Failed to find second DSI host\n");
/* Copy current DSI info, do not provide OF node since no driver needs to be attached */
strscpy(info.type, dsi->name);
info.channel = dsi->channel;
/* Register the second DSI device */
ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host, &info);
if (IS_ERR(ctx->dsi[1]))
return dev_err_probe(dev, PTR_ERR(ctx->dsi[1]),
"Failed to register second DSI device\n");
ctx->dsi[0] = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
/* Get panel orientation */
ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation);
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret,
"Failed to get panel orientation\n");
ctx->panel.prepare_prev_first = true;
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
ret = devm_drm_panel_add(dev, &ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to add panel\n");
/* Configure and attach both DSI devices */
for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
ctx->dsi[i]->lanes = ctx->desc->lanes;
ctx->dsi[i]->format = ctx->desc->format;
ctx->dsi[i]->mode_flags = ctx->desc->mode_flags;
ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to attach DSI device %d\n", i);
}
return 0;
}
static const struct of_device_id renesas_r63419_of_match[] = {
{
.compatible = "ayaneo,wt0600-2k",
.data = &wt0600_desc,
},
{
.compatible = "ayaneo,wt0630-2k",
.data = &wt0630_desc,
},
{}
};
MODULE_DEVICE_TABLE(of, renesas_r63419_of_match);
static struct mipi_dsi_driver renesas_r63419_driver = {
.probe = renesas_r63419_probe,
.driver = {
.name = "panel-renesas-r63419",
.of_match_table = renesas_r63419_of_match,
},
};
module_mipi_dsi_driver(renesas_r63419_driver);
MODULE_AUTHOR("Kancy Joe <kancy2333@outlook.com>");
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
MODULE_DESCRIPTION("DRM driver for Renesas R63419 based dual-DSI video mode panels");
MODULE_LICENSE("GPL");

View File

@@ -693,7 +693,7 @@ static struct panel_simple *panel_simple_probe(struct device *dev)
return dev_err_cast_probe(dev, panel->enable_gpio,
"failed to request GPIO\n");
err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation);
err = drm_of_get_panel_orientation(dev->of_node, &panel->orientation);
if (err) {
dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err);
return ERR_PTR(err);

View File

@@ -7,6 +7,7 @@
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <linux/bitfield.h>
@@ -1296,7 +1297,7 @@ static int st7701_probe(struct device *dev, int connector_type)
return PTR_ERR(st7701->reset);
}
ret = of_drm_get_panel_orientation(dev->of_node, &st7701->orientation);
ret = drm_of_get_panel_orientation(dev->of_node, &st7701->orientation);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get orientation\n");

View File

@@ -20,6 +20,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#define DRV_NAME "panel-sitronix-st7703"
@@ -873,7 +874,7 @@ static int st7703_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
"Failed to request iovcc regulator\n");
ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
ret = drm_of_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
if (ret < 0)
return dev_err_probe(&dsi->dev, ret, "Failed to get orientation\n");

View File

@@ -14,6 +14,7 @@
#include <drm/drm_device.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#define ST7789V_RAMCTRL_CMD 0xb0
@@ -646,7 +647,7 @@ static int st7789v_probe(struct spi_device *spi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
ret = of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation);
ret = drm_of_get_panel_orientation(spi->dev.of_node, &ctx->orientation);
if (ret)
return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n");

View File

@@ -9,6 +9,7 @@
#include <linux/of.h>
#include <drm/display/drm_dsc.h>
#include <drm/display/drm_dsc_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
@@ -17,6 +18,7 @@
struct visionox_vtdr6130 {
struct drm_panel panel;
struct drm_dsc_config dsc;
struct mipi_dsi_device *dsi;
struct gpio_desc *reset_gpio;
struct regulator_bulk_data *supplies;
@@ -47,9 +49,12 @@ static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
struct drm_dsc_picture_parameter_set pps;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x01);
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx,
@@ -123,6 +128,9 @@ static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx)
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 20);
drm_dsc_pps_payload_pack(&pps, dsi->dsc);
mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
return dsi_ctx.accum_err;
}
@@ -269,6 +277,16 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
ctx->dsi = dsi;
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsc.dsc_version_major = 0x1;
ctx->dsc.dsc_version_minor = 0x2;
ctx->dsc.slice_height = 40;
ctx->dsc.slice_width = 540;
ctx->dsc.slice_count = 2;
ctx->dsc.bits_per_component = 8;
ctx->dsc.bits_per_pixel = 8 << 4;
ctx->dsc.block_pred_enable = true;
dsi->dsc = &ctx->dsc;
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
@@ -280,28 +298,11 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi)
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);
ret = devm_drm_panel_add(dev, &ctx->panel);
if (ret)
return ret;
}
return 0;
}
static void visionox_vtdr6130_remove(struct mipi_dsi_device *dsi)
{
struct visionox_vtdr6130 *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);
return devm_mipi_dsi_attach(dev, dsi);
}
static const struct of_device_id visionox_vtdr6130_of_match[] = {
@@ -312,7 +313,6 @@ MODULE_DEVICE_TABLE(of, visionox_vtdr6130_of_match);
static struct mipi_dsi_driver visionox_vtdr6130_driver = {
.probe = visionox_vtdr6130_probe,
.remove = visionox_vtdr6130_remove,
.driver = {
.name = "panel-visionox-vtdr6130",
.of_match_table = visionox_vtdr6130_of_match,

View File

@@ -30,7 +30,6 @@
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"
@@ -303,6 +302,10 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
static const struct drm_encoder_funcs rockchip_dp_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs rockchip_dp_encoder_helper_funcs = {
.mode_fixup = rockchip_dp_drm_encoder_mode_fixup,
.mode_set = rockchip_dp_drm_encoder_mode_set,
@@ -361,8 +364,8 @@ static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
dev->of_node);
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
ret = drm_simple_encoder_init(drm_dev, encoder,
DRM_MODE_ENCODER_TMDS);
ret = drm_encoder_init(drm_dev, encoder, &rockchip_dp_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
DRM_ERROR("failed to initialize encoder with drm\n");
return ret;

View File

@@ -23,7 +23,6 @@
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "cdn-dp-core.h"
#include "cdn-dp-reg.h"
@@ -671,6 +670,10 @@ static int cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
static const struct drm_encoder_funcs cdn_dp_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
.atomic_check = cdn_dp_encoder_atomic_check,
};
@@ -988,8 +991,8 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
dev->of_node);
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
ret = drm_simple_encoder_init(drm_dev, encoder,
DRM_MODE_ENCODER_TMDS);
ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
DRM_ERROR("failed to initialize encoder with drm\n");
return ret;

View File

@@ -25,7 +25,6 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"
@@ -825,6 +824,10 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
clk_disable_unprepare(dsi->grf_clk);
}
static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs
dw_mipi_dsi_encoder_helper_funcs = {
.atomic_check = dw_mipi_dsi_encoder_atomic_check,
@@ -840,7 +843,9 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
dsi->dev->of_node);
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
ret = drm_encoder_init(drm_dev, encoder,
&dw_mipi_dsi_encoder_funcs,
DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
DRM_ERROR("Failed to initialize encoder with drm\n");
return ret;

View File

@@ -22,7 +22,6 @@
#include <drm/bridge/dw_mipi_dsi2.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_simple_kms_helper.h>
#include <uapi/linux/videodev2.h>
@@ -274,6 +273,10 @@ dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
static const struct drm_encoder_funcs dw_mipi_dsi2_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs
dw_mipi_dsi2_encoder_helper_funcs = {
.atomic_enable = dw_mipi_dsi2_encoder_atomic_enable,
@@ -289,7 +292,8 @@ static int rockchip_dsi2_drm_create_encoder(struct dw_mipi_dsi2_rockchip *dsi2,
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
dsi2->dev->of_node);
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
ret = drm_encoder_init(drm_dev, encoder, &dw_mipi_dsi2_encoder_funcs,
DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
dev_err(dsi2->dev, "Failed to initialize encoder with drm\n");
return ret;

View File

@@ -20,7 +20,6 @@
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"

View File

@@ -18,7 +18,6 @@
#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"

View File

@@ -25,7 +25,6 @@
#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"

View File

@@ -12,7 +12,6 @@
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
@@ -454,6 +453,10 @@ rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
static const struct drm_encoder_funcs rk3066_hdmi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const
struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
.atomic_check = rk3066_hdmi_encoder_atomic_check,
@@ -696,7 +699,8 @@ rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
return -EPROBE_DEFER;
drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
drm_encoder_init(drm, encoder, &rk3066_hdmi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &rk3066_hdmi_bridge_funcs;

View File

@@ -24,7 +24,6 @@
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"
#include "rockchip_lvds.h"
@@ -427,6 +426,10 @@ static void px30_lvds_encoder_disable(struct drm_encoder *encoder)
drm_panel_unprepare(lvds->panel);
}
static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const
struct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = {
.enable = rk3288_lvds_encoder_enable,
@@ -594,7 +597,9 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
dev->of_node);
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS);
ret = drm_encoder_init(drm_dev, encoder,
&rockchip_lvds_encoder_funcs,
DRM_MODE_ENCODER_LVDS, NULL);
if (ret < 0) {
drm_err(drm_dev,
"failed to initialize encoder: %d\n", ret);

View File

@@ -17,7 +17,6 @@
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"
#include "rockchip_rgb.h"
@@ -65,6 +64,10 @@ rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const
struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = {
.atomic_check = rockchip_rgb_encoder_atomic_check,
@@ -127,7 +130,8 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
encoder = &rgb->encoder.encoder;
encoder->possible_crtcs = drm_crtc_mask(crtc);
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_NONE);
ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
if (ret < 0) {
DRM_DEV_ERROR(drm_dev->dev,
"failed to initialize encoder: %d\n", ret);

View File

@@ -725,9 +725,9 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x,
struct drm_rect *rect, u8 *buf,
u8 *data_array)
{
unsigned int x = rect->x1;
unsigned int y = rect->y1;
unsigned int segment_width = SSD132X_SEGMENT_WIDTH;
unsigned int col = rect->x1 / segment_width;
unsigned int row = rect->y1;
unsigned int width = drm_rect_width(rect);
unsigned int height = drm_rect_height(rect);
unsigned int columns = DIV_ROUND_UP(width, segment_width);
@@ -737,7 +737,7 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x,
unsigned int i, j;
int ret;
drm_WARN_ONCE(drm, x % segment_width != 0, "x must be aligned to screen segment\n");
drm_WARN_ONCE(drm, rect->x1 % segment_width != 0, "x must be aligned to screen segment\n");
/*
* The screen is divided in Segment and Common outputs, where
@@ -754,12 +754,12 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x,
*/
/* Set column start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, x / segment_width, columns - 1);
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, col, col + columns - 1);
if (ret < 0)
return ret;
/* Set row start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, y, rows - 1);
ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, row, row + rows - 1);
if (ret < 0)
return ret;
@@ -805,12 +805,12 @@ static int ssd133x_update_rect(struct ssd130x_device *ssd130x,
*/
/* Set column start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, columns - 1);
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, x + columns - 1);
if (ret < 0)
return ret;
/* Set row start and end */
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, rows - 1);
ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, y + rows - 1);
if (ret < 0)
return ret;

View File

@@ -686,7 +686,7 @@ static int sun4i_backend_init_sat(struct device *dev) {
ret = clk_prepare_enable(backend->sat_clk);
if (ret) {
dev_err(dev, "Couldn't enable the SAT clock\n");
return ret;
goto err_assert_reset;
}
return 0;

View File

@@ -24,7 +24,6 @@
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
@@ -172,6 +171,10 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder,
writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
}
static const struct drm_encoder_funcs sun4i_hdmi_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
.atomic_disable = sun4i_hdmi_disable,
.atomic_enable = sun4i_hdmi_enable,
@@ -624,8 +627,8 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
drm_encoder_helper_add(&hdmi->encoder,
&sun4i_hdmi_helper_funcs);
ret = drm_simple_encoder_init(drm, &hdmi->encoder,
DRM_MODE_ENCODER_TMDS);
ret = drm_encoder_init(drm, &hdmi->encoder, &sun4i_hdmi_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
if (ret) {
dev_err(dev, "Couldn't initialise the HDMI encoder\n");
goto err_put_ddc_i2c;

View File

@@ -8,11 +8,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_encoder.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>
#include "sun4i_crtc.h"
#include "sun4i_tcon.h"
@@ -89,6 +89,10 @@ static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
}
}
static const struct drm_encoder_funcs sun4i_lvds_enc_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs sun4i_lvds_enc_helper_funcs = {
.disable = sun4i_lvds_encoder_disable,
.enable = sun4i_lvds_encoder_enable,
@@ -115,8 +119,8 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
drm_encoder_helper_add(&lvds->encoder,
&sun4i_lvds_enc_helper_funcs);
ret = drm_simple_encoder_init(drm, &lvds->encoder,
DRM_MODE_ENCODER_LVDS);
ret = drm_encoder_init(drm, &lvds->encoder, &sun4i_lvds_enc_funcs,
DRM_MODE_ENCODER_LVDS, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialise the lvds encoder\n");
goto err_out;

View File

@@ -10,11 +10,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_encoder.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>
#include "sun4i_crtc.h"
#include "sun4i_tcon.h"
@@ -180,6 +180,10 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
}
}
static const struct drm_encoder_funcs sun4i_rgb_enc_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
.disable = sun4i_rgb_encoder_disable,
.enable = sun4i_rgb_encoder_enable,
@@ -207,8 +211,8 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
drm_encoder_helper_add(&rgb->encoder,
&sun4i_rgb_enc_helper_funcs);
ret = drm_simple_encoder_init(drm, &rgb->encoder,
DRM_MODE_ENCODER_NONE);
ret = drm_encoder_init(drm, &rgb->encoder, &sun4i_rgb_enc_funcs,
DRM_MODE_ENCODER_NONE, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialise the rgb encoder\n");
goto err_out;

View File

@@ -16,11 +16,11 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_encoder.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>
#include "sun4i_crtc.h"
#include "sun4i_drv.h"
@@ -391,6 +391,10 @@ static void sun4i_tv_enable(struct drm_encoder *encoder,
SUN4I_TVE_EN_ENABLE);
}
static const struct drm_encoder_funcs sun4i_tv_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
.atomic_disable = sun4i_tv_disable,
.atomic_enable = sun4i_tv_enable,
@@ -474,8 +478,8 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
drm_encoder_helper_add(&tv->encoder,
&sun4i_tv_helper_funcs);
ret = drm_simple_encoder_init(drm, &tv->encoder,
DRM_MODE_ENCODER_TVDAC);
ret = drm_encoder_init(drm, &tv->encoder, &sun4i_tv_funcs,
DRM_MODE_ENCODER_TVDAC, NULL);
if (ret) {
dev_err(dev, "Couldn't initialise the TV encoder\n");
goto err_disable_clk;

View File

@@ -20,11 +20,11 @@
#include <linux/slab.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_encoder.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include "sun4i_crtc.h"
#include "sun4i_tcon.h"
@@ -842,6 +842,10 @@ static const struct drm_connector_funcs sun6i_dsi_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static const struct drm_encoder_funcs sun6i_dsi_enc_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs sun6i_dsi_enc_helper_funcs = {
.disable = sun6i_dsi_encoder_disable,
.enable = sun6i_dsi_encoder_enable,
@@ -1056,8 +1060,8 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
drm_encoder_helper_add(&dsi->encoder,
&sun6i_dsi_enc_helper_funcs);
ret = drm_simple_encoder_init(drm, &dsi->encoder,
DRM_MODE_ENCODER_DSI);
ret = drm_encoder_init(drm, &dsi->encoder, &sun6i_dsi_enc_funcs,
DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
dev_err(dsi->dev, "Couldn't initialise the DSI encoder\n");
return ret;

View File

@@ -9,8 +9,8 @@
#include <linux/platform_device.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_encoder.h>
#include <drm/drm_of.h>
#include <drm/drm_simple_kms_helper.h>
#include "sun8i_dw_hdmi.h"
#include "sun8i_tcon_top.h"
@@ -23,6 +23,9 @@ static void sun8i_dw_hdmi_encoder_mode_set(struct drm_encoder *encoder,
clk_set_rate(hdmi->clk_tmds, mode->crtc_clock * 1000);
}
static const struct drm_encoder_funcs sun8i_dw_hdmi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_encoder_helper_funcs
sun8i_dw_hdmi_encoder_helper_funcs = {
@@ -180,7 +183,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
goto err_disable_clk_tmds;
drm_encoder_helper_add(encoder, &sun8i_dw_hdmi_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
drm_encoder_init(drm, encoder, &sun8i_dw_hdmi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
plat_data->mode_valid = hdmi->quirks->mode_valid;
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;

View File

@@ -28,6 +28,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -623,6 +624,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
int width, height, stride;
u16 width_mm = 0, height_mm = 0;
struct device_node *panel_node;
enum drm_panel_orientation orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
const struct drm_format_info *format;
u64 size;
struct resource *res, *mem = NULL;
@@ -698,6 +700,12 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
ret = simplefb_get_panel_height_mm_of(dev, panel_node);
if (ret > 0)
height_mm = ret;
/*
* Ignore errors from parsing the panel orientation. With
* the orientation initialized to UNKNOWN, the connector
* helpers will do the right thing.
*/
drm_of_get_panel_orientation(panel_node, &orientation);
of_node_put(panel_node);
}
} else {
@@ -861,8 +869,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
if (ret)
return ERR_PTR(ret);
drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs);
drm_connector_set_panel_orientation_with_quirk(connector,
DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
drm_connector_set_panel_orientation_with_quirk(connector, orientation,
width, height);
ret = drm_connector_attach_encoder(connector, encoder);

View File

@@ -75,11 +75,13 @@ struct v3d_queue_state {
spinlock_t queue_lock;
};
/* Performance monitor object. The perform lifetime is controlled by userspace
* using perfmon related ioctls. A perfmon can be attached to a submit_cl
* request, and when this is the case, HW perf counters will be activated just
* before the submit_cl is submitted to the GPU and disabled when the job is
* done. This way, only events related to a specific job will be counted.
/* Performance monitor object
*
* The performance monitor (perfmon) lifetime is controlled by userspace using
* perfmon related ioctls. A perfmon can be attached to a CL or CSD submission
* request, and when it is, HW performance counters will be activated just
* before the job is submitted to the GPU and disabled when the job is done.
* This way, only events related to a specific submission will be counted.
*/
struct v3d_perfmon {
/* Tracks the number of users of the perfmon, when this counter reaches
@@ -87,9 +89,6 @@ struct v3d_perfmon {
*/
refcount_t refcnt;
/* Protects perfmon stop, as it can be invoked from multiple places. */
struct mutex lock;
/* Number of counters activated in this perfmon instance
* (should be less than DRM_V3D_MAX_PERF_COUNTERS).
*/
@@ -171,8 +170,32 @@ struct v3d_dev {
struct v3d_queue_state queue[V3D_MAX_QUEUES];
/* Used to track the active perfmon if any. */
struct v3d_perfmon *active_perfmon;
/*
* Tracks the performance monitor state and consistency.
*
* When a non-global perfmon is attached to a job, the scheduler must
* not run any other job on the HW concurrently (otherwise, the
* counters would be polluted by unrelated work).
*/
struct {
/* Protects @active. */
spinlock_t lock;
/* Perfmon currently programmed in HW (or NULL if none). */
struct v3d_perfmon *active;
/* Finished fence of the most recently submitted job that
* opened a serialization window (i.e. a job with a non-global
* perfmon attached).
*/
struct dma_fence *fence;
/* Finished fence of the most recently submitted job on each HW
* queue. Used so that a new perfmon-carrying job can depend on
* every job currently in-flight across all queues.
*/
struct dma_fence *last_hw_fence[V3D_MAX_QUEUES];
} perfmon_state;
/* Protects bo_stats */
struct mutex bo_lock;
@@ -667,6 +690,10 @@ void v3d_perfmon_put(struct v3d_perfmon *perfmon);
void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon);
void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
bool capture);
void v3d_perfmon_stop_locked(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
bool capture);
void v3d_perfmon_suspend(struct v3d_dev *v3d);
void v3d_perfmon_resume(struct v3d_dev *v3d);
struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id);
void v3d_perfmon_open_file(struct v3d_file_priv *v3d_priv);
void v3d_perfmon_close_file(struct v3d_file_priv *v3d_priv);

View File

@@ -137,7 +137,8 @@ v3d_reset(struct v3d_dev *v3d)
v3d_mmu_set_page_table(v3d);
v3d_irq_reset(v3d);
v3d_perfmon_stop(v3d, v3d->active_perfmon, false);
/* Re-arm the global perfmon HW counters that the reset zeroed. */
v3d_perfmon_resume(v3d);
trace_v3d_reset_end(dev);
}
@@ -307,6 +308,7 @@ v3d_gem_init(struct drm_device *dev)
}
spin_lock_init(&v3d->mm_lock);
spin_lock_init(&v3d->perfmon_state.lock);
ret = drmm_mutex_init(dev, &v3d->bo_lock);
if (ret)
goto err_stats;
@@ -369,8 +371,11 @@ v3d_gem_destroy(struct drm_device *dev)
for (q = 0; q < V3D_MAX_QUEUES; q++) {
WARN_ON(v3d->queue[q].active_job);
v3d_stats_put(v3d->queue[q].stats);
dma_fence_put(v3d->perfmon_state.last_hw_fence[q]);
}
dma_fence_put(v3d->perfmon_state.fence);
drm_mm_takedown(&v3d->mm);
dma_free_coherent(v3d->drm.dev, 4096 * 1024, (void *)v3d->pt,

View File

@@ -90,9 +90,12 @@ v3d_irq_signal_fence(struct v3d_dev *v3d, enum v3d_queue q,
void (*trace_irq)(struct drm_device *, uint64_t))
{
struct v3d_queue_state *queue = &v3d->queue[q];
struct v3d_fence *fence = to_v3d_fence(queue->active_job->irq_fence);
struct v3d_job *job = queue->active_job;
struct v3d_fence *fence = to_v3d_fence(job->irq_fence);
v3d_job_update_stats(queue->active_job);
v3d_perfmon_stop(v3d, job->perfmon, true);
v3d_job_update_stats(job);
trace_irq(&v3d->drm, fence->seqno);
queue->active_job = NULL;

View File

@@ -217,26 +217,15 @@ void v3d_perfmon_get(struct v3d_perfmon *perfmon)
void v3d_perfmon_put(struct v3d_perfmon *perfmon)
{
if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) {
mutex_destroy(&perfmon->lock);
if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
kfree(perfmon);
}
}
void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
static void v3d_perfmon_hw_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
{
u8 ncounters = perfmon->ncounters;
u32 mask = GENMASK(ncounters - 1, 0);
unsigned int i;
u32 mask;
u8 ncounters;
if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
return;
if (!pm_runtime_get_if_active(v3d->drm.dev))
return;
ncounters = perfmon->ncounters;
mask = GENMASK(ncounters - 1, 0);
for (i = 0; i < ncounters; i++) {
u32 source = i / 4;
@@ -258,39 +247,112 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
}
v3d->active_perfmon = perfmon;
static void v3d_perfmon_hw_capture(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
{
u32 mask = GENMASK(perfmon->ncounters - 1, 0);
for (int i = 0; i < perfmon->ncounters; i++)
perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
}
static void v3d_perfmon_hw_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
bool capture)
{
if (capture)
v3d_perfmon_hw_capture(v3d, perfmon);
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
}
void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
{
guard(spinlock_irqsave)(&v3d->perfmon_state.lock);
if (!perfmon || v3d->global_perfmon)
return;
/* Cross-queue serialization should have drained any previous perfmon
* job before this one runs.
*/
if (WARN_ON_ONCE(v3d->perfmon_state.active))
return;
if (!pm_runtime_get_if_active(v3d->drm.dev))
return;
v3d_perfmon_hw_start(v3d, perfmon);
v3d->perfmon_state.active = perfmon;
v3d_pm_runtime_put(v3d);
}
static void v3d_perfmon_capture_locked(struct v3d_dev *v3d,
struct v3d_perfmon *perfmon)
{
lockdep_assert_held(&v3d->perfmon_state.lock);
if (!perfmon || perfmon != v3d->perfmon_state.active)
return;
if (!pm_runtime_get_if_active(v3d->drm.dev))
return;
v3d_perfmon_hw_capture(v3d, perfmon);
v3d_pm_runtime_put(v3d);
}
void v3d_perfmon_stop_locked(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
bool capture)
{
lockdep_assert_held(&v3d->perfmon_state.lock);
if (!perfmon || perfmon != v3d->perfmon_state.active)
return;
v3d->perfmon_state.active = NULL;
/* If the device is suspended, the HW has already stopped counting. */
if (!pm_runtime_get_if_active(v3d->drm.dev))
return;
v3d_perfmon_hw_stop(v3d, perfmon, capture);
v3d_pm_runtime_put(v3d);
}
void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
bool capture)
{
unsigned int i;
if (!perfmon || !v3d->active_perfmon)
if (!perfmon)
return;
mutex_lock(&perfmon->lock);
if (perfmon != v3d->active_perfmon)
goto out;
guard(spinlock_irqsave)(&v3d->perfmon_state.lock);
v3d_perfmon_stop_locked(v3d, perfmon, capture);
}
if (!pm_runtime_get_if_active(v3d->drm.dev))
goto out_clear;
void
v3d_perfmon_suspend(struct v3d_dev *v3d)
{
guard(spinlock_irqsave)(&v3d->perfmon_state.lock);
if (capture)
for (i = 0; i < perfmon->ncounters; i++)
perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
if (!v3d->perfmon_state.active)
return;
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
v3d_perfmon_hw_stop(v3d, v3d->perfmon_state.active, true);
}
v3d_pm_runtime_put(v3d);
void
v3d_perfmon_resume(struct v3d_dev *v3d)
{
guard(spinlock_irqsave)(&v3d->perfmon_state.lock);
out_clear:
v3d->active_perfmon = NULL;
out:
mutex_unlock(&perfmon->lock);
if (!v3d->perfmon_state.active)
return;
v3d_perfmon_hw_start(v3d, v3d->perfmon_state.active);
}
struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id)
@@ -316,14 +378,17 @@ static void v3d_perfmon_delete(struct v3d_file_priv *v3d_priv,
struct v3d_dev *v3d = v3d_priv->v3d;
/* If the active perfmon is being destroyed, stop it first */
if (perfmon == v3d->active_perfmon)
v3d_perfmon_stop(v3d, perfmon, false);
scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) {
v3d_perfmon_stop_locked(v3d, perfmon, false);
/* If the global perfmon is being destroyed, clean it and release
* the reference stashed in v3d_perfmon_set_global_ioctl().
*/
if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon)
v3d_perfmon_put(perfmon);
/* If the global perfmon is being destroyed, clean it and release
* the reference stashed in v3d_perfmon_set_global_ioctl().
*/
if (v3d->global_perfmon == perfmon) {
v3d_perfmon_put(v3d->global_perfmon);
v3d->global_perfmon = NULL;
}
}
v3d_perfmon_put(perfmon);
}
@@ -371,12 +436,10 @@ int v3d_perfmon_create_ioctl(struct drm_device *dev, void *data,
perfmon->ncounters = req->ncounters;
refcount_set(&perfmon->refcnt, 1);
mutex_init(&perfmon->lock);
ret = xa_alloc(&v3d_priv->perfmons, &id, perfmon, xa_limit_32b,
GFP_KERNEL);
if (ret < 0) {
mutex_destroy(&perfmon->lock);
kfree(perfmon);
return ret;
}
@@ -408,7 +471,9 @@ int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
struct v3d_dev *v3d = to_v3d_dev(dev);
struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
struct drm_v3d_perfmon_get_values *req = data;
u64 values[DRM_V3D_MAX_PERF_COUNTERS];
struct v3d_perfmon *perfmon;
size_t size;
int ret = 0;
if (req->pad != 0)
@@ -418,10 +483,14 @@ int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
if (!perfmon)
return -EINVAL;
v3d_perfmon_stop(v3d, perfmon, true);
size = perfmon->ncounters * sizeof(u64);
if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
perfmon->ncounters * sizeof(u64)))
scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) {
v3d_perfmon_capture_locked(v3d, perfmon);
memcpy(values, perfmon->values, size);
}
if (copy_to_user(u64_to_user_ptr(req->values_ptr), values, size))
ret = -EFAULT;
v3d_perfmon_put(perfmon);
@@ -482,18 +551,36 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, void *data,
*/
v3d_perfmon_put(perfmon);
old = xchg(&v3d->global_perfmon, NULL);
if (!old)
return -EINVAL;
scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) {
old = v3d->global_perfmon;
if (!old)
return -EINVAL;
v3d_perfmon_stop_locked(v3d, old, true);
v3d->global_perfmon = NULL;
}
v3d_perfmon_put(old);
return 0;
}
if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) {
v3d_perfmon_put(perfmon);
return -EBUSY;
scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) {
if (v3d->perfmon_state.active || v3d->global_perfmon) {
v3d_perfmon_put(perfmon);
return -EBUSY;
}
v3d->global_perfmon = perfmon;
v3d->perfmon_state.active = perfmon;
/* If the device is suspended, v3d_perfmon_resume() will
* program the HW on the next resume.
*/
if (pm_runtime_get_if_active(v3d->drm.dev)) {
v3d_perfmon_hw_start(v3d, perfmon);
v3d_pm_runtime_put(v3d);
}
}
return 0;

View File

@@ -50,6 +50,8 @@ int v3d_power_suspend(struct device *dev)
struct v3d_dev *v3d = to_v3d_dev(drm);
int ret;
v3d_perfmon_suspend(v3d);
v3d_irq_disable(v3d);
v3d_clean_caches(v3d);
@@ -85,5 +87,7 @@ int v3d_power_resume(struct device *dev)
v3d_mmu_set_page_table(v3d);
v3d_irq_enable(v3d);
v3d_perfmon_resume(v3d);
return 0;
}

View File

@@ -125,24 +125,6 @@ v3d_performance_query_info_free(struct v3d_performance_query_info *query_info,
}
}
static void
v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job)
{
struct v3d_perfmon *perfmon = v3d->global_perfmon;
if (!perfmon)
perfmon = job->perfmon;
if (perfmon == v3d->active_perfmon)
return;
if (perfmon != v3d->active_perfmon)
v3d_perfmon_stop(v3d, v3d->active_perfmon, true);
if (perfmon && v3d->active_perfmon != perfmon)
v3d_perfmon_start(v3d, perfmon);
}
static void
v3d_stats_start(struct v3d_stats *stats, u64 now)
{
@@ -220,7 +202,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
job->start, job->end);
v3d_job_start_stats(&job->base);
v3d_switch_perfmon(v3d, &job->base);
v3d_perfmon_start(v3d, job->base.perfmon);
/* Set the current and end address of the control list.
* Writing the end register is what starts the job.
@@ -278,7 +260,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
job->start, job->end);
v3d_job_start_stats(&job->base);
v3d_switch_perfmon(v3d, &job->base);
v3d_perfmon_start(v3d, job->base.perfmon);
/* XXX: Set the QCFG */
@@ -381,7 +363,7 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
trace_v3d_submit_csd(dev, to_v3d_fence(fence)->seqno);
v3d_job_start_stats(&job->base);
v3d_switch_perfmon(v3d, &job->base);
v3d_perfmon_start(v3d, job->base.perfmon);
csd_cfg0_reg = V3D_CSD_QUEUED_CFG0(v3d->ver);
for (i = 1; i <= 6; i++)
@@ -605,15 +587,8 @@ v3d_write_performance_query_result(struct v3d_cpu_job *job, void *data,
v3d_perfmon_stop(v3d, perfmon, true);
if (job->copy.do_64bit) {
for (j = 0; j < perfmon->ncounters; j++)
write_to_buffer_64(data, offset + j,
perfmon->values[j]);
} else {
for (j = 0; j < perfmon->ncounters; j++)
write_to_buffer_32(data, offset + j,
perfmon->values[j]);
}
for (j = 0; j < perfmon->ncounters; j++)
write_to_buffer(data, offset + j, job->copy.do_64bit, perfmon->values[j]);
v3d_perfmon_put(perfmon);
}
@@ -723,6 +698,8 @@ v3d_gpu_reset_for_timeout(struct v3d_dev *v3d, struct drm_sched_job *sched_job,
if (sched_job)
drm_sched_increase_karma(sched_job);
v3d_perfmon_stop(v3d, job->perfmon, false);
/* get the GPU back into the init state */
v3d_reset(v3d);

View File

@@ -4,6 +4,9 @@
* Copyright (C) 2023 Raspberry Pi
*/
#include <linux/dma-fence-unwrap.h>
#include <linux/overflow.h>
#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
@@ -302,8 +305,10 @@ v3d_attach_perfmon_to_jobs(struct v3d_submit *submit, u32 perfmon_id)
if (!perfmon_id)
return 0;
if (v3d->global_perfmon)
return -EAGAIN;
scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) {
if (v3d->global_perfmon)
return -EAGAIN;
}
perfmon = v3d_perfmon_find(v3d_priv, perfmon_id);
if (!perfmon)
@@ -318,6 +323,73 @@ v3d_attach_perfmon_to_jobs(struct v3d_submit *submit, u32 perfmon_id)
return 0;
}
/*
* Prepare fences to enforce job serialization when a perfmon is active. A job
* that carries a non-global perfmon must wait for every job currently in-flight
* across all HW queues to finish, otherwise concurrent unrelated work on the
* same core would pollute the performance counters. Symmetrically, while such a
* job is still in-flight, all subsequently submitted jobs must wait for it.
*
* We don't serialize the jobs when using a global perfmon as it's expected to
* track concurrent activity from all jobs.
*/
static int
v3d_serialize_for_perfmon(struct v3d_job *job)
{
struct v3d_dev *v3d = job->v3d;
struct dma_fence *merged;
bool is_global_perfmon;
int ret;
lockdep_assert_held(&v3d->sched_lock);
scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock)
is_global_perfmon = !!v3d->global_perfmon;
if (is_global_perfmon)
goto publish;
if (job->perfmon) {
for (enum v3d_queue q = 0; q < V3D_MAX_QUEUES; q++) {
struct dma_fence *f = v3d->perfmon_state.last_hw_fence[q];
if (!f || dma_fence_is_signaled(f))
continue;
ret = drm_sched_job_add_dependency(&job->base, dma_fence_get(f));
if (ret)
return ret;
}
} else if (v3d->perfmon_state.fence &&
!dma_fence_is_signaled(v3d->perfmon_state.fence)) {
ret = drm_sched_job_add_dependency(&job->base,
dma_fence_get(v3d->perfmon_state.fence));
if (ret)
return ret;
}
publish:
/*
* Accumulate every in-flight job on this queue into one merged fence.
* A HW queue is fed by several scheduler entities (one per-fd), so jobs
* on it can complete out of order.
*/
merged = dma_fence_unwrap_merge(v3d->perfmon_state.last_hw_fence[job->queue],
job->done_fence);
if (!merged)
return -ENOMEM;
dma_fence_put(v3d->perfmon_state.last_hw_fence[job->queue]);
v3d->perfmon_state.last_hw_fence[job->queue] = merged;
if (job->perfmon && !is_global_perfmon) {
dma_fence_put(v3d->perfmon_state.fence);
v3d->perfmon_state.fence = dma_fence_get(job->done_fence);
}
return 0;
}
static void
v3d_submit_attach_object_fences(struct v3d_submit *submit)
{
@@ -394,6 +466,12 @@ v3d_submit_jobs(struct v3d_submit *submit, struct drm_syncobj *sync_out,
goto err;
}
for (int i = 0; i < submit->job_count; i++) {
ret = v3d_serialize_for_perfmon(submit->jobs[i]);
if (ret)
goto err;
}
for (int i = 0; i < submit->job_count; i++)
drm_sched_entity_push_job(&submit->jobs[i]->base);
@@ -1300,6 +1378,126 @@ static const unsigned int cpu_job_bo_handle_count[] = {
[V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY] = 1,
};
/* Reject offset + (count - 1) * stride + write_size if it leaves the BO. */
static int
v3d_check_copy_extent(struct drm_device *dev, size_t bo_size,
u32 offset, u32 stride, u32 count, u64 write_size)
{
u64 last;
if (!count)
return 0;
/*
* The executors walk a u8 * cursor, so the furthest written byte is
* offset + (count - 1) * stride + write_size, matching the pointer
* arithmetic in v3d_copy_query_results()/v3d_copy_performance_query().
* (count - 1) * stride is a u32 * u32 product that is exact in u64,
* and offset + write_size stays far below the u64 range, so a single
* overflow check guards the total.
*/
last = write_size + offset;
if (check_add_overflow((u64)(count - 1) * stride, last, &last) ||
last > bo_size) {
drm_dbg(dev, "CPU job copy buffer exceeds the destination BO.\n");
return -EINVAL;
}
return 0;
}
/* Reject a query CPU job whose writes would land outside their BO. */
static int
v3d_cpu_job_bounds_check(struct v3d_cpu_job *job)
{
struct drm_device *dev = &job->base.v3d->drm;
struct v3d_timestamp_query_info *tquery = &job->timestamp_query;
struct v3d_copy_query_results_info *copy = &job->copy;
u32 elem = copy->do_64bit ? sizeof(u64) : sizeof(u32);
struct v3d_bo *dst, *src;
u64 slots, write_size;
u32 i;
switch (job->job_type) {
case V3D_CPU_JOB_TYPE_TIMESTAMP_QUERY:
case V3D_CPU_JOB_TYPE_RESET_TIMESTAMP_QUERY:
/* Each query writes one u64 timestamp slot into bo[0]. */
dst = to_v3d_bo(job->base.bo[0]);
for (i = 0; i < tquery->count; i++) {
if ((u64)tquery->queries[i].offset + sizeof(u64) >
dst->base.base.size)
goto err_range;
}
return 0;
case V3D_CPU_JOB_TYPE_COPY_TIMESTAMP_QUERY:
/* Copies one u64 per query from bo[1] into bo[0]. */
dst = to_v3d_bo(job->base.bo[0]);
src = to_v3d_bo(job->base.bo[1]);
for (i = 0; i < tquery->count; i++) {
if ((u64)tquery->queries[i].offset + sizeof(u64) >
src->base.base.size)
goto err_range;
}
write_size = (copy->availability_bit ? 2 : 1) * elem;
return v3d_check_copy_extent(dev, dst->base.base.size,
copy->offset, copy->stride,
tquery->count, write_size);
case V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY:
/*
* Each query writes nperfmons * DRM_V3D_MAX_PERF_COUNTERS
* counter slots into bo[0], plus an availability slot at
* index ncounters. nperfmons and ncounters are user values,
* so the slot count is computed overflow-safe.
*/
dst = to_v3d_bo(job->base.bo[0]);
slots = (u64)job->performance_query.nperfmons *
DRM_V3D_MAX_PERF_COUNTERS;
if (copy->availability_bit)
slots = max(slots,
(u64)job->performance_query.ncounters + 1);
write_size = slots * elem;
return v3d_check_copy_extent(dev, dst->base.base.size,
copy->offset, copy->stride,
job->performance_query.count,
write_size);
case V3D_CPU_JOB_TYPE_INDIRECT_CSD: {
struct v3d_indirect_csd_info *indirect_csd = &job->indirect_csd;
/* 3 is the three dimensions (x, y, z) of the workgroup counts. */
src = to_v3d_bo(job->base.bo[0]);
if ((u64)indirect_csd->offset + 3 * sizeof(u32) >
src->base.base.size)
goto err_range;
dst = to_v3d_bo(indirect_csd->indirect);
for (i = 0; i < 3; i++) {
u32 uidx = indirect_csd->wg_uniform_offsets[i];
/*
* 0xffffffff means "skip this rewrite", so the exec
* path never writes that index and it needs no check.
*/
if (uidx != 0xffffffff &&
(u64)uidx * sizeof(u32) + sizeof(u32) >
dst->base.base.size)
goto err_range;
}
return 0;
}
default:
return 0;
}
err_range:
drm_dbg(dev, "CPU job query offset exceeds the BO.\n");
return -EINVAL;
}
/**
* v3d_submit_cpu_ioctl() - Submits a CPU job to the V3D.
* @dev: DRM device
@@ -1362,6 +1560,10 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count);
if (ret)
goto fail;
ret = v3d_cpu_job_bounds_check(cpu_job);
if (ret)
goto fail;
}
if (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) {

View File

@@ -159,8 +159,6 @@ struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm,
u64 addr, u64 range);
struct drm_gpuva *drm_gpuva_find_first(struct drm_gpuvm *gpuvm,
u64 addr, u64 range);
struct drm_gpuva *drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start);
struct drm_gpuva *drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end);
/**
* drm_gpuva_invalidate() - sets whether the backing GEM of this &drm_gpuva is
@@ -930,6 +928,8 @@ struct drm_gpuva_op_unmap {
* If either a new mapping's start address is aligned with the start address
* of the old mapping or the new mapping's end address is aligned with the
* end address of the old mapping, either @prev or @next is NULL.
* This will also be the case when the requested mapping begins before the
* old mapping's start address or stretches beyond its end address.
*
* Note, the reason for a dedicated remap operation, rather than arbitrary
* unmap and map operations, is to give drivers the chance of extracting driver

View File

@@ -20,6 +20,8 @@ struct device_node;
struct mipi_dsi_device_info;
struct mipi_dsi_host;
enum drm_panel_orientation;
/**
* enum drm_lvds_dual_link_pixels - Pixel order of an LVDS dual-link connection
* @DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated
@@ -47,6 +49,8 @@ int drm_of_component_probe(struct device *dev,
int drm_of_encoder_active_endpoint(struct device_node *node,
struct drm_encoder *encoder,
struct of_endpoint *endpoint);
int drm_of_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation);
int drm_of_find_panel_or_bridge(const struct device_node *np,
int port, int endpoint,
struct drm_panel **panel,
@@ -101,6 +105,13 @@ static inline int drm_of_encoder_active_endpoint(struct device_node *node,
{
return -EINVAL;
}
static inline int drm_of_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation)
{
return -EINVAL;
}
static inline int drm_of_find_panel_or_bridge(const struct device_node *np,
int port, int endpoint,
struct drm_panel **panel,

View File

@@ -337,19 +337,11 @@ int drm_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector
#if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL)
struct drm_panel *of_drm_find_panel(const struct device_node *np);
int of_drm_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation);
#else
static inline struct drm_panel *of_drm_find_panel(const struct device_node *np)
{
return ERR_PTR(-ENODEV);
}
static inline int of_drm_get_panel_orientation(const struct device_node *np,
enum drm_panel_orientation *orientation)
{
return -ENODEV;
}
#endif
#if defined(CONFIG_DRM_PANEL)

View File

@@ -217,8 +217,7 @@ struct drm_sched_entity {
* @stopped:
*
* Marks the enity as removed from rq and destined for
* termination. This is set by calling drm_sched_entity_flush() and by
* drm_sched_fini().
* termination. This is set by calling drm_sched_entity_flush().
*/
bool stopped;

View File

@@ -8,6 +8,8 @@
#ifndef __LINUX_DMA_FENCE_UNWRAP_H
#define __LINUX_DMA_FENCE_UNWRAP_H
#include <linux/types.h>
struct dma_fence;
/**
@@ -48,11 +50,11 @@ struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor);
for (fence = dma_fence_unwrap_first(head, cursor); fence; \
fence = dma_fence_unwrap_next(cursor))
struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences,
struct dma_fence **fences,
struct dma_fence_unwrap *cursors);
int dma_fence_dedup_array(struct dma_fence **array, int num_fences);
size_t dma_fence_dedup_array(struct dma_fence **array, size_t num_fences);
/**
* dma_fence_unwrap_merge - unwrap and merge fences