diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-07-10 10:22:38 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-07-10 10:22:43 +1000 |
| commit | 9efb67c613b04794aa4777ee97ca6250d98df1a2 (patch) | |
| tree | 6ddf2169e1bf14e9d9d0900be662748446bfd782 | |
| parent | 0461ba9a7994a9bfa2ceefe730e2c87759edc267 (diff) | |
| parent | 671b7825dbfe9ea6e3ad3001003aeee0df48d1b5 (diff) | |
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
69 files changed, 1502 insertions, 302 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili7807s.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili7807s.yaml new file mode 100644 index 000000000000..ba8c5bbf8ffc --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili7807s.yaml @@ -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>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/renesas,r63419.yaml b/Documentation/devicetree/bindings/display/panel/renesas,r63419.yaml new file mode 100644 index 000000000000..adfdd2c300a3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/renesas,r63419.yaml @@ -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>; + }; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 50ad1d28923d..6dea93a41962 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c index 2f1861f960cc..bb77b6bf0f23 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -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; diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index 364cbf79ad73..120fe0c2a75a 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -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); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 7a85774aaac1..8d3d4a6e6ca2 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -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); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 94348c4e3623..c7997677a286 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -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; }; diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index 7988f5e7d56a..fa923852fae4 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -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; diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index c422c5af1f4b..d1c80ad3dead 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -2230,50 +2230,6 @@ out: 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 * @gpuvm: the &drm_gpuvm to check the range for diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index d03ada82eac9..96eef327bf7e 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -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> @@ -221,6 +222,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 * @port: port in the device tree node diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 2c5649e433df..d7c6f4824b2d 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -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(). */ diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index c9dbf64c0c9f..2fa170a29a62 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -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; diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index c0cc2bb4a3eb..cfbfb371bc67 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -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. diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 6926ef28ab8d..0f29f22f589e 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -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 diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c index 9a1b4525423c..3b6de1b2fbc6 100644 --- a/drivers/gpu/drm/panel/panel-anbernic-td4310.c +++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c b/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c index 01b4458e55ad..a70a2e58f88c 100644 --- a/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c +++ b/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c @@ -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, diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 658ce64c71eb..150dff3ab6c3 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-chipone-icna35xx.c b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c index 86d096455caa..2cc16494a4ce 100644 --- a/drivers/gpu/drm/panel/panel-chipone-icna35xx.c +++ b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c index be76bc825c3f..562dc573528d 100644 --- a/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c +++ b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 4aafdc7bef7e..f66474d3ef65 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -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"), diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index 1f177834d629..d23002b5a2d7 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c b/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c index ed02a8daf96f..bbc870b8fda8 100644 --- a/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c +++ b/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c @@ -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); diff --git a/drivers/gpu/drm/panel/panel-himax-hx83102.c b/drivers/gpu/drm/panel/panel-himax-hx83102.c index d7e5664a5838..6a0851ccf9bb 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83102.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83102.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c index 416203da2f45..09124610fdc8 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx8394.c +++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c b/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c new file mode 100644 index 000000000000..12b491b0bca4 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9488.c b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c index 7302766034fc..6ab8ae72abbf 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9488.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c index ad33414719fc..0fb06c425545 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 0652cdb57d11..3abdd0870e83 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -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); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c index 5f4e0d82ee67..6d07fe901357 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index f6b04de1182e..67ef9e0fea03 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-lvds.c b/drivers/gpu/drm/panel/panel-lvds.c index 46b07f38559f..37f7498449ec 100644 --- a/drivers/gpu/drm/panel/panel-lvds.c +++ b/drivers/gpu/drm/panel/panel-lvds.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36523.c b/drivers/gpu/drm/panel/panel-novatek-nt36523.c index 226d91daf8c7..34b9123d3fd7 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36523.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36523.c @@ -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; diff --git a/drivers/gpu/drm/panel/panel-renesas-r63419.c b/drivers/gpu/drm/panel/panel-renesas-r63419.c new file mode 100644 index 000000000000..ff3846d574e5 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-renesas-r63419.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index c09bf3db5e78..5c169fdf265c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -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); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 2f79ec4a2063..f16e0de1ea60 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index f1641c9c7d13..fcbb0b7d50e4 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index d5f821d6b23c..da6ed31d151e 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -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"); diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 97a79411e1ec..6733e0e1d061 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -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, diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 776954c7d052..46c245e35d21 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -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; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index e159e1627f3c..68aa66915e8a 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -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; diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 3547d91b25d3..268d4993a04f 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -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; diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c index a2810e16765c..6db88665060d 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c @@ -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; diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c index 0de822360c8d..b23efb153c9e 100644 --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c @@ -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" diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index cffc0af5190f..b6e154c35e7c 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -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" diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c index 815f9ea7bcbe..9c4cf68d79d5 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -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" diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index 9b8981fa19ae..f28d6a883e09 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -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; diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 31dc206bedeb..262f81875278 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -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); diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c index add3123e5ce7..2ad24b914989 100644 --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c @@ -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); diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 04da4f2f7d08..4907be694aec 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 03e46a2af646..adf5b6e09ca6 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 8f64464621c9..e99f52ebb26f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c index 6716e895ae8a..35a3f987c37a 100644 --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index dfb6acc42f02..9c3fbf1b949e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c index 814b77f278f6..ce22f1662c0c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tv.c +++ b/drivers/gpu/drm/sun4i/sun4i_tv.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index c35b70d83e53..f0c9f0e573d2 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -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; diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index 96532709c2a7..9694ad142a7d 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c @@ -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; diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index 9e0711e0095a..21ddf4502ecc 100644 --- a/drivers/gpu/drm/sysfb/simpledrm.c +++ b/drivers/gpu/drm/sysfb/simpledrm.c @@ -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); diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 2e298d7545c0..1b88b3dff757 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -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); diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index c43d9af41374..18c941f15e2e 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -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, diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c index 86efaef2722c..963d711dc16b 100644 --- a/drivers/gpu/drm/v3d/v3d_irq.c +++ b/drivers/gpu/drm/v3d/v3d_irq.c @@ -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; diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c index 48ae748247be..07dab7fb3060 100644 --- a/drivers/gpu/drm/v3d/v3d_perfmon.c +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c @@ -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); +} + +static void v3d_perfmon_hw_capture(struct v3d_dev *v3d, struct v3d_perfmon *perfmon) +{ + u32 mask = GENMASK(perfmon->ncounters - 1, 0); - v3d->active_perfmon = perfmon; + for (int i = 0; i < perfmon->ncounters; i++) + perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i)); - v3d_pm_runtime_put(v3d); + V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask); } -void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon, - bool capture) +static void v3d_perfmon_hw_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon, + bool capture) { - unsigned int i; + if (capture) + v3d_perfmon_hw_capture(v3d, perfmon); + + V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0); +} - if (!perfmon || !v3d->active_perfmon) +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; - mutex_lock(&perfmon->lock); - if (perfmon != v3d->active_perfmon) - goto out; + /* 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)) - goto out_clear; + return; - if (capture) - for (i = 0; i < perfmon->ncounters; i++) - perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i)); + v3d_perfmon_hw_start(v3d, perfmon); + v3d->perfmon_state.active = perfmon; - V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0); + 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); -out_clear: - v3d->active_perfmon = NULL; -out: - mutex_unlock(&perfmon->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) +{ + if (!perfmon) + return; + + guard(spinlock_irqsave)(&v3d->perfmon_state.lock); + v3d_perfmon_stop_locked(v3d, perfmon, capture); +} + +void +v3d_perfmon_suspend(struct v3d_dev *v3d) +{ + guard(spinlock_irqsave)(&v3d->perfmon_state.lock); + + if (!v3d->perfmon_state.active) + return; + + v3d_perfmon_hw_stop(v3d, v3d->perfmon_state.active, true); +} + +void +v3d_perfmon_resume(struct v3d_dev *v3d) +{ + guard(spinlock_irqsave)(&v3d->perfmon_state.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); + + 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), perfmon->values, - perfmon->ncounters * sizeof(u64))) + 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; diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c index f7df6393d38f..ade8e932fb9c 100644 --- a/drivers/gpu/drm/v3d/v3d_power.c +++ b/drivers/gpu/drm/v3d/v3d_power.c @@ -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; } diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index e950cee31bcb..c49b71b6e6ee 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -126,24 +126,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) { raw_write_seqcount_begin(&stats->lock); @@ -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); diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index ee2ac2540ed5..5c05f1ea24bc 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -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) { diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h index 655bd9104ffb..38221d83285b 100644 --- a/include/drm/drm_gpuvm.h +++ b/include/drm/drm_gpuvm.h @@ -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 diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 7bcc0ccfe0f4..ebebed14c611 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -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, diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 86b3f9c65c92..b87323443f49 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -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) diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index d61c19e78182..363d13fc929f 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -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; diff --git a/include/linux/dma-fence-unwrap.h b/include/linux/dma-fence-unwrap.h index 62df222fe0f1..7bfacdf79de2 100644 --- a/include/linux/dma-fence-unwrap.h +++ b/include/linux/dma-fence-unwrap.h @@ -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 |
