mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 08:15:07 -04:00
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:
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(). */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"),
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
285
drivers/gpu/drm/panel/panel-ilitek-ili7807s.c
Normal file
285
drivers/gpu/drm/panel/panel-ilitek-ili7807s.c
Normal 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");
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
351
drivers/gpu/drm/panel/panel-renesas-r63419.c
Normal file
351
drivers/gpu/drm/panel/panel-renesas-r63419.c
Normal 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");
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user