diff --git a/Documentation/ABI/obsolete/sysfs-driver-ivpu b/Documentation/ABI/obsolete/sysfs-driver-ivpu new file mode 100644 index 000000000000..b906e7149729 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-ivpu @@ -0,0 +1,30 @@ +What: /sys/bus/pci/drivers/intel_vpu/.../sched_mode +Date: October 2024 +KernelVersion: 6.12 +Contact: dri-devel@lists.freedesktop.org +Description: Current NPU scheduling mode. Returns one of the following strings: + - "HW" - Hardware Scheduler mode + - "OS" - Operating System Scheduler mode + Read-only. + Deprecated since the "OS" scheduling mode is not usable + and will be removed from future versions of the driver. + Will be removed in 2027 + +What: /sys/bus/pci/drivers/intel_vpu/.../npu_max_frequency_mhz +Date: April 2025 +KernelVersion: 6.15 +Contact: dri-devel@lists.freedesktop.org +Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq. + Shows maximum frequency in MHz of the NPU's data processing unit. + Read-only. + Will be removed in 2027 + +What: /sys/bus/pci/drivers/intel_vpu/.../npu_current_frequency_mhz +Date: April 2025 +KernelVersion: 6.15 +Contact: dri-devel@lists.freedesktop.org +Description: Legacy alias for /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq. + Shows current frequency in MHz of the NPU's data processing unit. + The value is read only when the device is active; otherwise it returns 0. + Read-only. + Will be removed in 2027 diff --git a/Documentation/ABI/testing/sysfs-driver-ivpu b/Documentation/ABI/testing/sysfs-driver-ivpu new file mode 100644 index 000000000000..91685774edfc --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-ivpu @@ -0,0 +1,65 @@ +What: /sys/bus/pci/drivers/intel_vpu/.../npu_busy_time_us +Date: May 2024 +KernelVersion: 6.11 +Contact: dri-devel@lists.freedesktop.org +Description: Time in microseconds that the device spent executing jobs. The time is + counted when and only when there are jobs submitted to firmware. This time + can be used to measure the utilization of NPU, either by calculating the + difference between two timepoints or monitoring utilization percentage by + reading periodically. Recommended read period is 1 second to avoid impact + on job submission performance. Read-only. + +What: /sys/bus/pci/drivers/intel_vpu/.../npu_memory_utilization +Date: Jan 2025 +KernelVersion: 6.15 +Contact: dri-devel@lists.freedesktop.org +Description: Current NPU memory utilization in bytes. Reports the total size of all + resident buffer objects allocated for NPU use. Read-only. + +What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_min_freq +Date: April 2026 +KernelVersion: 7.2 +Contact: dri-devel@lists.freedesktop.org +Description: Minimum frequency in MHz supported by the NPU hardware. This is a + hardware capability and cannot be changed. Read-only. + +What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_efficient_freq +Date: April 2026 +KernelVersion: 7.2 +Contact: dri-devel@lists.freedesktop.org +Description: Most efficient operating frequency in MHz for the NPU. This represents + the frequency at which the NPU operates most efficiently in terms of power + and performance. Read-only. + +What: /sys/bus/pci/drivers/intel_vpu/.../freq/hw_max_freq +Date: April 2026 +KernelVersion: 7.2 +Contact: dri-devel@lists.freedesktop.org +Description: Maximum frequency in MHz supported by the NPU hardware. This is a + hardware capability and cannot be changed. Read-only. + +What: /sys/bus/pci/drivers/intel_vpu/.../freq/current_freq +Date: April 2026 +KernelVersion: 7.2 +Contact: dri-devel@lists.freedesktop.org +Description: Current operating frequency in MHz of the NPU. The value is valid only + when the device is active; returns 0 when idle. The actual frequency may + be lower than the requested range due to power or thermal constraints. + Read-only. + +What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_min_freq +Date: April 2026 +KernelVersion: 7.2 +Contact: dri-devel@lists.freedesktop.org +Description: Configured minimum operating frequency in MHz (50XX devices and newer). + Values written are clamped to hardware limits (hw_min_freq to hw_max_freq). + If set_min_freq exceeds set_max_freq, the driver clamps set_min_freq to + set_max_freq when selecting the operating frequency. Read-write. + +What: /sys/bus/pci/drivers/intel_vpu/.../freq/set_max_freq +Date: April 2026 +KernelVersion: 7.2 +Contact: dri-devel@lists.freedesktop.org +Description: Configured maximum operating frequency in MHz (50XX devices and newer). + Values written are clamped to hardware limits (hw_min_freq to hw_max_freq). + Read-write. diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml index 7586d681bcc6..0363201f0e61 100644 --- a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml +++ b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml @@ -34,6 +34,7 @@ properties: - items: - enum: - doestek,dtc34lm85am # For the Doestek DTC34LM85AM Flat Panel Display (FPD) Transmitter + - idt,v103 # For the Triple 10-BIT LVDS Transmitter - onnn,fin3385 # OnSemi FIN3385 - ti,ds90c185 # For the TI DS90C185 FPD-Link Serializer - ti,ds90c187 # For the TI DS90C187 FPD-Link Serializer diff --git a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml index e6808419f625..7636c24906ba 100644 --- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml +++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml @@ -30,6 +30,7 @@ properties: - algoltek,ag6311 - asl-tek,cs5263 - dumb-vga-dac + - mstar,tsumu88adt3-lf-1 - parade,ps185hdm - radxa,ra620 - realtek,rtd2171 diff --git a/Documentation/devicetree/bindings/display/panel/chipwealth,ch13726a.yaml b/Documentation/devicetree/bindings/display/panel/chipwealth,ch13726a.yaml new file mode 100644 index 000000000000..c8d7b61037e6 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/chipwealth,ch13726a.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/chipwealth,ch13726a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Chip Wealth Technology CH13726A AMOLED driver + +maintainers: + - Neil Armstrong + +description: + Chip Wealth Technology CH13726A is a single-chip solution + for AMOLED connected using a MIPI-DSI video interface. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - const: ayntec,thor-panel-bottom + - const: chipwealth,ch13726a + + reg: + maxItems: 1 + description: DSI virtual channel + + vdd-supply: true + vddio-supply: true + vdd1v2-supply: true + avdd-supply: true + + port: true + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vdd1v2-supply + - avdd-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "ayntec,thor-panel-bottom", "chipwealth,ch13726a"; + reg = <0>; + vdd1v2-supply = <&vreg_l11b_1p2>; + vddio-supply = <&vdd_disp_1v8>; + vdd-supply = <&vreg_l13b_3p0>; + avdd-supply = <&vdd_disp2_2v8>; + reset-gpios = <&tlmm 133 GPIO_ACTIVE_HIGH>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml index d979701a00a8..42e35986fbf6 100644 --- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml @@ -24,6 +24,7 @@ properties: - raspberrypi,dsi-7inch - startek,kd050hdfia020 - tdo,tl050hdv35 + - waveshare,7.0-dsi-touch-a - wanchanglong,w552946aaa - wanchanglong,w552946aba - const: ilitek,ili9881c @@ -34,6 +35,7 @@ properties: backlight: true port: true power-supply: true + iovcc-supply: true reset-gpios: true rotation: true diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt35532.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt35532.yaml new file mode 100644 index 000000000000..ff6fdad7febf --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt35532.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/novatek,nt35532.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT35532-based DSI display panels + +maintainers: + - Cristian Cozzolino + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - flipkart,rimob-panel-nt35532-cs + - const: novatek,nt35532 + + reg: + maxItems: 1 + + backlight: true + reset-gpios: true + + avdd-supply: + description: positive boost supply regulator + + avee-supply: + description: negative boost supply regulator + + vci-supply: + description: regulator that supplies the analog voltage + + vddam-supply: + description: power supply for MIPI interface + + vddi-supply: + description: regulator that supplies the I/O voltage + + port: true + +required: + - compatible + - reg + - reset-gpios + - vddi-supply + - port + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "flipkart,rimob-panel-nt35532-cs", "novatek,nt35532"; + reg = <0>; + + backlight = <&pmi8950_wled>; + reset-gpios = <&tlmm 61 GPIO_ACTIVE_LOW>; + avdd-supply = <&lab>; + avee-supply = <&ibb>; + vci-supply = <&pm8953_l17>; + vddi-supply = <&pm8953_l6>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index cc8d795df732..6d4133b91e7c 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -63,6 +63,8 @@ properties: - samsung,s6e3fa7-ams559nk06 # Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel - tdo,tl070wsh30 + # Team Source Display Technology 7" TST070WSBE-196C 1024x600 TFT LCD panel + - team-source-display,tst070wsbe-196c reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 6e83e03d7456..44ec7388d851 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -97,6 +97,8 @@ properties: - dataimage,fg1001l0dsswmg01 # DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface. - dataimage,scf0700c48ggu18 + # Displaytech DT050BTFT-PTS 5.0" 800x480 TFT LCD Panel + - displaytech,dt050btft-pts # DLC Display Co. DLC1010GIG 10.1" WXGA TFT LCD Panel - dlc,dlc1010gig # Emerging Display Technology Corp. 3.5" QVGA TFT LCD panel @@ -304,6 +306,8 @@ properties: - shelly,sca07010-bfn-lnn # Starry KR070PE2T 7" WVGA TFT LCD panel - starry,kr070pe2t + # Startek KD070HDFLD092 7" WSVGA TFT LCD panel + - startek,kd070hdfld092 # Startek KD070WVFPA043-C069A 7" TFT LCD panel - startek,kd070wvfpa # Team Source Display Technology TST043015CMHX 4.3" WQVGA TFT LCD panel diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 28784d66ae7b..11c55b5df0e4 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -437,6 +437,8 @@ patternProperties: description: Diodes, Inc. "^dioo,.*": description: Dioo Microcircuit Co., Ltd + "^displaytech,.*": + description: Displaytech Ltd. "^djn,.*": description: Shenzhen DJN Optronics Technology Co., Ltd "^dlc,.*": diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 579e87cb9ff7..32206ce62931 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -118,6 +118,10 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs for the same thing co-existing. If we add a few more complete mistakes into the mix every year it would be entirely unmanageable. +The DRM subsystem has however no concern with independent closed-source +userspace implementations. To officialize that position, the DRM uAPI headers +are covered by the MIT license. + .. _drm_render_node: Render nodes diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db..2e8d160babc2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7835,11 +7835,13 @@ F: drivers/soc/ti/smartreflex.c F: include/linux/power/smartreflex.h DRM ACCEL DRIVERS FOR INTEL VPU -M: Maciej Falkowski M: Karol Wachowski +M: Andrzej Kacprowski L: dri-devel@lists.freedesktop.org S: Supported T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/ABI/obsolete/sysfs-driver-ivpu +F: Documentation/ABI/testing/sysfs-driver-ivpu F: drivers/accel/ivpu/ F: include/uapi/drm/ivpu_accel.h @@ -8148,6 +8150,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/panel/novatek,nt35510.yaml F: drivers/gpu/drm/panel/panel-novatek-nt35510.c +DRM DRIVER FOR NOVATEK NT35532 PANELS +M: Cristian Cozzolino +S: Maintained +F: Documentation/devicetree/bindings/display/panel/novatek,nt35532.yaml +F: drivers/gpu/drm/panel/panel-novatek-nt35532.c + DRM DRIVER FOR NOVATEK NT35560 PANELS M: Linus Walleij S: Maintained @@ -8558,6 +8566,7 @@ M: Robert Foss R: Laurent Pinchart R: Jonas Karlman R: Jernej Skrabec +R: Luca Ceresoli S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/bridge/ @@ -15018,7 +15027,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git F: drivers/hid/hid-lg-g15.c LONTIUM LT8912B MIPI TO HDMI BRIDGE -M: Adrien Grassein +M: Francesco Dolcini S: Maintained F: Documentation/devicetree/bindings/display/bridge/lontium,lt8912b.yaml F: drivers/gpu/drm/bridge/lontium-lt8912b.c diff --git a/drivers/accel/amdxdna/Makefile b/drivers/accel/amdxdna/Makefile index 79369e497540..d7720c8c8a98 100644 --- a/drivers/accel/amdxdna/Makefile +++ b/drivers/accel/amdxdna/Makefile @@ -12,6 +12,7 @@ amdxdna-y := \ aie2_solver.o \ aie4_message.o \ aie4_pci.o \ + amdxdna_cbuf.o \ amdxdna_ctx.o \ amdxdna_gem.o \ amdxdna_iommu.o \ @@ -28,4 +29,5 @@ amdxdna-y := \ npu6_regs.o amdxdna-$(CONFIG_PCI_IOV) += aie4_sriov.o +amdxdna-$(CONFIG_DEBUG_FS) += amdxdna_debugfs.o obj-$(CONFIG_DRM_ACCEL_AMDXDNA) = amdxdna.o diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index d37123d925b6..139825ac8515 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -64,6 +64,7 @@ static void aie2_job_release(struct kref *ref) struct amdxdna_sched_job *job; job = container_of(ref, struct amdxdna_sched_job, refcnt); + amdxdna_sched_job_cleanup(job); atomic64_inc(&job->hwctx->job_free_cnt); wake_up(&job->hwctx->priv->job_free_wq); @@ -195,7 +196,8 @@ aie2_sched_notify(struct amdxdna_sched_job *job) { struct dma_fence *fence = job->fence; - trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq); + trace_xdna_job(&job->base, job->hwctx->name, "signaling fence", + job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO); aie2_tdr_signal(job->hwctx->client->xdna); job->hwctx->priv->completed++; @@ -366,6 +368,9 @@ aie2_sched_job_run(struct drm_sched_job *sched_job) struct dma_fence *fence; int ret; + trace_xdna_job(sched_job, hwctx->name, "job run", + job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO); + if (!hwctx->priv->mbox_chann) return NULL; @@ -409,7 +414,8 @@ aie2_sched_job_run(struct drm_sched_job *sched_job) } else { aie2_tdr_signal(hwctx->client->xdna); } - trace_xdna_job(sched_job, hwctx->name, "sent to device", job->seq); + trace_xdna_job(sched_job, hwctx->name, "sent to device", + job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO); return fence; } @@ -419,7 +425,8 @@ static void aie2_sched_job_free(struct drm_sched_job *sched_job) struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job); struct amdxdna_hwctx *hwctx = job->hwctx; - trace_xdna_job(sched_job, hwctx->name, "job free", job->seq); + trace_xdna_job(sched_job, hwctx->name, "job free", + job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO); if (!job->job_done) up(&hwctx->priv->job_sem); @@ -437,7 +444,6 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job) int ret; xdna = hwctx->client->xdna; - trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq); guard(mutex)(&xdna->dev_lock); @@ -540,22 +546,24 @@ static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx) { struct amdxdna_dev *xdna = hwctx->client->xdna; struct alloc_requests *xrs_req; + u32 temporal_only_col = 0; int ret; - if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) { - hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col; - hwctx->num_col = xdna->dev_handle->total_col; - return aie2_create_context(xdna->dev_handle, hwctx); - } - xrs_req = kzalloc_obj(*xrs_req); if (!xrs_req) return -ENOMEM; - xrs_req->cdo.start_cols = hwctx->col_list; - xrs_req->cdo.cols_len = hwctx->col_list_len; - xrs_req->cdo.ncols = hwctx->num_col; - xrs_req->cdo.qos_cap.opc = hwctx->max_opc; + if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) { + xrs_req->cdo.start_cols = &temporal_only_col; + xrs_req->cdo.cols_len = 1; + xrs_req->cdo.ncols = xdna->dev_handle->total_col; + } else { + xrs_req->cdo.start_cols = hwctx->col_list; + xrs_req->cdo.cols_len = hwctx->col_list_len; + xrs_req->cdo.ncols = hwctx->num_col; + } + /* Use platform opc */ + xrs_req->cdo.qos_cap.opc = xdna->dev_handle->priv->col_opc * hwctx->num_col; xrs_req->rqos.gops = hwctx->qos.gops; xrs_req->rqos.fps = hwctx->qos.fps; @@ -579,15 +587,9 @@ static void aie2_release_resource(struct amdxdna_hwctx *hwctx) struct amdxdna_dev *xdna = hwctx->client->xdna; int ret; - if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) { - ret = aie2_destroy_context(xdna->dev_handle, hwctx); - if (ret && ret != -ENODEV) - XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret); - } else { - ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx); - if (ret) - XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret); - } + ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx); + if (ret) + XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret); } static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx) diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 1d1fb012294a..f0ddb843eb21 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -246,6 +246,7 @@ static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action) xdna = hwctx->client->xdna; hwctx->start_col = action->part.start_col; + hwctx->num_unused_col = action->part.ncols - hwctx->num_col; hwctx->num_col = action->part.ncols; ret = aie2_create_context(xdna->dev_handle, hwctx); if (ret) @@ -594,7 +595,7 @@ static int aie2_init(struct amdxdna_dev *xdna) xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1; for (i = 0; i < xrs_cfg.clk_list.num_levels; i++) xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk; - xrs_cfg.sys_eff_factor = 1; + xrs_cfg.sys_eff_factor = 2; xrs_cfg.ddev = &xdna->ddev; xrs_cfg.actions = &aie2_xrs_actions; xrs_cfg.total_col = ndev->total_col; diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index c44616065058..f12073175676 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -237,6 +237,7 @@ struct amdxdna_dev_priv { #define COL_ALIGN_NONE 0 #define COL_ALIGN_NATURE 1 u32 col_align; + u32 col_opc; u32 mbox_dev_addr; /* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */ u32 mbox_size; diff --git a/drivers/accel/amdxdna/aie2_pm.c b/drivers/accel/amdxdna/aie2_pm.c index 786d688bd82c..4fe6030d2c41 100644 --- a/drivers/accel/amdxdna/aie2_pm.c +++ b/drivers/accel/amdxdna/aie2_pm.c @@ -74,7 +74,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev) return ret; ndev->pw_mode = POWER_MODE_DEFAULT; - ndev->dft_dpm_level = ndev->max_dpm_level; + ndev->dft_dpm_level = 0; return 0; } @@ -108,6 +108,14 @@ int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type clk_gating = AIE2_CLK_GATING_ENABLE; dpm_level = ndev->dft_dpm_level; break; + case POWER_MODE_LOW: + clk_gating = AIE2_CLK_GATING_ENABLE; + dpm_level = 0; + break; + case POWER_MODE_MEDIUM: + clk_gating = AIE2_CLK_GATING_ENABLE; + dpm_level = ndev->max_dpm_level / 2; + break; default: return -EOPNOTSUPP; } diff --git a/drivers/accel/amdxdna/aie2_solver.c b/drivers/accel/amdxdna/aie2_solver.c index 3611e3268d79..6f3ee77d5264 100644 --- a/drivers/accel/amdxdna/aie2_solver.c +++ b/drivers/accel/amdxdna/aie2_solver.c @@ -52,7 +52,7 @@ static u32 calculate_gops(struct aie_qos *rqos) u32 service_rate = 0; if (rqos->latency) - service_rate = (1000 / rqos->latency); + service_rate = max_t(u32, 1000 / rqos->latency, 1); if (rqos->fps > service_rate) return rqos->fps * rqos->gops; @@ -348,6 +348,7 @@ int xrs_release_resource(void *hdl, u64 rid) { struct solver_state *xrs = hdl; struct solver_node *node; + u32 level = 0; node = rg_search_node(&xrs->rgp, rid); if (!node) { @@ -358,6 +359,13 @@ int xrs_release_resource(void *hdl, u64 rid) xrs->cfg.actions->unload(node->cb_arg); remove_solver_node(&xrs->rgp, node); + /* set the dpm level which fits all the sessions */ + list_for_each_entry(node, &xrs->rgp.node_list, list) { + if (node->dpm_level > level) + level = node->dpm_level; + } + xrs->cfg.actions->set_dft_dpm_level(xrs->cfg.ddev, level); + return 0; } diff --git a/drivers/accel/amdxdna/amdxdna_cbuf.c b/drivers/accel/amdxdna/amdxdna_cbuf.c new file mode 100644 index 000000000000..a504560aae98 --- /dev/null +++ b/drivers/accel/amdxdna/amdxdna_cbuf.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ + +#include +#include + +#include "amdxdna_cbuf.h" +#include "amdxdna_pci_drv.h" + +/* + * Carveout memory is a chunk of memory which is physically contiguous and + * is reserved during early boot time. There is only one chunk of such memory + * per device. Once available, all BOs accessible from device should be + * allocated from this memory. This is a platform debug/bringup feature. + */ +struct amdxdna_carveout { + u64 addr; + u64 size; + struct drm_mm mm; + struct mutex lock; /* protect mm */ +}; + +bool amdxdna_use_carveout(struct amdxdna_dev *xdna) +{ + return !!xdna->carveout; +} + +void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size) +{ + if (amdxdna_use_carveout(xdna)) { + *addr = xdna->carveout->addr; + *size = xdna->carveout->size; + } else { + *addr = 0; + *size = 0; + } +} + +int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size) +{ + struct amdxdna_carveout *carveout; + + /* Only allow carveout memory to be set up once. */ + if (amdxdna_use_carveout(xdna)) { + XDNA_ERR(xdna, "Carveout memory has already been set up."); + return -EBUSY; + } + + carveout = kzalloc_obj(*carveout); + if (!carveout) + return -ENOMEM; + + carveout->addr = carveout_addr; + carveout->size = carveout_size; + mutex_init(&carveout->lock); + drm_mm_init(&carveout->mm, carveout->addr, carveout->size); + + xdna->carveout = carveout; + XDNA_INFO(xdna, "Use carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr); + return 0; +} + +void amdxdna_carveout_fini(struct amdxdna_dev *xdna) +{ + struct amdxdna_carveout *carveout = xdna->carveout; + + if (!amdxdna_use_carveout(xdna)) + return; + + XDNA_INFO(xdna, "Cleanup carveout mem: 0x%llx@0x%llx\n", carveout->size, carveout->addr); + drm_mm_takedown(&carveout->mm); + mutex_destroy(&carveout->lock); + kfree(carveout); + xdna->carveout = NULL; +} + +struct amdxdna_cbuf_priv { + struct amdxdna_dev *xdna; + struct drm_mm_node node; +}; + +static struct sg_table *amdxdna_cbuf_map(struct dma_buf_attachment *attach, + enum dma_data_direction direction) +{ + struct amdxdna_cbuf_priv *cbuf = attach->dmabuf->priv; + struct device *dev = attach->dev; + struct scatterlist *sgl, *sg; + int ret, n_entries, i; + struct sg_table *sgt; + dma_addr_t dma_addr; + size_t dma_size; + size_t max_seg; + + sgt = kzalloc_obj(*sgt); + if (!sgt) + return ERR_PTR(-ENOMEM); + + max_seg = min_t(size_t, UINT_MAX, dma_max_mapping_size(dev)); + n_entries = (cbuf->node.size + max_seg - 1) / max_seg; + sgl = kzalloc_objs(*sg, n_entries); + if (!sgl) { + ret = -ENOMEM; + goto free_sgt; + } + sg_init_table(sgl, n_entries); + sgt->orig_nents = n_entries; + sgt->nents = n_entries; + sgt->sgl = sgl; + + dma_size = cbuf->node.size; + dma_addr = dma_map_resource(dev, cbuf->node.start, dma_size, + direction, DMA_ATTR_SKIP_CPU_SYNC); + ret = dma_mapping_error(dev, dma_addr); + if (ret) { + pr_err("Failed to dma_map_resource carveout dma buf, ret %d\n", ret); + goto free_sgl; + } + + for_each_sgtable_dma_sg(sgt, sg, i) { + size_t len = min_t(size_t, max_seg, dma_size); + + sg_dma_address(sg) = dma_addr; + sg_dma_len(sg) = len; + dma_addr += len; + dma_size -= len; + } + + return sgt; + +free_sgl: + kfree(sgl); +free_sgt: + kfree(sgt); + return ERR_PTR(ret); +} + +static void amdxdna_cbuf_unmap(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction direction) +{ + dma_unmap_resource(attach->dev, sg_dma_address(sgt->sgl), + drm_prime_get_contiguous_size(sgt), direction, + DMA_ATTR_SKIP_CPU_SYNC); + sg_free_table(sgt); + kfree(sgt); +} + +static void amdxdna_cbuf_release(struct dma_buf *dbuf) +{ + struct amdxdna_cbuf_priv *cbuf = dbuf->priv; + struct amdxdna_carveout *carveout; + + carveout = cbuf->xdna->carveout; + mutex_lock(&carveout->lock); + drm_mm_remove_node(&cbuf->node); + mutex_unlock(&carveout->lock); + + kfree(cbuf); +} + +static vm_fault_t amdxdna_cbuf_vm_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct amdxdna_cbuf_priv *cbuf; + unsigned long pfn; + pgoff_t pgoff; + + cbuf = vma->vm_private_data; + pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; + pfn = (cbuf->node.start >> PAGE_SHIFT) + pgoff; + + return vmf_insert_pfn(vma, vmf->address, pfn); +} + +static const struct vm_operations_struct amdxdna_cbuf_vm_ops = { + .fault = amdxdna_cbuf_vm_fault, +}; + +static int amdxdna_cbuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) +{ + struct amdxdna_cbuf_priv *cbuf = dbuf->priv; + + vma->vm_ops = &amdxdna_cbuf_vm_ops; + vma->vm_private_data = cbuf; + vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); + + return 0; +} + +static int amdxdna_cbuf_vmap(struct dma_buf *dbuf, struct iosys_map *map) +{ + struct amdxdna_cbuf_priv *cbuf = dbuf->priv; + void *kva; + + kva = memremap(cbuf->node.start, cbuf->node.size, MEMREMAP_WB); + if (!kva) { + pr_err("Failed to vmap carveout dma buf\n"); + return -ENOMEM; + } + + iosys_map_set_vaddr(map, kva); + return 0; +} + +static void amdxdna_cbuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map) +{ + memunmap(map->vaddr); +} + +static const struct dma_buf_ops amdxdna_cbuf_dmabuf_ops = { + .map_dma_buf = amdxdna_cbuf_map, + .unmap_dma_buf = amdxdna_cbuf_unmap, + .release = amdxdna_cbuf_release, + .mmap = amdxdna_cbuf_mmap, + .vmap = amdxdna_cbuf_vmap, + .vunmap = amdxdna_cbuf_vunmap, +}; + +static int amdxdna_cbuf_clear(struct dma_buf *dbuf) +{ + struct iosys_map vmap = IOSYS_MAP_INIT_VADDR(NULL); + + dma_buf_vmap(dbuf, &vmap); + if (!vmap.vaddr) + return -EFAULT; + + memset(vmap.vaddr, 0, dbuf->size); + dma_buf_vunmap(dbuf, &vmap); + + return 0; +} + +struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment) +{ + struct amdxdna_dev *xdna = to_xdna_dev(dev); + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct amdxdna_carveout *carveout; + struct amdxdna_cbuf_priv *cbuf; + struct dma_buf *dbuf; + int ret; + + cbuf = kzalloc_obj(*cbuf); + if (!cbuf) + return ERR_PTR(-ENOMEM); + cbuf->xdna = xdna; + + carveout = xdna->carveout; + mutex_lock(&carveout->lock); + ret = drm_mm_insert_node_generic(&carveout->mm, &cbuf->node, size, + alignment, 0, DRM_MM_INSERT_BEST); + mutex_unlock(&carveout->lock); + if (ret) + goto free_cbuf; + + exp_info.size = size; + exp_info.ops = &amdxdna_cbuf_dmabuf_ops; + exp_info.priv = cbuf; + exp_info.flags = O_RDWR; + dbuf = dma_buf_export(&exp_info); + if (IS_ERR(dbuf)) { + ret = PTR_ERR(dbuf); + goto remove_node; + } + + ret = amdxdna_cbuf_clear(dbuf); + if (ret) { + dma_buf_put(dbuf); + goto out; + } + return dbuf; + +remove_node: + drm_mm_remove_node(&cbuf->node); +free_cbuf: + kfree(cbuf); +out: + return ERR_PTR(ret); +} diff --git a/drivers/accel/amdxdna/amdxdna_cbuf.h b/drivers/accel/amdxdna/amdxdna_cbuf.h new file mode 100644 index 000000000000..8e89336ffd50 --- /dev/null +++ b/drivers/accel/amdxdna/amdxdna_cbuf.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ +#ifndef _AMDXDNA_CBUF_H_ +#define _AMDXDNA_CBUF_H_ + +#include "amdxdna_pci_drv.h" +#include +#include + +bool amdxdna_use_carveout(struct amdxdna_dev *xdna); +int amdxdna_carveout_init(struct amdxdna_dev *xdna, u64 carveout_addr, u64 carveout_size); +void amdxdna_carveout_fini(struct amdxdna_dev *xdna); +void amdxdna_get_carveout_conf(struct amdxdna_dev *xdna, u64 *addr, u64 *size); +struct dma_buf *amdxdna_get_cbuf(struct drm_device *dev, size_t size, u64 alignment); + +#endif diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index ff6c3e8e5a15..2c2c21992c87 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -514,7 +514,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, goto unlock_srcu; } - job->hwctx = hwctx; job->mm = current->mm; @@ -612,6 +611,8 @@ int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_ if (args->ext || args->ext_flags) return -EINVAL; + trace_amdxdna_debug_point(current->comm, args->type, "job received"); + switch (args->type) { case AMDXDNA_CMD_SUBMIT_EXEC_BUF: return amdxdna_drm_submit_execbuf(client, args); diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h index a8557d7e8923..355798687376 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.h +++ b/drivers/accel/amdxdna/amdxdna_ctx.h @@ -119,6 +119,7 @@ struct amdxdna_hwctx { container_of(j, struct amdxdna_sched_job, base) enum amdxdna_job_opcode { + DEFAULT_IO, SYNC_DEBUG_BO, ATTACH_DEBUG_BO, DETACH_DEBUG_BO, diff --git a/drivers/accel/amdxdna/amdxdna_debugfs.c b/drivers/accel/amdxdna/amdxdna_debugfs.c new file mode 100644 index 000000000000..a6ec17c63629 --- /dev/null +++ b/drivers/accel/amdxdna/amdxdna_debugfs.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ + +#include "amdxdna_cbuf.h" +#include "amdxdna_debugfs.h" + +#include +#include +#include +#include +#include +#include + +#define _DBGFS_FOPS(_open, _release, _write) \ +{ \ + .owner = THIS_MODULE, \ + .open = _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = _release, \ + .write = _write, \ +} + +#define AMDXDNA_DBGFS_FOPS(_name, _show, _write) \ + static int amdxdna_dbgfs_##_name##_open(struct inode *inode, struct file *file) \ + { \ + return single_open(file, _show, inode->i_private); \ + } \ + static int amdxdna_dbgfs_##_name##_release(struct inode *inode, struct file *file) \ + { \ + return single_release(inode, file); \ + } \ + static const struct file_operations amdxdna_fops_##_name = \ + _DBGFS_FOPS(amdxdna_dbgfs_##_name##_open, amdxdna_dbgfs_##_name##_release, _write) + +#define AMDXDNA_DBGFS_FILE(_name, _mode) { #_name, &amdxdna_fops_##_name, _mode } + +#define file_to_xdna(file) (((struct seq_file *)(file)->private_data)->private) + +static ssize_t amdxdna_carveout_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct amdxdna_dev *xdna = file_to_xdna(file); + char kbuf[128]; + u64 size, addr; + char *sep; + int ret; + + if (count == 0 || count >= sizeof(kbuf)) + return -EINVAL; + + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + kbuf[count] = '\0'; + strim(kbuf); + XDNA_DBG(xdna, "Trying to set carveout to %s", kbuf); + + sep = strchr(kbuf, '@'); + if (!sep) + return -EINVAL; + *sep = '\0'; + sep++; + + ret = kstrtou64(kbuf, 0, &size); + if (ret) + return ret; + + ret = kstrtou64(sep, 0, &addr); + if (ret) + return ret; + + /* Sanity check the addr and size. */ + if (!size) + return -EINVAL; + if (!IS_ALIGNED(addr, PAGE_SIZE) || !IS_ALIGNED(size, PAGE_SIZE)) + return -EINVAL; + + guard(mutex)(&xdna->dev_lock); + + ret = amdxdna_carveout_init(xdna, addr, size); + if (ret) + return ret; + + return count; +} + +static int amdxdna_carveout_show(struct seq_file *m, void *unused) +{ + struct amdxdna_dev *xdna = m->private; + u64 addr, size; + + guard(mutex)(&xdna->dev_lock); + amdxdna_get_carveout_conf(xdna, &addr, &size); + seq_printf(m, "0x%llx@0x%llx\n", size, addr); + return 0; +} + +/* + * Input/output format: @ + */ +AMDXDNA_DBGFS_FOPS(carveout, amdxdna_carveout_show, amdxdna_carveout_write); + +static const struct { + const char *name; + const struct file_operations *fops; + umode_t mode; +} amdxdna_dbgfs_files[] = { + AMDXDNA_DBGFS_FILE(carveout, 0600), +}; + +void amdxdna_debugfs_init(struct amdxdna_dev *xdna) +{ + struct drm_minor *minor = xdna->ddev.accel; + int i; + + /* + * It should be okay that debugfs fails to init. + * We rely on DRM framework to finish debugfs. + */ + for (i = 0; i < ARRAY_SIZE(amdxdna_dbgfs_files); i++) { + debugfs_create_file(amdxdna_dbgfs_files[i].name, + amdxdna_dbgfs_files[i].mode, + minor->debugfs_root, + xdna, + amdxdna_dbgfs_files[i].fops); + } +} diff --git a/drivers/accel/amdxdna/amdxdna_debugfs.h b/drivers/accel/amdxdna/amdxdna_debugfs.h new file mode 100644 index 000000000000..2abb45de3f7e --- /dev/null +++ b/drivers/accel/amdxdna/amdxdna_debugfs.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026, Advanced Micro Devices, Inc. + */ +#ifndef _AMDXDNA_DEBUGFS_H_ +#define _AMDXDNA_DEBUGFS_H_ + +#include "amdxdna_pci_drv.h" + +#if defined(CONFIG_DEBUG_FS) +void amdxdna_debugfs_init(struct amdxdna_dev *xdna); +#else +static inline void amdxdna_debugfs_init(struct amdxdna_dev *xdna) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +#endif diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 238ee244d4a6..ebfc472aa9e7 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -16,6 +16,7 @@ #include #include +#include "amdxdna_cbuf.h" #include "amdxdna_ctx.h" #include "amdxdna_gem.h" #include "amdxdna_pci_drv.h" @@ -516,10 +517,6 @@ static void amdxdna_imported_obj_free(struct amdxdna_gem_obj *abo) static inline bool amdxdna_gem_skip_bo_usage(struct amdxdna_gem_obj *abo) { - /* Do not count imported BOs since the buffer is not allocated by us. */ - if (is_import_bo(abo)) - return true; - /* Already counted as part of HEAP BO */ if (abo->type == AMDXDNA_BO_DEV) return true; @@ -571,9 +568,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj) if (abo->type == AMDXDNA_BO_DEV_HEAP) drm_mm_takedown(&abo->mm); - if (amdxdna_iova_on(xdna)) - amdxdna_iommu_unmap_bo(xdna, abo); - + amdxdna_dma_unmap_bo(xdna, abo); amdxdna_gem_vunmap(abo); mutex_destroy(&abo->lock); @@ -591,18 +586,20 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi guard(mutex)(&abo->lock); abo->open_ref++; + if (abo->open_ref > 1) + return 0; - if (abo->open_ref == 1) { - /* Attached to the client when first opened by it. */ - abo->client = filp->driver_priv; - amdxdna_gem_add_bo_usage(abo); - } - if (amdxdna_iova_on(xdna)) { - ret = amdxdna_iommu_map_bo(xdna, abo); + /* Attached to the client when first opened by it. */ + abo->client = filp->driver_priv; + + /* No need to set up dma addr mapping in PASID mode. */ + if (!amdxdna_pasid_on(abo->client)) { + ret = amdxdna_dma_map_bo(xdna, abo); if (ret) return ret; } + amdxdna_gem_add_bo_usage(abo); return 0; } @@ -620,6 +617,39 @@ static void amdxdna_gem_obj_close(struct drm_gem_object *gobj, struct drm_file * } } +static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); + int ret; + + iosys_map_clear(map); + + dma_resv_assert_held(obj->resv); + + if (is_import_bo(abo)) + ret = dma_buf_vmap(abo->dma_buf, map); + else + ret = drm_gem_shmem_object_vmap(obj, map); + if (ret) + return ret; + if (!map->vaddr) + return -ENOMEM; + + return 0; +} + +static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); + + dma_resv_assert_held(obj->resv); + + if (is_import_bo(abo)) + dma_buf_vunmap(abo->dma_buf, map); + else + drm_gem_shmem_object_vunmap(obj, map); +} + static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) { struct amdxdna_gem_obj *abo = to_xdna_obj(obj); @@ -645,8 +675,8 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { .pin = drm_gem_shmem_object_pin, .unpin = drm_gem_shmem_object_unpin, .get_sg_table = drm_gem_shmem_object_get_sg_table, - .vmap = drm_gem_shmem_object_vmap, - .vunmap = drm_gem_shmem_object_vunmap, + .vmap = amdxdna_gem_obj_vmap, + .vunmap = amdxdna_gem_obj_vunmap, .mmap = amdxdna_gem_obj_mmap, .vm_ops = &drm_gem_shmem_vm_ops, .export = amdxdna_gem_prime_export, @@ -714,6 +744,36 @@ amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create return to_xdna_obj(gobj); } +static struct amdxdna_gem_obj * +amdxdna_gem_create_cbuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args) +{ + struct amdxdna_dev *xdna = to_xdna_dev(dev); + size_t size = PAGE_ALIGN(args->size); + struct drm_gem_object *gobj; + struct amdxdna_gem_obj *ret; + struct dma_buf *dma_buf; + u64 align; + + if (!size) { + XDNA_ERR(xdna, "Invalid BO size 0x%llx", args->size); + return ERR_PTR(-EINVAL); + } + + align = (args->type == AMDXDNA_BO_DEV_HEAP) ? xdna->dev_info->dev_mem_size : 0; + dma_buf = amdxdna_get_cbuf(dev, size, align); + if (IS_ERR(dma_buf)) + return ERR_CAST(dma_buf); + + gobj = amdxdna_gem_prime_import(dev, dma_buf); + if (IS_ERR(gobj)) + ret = ERR_CAST(gobj); + else + ret = to_xdna_obj(gobj); + + dma_buf_put(dma_buf); + return ret; +} + struct drm_gem_object * amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) { @@ -769,6 +829,8 @@ amdxdna_drm_create_share_bo(struct drm_device *dev, if (args->vaddr) abo = amdxdna_gem_create_ubuf_object(dev, args); + else if (amdxdna_use_carveout(to_xdna_dev(dev))) + abo = amdxdna_gem_create_cbuf_object(dev, args); else abo = amdxdna_gem_create_shmem_object(dev, args); if (IS_ERR(abo)) @@ -884,7 +946,6 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f args->type, args->vaddr, args->size, args->flags); switch (args->type) { case AMDXDNA_BO_CMD: - fallthrough; case AMDXDNA_BO_SHARE: abo = amdxdna_drm_create_share_bo(dev, args, filp); break; diff --git a/drivers/accel/amdxdna/amdxdna_iommu.c b/drivers/accel/amdxdna/amdxdna_iommu.c index 5a9f06183487..eff00131d0f8 100644 --- a/drivers/accel/amdxdna/amdxdna_iommu.c +++ b/drivers/accel/amdxdna/amdxdna_iommu.c @@ -35,14 +35,15 @@ static struct iova *amdxdna_iommu_alloc_iova(struct amdxdna_dev *xdna, return iova; } -int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) +int amdxdna_dma_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) { + unsigned long contig_sz; struct sg_table *sgt; dma_addr_t dma_addr; struct iova *iova; ssize_t size; - if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM) + if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHARE) return 0; sgt = drm_gem_shmem_get_pages_sgt(&abo->base); @@ -51,47 +52,63 @@ int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) return PTR_ERR(sgt); } - if (!sgt->orig_nents || !sg_page(sgt->sgl)) { - XDNA_ERR(xdna, "sgl is zero length or not page backed"); + if (!sgt->orig_nents) { + XDNA_ERR(xdna, "sgl is zero length"); return -EOPNOTSUPP; } - iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr, - (abo->type == AMDXDNA_BO_DEV_HEAP)); - if (IS_ERR(iova)) { - XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova)); - return PTR_ERR(iova); + if (amdxdna_iova_on(xdna)) { + if (!sg_page(sgt->sgl)) { + XDNA_ERR(xdna, "sgl is not page backed"); + return -EOPNOTSUPP; + } + + iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr, + (abo->type == AMDXDNA_BO_DEV_HEAP)); + if (IS_ERR(iova)) { + XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova)); + return PTR_ERR(iova); + } + + size = iommu_map_sgtable(xdna->domain, dma_addr, sgt, + IOMMU_READ | IOMMU_WRITE); + if (size < 0) { + XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size); + __free_iova(&xdna->iovad, iova); + return size; + } + if (size < abo->mem.size) { + iommu_unmap(xdna->domain, dma_addr, size); + __free_iova(&xdna->iovad, iova); + return -ENXIO; + } + abo->mem.dma_addr = dma_addr; + } else { + /* Device doesn't support scatter/gather list, fail non-contiguous mapping. */ + contig_sz = drm_prime_get_contiguous_size(sgt); + if (contig_sz < abo->mem.size) { + XDNA_ERR(xdna, + "noncontiguous dma addr, contig size:%ld, expected size:%ld", + contig_sz, abo->mem.size); + return -EINVAL; + } + abo->mem.dma_addr = sg_dma_address(sgt->sgl); } - - size = iommu_map_sgtable(xdna->domain, dma_addr, sgt, - IOMMU_READ | IOMMU_WRITE); - if (size < 0) { - XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size); - __free_iova(&xdna->iovad, iova); - return size; - } - - if (size < abo->mem.size) { - iommu_unmap(xdna->domain, dma_addr, size); - __free_iova(&xdna->iovad, iova); - return -ENXIO; - } - - abo->mem.dma_addr = dma_addr; - return 0; } -void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) +void amdxdna_dma_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) { size_t size; if (abo->mem.dma_addr == AMDXDNA_INVALID_ADDR) return; - size = iova_align(&xdna->iovad, abo->mem.size); - iommu_unmap(xdna->domain, abo->mem.dma_addr, size); - free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr)); + if (amdxdna_iova_on(xdna)) { + size = iova_align(&xdna->iovad, abo->mem.size); + iommu_unmap(xdna->domain, abo->mem.dma_addr, size); + free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr)); + } abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; } diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c index 37771bdb24a1..cc8865f4e79c 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox.c +++ b/drivers/accel/amdxdna/amdxdna_mailbox.c @@ -361,6 +361,7 @@ static void mailbox_rx_worker(struct work_struct *rx_work) int ret; mb_chann = container_of(rx_work, struct mailbox_channel, rx_work); + trace_mbox_rx_worker(MAILBOX_NAME, mb_chann->msix_irq); if (READ_ONCE(mb_chann->bad_state)) { MB_ERR(mb_chann, "Channel in bad state, work aborted"); diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index 21eddfc538d0..1b08a08343cf 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -14,7 +14,9 @@ #include #include +#include "amdxdna_cbuf.h" #include "amdxdna_ctx.h" +#include "amdxdna_debugfs.h" #include "amdxdna_gem.h" #include "amdxdna_pci_drv.h" #include "amdxdna_pm.h" @@ -67,11 +69,40 @@ static const struct amdxdna_device_id amdxdna_ids[] = { {0} }; +static int amdxdna_sva_init(struct amdxdna_client *client) +{ + struct amdxdna_dev *xdna = client->xdna; + + client->sva = iommu_sva_bind_device(xdna->ddev.dev, client->mm); + if (IS_ERR(client->sva)) { + XDNA_ERR(xdna, "SVA bind device failed, ret %ld", PTR_ERR(client->sva)); + return PTR_ERR(client->sva); + } + + client->pasid = iommu_sva_get_pasid(client->sva); + if (client->pasid == IOMMU_PASID_INVALID) { + iommu_sva_unbind_device(client->sva); + XDNA_ERR(xdna, "SVA get pasid failed"); + return -ENODEV; + } + + return 0; +} + +static void amdxdna_sva_fini(struct amdxdna_client *client) +{ + if (IS_ERR_OR_NULL(client->sva)) + return; + + iommu_sva_unbind_device(client->sva); + client->sva = NULL; + client->pasid = IOMMU_PASID_INVALID; +} + static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) { struct amdxdna_dev *xdna = to_xdna_dev(ddev); struct amdxdna_client *client; - int ret; client = kzalloc_obj(*client); if (!client) @@ -80,22 +111,13 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) client->pid = pid_nr(rcu_access_pointer(filp->pid)); client->xdna = xdna; client->pasid = IOMMU_PASID_INVALID; + client->mm = current->mm; if (!amdxdna_iova_on(xdna)) { - client->sva = iommu_sva_bind_device(xdna->ddev.dev, current->mm); - if (IS_ERR(client->sva)) { - ret = PTR_ERR(client->sva); - XDNA_ERR(xdna, "SVA bind device failed, ret %d", ret); - goto failed; - } - client->pasid = iommu_sva_get_pasid(client->sva); - if (client->pasid == IOMMU_PASID_INVALID) { - XDNA_ERR(xdna, "SVA get pasid failed"); - ret = -ENODEV; - goto unbind_sva; - } + /* No need to fail open since user may use pa + carveout later. */ + if (amdxdna_sva_init(client)) + XDNA_WARN(xdna, "PASID not available for pid %d", client->pid); } - client->mm = current->mm; mmgrab(client->mm); init_srcu_struct(&client->hwctx_srcu); xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); @@ -110,14 +132,6 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) XDNA_DBG(xdna, "pid %d opened", client->pid); return 0; - -unbind_sva: - if (!IS_ERR_OR_NULL(client->sva)) - iommu_sva_unbind_device(client->sva); -failed: - kfree(client); - - return ret; } static void amdxdna_client_cleanup(struct amdxdna_client *client) @@ -131,11 +145,8 @@ static void amdxdna_client_cleanup(struct amdxdna_client *client) drm_gem_object_put(to_gobj(client->dev_heap)); mutex_destroy(&client->mm_lock); - - if (!IS_ERR_OR_NULL(client->sva)) - iommu_sva_unbind_device(client->sva); mmdrop(client->mm); - + amdxdna_sva_fini(client); kfree(client); } @@ -242,15 +253,17 @@ static void amdxdna_show_fdinfo(struct drm_printer *p, struct drm_file *filp) /* * Note for driver specific BO memory usage stat. - * Total memory alloc = amdxdna-internal-alloc + amdxdna-external-alloc + * Total memory in use = amdxdna-internal-alloc + amdxdna-external-alloc, which + * includes both imported and created BOs. To avoid double counts, it includes + * HEAP BO, but not DEV BO. DEV BO is counted by amdxdna-heap-alloc. */ drm_fdinfo_print_size(p, drv_name, "heap", "alloc", heap_usage); drm_fdinfo_print_size(p, drv_name, "internal", "alloc", internal_usage); drm_fdinfo_print_size(p, drv_name, "external", "alloc", external_usage); /* * Note for DRM standard BO memory stat. - * drm-total-memory counts both DEV BO and HEAP BO - * drm-shared-memory counts BO imported + * drm-total-memory counts both DEV BO and HEAP BO. The DEV BO size is double counted. + * drm-shared-memory counts BO shared with other processes/devices. */ drm_show_memory_stats(p, filp); } @@ -299,25 +312,38 @@ amdxdna_get_dev_info(struct pci_dev *pdev) return NULL; } +static void amdxdna_xdna_drm_release(struct drm_device *drm, void *res) +{ + struct amdxdna_dev *xdna = res; + + amdxdna_carveout_fini(xdna); +} + static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *dev = &pdev->dev; struct amdxdna_dev *xdna; + struct drm_device *ddev; int ret; xdna = devm_drm_dev_alloc(dev, &amdxdna_drm_drv, typeof(*xdna), ddev); if (IS_ERR(xdna)) return PTR_ERR(xdna); + ddev = &xdna->ddev; xdna->dev_info = amdxdna_get_dev_info(pdev); if (!xdna->dev_info) return -ENODEV; - drmm_mutex_init(&xdna->ddev, &xdna->dev_lock); + drmm_mutex_init(ddev, &xdna->dev_lock); init_rwsem(&xdna->notifier_lock); INIT_LIST_HEAD(&xdna->client_list); pci_set_drvdata(pdev, xdna); + ret = drmm_add_action(ddev, amdxdna_xdna_drm_release, xdna); + if (ret) + return ret; + if (IS_ENABLED(CONFIG_LOCKDEP)) { fs_reclaim_acquire(GFP_KERNEL); might_lock(&xdna->notifier_lock); @@ -348,12 +374,13 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto failed_dev_fini; } - ret = drm_dev_register(&xdna->ddev, 0); + ret = drm_dev_register(ddev, 0); if (ret) { XDNA_ERR(xdna, "DRM register failed, ret %d", ret); goto failed_sysfs_fini; } + amdxdna_debugfs_init(xdna); return 0; failed_sysfs_fini: diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h index bdd0dc83f92e..b1548cf16f59 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.h +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h @@ -104,6 +104,8 @@ struct amdxdna_fw_ver { u32 build; }; +struct amdxdna_carveout; + struct amdxdna_dev { struct drm_device ddev; struct amdxdna_dev_hdl *dev_handle; @@ -121,6 +123,8 @@ struct amdxdna_dev { struct iova_domain iovad; /* Accurate board name queried from firmware, or default_vbnv as fallback */ const char *vbnv; + + struct amdxdna_carveout *carveout; }; /* @@ -172,11 +176,11 @@ void amdxdna_sysfs_fini(struct amdxdna_dev *xdna); int amdxdna_iommu_init(struct amdxdna_dev *xdna); void amdxdna_iommu_fini(struct amdxdna_dev *xdna); -int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo); -void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo); void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr); void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size, void *cpu_addr, dma_addr_t dma_addr); +int amdxdna_dma_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo); +void amdxdna_dma_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo); static inline bool amdxdna_iova_on(struct amdxdna_dev *xdna) { diff --git a/drivers/accel/amdxdna/npu1_regs.c b/drivers/accel/amdxdna/npu1_regs.c index d7e50c6b06ef..4e48c030a69f 100644 --- a/drivers/accel/amdxdna/npu1_regs.c +++ b/drivers/accel/amdxdna/npu1_regs.c @@ -97,6 +97,7 @@ static const struct amdxdna_dev_priv npu1_dev_priv = { .rt_config = npu1_default_rt_cfg, .dpm_clk_tbl = npu1_dpm_clk_table, .col_align = COL_ALIGN_NONE, + .col_opc = 2048, .mbox_dev_addr = NPU1_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ .sram_dev_addr = NPU1_SRAM_BAR_BASE, diff --git a/drivers/accel/amdxdna/npu4_regs.c b/drivers/accel/amdxdna/npu4_regs.c index 935999ced70f..eddc31803a50 100644 --- a/drivers/accel/amdxdna/npu4_regs.c +++ b/drivers/accel/amdxdna/npu4_regs.c @@ -160,6 +160,7 @@ static const struct amdxdna_dev_priv npu4_dev_priv = { .rt_config = npu4_default_rt_cfg, .dpm_clk_tbl = npu4_dpm_clk_table, .col_align = COL_ALIGN_NATURE, + .col_opc = 4096, .mbox_dev_addr = NPU4_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ .sram_dev_addr = NPU4_SRAM_BAR_BASE, diff --git a/drivers/accel/amdxdna/npu5_regs.c b/drivers/accel/amdxdna/npu5_regs.c index 795bd1996845..a9102978e4a8 100644 --- a/drivers/accel/amdxdna/npu5_regs.c +++ b/drivers/accel/amdxdna/npu5_regs.c @@ -67,6 +67,7 @@ static const struct amdxdna_dev_priv npu5_dev_priv = { .rt_config = npu4_default_rt_cfg, .dpm_clk_tbl = npu4_dpm_clk_table, .col_align = COL_ALIGN_NATURE, + .col_opc = 4096, .mbox_dev_addr = NPU5_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ .sram_dev_addr = NPU5_SRAM_BAR_BASE, diff --git a/drivers/accel/amdxdna/npu6_regs.c b/drivers/accel/amdxdna/npu6_regs.c index 3125d1ce45ab..e0db3a09740b 100644 --- a/drivers/accel/amdxdna/npu6_regs.c +++ b/drivers/accel/amdxdna/npu6_regs.c @@ -67,6 +67,7 @@ static const struct amdxdna_dev_priv npu6_dev_priv = { .rt_config = npu4_default_rt_cfg, .dpm_clk_tbl = npu4_dpm_clk_table, .col_align = COL_ALIGN_NATURE, + .col_opc = 4096, .mbox_dev_addr = NPU6_MBOX_BAR_BASE, .mbox_size = 0, /* Use BAR size */ .sram_dev_addr = NPU6_SRAM_BAR_BASE, diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 2801378e3e19..e6d631108145 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2020-2025 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #include @@ -234,7 +234,7 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f args->value = vdev->platform; break; case DRM_IVPU_PARAM_CORE_CLOCK_RATE: - args->value = ivpu_hw_dpu_max_freq_get(vdev); + args->value = ivpu_hw_btrs_pll_ratio_to_hz(vdev, vdev->hw->pll.max_ratio); break; case DRM_IVPU_PARAM_NUM_CONTEXTS: args->value = file_priv->user_limits->max_ctx_count; @@ -487,6 +487,10 @@ int ivpu_boot(struct ivpu_device *vdev) ret = ivpu_hw_sched_init(vdev); if (ret) goto err_disable_ipc; + + ret = ivpu_hw_btrs_cfg_freq_init(vdev); + if (ret) + goto err_disable_ipc; } return 0; diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h index b6d0f0d0dccc..487a918e2fa9 100644 --- a/drivers/accel/ivpu/ivpu_hw.h +++ b/drivers/accel/ivpu/ivpu_hw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2020-2025 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #ifndef __IVPU_HW_H__ @@ -28,6 +28,7 @@ struct ivpu_hw_info { struct ivpu_addr_range dma; } ranges; struct { + /* Hardware min and max pll ratio */ u8 min_ratio; u8 max_ratio; /* @@ -35,6 +36,9 @@ struct ivpu_hw_info { * performance to power ratio at this frequency. */ u8 pn_ratio; + /* Pll ratios configured via sysfs interface */ + u8 cfg_min_ratio; + u8 cfg_max_ratio; u32 profiling_freq; } pll; struct { @@ -80,16 +84,6 @@ static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range) return range->end - range->start; } -static inline u32 ivpu_hw_dpu_max_freq_get(struct ivpu_device *vdev) -{ - return ivpu_hw_btrs_dpu_max_freq_get(vdev); -} - -static inline u32 ivpu_hw_dpu_freq_get(struct ivpu_device *vdev) -{ - return ivpu_hw_btrs_dpu_freq_get(vdev); -} - static inline void ivpu_hw_irq_clear(struct ivpu_device *vdev) { ivpu_hw_ip_irq_clear(vdev); diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c index 06e65c592618..dac935164e11 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.c +++ b/drivers/accel/ivpu/ivpu_hw_btrs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2020-2025 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #include @@ -11,6 +11,7 @@ #include "ivpu_hw_btrs_lnl_reg.h" #include "ivpu_hw_btrs_mtl_reg.h" #include "ivpu_hw_reg_io.h" +#include "ivpu_jsm_msg.h" #include "ivpu_pm.h" #define BTRS_MTL_IRQ_MASK ((REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR)) | \ @@ -33,8 +34,7 @@ #define PLL_CDYN_DEFAULT 0x80 #define PLL_EPP_DEFAULT 0x80 -#define PLL_REF_CLK_FREQ 50000000ull -#define PLL_RATIO_TO_FREQ(x) ((x) * PLL_REF_CLK_FREQ) +#define PLL_REF_CLK_FREQ_MHZ 50 #define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC) #define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC) @@ -59,8 +59,6 @@ #define DCT_ENABLE 0x1 #define DCT_DISABLE 0x0 -static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio); - int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev) { REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, BTRS_MTL_ALL_IRQ_MASK); @@ -111,6 +109,8 @@ void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev) hw->pll.min_ratio = clamp_t(u8, ivpu_pll_min_ratio, hw->pll.min_ratio, hw->pll.max_ratio); hw->pll.max_ratio = clamp_t(u8, ivpu_pll_max_ratio, hw->pll.min_ratio, hw->pll.max_ratio); hw->pll.pn_ratio = clamp_t(u8, hw->pll.pn_ratio, hw->pll.min_ratio, hw->pll.max_ratio); + hw->pll.cfg_max_ratio = hw->pll.max_ratio; + hw->pll.cfg_min_ratio = hw->pll.min_ratio; } static bool tile_disable_check(u32 config) @@ -341,8 +341,8 @@ int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable) prepare_wp_request(vdev, &wp, enable); - ivpu_dbg(vdev, PM, "PLL workpoint request: %lu MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n", - pll_ratio_to_dpu_freq(vdev, wp.target) / HZ_PER_MHZ, wp.cfg, wp.epp, wp.cdyn); + ivpu_dbg(vdev, PM, "PLL workpoint request: %u MHz, config: 0x%x, epp: 0x%x, cdyn: 0x%x\n", + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, wp.target), wp.cfg, wp.epp, wp.cdyn); ret = wp_request_send(vdev, &wp); if (ret) { @@ -598,35 +598,101 @@ static u32 pll_config_get_lnl(struct ivpu_device *vdev) return REGB_RD32(VPU_HW_BTRS_LNL_PLL_FREQ); } -static u32 pll_ratio_to_dpu_freq_mtl(u16 ratio) +static u32 pll_ratio_to_mhz_mtl(u8 pll_ratio) { - return (PLL_RATIO_TO_FREQ(ratio) * 2) / 3; + return (pll_ratio * PLL_REF_CLK_FREQ_MHZ * 2) / 3; } -static u32 pll_ratio_to_dpu_freq_lnl(u16 ratio) +static u32 pll_ratio_to_mhz_lnl(u8 pll_ratio) { - return PLL_RATIO_TO_FREQ(ratio) / 2; + return (pll_ratio * PLL_REF_CLK_FREQ_MHZ) / 2; } -static u32 pll_ratio_to_dpu_freq(struct ivpu_device *vdev, u32 ratio) +u32 ivpu_hw_btrs_pll_ratio_to_mhz(struct ivpu_device *vdev, u8 pll_ratio) { if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) - return pll_ratio_to_dpu_freq_mtl(ratio); + return pll_ratio_to_mhz_mtl(pll_ratio); else - return pll_ratio_to_dpu_freq_lnl(ratio); + return pll_ratio_to_mhz_lnl(pll_ratio); } -u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev) +u32 ivpu_hw_btrs_pll_ratio_to_hz(struct ivpu_device *vdev, u8 pll_ratio) { - return pll_ratio_to_dpu_freq(vdev, vdev->hw->pll.max_ratio); + return ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pll_ratio) * HZ_PER_MHZ; } -u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev) +u32 ivpu_hw_btrs_current_freq_get(struct ivpu_device *vdev) { if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) - return pll_ratio_to_dpu_freq_mtl(pll_config_get_mtl(vdev)); + return pll_ratio_to_mhz_mtl(pll_config_get_mtl(vdev)); else - return pll_ratio_to_dpu_freq_lnl(pll_config_get_lnl(vdev)); + return pll_ratio_to_mhz_lnl(pll_config_get_lnl(vdev)); +} + +static int ivpu_hw_btrs_cfg_freq_set(struct ivpu_device *vdev, u8 cfg_min_ratio, u8 cfg_max_ratio) +{ + u8 min_ratio = clamp_t(u8, cfg_min_ratio, vdev->hw->pll.min_ratio, cfg_max_ratio); + u8 pn_ratio = clamp_t(u8, vdev->hw->pll.pn_ratio, min_ratio, cfg_max_ratio); + int ret; + + ivpu_dbg(vdev, PM, "Set frequency range to min: %u, pn: %u, max: %u MHz\n", + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, min_ratio), + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pn_ratio), + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, cfg_max_ratio)); + + ret = ivpu_rpm_get(vdev); + if (ret < 0) + return ret; + + ret = ivpu_jsm_msg_freq_config(vdev, min_ratio, pn_ratio, cfg_max_ratio); + ivpu_rpm_put(vdev); + + if (ret) { + ivpu_warn(vdev, + "Failed to set frequency to min: %u, pn: %u, max: %u MHz, ret %d\n", + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, min_ratio), + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, pn_ratio), + ivpu_hw_btrs_pll_ratio_to_mhz(vdev, cfg_max_ratio), + ret); + return ret; + } + + vdev->hw->pll.cfg_min_ratio = cfg_min_ratio; + vdev->hw->pll.cfg_max_ratio = cfg_max_ratio; + + return 0; +} + +static u8 dpu_mhz_to_pll_ratio_lnl(u32 freq_mhz) +{ + return clamp_t(u32, freq_mhz / (PLL_REF_CLK_FREQ_MHZ / 2), 0, U8_MAX); +} + +int ivpu_hw_btrs_cfg_max_freq_set(struct ivpu_device *vdev, u32 max_freq_mhz) +{ + u8 ratio = dpu_mhz_to_pll_ratio_lnl(max_freq_mhz); + u8 cfg_max_ratio = clamp_t(u8, ratio, vdev->hw->pll.min_ratio, vdev->hw->pll.max_ratio); + + return ivpu_hw_btrs_cfg_freq_set(vdev, vdev->hw->pll.cfg_min_ratio, cfg_max_ratio); +} + +int ivpu_hw_btrs_cfg_min_freq_set(struct ivpu_device *vdev, u32 min_freq_mhz) +{ + u8 ratio = dpu_mhz_to_pll_ratio_lnl(min_freq_mhz); + u8 cfg_min_ratio = clamp_t(u8, ratio, vdev->hw->pll.min_ratio, vdev->hw->pll.max_ratio); + + return ivpu_hw_btrs_cfg_freq_set(vdev, cfg_min_ratio, vdev->hw->pll.cfg_max_ratio); +} + +int ivpu_hw_btrs_cfg_freq_init(struct ivpu_device *vdev) +{ + if (vdev->hw->pll.min_ratio == vdev->hw->pll.cfg_min_ratio && + vdev->hw->pll.max_ratio == vdev->hw->pll.cfg_max_ratio) + return 0; + + return ivpu_hw_btrs_cfg_freq_set(vdev, + vdev->hw->pll.cfg_min_ratio, + vdev->hw->pll.cfg_max_ratio); } /* Handler for IRQs from Buttress core (irqB) */ @@ -641,8 +707,8 @@ bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq) if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, FREQ_CHANGE, status)) { u32 pll = pll_config_get_mtl(vdev); - ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz", - pll, pll_ratio_to_dpu_freq_mtl(pll) / HZ_PER_MHZ); + ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %u MHz", + pll, pll_ratio_to_mhz_mtl(pll)); } if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, status)) { @@ -695,8 +761,8 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq) if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) { u32 pll = pll_config_get_lnl(vdev); - ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %lu MHz", - pll, pll_ratio_to_dpu_freq_lnl(pll) / HZ_PER_MHZ); + ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq, wp %08x, %u MHz", + pll, pll_ratio_to_mhz_lnl(pll)); } if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, status)) { diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.h b/drivers/accel/ivpu/ivpu_hw_btrs.h index c4c10e22f30f..d6343d734570 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.h +++ b/drivers/accel/ivpu/ivpu_hw_btrs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2020-2025 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #ifndef __IVPU_HW_BTRS_H__ @@ -31,8 +31,12 @@ int ivpu_hw_btrs_ip_reset(struct ivpu_device *vdev); void ivpu_hw_btrs_profiling_freq_reg_set_lnl(struct ivpu_device *vdev); void ivpu_hw_btrs_ats_print_lnl(struct ivpu_device *vdev); void ivpu_hw_btrs_clock_relinquish_disable_lnl(struct ivpu_device *vdev); -u32 ivpu_hw_btrs_dpu_max_freq_get(struct ivpu_device *vdev); -u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev); +u32 ivpu_hw_btrs_pll_ratio_to_mhz(struct ivpu_device *vdev, u8 pll_ratio); +u32 ivpu_hw_btrs_pll_ratio_to_hz(struct ivpu_device *vdev, u8 pll_ratio); +u32 ivpu_hw_btrs_current_freq_get(struct ivpu_device *vdev); +int ivpu_hw_btrs_cfg_min_freq_set(struct ivpu_device *vdev, u32 freq_mhz); +int ivpu_hw_btrs_cfg_max_freq_set(struct ivpu_device *vdev, u32 freq_mhz); +int ivpu_hw_btrs_cfg_freq_init(struct ivpu_device *vdev); bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq); bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq); int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable); diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c index 37f95a0551ed..81f0b1f8f5a6 100644 --- a/drivers/accel/ivpu/ivpu_hw_ip.c +++ b/drivers/accel/ivpu/ivpu_hw_ip.c @@ -307,18 +307,6 @@ static void pwr_island_trickle_drive_40xx(struct ivpu_device *vdev, bool enable) } static void pwr_island_drive_37xx(struct ivpu_device *vdev, bool enable) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0); - - if (enable) - val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val); - else - val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val); - - REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val); -} - -static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0); @@ -330,6 +318,18 @@ static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable) REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, val); } +static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable) +{ + u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0); + + if (enable) + val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val); + else + val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val); + + REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val); +} + static void pwr_island_enable(struct ivpu_device *vdev) { if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c index 07b1d6f615a9..17b42a76aef9 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.c +++ b/drivers/accel/ivpu/ivpu_jsm_msg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2020-2024 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #include "ivpu_drv.h" @@ -86,6 +86,9 @@ const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type) IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE_DONE); IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE); IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE_DONE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_FREQ_CONFIG); + IVPU_CASE_TO_STR(VPU_JSM_MSG_FREQ_CONFIG_RSP); + IVPU_CASE_TO_STR(VPU_JSM_MSG_RESERVED_111E); } #undef IVPU_CASE_TO_STR @@ -571,3 +574,16 @@ int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev) return ivpu_ipc_send_and_wait(vdev, &req, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.state_dump_msg); } + +int ivpu_jsm_msg_freq_config(struct ivpu_device *vdev, u16 min_ratio, u16 pn_ratio, u16 max_ratio) +{ + struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_FREQ_CONFIG}; + struct vpu_jsm_msg resp; + + req.payload.freq_config.min_freq_pll_ratio = min_ratio; + req.payload.freq_config.pn_freq_pll_ratio = pn_ratio; + req.payload.freq_config.max_freq_pll_ratio = max_ratio; + + return ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_FREQ_CONFIG_RSP, &resp, + VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm); +} diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.h b/drivers/accel/ivpu/ivpu_jsm_msg.h index a74f5a0b0d93..28ffe07422b0 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.h +++ b/drivers/accel/ivpu/ivpu_jsm_msg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2020-2024 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #ifndef __IVPU_JSM_MSG_H__ @@ -45,5 +45,6 @@ int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us int ivpu_jsm_dct_disable(struct ivpu_device *vdev); int ivpu_jsm_state_dump(struct ivpu_device *vdev); int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev); +int ivpu_jsm_msg_freq_config(struct ivpu_device *vdev, u16 min_ratio, u16 pn_ratio, u16 max_ratio); #endif diff --git a/drivers/accel/ivpu/ivpu_sysfs.c b/drivers/accel/ivpu/ivpu_sysfs.c index d250a10caca9..95d201b3b8aa 100644 --- a/drivers/accel/ivpu/ivpu_sysfs.c +++ b/drivers/accel/ivpu/ivpu_sysfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -82,8 +82,7 @@ static DEVICE_ATTR_RO(npu_memory_utilization); * - "OS" - Operating System Scheduler mode * */ -static ssize_t -sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *drm = dev_get_drvdata(dev); struct ivpu_device *vdev = to_ivpu_device(drm); @@ -94,47 +93,162 @@ sched_mode_show(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR_RO(sched_mode); /** - * DOC: npu_max_frequency + * DOC: NPU frequency control and information * - * The npu_max_frequency shows maximum frequency in MHz of the NPU's data - * processing unit + * Hardware frequency capabilities: + * freq/hw_min_freq - Minimum frequency supported by the NPU hardware. + * freq/hw_max_freq - Maximum frequency supported by the NPU hardware. + * freq/hw_efficient_freq - Most efficient operating frequency for the NPU. + * + * Configurable frequency limits (50XX devices and newer): + * freq/set_min_freq - Configured minimum operating frequency. + * freq/set_max_freq - Configured maximum operating frequency. + * + * Clamping behavior: Values written to set_min_freq and set_max_freq are + * clamped to hardware limits. If set_min_freq exceeds set_max_freq, the driver + * clamps set_min_freq to set_max_freq when selecting the operating frequency. + * + * Current operating frequency: + * freq/current_freq - Current frequency in MHz. Valid only when the device is + * active; returns 0 when idle. May be lower than the requested range due to + * power or thermal constraints. + * + * Legacy attributes (backward compatibility): + * npu_max_frequency_mhz - Alias for freq/hw_max_freq. + * npu_current_frequency_mhz - Alias for freq/current_freq. */ -static ssize_t -npu_max_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t hw_min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *drm = dev_get_drvdata(dev); struct ivpu_device *vdev = to_ivpu_device(drm); - u32 freq = ivpu_hw_dpu_max_freq_get(vdev); + u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.min_ratio); - return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ); + return sysfs_emit(buf, "%u\n", freq_mhz); } -static DEVICE_ATTR_RO(npu_max_frequency_mhz); +static DEVICE_ATTR_RO(hw_min_freq); -/** - * DOC: npu_current_frequency_mhz - * - * The npu_current_frequency_mhz shows current frequency in MHz of the NPU's - * data processing unit - */ -static ssize_t -npu_current_frequency_mhz_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t hw_efficient_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *drm = dev_get_drvdata(dev); struct ivpu_device *vdev = to_ivpu_device(drm); - u32 freq = 0; + u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.pn_ratio); + + return sysfs_emit(buf, "%u\n", freq_mhz); +} + +static DEVICE_ATTR_RO(hw_efficient_freq); + +static ssize_t hw_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct ivpu_device *vdev = to_ivpu_device(drm); + u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.max_ratio); + + return sysfs_emit(buf, "%u\n", freq_mhz); +} + +static DEVICE_ATTR_RO(hw_max_freq); + +static ssize_t set_min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct ivpu_device *vdev = to_ivpu_device(drm); + u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.cfg_min_ratio); + + return sysfs_emit(buf, "%u\n", freq_mhz); +} + +static ssize_t +set_min_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct ivpu_device *vdev = to_ivpu_device(drm); + u32 freq_mhz; + int ret; + + ret = kstrtou32(buf, 10, &freq_mhz); + if (ret) + return ret; + + ret = ivpu_hw_btrs_cfg_min_freq_set(vdev, freq_mhz); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(set_min_freq); + +static ssize_t set_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct ivpu_device *vdev = to_ivpu_device(drm); + u32 freq_mhz = ivpu_hw_btrs_pll_ratio_to_mhz(vdev, vdev->hw->pll.cfg_max_ratio); + + return sysfs_emit(buf, "%u\n", freq_mhz); +} + +static ssize_t +set_max_freq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct ivpu_device *vdev = to_ivpu_device(drm); + u32 freq_mhz; + int ret; + + ret = kstrtou32(buf, 10, &freq_mhz); + if (ret) + return ret; + + /* Convert MHz to Hz and set max frequency */ + ret = ivpu_hw_btrs_cfg_max_freq_set(vdev, freq_mhz); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(set_max_freq); + +static ssize_t current_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct ivpu_device *vdev = to_ivpu_device(drm); + u32 freq_mhz = 0; /* Read frequency only if device is active, otherwise frequency is 0 */ if (pm_runtime_get_if_active(vdev->drm.dev) > 0) { - freq = ivpu_hw_dpu_freq_get(vdev); + freq_mhz = ivpu_hw_btrs_current_freq_get(vdev); pm_runtime_put_autosuspend(vdev->drm.dev); } - return sysfs_emit(buf, "%lu\n", freq / HZ_PER_MHZ); + return sysfs_emit(buf, "%u\n", freq_mhz); } -static DEVICE_ATTR_RO(npu_current_frequency_mhz); +static DEVICE_ATTR_RO(current_freq); + +/* Alias to current_freq for legacy compat */ +static struct device_attribute dev_attr_npu_max_frequency_mhz = + __ATTR(npu_max_frequency_mhz, 0444, hw_max_freq_show, NULL); + +static struct device_attribute dev_attr_npu_current_frequency_mhz = + __ATTR(npu_current_frequency_mhz, 0444, current_freq_show, NULL); + +static struct attribute *ivpu_freq_attrs[] = { + &dev_attr_hw_min_freq.attr, + &dev_attr_hw_efficient_freq.attr, + &dev_attr_hw_max_freq.attr, + &dev_attr_current_freq.attr, + NULL, +}; + +static struct attribute_group ivpu_freq_attr_group = { + .name = "freq", + .attrs = ivpu_freq_attrs, +}; static struct attribute *ivpu_dev_attrs[] = { &dev_attr_npu_busy_time_us.attr, @@ -154,6 +268,32 @@ void ivpu_sysfs_init(struct ivpu_device *vdev) int ret; ret = devm_device_add_group(vdev->drm.dev, &ivpu_dev_attr_group); - if (ret) - ivpu_warn(vdev, "Failed to add group to device, ret %d", ret); + if (ret) { + ivpu_warn(vdev, "Failed to add sysfs group to device, ret %d", ret); + return; + } + + ret = devm_device_add_group(vdev->drm.dev, &ivpu_freq_attr_group); + if (ret) { + ivpu_warn(vdev, "Failed to add sysfs freq group, ret %d", ret); + return; + } + + if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_50XX) { + ret = sysfs_add_file_to_group(&vdev->drm.dev->kobj, + &dev_attr_set_min_freq.attr, + "freq"); + if (ret) { + ivpu_warn(vdev, "Failed to add sysfs set_min_freq to device, ret %d", ret); + return; + } + + ret = sysfs_add_file_to_group(&vdev->drm.dev->kobj, + &dev_attr_set_max_freq.attr, + "freq"); + if (ret) { + ivpu_warn(vdev, "Failed to add sysfs set_max_freq to device, ret %d", ret); + return; + } + } } diff --git a/drivers/accel/ivpu/vpu_jsm_api.h b/drivers/accel/ivpu/vpu_jsm_api.h index bca6a44dc041..6d58d04cc0c4 100644 --- a/drivers/accel/ivpu/vpu_jsm_api.h +++ b/drivers/accel/ivpu/vpu_jsm_api.h @@ -23,7 +23,7 @@ /* * Minor version changes when API backward compatibility is preserved. */ -#define VPU_JSM_API_VER_MINOR 33 +#define VPU_JSM_API_VER_MINOR 34 /* * API header changed (field names, documentation, formatting) but API itself has not been changed @@ -604,6 +604,16 @@ enum vpu_ipc_msg_type { * This command has no payload */ VPU_JSM_MSG_DCT_DISABLE = 0x111d, + /** + * Reserved command ID to ensure that the following command requests / + * responses have the same lower byte value. + */ + VPU_JSM_MSG_RESERVED_111E = 0x111e, + /** + * Control command: Configure VPU frequency scaling parameters. + * @see vpu_ipc_msg_payload_freq_config + */ + VPU_JSM_MSG_FREQ_CONFIG = 0x111f, /** * Dump VPU state. To be used for debug purposes only. * This command has no payload. @@ -766,6 +776,11 @@ enum vpu_ipc_msg_type { * This command has no payload */ VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e, + /** + * Response to control command: Configure VPU frequency scaling parameters. + * @see vpu_ipc_msg_payload_freq_config + */ + VPU_JSM_MSG_FREQ_CONFIG_RSP = 0x221f, /** * Response to state dump control command. * This command has no payload. @@ -1650,6 +1665,22 @@ struct vpu_ipc_msg_payload_pwr_dct_control { u32 dct_inactive_us; }; +/** + * Payload for @ref VPU_JSM_MSG_FREQ_CONFIG message. + * + * This payload allows the host to configure the VPU frequency scaling parameters. + */ +struct vpu_ipc_msg_payload_freq_config { + /** Minimum frequency PLL ratio */ + u32 min_freq_pll_ratio; + /** Efficiency frequency PLL ratio */ + u32 pn_freq_pll_ratio; + /** Maximum frequency PLL ratio */ + u32 max_freq_pll_ratio; + /** Reserved for 64-bit alignment */ + u32 reserved_0; +}; + /* * Payloads union, used to define complete message format. */ @@ -1692,6 +1723,7 @@ union vpu_ipc_msg_payload { struct vpu_ipc_msg_payload_hws_resume_engine hws_resume_engine; struct vpu_ipc_msg_payload_pwr_d0i3_enter pwr_d0i3_enter; struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control; + struct vpu_ipc_msg_payload_freq_config freq_config; }; /** diff --git a/drivers/dma-buf/dma-fence-array.c b/drivers/dma-buf/dma-fence-array.c index 089f69469524..5e10e8df372f 100644 --- a/drivers/dma-buf/dma-fence-array.c +++ b/drivers/dma-buf/dma-fence-array.c @@ -190,15 +190,13 @@ EXPORT_SYMBOL(dma_fence_array_alloc); * @fences: [in] array containing the fences * @context: [in] fence context to use * @seqno: [in] sequence number to use - * @signal_on_any: [in] signal on any fence in the array * * Implementation of @dma_fence_array_create without allocation. Useful to init * a preallocated dma fence array in the path of reclaim or dma fence signaling. */ void dma_fence_array_init(struct dma_fence_array *array, int num_fences, struct dma_fence **fences, - u64 context, unsigned seqno, - bool signal_on_any) + u64 context, unsigned seqno) { static struct lock_class_key dma_fence_array_lock_key; @@ -222,7 +220,7 @@ void dma_fence_array_init(struct dma_fence_array *array, */ lockdep_set_class(&array->base.inline_lock, &dma_fence_array_lock_key); - atomic_set(&array->num_pending, signal_on_any ? 1 : num_fences); + atomic_set(&array->num_pending, num_fences); array->fences = fences; array->base.error = PENDING_ERROR; @@ -249,7 +247,6 @@ EXPORT_SYMBOL(dma_fence_array_init); * @fences: [in] array containing the fences * @context: [in] fence context to use * @seqno: [in] sequence number to use - * @signal_on_any: [in] signal on any fence in the array * * Allocate a dma_fence_array object and initialize the base fence with * dma_fence_init(). @@ -264,8 +261,7 @@ EXPORT_SYMBOL(dma_fence_array_init); */ struct dma_fence_array *dma_fence_array_create(int num_fences, struct dma_fence **fences, - u64 context, unsigned seqno, - bool signal_on_any) + u64 context, unsigned seqno) { struct dma_fence_array *array; @@ -273,8 +269,7 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, if (!array) return NULL; - dma_fence_array_init(array, num_fences, fences, - context, seqno, signal_on_any); + dma_fence_array_init(array, num_fences, fences, context, seqno); return array; } diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index 07fe9bf45aea..53bb40e70b27 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -180,8 +180,7 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences, if (count > 1) { result = dma_fence_array_create(count, array, - dma_fence_context_alloc(1), - 1, false); + dma_fence_context_alloc(1), 1); if (!result) { for (i = 0; i < count; i++) dma_fence_put(array[i]); diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index ce9e6c04897f..39a92d9f2413 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -648,8 +648,7 @@ int dma_resv_get_singleton(struct dma_resv *obj, enum dma_resv_usage usage, } array = dma_fence_array_create(count, fences, - dma_fence_context_alloc(1), - 1, false); + dma_fence_context_alloc(1), 1); if (!array) { while (count--) dma_fence_put(fences[count]); diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c index 51c87869b7b8..4e7ee25372ba 100644 --- a/drivers/dma-buf/st-dma-fence-unwrap.c +++ b/drivers/dma-buf/st-dma-fence-unwrap.c @@ -64,7 +64,7 @@ static struct dma_fence *mock_array(unsigned int num_fences, ...) array = dma_fence_array_create(num_fences, fences, dma_fence_context_alloc(1), - 1, false); + 1); if (!array) goto error_free; return &array->base; diff --git a/drivers/gpu/drm/adp/adp_drv.c b/drivers/gpu/drm/adp/adp_drv.c index a5913c71b39c..ce9dd6222634 100644 --- a/drivers/gpu/drm/adp/adp_drv.c +++ b/drivers/gpu/drm/adp/adp_drv.c @@ -444,8 +444,6 @@ static int adp_setup_mode_config(struct adp_drv_private *adp) if (IS_ERR(adp->connector)) return PTR_ERR(adp->connector); - drm_connector_attach_encoder(adp->connector, adp->encoder); - ret = drm_vblank_init(drm, drm->mode_config.num_crtc); if (ret < 0) { drm_err(drm, "failed to initialize vblank"); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index b47d8eea4566..821a3008a1e7 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -768,8 +768,6 @@ static int adv7511_connector_init(struct adv7511 *adv) return PTR_ERR(connector); } - drm_connector_attach_encoder(connector, bridge->encoder); - return 0; } diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 6bfb43b07604..8cf6b73bceac 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1486,7 +1486,7 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) dp->plat_data->next_bridge = devm_drm_panel_bridge_add(dp->dev, dp->plat_data->panel); if (IS_ERR(dp->plat_data->next_bridge)) { - ret = PTR_ERR(bridge); + ret = PTR_ERR(dp->plat_data->next_bridge); goto err_unregister_aux; } } diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index 50ea40a6ae05..cf90d4468b5c 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -1230,7 +1230,7 @@ static const struct mipi_dsi_host_ops cdns_dsi_ops = { .transfer = cdns_dsi_transfer, }; -static int __maybe_unused cdns_dsi_resume(struct device *dev) +static int cdns_dsi_resume(struct device *dev) { struct cdns_dsi *dsi = dev_get_drvdata(dev); @@ -1241,7 +1241,7 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev) return 0; } -static int __maybe_unused cdns_dsi_suspend(struct device *dev) +static int cdns_dsi_suspend(struct device *dev) { struct cdns_dsi *dsi = dev_get_drvdata(dev); @@ -1251,8 +1251,9 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev) return 0; } -static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume, - NULL); +static const struct dev_pm_ops cdns_dsi_pm_ops = { + RUNTIME_PM_OPS(cdns_dsi_suspend, cdns_dsi_resume, NULL) +}; static int cdns_dsi_drm_probe(struct platform_device *pdev) { @@ -1399,7 +1400,7 @@ static struct platform_driver cdns_dsi_platform_driver = { .driver = { .name = "cdns-dsi", .of_match_table = cdns_dsi_of_match, - .pm = &cdns_dsi_pm_ops, + .pm = pm_ptr(&cdns_dsi_pm_ops), }, }; module_platform_driver(cdns_dsi_platform_driver); diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index 48499aaba6a9..c7d588be12cb 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -200,6 +200,7 @@ struct it6263 { struct regmap *lvds_regmap; struct drm_bridge bridge; struct drm_bridge *next_bridge; + struct gpio_desc *reset_gpio; int lvds_data_mapping; bool lvds_dual_link; bool lvds_link12_swap; @@ -603,6 +604,15 @@ static void it6263_bridge_atomic_enable(struct drm_bridge *bridge, bool pclk_high; int i, ret; + it6263_hw_reset(it->reset_gpio); + + ret = it6263_lvds_set_i2c_addr(it); + if (ret) + dev_err(it->dev, "failed to set I2C addr\n"); + + it6263_lvds_config(it); + it6263_hdmi_config(it); + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; @@ -697,8 +707,6 @@ static int it6263_bridge_attach(struct drm_bridge *bridge, return ret; } - drm_connector_attach_encoder(connector, encoder); - return 0; } @@ -815,7 +823,6 @@ static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge, regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL, ENABLE_PKT | REPEAT_PKT); - return 0; } @@ -840,7 +847,6 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = { static int it6263_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct gpio_desc *reset_gpio; struct it6263 *it; int ret; @@ -858,9 +864,9 @@ static int it6263_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(it->hdmi_regmap), "failed to init I2C regmap for HDMI\n"); - reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(reset_gpio)) - return dev_err_probe(dev, PTR_ERR(reset_gpio), + it->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(it->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(it->reset_gpio), "failed to get reset gpio\n"); ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it6263_supplies), @@ -872,12 +878,6 @@ static int it6263_probe(struct i2c_client *client) if (ret) return ret; - it6263_hw_reset(reset_gpio); - - ret = it6263_lvds_set_i2c_addr(it); - if (ret) - return dev_err_probe(dev, ret, "failed to set I2C addr\n"); - it->lvds_i2c = devm_i2c_new_dummy_device(dev, client->adapter, LVDS_INPUT_CTRL_I2C_ADDR); if (IS_ERR(it->lvds_i2c)) @@ -890,9 +890,6 @@ static int it6263_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(it->lvds_regmap), "failed to init I2C regmap for LVDS\n"); - it6263_lvds_config(it); - it6263_hdmi_config(it); - i2c_set_clientdata(client, it); it->bridge.of_node = dev->of_node; diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 0651e44fc07e..6b7cb132a437 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -2048,7 +2048,7 @@ static void it6505_start_hdcp(struct it6505 *it6505) DRM_DEV_DEBUG_DRIVER(dev, "start"); it6505_reset_hdcp(it6505); - queue_delayed_work(system_wq, &it6505->hdcp_work, + queue_delayed_work(system_percpu_wq, &it6505->hdcp_work, msecs_to_jiffies(2400)); } diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index 8a0b48efca58..729b12b67470 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -493,6 +493,7 @@ static int lt8912_attach_dsi(struct lt8912 *lt) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | + MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_NO_EOT_PACKET; ret = devm_mipi_dsi_attach(dev, dsi); diff --git a/drivers/gpu/drm/bridge/microchip-lvds.c b/drivers/gpu/drm/bridge/microchip-lvds.c index 9f4ff82bc6b4..12fc442f9867 100644 --- a/drivers/gpu/drm/bridge/microchip-lvds.c +++ b/drivers/gpu/drm/bridge/microchip-lvds.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -41,9 +41,11 @@ /* Bitfields in LVDSC_CFGR (Configuration Register) */ #define LVDSC_CFGR_PIXSIZE_24BITS 0 +#define LVDSC_CFGR_PIXSIZE_18BITS BIT(0) #define LVDSC_CFGR_DEN_POL_HIGH 0 #define LVDSC_CFGR_DC_UNBALANCED 0 #define LVDSC_CFGR_MAPPING_JEIDA BIT(6) +#define LVDSC_CFGR_MAPPING_VESA 0 /*Bitfields in LVDSC_SR */ #define LVDSC_SR_CS BIT(0) @@ -56,7 +58,6 @@ struct mchp_lvds { struct device *dev; void __iomem *regs; struct clk *pclk; - struct drm_panel *panel; struct drm_bridge bridge; struct drm_bridge *panel_bridge; }; @@ -76,9 +77,10 @@ static inline void lvds_writel(struct mchp_lvds *lvds, u32 offset, u32 val) writel_relaxed(val, lvds->regs + offset); } -static void lvds_serialiser_on(struct mchp_lvds *lvds) +static void lvds_serialiser_on(struct mchp_lvds *lvds, u32 bus_format) { unsigned long timeout = jiffies + msecs_to_jiffies(LVDS_POLL_TIMEOUT_MS); + u8 map, pix_size; /* The LVDSC registers can only be written if WPEN is cleared */ lvds_writel(lvds, LVDSC_WPMR, (LVDSC_WPMR_WPKEY_PSSWD & @@ -93,11 +95,24 @@ static void lvds_serialiser_on(struct mchp_lvds *lvds) usleep_range(1000, 2000); } + switch (bus_format) { + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + map = LVDSC_CFGR_MAPPING_JEIDA; + pix_size = LVDSC_CFGR_PIXSIZE_18BITS; + break; + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + map = LVDSC_CFGR_MAPPING_VESA; + pix_size = LVDSC_CFGR_PIXSIZE_24BITS; + break; + default: + map = LVDSC_CFGR_MAPPING_JEIDA; + pix_size = LVDSC_CFGR_PIXSIZE_24BITS; + break; + } + /* Configure the LVDSC */ - lvds_writel(lvds, LVDSC_CFGR, (LVDSC_CFGR_MAPPING_JEIDA | - LVDSC_CFGR_DC_UNBALANCED | - LVDSC_CFGR_DEN_POL_HIGH | - LVDSC_CFGR_PIXSIZE_24BITS)); + lvds_writel(lvds, LVDSC_CFGR, map | LVDSC_CFGR_DC_UNBALANCED | + LVDSC_CFGR_DEN_POL_HIGH | pix_size); /* Enable the LVDS serializer */ lvds_writel(lvds, LVDSC_CR, LVDSC_CR_SER_EN); @@ -113,9 +128,11 @@ static int mchp_lvds_attach(struct drm_bridge *bridge, bridge, flags); } -static void mchp_lvds_enable(struct drm_bridge *bridge) +static void mchp_lvds_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct mchp_lvds *lvds = bridge_to_lvds(bridge); + struct drm_connector *connector; int ret; ret = clk_prepare_enable(lvds->pclk); @@ -130,10 +147,18 @@ static void mchp_lvds_enable(struct drm_bridge *bridge) return; } - lvds_serialiser_on(lvds); + /* default to jeida-24 */ + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (connector && connector->display_info.num_bus_formats) + bus_format = connector->display_info.bus_formats[0]; + + lvds_serialiser_on(lvds, bus_format); } -static void mchp_lvds_disable(struct drm_bridge *bridge) +static void mchp_lvds_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct mchp_lvds *lvds = bridge_to_lvds(bridge); @@ -143,15 +168,14 @@ static void mchp_lvds_disable(struct drm_bridge *bridge) static const struct drm_bridge_funcs mchp_lvds_bridge_funcs = { .attach = mchp_lvds_attach, - .enable = mchp_lvds_enable, - .disable = mchp_lvds_disable, + .atomic_enable = mchp_lvds_atomic_enable, + .atomic_disable = mchp_lvds_atomic_disable, }; static int mchp_lvds_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mchp_lvds *lvds; - struct device_node *port; int ret; if (!dev->of_node) @@ -173,19 +197,6 @@ static int mchp_lvds_probe(struct platform_device *pdev) return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk), "could not get pclk_lvds\n"); - port = of_graph_get_remote_node(dev->of_node, 1, 0); - if (!port) { - dev_err(dev, - "can't find port point, please init lvds panel port!\n"); - return -ENODEV; - } - - lvds->panel = of_drm_find_panel(port); - of_node_put(port); - - if (IS_ERR(lvds->panel)) - return -EPROBE_DEFER; - lvds->panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); if (IS_ERR(lvds->panel_bridge)) diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 8aa31ca3c72d..cc13c98f9be6 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -270,6 +270,11 @@ static const struct of_device_id simple_bridge_match[] = { .data = &(const struct simple_bridge_info) { .connector_type = DRM_MODE_CONNECTOR_HDMIA, }, + }, { + .compatible = "mstar,tsumu88adt3-lf-1", + .data = &(const struct simple_bridge_info) { + .connector_type = DRM_MODE_CONNECTOR_HDMIA, + }, }, { .compatible = "parade,ps185hdm", .data = &(const struct simple_bridge_info) { diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index e652a2bcd64a..0f2820b37302 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -45,6 +45,9 @@ /* Debug (16-bit addressable) */ #define TC358768_VBUFCTRL 0x00E0 +#define TC358768_VBUFCTRL_VBUF_EN BIT(15) +#define TC358768_VBUFCTRL_TX_EN BIT(14) +#define TC358768_VBUFCTRL_MASK BIT(13) #define TC358768_DBG_WIDTH 0x00E2 #define TC358768_DBG_VBLANK 0x00E4 #define TC358768_DBG_DATA 0x00E8 @@ -115,7 +118,7 @@ #define TC358768_DSI_HACT 0x062C /* TC358768_DSI_CONTROL (0x040C) register */ -#define TC358768_DSI_CONTROL_DIS_MODE BIT(15) +#define TC358768_DSI_CONTROL_DSI_MODE BIT(15) #define TC358768_DSI_CONTROL_TXMD BIT(7) #define TC358768_DSI_CONTROL_HSCKMD BIT(5) #define TC358768_DSI_CONTROL_EOTDIS BIT(0) @@ -123,7 +126,7 @@ /* TC358768_DSI_CONFW (0x0500) register */ #define TC358768_DSI_CONFW_MODE_SET (5 << 29) #define TC358768_DSI_CONFW_MODE_CLR (6 << 29) -#define TC358768_DSI_CONFW_ADDR_DSI_CONTROL (0x3 << 24) +#define TC358768_DSI_CONFW_ADDR(x) ((x) << 24) /* TC358768_DSICMD_TX (0x0600) register */ #define TC358768_DSI_CMDTX_DC_START BIT(0) @@ -232,6 +235,36 @@ static void tc358768_update_bits(struct tc358768_priv *priv, u32 reg, u32 mask, tc358768_write(priv, reg, tmp); } +static void tc358768_confw_update_bits(struct tc358768_priv *priv, u16 reg, + u16 mask, u16 val) +{ + u8 confw_addr; + u32 confw_val; + + switch (reg) { + case TC358768_DSI_CONTROL: + confw_addr = 0x3; + break; + default: + priv->error = -EINVAL; + return; + } + + if (mask != val) { + confw_val = TC358768_DSI_CONFW_MODE_CLR | + TC358768_DSI_CONFW_ADDR(confw_addr) | + mask; + tc358768_write(priv, TC358768_DSI_CONFW, confw_val); + } + + if (val & mask) { + confw_val = TC358768_DSI_CONFW_MODE_SET | + TC358768_DSI_CONFW_ADDR(confw_addr) | + (val & mask); + tc358768_write(priv, TC358768_DSI_CONFW, confw_val); + } +} + static void tc358768_dsicmd_tx(struct tc358768_priv *priv) { u32 val; @@ -448,6 +481,8 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, DRM_MODE_CONNECTOR_DSI); if (IS_ERR(bridge)) return PTR_ERR(bridge); + + bridge->pre_enable_prev_first = true; } priv->output.dev = dev; @@ -505,9 +540,21 @@ static ssize_t tc358768_dsi_host_transfer(struct mipi_dsi_host *host, return -ENOTSUPP; } + if (msg->tx_len > 1024) { + dev_warn(priv->dev, "Maximum 1024 byte MIPI tx is supported\n"); + return -EINVAL; + } + if (msg->tx_len > 8) { - dev_warn(priv->dev, "Maximum 8 byte MIPI tx is supported\n"); - return -ENOTSUPP; + u32 confctl; + + tc358768_read(priv, TC358768_CONFCTL, &confctl); + + if (confctl & BIT(6)) { + dev_warn(priv->dev, + "Video is currently active. Unable to transmit long command\n"); + return -EBUSY; + } } ret = mipi_dsi_create_packet(&packet, msg); @@ -520,23 +567,66 @@ static ssize_t tc358768_dsi_host_transfer(struct mipi_dsi_host *host, tc358768_write(priv, TC358768_DSICMD_WC, 0); tc358768_write(priv, TC358768_DSICMD_WD0, (packet.header[2] << 8) | packet.header[1]); - } else { - int i; - + tc358768_dsicmd_tx(priv); + } else if (packet.payload_length <= 8) { tc358768_write(priv, TC358768_DSICMD_TYPE, (0x40 << 8) | (packet.header[0] & 0x3f)); tc358768_write(priv, TC358768_DSICMD_WC, packet.payload_length); - for (i = 0; i < packet.payload_length; i += 2) { + + for (int i = 0; i < packet.payload_length; i += 2) { u16 val = packet.payload[i]; if (i + 1 < packet.payload_length) val |= packet.payload[i + 1] << 8; - tc358768_write(priv, TC358768_DSICMD_WD0 + i, val); } - } - tc358768_dsicmd_tx(priv); + tc358768_dsicmd_tx(priv); + } else { + unsigned long tx_sleep_us; + size_t len; + + /* For packets over 8 bytes we need to use the video buffer */ + tc358768_write(priv, TC358768_DATAFMT, BIT(0)); /* txdt_en */ + tc358768_write(priv, TC358768_DSITX_DT, packet.header[0] & 0x3f); + tc358768_write(priv, TC358768_CMDBYTE, packet.payload_length); + tc358768_write(priv, TC358768_VBUFCTRL, TC358768_VBUFCTRL_VBUF_EN); + + /* + * Write the payload in 2-byte chunks, and pad with zeroes to + * align to 4 bytes. + */ + len = ALIGN(packet.payload_length, 4); + + for (int i = 0; i < len; i += 2) { + u16 val = 0; + + if (i < packet.payload_length) + val |= packet.payload[i]; + if (i + 1 < packet.payload_length) + val |= packet.payload[i + 1] << 8; + + tc358768_write(priv, TC358768_DBG_DATA, val); + } + + /* Start transmission */ + tc358768_write(priv, TC358768_VBUFCTRL, + TC358768_VBUFCTRL_VBUF_EN | + TC358768_VBUFCTRL_TX_EN | + TC358768_VBUFCTRL_MASK); + + /* + * The TC358768 spec says to wait until the transmission has + * been finished, estimating the sleep time based on the payload + * and clock rates. We use a simple safe estimate of 2us per + * byte (LP mode transmission). + */ + tx_sleep_us = packet.payload_length * 2; + usleep_range(tx_sleep_us, tx_sleep_us * 2); + + tc358768_write(priv, TC358768_VBUFCTRL, TC358768_VBUFCTRL_MASK); + tc358768_write(priv, TC358768_VBUFCTRL, 0); /* Stop transmission */ + } ret = tc358768_clear_error(priv); if (ret) @@ -690,8 +780,7 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, { struct tc358768_priv *priv = bridge_to_tc358768(bridge); struct mipi_dsi_device *dsi_dev = priv->output.dev; - unsigned long mode_flags = dsi_dev->mode_flags; - u32 val, val2, lptxcnt, hact, data_type; + u32 val, mask, val2, lptxcnt, hact; s32 raw_val; struct drm_crtc_state *crtc_state; struct drm_connector_state *conn_state; @@ -712,11 +801,6 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, u32 dsi_vsdly; const u32 internal_dly = 40; - if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { - dev_warn_once(dev, "Non-continuous mode unimplemented, falling back to continuous\n"); - mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; - } - tc358768_hw_enable(priv); ret = tc358768_sw_reset(priv); @@ -726,6 +810,9 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, return; } + /* Release RstPtr so that the video buffer can be used for DSI commands */ + tc358768_update_bits(priv, TC358768_PP_MISC, BIT(14), 0); + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); conn_state = drm_atomic_get_new_connector_state(state, connector); crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); @@ -742,30 +829,20 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, dsiclk = priv->dsiclk; hsbyteclk = dsiclk / 4; - /* Data Format Control Register */ - val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */ switch (dsi_dev->format) { case MIPI_DSI_FMT_RGB888: - val |= (0x3 << 4); hact = vm.hactive * 3; - data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; break; case MIPI_DSI_FMT_RGB666: - val |= (0x4 << 4); hact = vm.hactive * 3; - data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; break; case MIPI_DSI_FMT_RGB666_PACKED: - val |= (0x4 << 4) | BIT(3); hact = vm.hactive * 18 / 8; - data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; break; case MIPI_DSI_FMT_RGB565: - val |= (0x5 << 4); hact = vm.hactive * 2; - data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; break; default: dev_err(dev, "Invalid data format (%u)\n", @@ -921,9 +998,6 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, /* VSDly[9:0] */ tc358768_write(priv, TC358768_VSDLY, dsi_vsdly - internal_dly); - tc358768_write(priv, TC358768_DATAFMT, val); - tc358768_write(priv, TC358768_DSITX_DT, data_type); - /* Enable D-PHY (HiZ->LP11) */ tc358768_write(priv, TC358768_CLW_CNTRL, 0x0000); /* Enable lanes */ @@ -1000,7 +1074,7 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, tc358768_write(priv, TC358768_HSTXVREGEN, val); tc358768_write(priv, TC358768_TXOPTIONCNTRL, - (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0)); + (dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0)); /* TXTAGOCNT[26:16] RXTASURECNT[10:0] */ val = tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps * 4); @@ -1063,33 +1137,63 @@ static void tc358768_bridge_atomic_pre_enable(struct drm_bridge *bridge, tc358768_write(priv, TC358768_DSI_START, 0x1); /* Configure DSI_Control register */ - val = TC358768_DSI_CONFW_MODE_CLR | TC358768_DSI_CONFW_ADDR_DSI_CONTROL; - val |= TC358768_DSI_CONTROL_TXMD | TC358768_DSI_CONTROL_HSCKMD | - 0x3 << 1 | TC358768_DSI_CONTROL_EOTDIS; - tc358768_write(priv, TC358768_DSI_CONFW, val); + val = (dsi_dev->lanes - 1) << 1; - val = TC358768_DSI_CONFW_MODE_SET | TC358768_DSI_CONFW_ADDR_DSI_CONTROL; - val |= (dsi_dev->lanes - 1) << 1; + if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_LPM)) + val |= TC358768_DSI_CONTROL_TXMD; - val |= TC358768_DSI_CONTROL_TXMD; - - if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) val |= TC358768_DSI_CONTROL_HSCKMD; if (dsi_dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) val |= TC358768_DSI_CONTROL_EOTDIS; - tc358768_write(priv, TC358768_DSI_CONFW, val); + mask = TC358768_DSI_CONTROL_TXMD | TC358768_DSI_CONTROL_HSCKMD | + 0x3 << 1 | TC358768_DSI_CONTROL_EOTDIS; - val = TC358768_DSI_CONFW_MODE_CLR | TC358768_DSI_CONFW_ADDR_DSI_CONTROL; - val |= TC358768_DSI_CONTROL_DIS_MODE; /* DSI mode */ - tc358768_write(priv, TC358768_DSI_CONFW, val); + tc358768_confw_update_bits(priv, TC358768_DSI_CONTROL, mask, val); + + tc358768_confw_update_bits(priv, TC358768_DSI_CONTROL, + TC358768_DSI_CONTROL_DSI_MODE, 0); ret = tc358768_clear_error(priv); if (ret) dev_err(dev, "Bridge pre_enable failed: %d\n", ret); } +static void tc358768_config_video_format(struct tc358768_priv *priv) +{ + struct mipi_dsi_device *dsi_dev = priv->output.dev; + u32 val, data_type; + + /* Data Format Control Register */ + val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */ + switch (dsi_dev->format) { + case MIPI_DSI_FMT_RGB888: + val |= (0x3 << 4); + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; + break; + case MIPI_DSI_FMT_RGB666: + val |= (0x4 << 4); + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + val |= (0x4 << 4) | BIT(3); + data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; + break; + case MIPI_DSI_FMT_RGB565: + val |= (0x5 << 4); + data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; + break; + default: + dev_err(priv->dev, "Invalid data format (%u)\n", dsi_dev->format); + return; + } + + tc358768_write(priv, TC358768_DATAFMT, val); + tc358768_write(priv, TC358768_DSITX_DT, data_type); +} + static void tc358768_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_commit *state) { @@ -1101,6 +1205,14 @@ static void tc358768_bridge_atomic_enable(struct drm_bridge *bridge, return; } + /* Configure video format registers */ + tc358768_config_video_format(priv); + + /* Enable HS mode for video TX */ + tc358768_confw_update_bits(priv, TC358768_DSI_CONTROL, + TC358768_DSI_CONTROL_TXMD, + TC358768_DSI_CONTROL_TXMD); + /* clear FrmStop and RstPtr */ tc358768_update_bits(priv, TC358768_PP_MISC, 0x3 << 14, 0); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 108953a8fa45..42b451432bbb 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -114,6 +114,7 @@ #define REG_VID_CHA_HORIZONTAL_FRONT_PORCH 0x38 #define REG_VID_CHA_VERTICAL_FRONT_PORCH 0x3a #define REG_VID_CHA_TEST_PATTERN 0x3c +#define REG_VID_CHA_TEST_PATTERN_EN BIT(4) /* IRQ registers */ #define REG_IRQ_GLOBAL 0xe0 #define REG_IRQ_GLOBAL_IRQ_EN BIT(0) @@ -134,6 +135,9 @@ #define REG_IRQ_STAT_CHA_SOT_BIT_ERR BIT(2) #define REG_IRQ_STAT_CHA_PLL_UNLOCK BIT(0) +static bool sn65dsi83_test_pattern; +module_param_named(test_pattern, sn65dsi83_test_pattern, bool, 0644); + enum sn65dsi83_channel { CHANNEL_A, CHANNEL_B @@ -523,6 +527,7 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, const struct drm_display_mode *mode; struct drm_connector *connector; struct drm_crtc *crtc; + bool test_pattern = sn65dsi83_test_pattern; bool lvds_format_24bpp; bool lvds_format_jeida; unsigned int pval; @@ -645,7 +650,11 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, REG_LVDS_LANE_CHB_LVDS_TERM : 0)); regmap_write(ctx->regmap, REG_LVDS_CM, 0x00); - le16val = cpu_to_le16(mode->hdisplay); + /* + * Active line length needs to be halved for test pattern + * generation in dual LVDS output. + */ + le16val = cpu_to_le16(mode->hdisplay / (test_pattern ? dual_factor : 1)); regmap_bulk_write(ctx->regmap, REG_VID_CHA_ACTIVE_LINE_LENGTH_LOW, &le16val, 2); le16val = cpu_to_le16(mode->vdisplay); @@ -668,7 +677,8 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, (mode->hsync_start - mode->hdisplay) / dual_factor); regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH, mode->vsync_start - mode->vdisplay); - regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00); + regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, + test_pattern ? REG_VID_CHA_TEST_PATTERN_EN : 0); /* Enable PLL */ regmap_write(ctx->regmap, REG_RC_PLL_EN, REG_RC_PLL_EN_PLL_EN); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 671b2bc75cb1..0884ec459cc5 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -776,8 +776,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, goto err_initted_aux; } - drm_connector_attach_encoder(pdata->connector, pdata->bridge.encoder); - return 0; err_initted_aux: diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 3b6b0e92cf89..bf4ab4eaf269 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -114,7 +114,7 @@ static void tfp410_hpd_callback(void *arg, enum drm_connector_status status) { struct tfp410 *dvi = arg; - mod_delayed_work(system_wq, &dvi->hpd_work, + mod_delayed_work(system_percpu_wq, &dvi->hpd_work, msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); } diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index a23b7e33fba6..649969fca141 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -773,8 +773,11 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data) * @drm: the DRM device * @encoder: the encoder where the bridge chain starts * - * Allocate, initialise and register a &drm_bridge_connector with the @drm - * device. The connector is associated with a chain of bridges that starts at + * Create a new &drm_bridge_connector for the @drm device. The connector is + * allocated, initialised, registered with the @drm device and attached to + * @encoder. + * + * The connector is associated with a chain of bridges that starts at * the @encoder. All bridges in the chain shall report bridge operation flags * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of * them may create a DRM connector directly. @@ -1055,6 +1058,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER)) drm_connector_attach_content_protection_property(connector, true); + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ERR_PTR(ret); + return connector; } EXPORT_SYMBOL_GPL(drm_bridge_connector_init); diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 4ffa057e2784..170de30c28ae 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -463,19 +463,19 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, struct drm_crtc *crtc = state->crtc; drm_printf(p, "crtc[%u]: %s\n", crtc->base.id, crtc->name); - drm_printf(p, "\tenable=%d\n", state->enable); - drm_printf(p, "\tactive=%d\n", state->active); - drm_printf(p, "\tself_refresh_active=%d\n", state->self_refresh_active); - drm_printf(p, "\tplanes_changed=%d\n", state->planes_changed); - drm_printf(p, "\tmode_changed=%d\n", state->mode_changed); - drm_printf(p, "\tactive_changed=%d\n", state->active_changed); - drm_printf(p, "\tconnectors_changed=%d\n", state->connectors_changed); - drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - drm_printf(p, "\tplane_mask=%x\n", state->plane_mask); - drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); - drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); - drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); - drm_printf(p, "\tbackground_color=%llx\n", state->background_color); + drm_printf_indent(p, 1, "enable=%d\n", state->enable); + drm_printf_indent(p, 1, "active=%d\n", state->active); + drm_printf_indent(p, 1, "self_refresh_active=%d\n", state->self_refresh_active); + drm_printf_indent(p, 1, "planes_changed=%d\n", state->planes_changed); + drm_printf_indent(p, 1, "mode_changed=%d\n", state->mode_changed); + drm_printf_indent(p, 1, "active_changed=%d\n", state->active_changed); + drm_printf_indent(p, 1, "connectors_changed=%d\n", state->connectors_changed); + drm_printf_indent(p, 1, "color_mgmt_changed=%d\n", state->color_mgmt_changed); + drm_printf_indent(p, 1, "plane_mask=%x\n", state->plane_mask); + drm_printf_indent(p, 1, "connector_mask=%x\n", state->connector_mask); + drm_printf_indent(p, 1, "encoder_mask=%x\n", state->encoder_mask); + drm_printf_indent(p, 1, "mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); + drm_printf_indent(p, 1, "background_color=%llx\n", state->background_color); if (crtc->funcs->atomic_print_state) crtc->funcs->atomic_print_state(p, state); @@ -818,38 +818,38 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, struct drm_colorop *colorop = state->colorop; drm_printf(p, "colorop[%u]:\n", colorop->base.id); - drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); + drm_printf_indent(p, 1, "type=%s\n", drm_get_colorop_type_name(colorop->type)); if (colorop->bypass_property) - drm_printf(p, "\tbypass=%u\n", state->bypass); + drm_printf_indent(p, 1, "bypass=%u\n", state->bypass); switch (colorop->type) { case DRM_COLOROP_1D_CURVE: - drm_printf(p, "\tcurve_1d_type=%s\n", - drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); + drm_printf_indent(p, 1, "curve_1d_type=%s\n", + drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); break; case DRM_COLOROP_1D_LUT: - drm_printf(p, "\tsize=%d\n", colorop->size); - drm_printf(p, "\tinterpolation=%s\n", - drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation)); - drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + drm_printf_indent(p, 1, "size=%d\n", colorop->size); + drm_printf_indent(p, 1, "interpolation=%s\n", + drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation)); + drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); break; case DRM_COLOROP_CTM_3X4: - drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); break; case DRM_COLOROP_MULTIPLIER: - drm_printf(p, "\tmultiplier=%llu\n", state->multiplier); + drm_printf_indent(p, 1, "multiplier=%llu\n", state->multiplier); break; case DRM_COLOROP_3D_LUT: - drm_printf(p, "\tsize=%d\n", colorop->size); - drm_printf(p, "\tinterpolation=%s\n", - drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation)); - drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + drm_printf_indent(p, 1, "size=%d\n", colorop->size); + drm_printf_indent(p, 1, "interpolation=%s\n", + drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation)); + drm_printf_indent(p, 1, "data blob id=%d\n", state->data ? state->data->base.id : 0); break; default: break; } - drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 0); + drm_printf_indent(p, 1, "next=%d\n", colorop->next ? colorop->next->base.id : 0); } static void drm_atomic_plane_print_state(struct drm_printer *p, @@ -860,21 +860,21 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, struct drm_rect dest = drm_plane_state_dest(state); drm_printf(p, "plane[%u]: %s\n", plane->base.id, plane->name); - drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); - drm_printf(p, "\tfb=%u\n", state->fb ? state->fb->base.id : 0); + drm_printf_indent(p, 1, "crtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf_indent(p, 1, "fb=%u\n", state->fb ? state->fb->base.id : 0); if (state->fb) drm_framebuffer_print_info(p, 2, state->fb); - drm_printf(p, "\tcrtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest)); - drm_printf(p, "\tsrc-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src)); - drm_printf(p, "\trotation=%x\n", state->rotation); - drm_printf(p, "\tnormalized-zpos=%x\n", state->normalized_zpos); - drm_printf(p, "\tcolor-encoding=%s\n", - drm_get_color_encoding_name(state->color_encoding)); - drm_printf(p, "\tcolor-range=%s\n", - drm_get_color_range_name(state->color_range)); - drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - drm_printf(p, "\tcolor-pipeline=%d\n", - state->color_pipeline ? state->color_pipeline->base.id : 0); + drm_printf_indent(p, 1, "crtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest)); + drm_printf_indent(p, 1, "src-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src)); + drm_printf_indent(p, 1, "rotation=%x\n", state->rotation); + drm_printf_indent(p, 1, "normalized-zpos=%x\n", state->normalized_zpos); + drm_printf_indent(p, 1, "color-encoding=%s\n", + drm_get_color_encoding_name(state->color_encoding)); + drm_printf_indent(p, 1, "color-range=%s\n", + drm_get_color_range_name(state->color_range)); + drm_printf_indent(p, 1, "color_mgmt_changed=%d\n", state->color_mgmt_changed); + drm_printf_indent(p, 1, "color-pipeline=%d\n", + state->color_pipeline ? state->color_pipeline->base.id : 0); if (plane->funcs->atomic_print_state) plane->funcs->atomic_print_state(p, state); } @@ -1347,27 +1347,27 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, struct drm_connector *connector = state->connector; drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); - drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); - drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); - drm_printf(p, "\tinterlace_allowed=%d\n", connector->interlace_allowed); - drm_printf(p, "\tycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed); - drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc); - drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace)); + drm_printf_indent(p, 1, "crtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + drm_printf_indent(p, 1, "self_refresh_aware=%d\n", state->self_refresh_aware); + drm_printf_indent(p, 1, "interlace_allowed=%d\n", connector->interlace_allowed); + drm_printf_indent(p, 1, "ycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed); + drm_printf_indent(p, 1, "max_requested_bpc=%d\n", state->max_requested_bpc); + drm_printf_indent(p, 1, "colorspace=%s\n", drm_get_colorspace_name(state->colorspace)); if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { - drm_printf(p, "\tbroadcast_rgb=%s\n", - drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); - drm_printf(p, "\tis_limited_range=%c\n", state->hdmi.is_limited_range ? 'y' : 'n'); - drm_printf(p, "\toutput_bpc=%u\n", state->hdmi.output_bpc); - drm_printf(p, "\toutput_format=%s\n", - drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); - drm_printf(p, "\ttmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); + drm_printf_indent(p, 1, "broadcast_rgb=%s\n", + drm_hdmi_connector_get_broadcast_rgb_name(state->hdmi.broadcast_rgb)); + drm_printf_indent(p, 1, "is_limited_range=%c\n", state->hdmi.is_limited_range ? 'y' : 'n'); + drm_printf_indent(p, 1, "output_bpc=%u\n", state->hdmi.output_bpc); + drm_printf_indent(p, 1, "output_format=%s\n", + drm_hdmi_connector_get_output_format_name(state->hdmi.output_format)); + drm_printf_indent(p, 1, "tmds_char_rate=%llu\n", state->hdmi.tmds_char_rate); } if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) if (state->writeback_job && state->writeback_job->fb) - drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id); + drm_printf_indent(p, 1, "fb=%d\n", state->writeback_job->fb->base.id); if (connector->funcs->atomic_print_state) connector->funcs->atomic_print_state(p, state); diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index e17bb0f1f9e0..e5013b870ba0 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -347,7 +347,7 @@ void drm_master_release(struct drm_file *file_priv) if (!drm_is_current_master_locked(file_priv)) goto out; - if (dev->master == file_priv->master) + if (dev->master && dev->master == file_priv->master) drm_drop_master(dev, file_priv); out: if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) { diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index d33ca2099135..ce180f0b26b2 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1614,18 +1614,17 @@ static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); - drm_printf(p, "\trefcount: %u%s\n", refcount, - lingering ? " [lingering]" : ""); + drm_printf_indent(p, 1, "refcount: %u%s\n", refcount, + lingering ? " [lingering]" : ""); - drm_printf(p, "\ttype: [%d] %s\n", - bridge->type, - drm_get_connector_type_name(bridge->type)); + drm_printf_indent(p, 1, "type: [%d] %s\n", bridge->type, + drm_get_connector_type_name(bridge->type)); /* The OF node could be freed after drm_bridge_remove() */ if (bridge->of_node && !lingering) - drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node); + drm_printf_indent(p, 1, "OF: %pOFfc\n", bridge->of_node); - drm_printf(p, "\tops: [0x%x]", bridge->ops); + drm_printf_indent(p, 1, "ops: [0x%x]", bridge->ops); if (bridge->ops & DRM_BRIDGE_OP_DETECT) drm_puts(p, " detect"); if (bridge->ops & DRM_BRIDGE_OP_EDID) diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index e0d533611040..60cd02b7ea64 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -275,6 +275,7 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_YVU422, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_YUV444, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 1, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_YVU444, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 1, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_Y8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_NV12, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true }, { .format = DRM_FORMAT_NV21, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true }, { .format = DRM_FORMAT_NV16, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, @@ -287,6 +288,7 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_XYUV8888, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_VUY888, .depth = 0, .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_XVUY2101010, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true }, { .format = DRM_FORMAT_Y210, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_Y212, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, @@ -354,6 +356,9 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_P030, .depth = 0, .num_planes = 2, .char_per_block = { 4, 8, 0 }, .block_w = { 3, 3, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true}, + { .format = DRM_FORMAT_P230, .depth = 0, .num_planes = 2, + .char_per_block = { 4, 8, 0 }, .block_w = { 3, 3, 0 }, .block_h = { 1, 1, 0 }, + .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_S010, .depth = 0, .num_planes = 3, .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 2, .vsub = 2, .is_yuv = true}, @@ -381,6 +386,12 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_S416, .depth = 0, .num_planes = 3, .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 1, .vsub = 1, .is_yuv = true}, + { .format = DRM_FORMAT_XYYY2101010, .depth = 0, .num_planes = 1, + .char_per_block = { 4, 0, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 }, + .hsub = 1, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_T430, .depth = 0, .num_planes = 3, + .char_per_block = { 4, 4, 4 }, .block_w = { 3, 3, 3 }, .block_h = { 1, 1, 1 }, + .hsub = 1, .vsub = 1, .is_yuv = true }, }; unsigned int i; @@ -491,12 +502,20 @@ EXPORT_SYMBOL(drm_format_info_block_height); */ unsigned int drm_format_info_bpp(const struct drm_format_info *info, int plane) { + unsigned int block_size; + if (!info || plane < 0 || plane >= info->num_planes) return 0; - return info->char_per_block[plane] * 8 / - (drm_format_info_block_width(info, plane) * - drm_format_info_block_height(info, plane)); + block_size = drm_format_info_block_width(info, plane) * + drm_format_info_block_height(info, plane); + + if (info->char_per_block[plane] * 8 % block_size) { + pr_warn("unable to return an integer bpp\n"); + return 0; + } + + return info->char_per_block[plane] * 8 / block_size; } EXPORT_SYMBOL(drm_format_info_bpp); diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 0390e14d3157..3ac1dd5ad640 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -587,6 +587,9 @@ EXPORT_SYMBOL(mipi_dsi_create_packet); * mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command * @dsi: DSI peripheral device * + * This function is deprecated. Use mipi_dsi_shutdown_peripheral_multi() + * instead. + * * Return: 0 on success or a negative error code on failure. */ int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi) @@ -1980,6 +1983,31 @@ void mipi_dsi_dcs_set_tear_scanline_multi(struct mipi_dsi_multi_context *ctx, } EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline_multi); +/** + * mipi_dsi_shutdown_peripheral_multi() - sends a Shutdown Peripheral command + * @ctx: Context for multiple DSI transactions + * + * Like mipi_dsi_shutdown_peripheral() but deals with errors in a way that + * makes it convenient to make several calls in a row. + */ +void mipi_dsi_shutdown_peripheral_multi(struct mipi_dsi_multi_context *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + if (ctx->accum_err) + return; + + ret = mipi_dsi_shutdown_peripheral(dsi); + if (ret < 0) { + ctx->accum_err = ret; + dev_err(dev, "Failed to shutdown peripheral: %d\n", + ctx->accum_err); + } +} +EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral_multi); + static int mipi_dsi_drv_probe(struct device *dev) { struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 20dad16fd554..55a9e745604d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -93,18 +93,6 @@ void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) -/* - * Return the storage size of a structure with a variable length array. - * The array is nelem elements of elem_size, where the base structure - * is defined by base. If the size overflows size_t, return zero. - */ -static inline size_t size_vstruct(size_t nelem, size_t elem_size, size_t base) -{ - if (elem_size && nelem > (SIZE_MAX - base) / elem_size) - return 0; - return base + nelem * elem_size; -} - /* * Etnaviv timeouts are specified wrt CLOCK_MONOTONIC, not jiffies. * We need to calculate the timeout in terms of number of jiffies diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index fbf693addd74..1a77a09b3377 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -32,9 +32,8 @@ static struct etnaviv_gem_submit *submit_create(struct drm_device *dev, struct etnaviv_gpu *gpu, size_t nr_bos, size_t nr_pmrs) { struct etnaviv_gem_submit *submit; - size_t sz = size_vstruct(nr_bos, sizeof(submit->bos[0]), sizeof(*submit)); - submit = kzalloc(sz, GFP_KERNEL); + submit = kzalloc_flex(*submit, bos, nr_bos); if (!submit) return NULL; diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 6884ea6d04eb..b80540328150 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -118,7 +118,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) return ret; } - return drm_connector_attach_encoder(connector, dp->plat_data.encoder); + return 0; } static void exynos_dp_unbind(struct device *dev, struct device *master, diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 005ab7f5355f..947f7c4ff4e9 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 051ecc526832..753d97bff76f 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -10,7 +10,7 @@ #include "hyperv_drm.h" -#define VMBUS_RING_BUFSIZE (256 * 1024) +#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024) #define VMBUS_VSP_TIMEOUT (10 * HZ) #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 65ce54b20ec2..05997e8bbb29 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -3205,8 +3205,7 @@ eb_composite_fence_create(struct i915_execbuffer *eb, int out_fence_fd) fence_array = dma_fence_array_create(eb->num_batches, fences, eb->context->parallel.fence_context, - eb->context->parallel.seqno++, - false); + eb->context->parallel.seqno++); if (!fence_array) { kfree(fences); return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c index 6193811ef7be..6bb5baa6c41b 100644 --- a/drivers/gpu/drm/imagination/pvr_fw_trace.c +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -77,7 +77,7 @@ const struct kernel_param_ops pvr_fw_trace_init_mask_ops = { }; param_check_hexint(init_fw_trace_mask, &pvr_fw_trace_init_mask); -module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0600); +module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0400); __MODULE_PARM_TYPE(init_fw_trace_mask, "hexint"); MODULE_PARM_DESC(init_fw_trace_mask, "Enable FW trace for the specified groups at device init time"); diff --git a/drivers/gpu/drm/imx/dc/dc-kms.c b/drivers/gpu/drm/imx/dc/dc-kms.c index 2b18aa37a4a8..0f8cfaf4c4d1 100644 --- a/drivers/gpu/drm/imx/dc/dc-kms.c +++ b/drivers/gpu/drm/imx/dc/dc-kms.c @@ -81,13 +81,7 @@ static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm, return ret; } - ret = drm_connector_attach_encoder(connector, encoder); - if (ret) - dev_err(dev, - "failed to attach encoder to connector for CRTC%u: %d\n", - crtc->index, ret); - - return ret; + return 0; } int dc_kms_init(struct dc_drm_device *dc_drm) diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c index 3633e8f3aff6..50bd7f36d36d 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-kms.c +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c @@ -103,8 +103,6 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms) return PTR_ERR(kms->connector); } - drm_connector_attach_encoder(kms->connector, encoder); - return 0; } diff --git a/drivers/gpu/drm/imx/ipuv3/Kconfig b/drivers/gpu/drm/imx/ipuv3/Kconfig index 351dc65913eb..3ca237515e46 100644 --- a/drivers/gpu/drm/imx/ipuv3/Kconfig +++ b/drivers/gpu/drm/imx/ipuv3/Kconfig @@ -13,6 +13,7 @@ config DRM_IMX config DRM_IMX_PARALLEL_DISPLAY tristate "Support for parallel displays" depends on DRM_IMX + depends on OF select DRM_BRIDGE select DRM_BRIDGE_CONNECTOR select DRM_DISPLAY_HELPER @@ -33,6 +34,7 @@ config DRM_IMX_LDB tristate "Support for LVDS displays" depends on DRM_IMX depends on COMMON_CLK + depends on OF select MFD_SYSCON select DRM_BRIDGE select DRM_BRIDGE_CONNECTOR diff --git a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c index 730caf883e83..63d6a1e7c1bd 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c @@ -407,8 +407,6 @@ static int imx_ldb_register(struct drm_device *drm, if (IS_ERR(connector)) return PTR_ERR(connector); - drm_connector_attach_encoder(connector, encoder); - return 0; } diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c index 6c505becb31d..0f06db95f00f 100644 --- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c +++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c @@ -216,8 +216,6 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(connector)) return PTR_ERR(connector); - drm_connector_attach_encoder(connector, encoder); - return 0; } diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c index e200b40f30fe..f52832b43aca 100644 --- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c +++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c @@ -448,8 +448,6 @@ static int imx_lcdc_probe(struct platform_device *pdev) if (IS_ERR(lcdc->connector)) return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n"); - drm_connector_attach_encoder(lcdc->connector, &lcdc->pipe.encoder); - /* * The LCDC controller does not have an enable bit. The * controller starts directly when the clocks are enabled. diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 2350c9499aef..42c86f195c66 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1330,8 +1330,6 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) ret = PTR_ERR(connector); goto err_drvdata; } - - drm_connector_attach_encoder(connector, encoder); } drm_for_each_encoder(encoder, drm) { diff --git a/drivers/gpu/drm/kmb/kmb_dsi.c b/drivers/gpu/drm/kmb/kmb_dsi.c index 4b5a9c051e65..59d0e856392f 100644 --- a/drivers/gpu/drm/kmb/kmb_dsi.c +++ b/drivers/gpu/drm/kmb/kmb_dsi.c @@ -1457,7 +1457,7 @@ int kmb_dsi_encoder_init(struct drm_device *dev, struct kmb_dsi *kmb_dsi) drm_encoder_cleanup(encoder); return PTR_ERR(connector); } - drm_connector_attach_encoder(connector, encoder); + return 0; } diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 53360b5d12ba..cc6d74ce8b5e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -1049,7 +1049,6 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) ret = PTR_ERR(dpi->connector); goto err_cleanup; } - drm_connector_attach_encoder(dpi->connector, &dpi->encoder); return 0; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index dacc36ccb731..46cf4b043f12 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -936,7 +936,6 @@ static int mtk_dsi_encoder_init(struct drm_device *drm, struct mtk_dsi *dsi) ret = PTR_ERR(dsi->connector); goto err_cleanup_encoder; } - drm_connector_attach_encoder(dsi->connector, &dsi->encoder); return 0; diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index 574ffffd4a27..8b26a0031cde 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -279,8 +279,6 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) return dev_err_probe(priv->dev, PTR_ERR(connector), "Unable to create CVBS bridge connector\n"); - drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder); - priv->encoders[MESON_ENC_CVBS] = meson_encoder_cvbs; return 0; diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index d766625aa1d0..55c0601df3c6 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -433,8 +433,6 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv) "Unable to create HDMI bridge connector\n"); goto err_put_node; } - drm_connector_attach_encoder(meson_encoder_hdmi->connector, - &meson_encoder_hdmi->encoder); /* * We should have now in place: diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index a32be27c39e8..d7b5dfdb28aa 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include "mgag200_drv.h" diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c index 809ca191e9de..7726edb0d4ed 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c @@ -232,13 +232,6 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, return PTR_ERR(connector); } - ret = drm_connector_attach_encoder(connector, encoder); - if (ret) { - DRM_DEV_ERROR(dev->dev, "failed to attach LVDS connector: %d\n", ret); - - return ret; - } - break; case DRM_MODE_ENCODER_TMDS: encoder = mdp4_dtv_encoder_init(dev); diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index ec4652e620c4..349175457566 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -376,7 +376,5 @@ struct drm_connector *msm_dp_drm_connector_init(struct msm_dp *msm_dp_display, if (!msm_dp_display->is_edp) drm_connector_attach_dp_subconnector_property(connector); - drm_connector_attach_encoder(connector, encoder); - return connector; } diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index ca400924d4ee..46faeaae9d94 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -485,10 +485,6 @@ int msm_dsi_manager_connector_init(struct msm_dsi *msm_dsi, return PTR_ERR(connector); } - ret = drm_connector_attach_encoder(connector, encoder); - if (ret < 0) - return ret; - return 0; } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 852abb2466f0..d9491aac1a89 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -190,8 +190,6 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi, goto fail; } - drm_connector_attach_encoder(hdmi->connector, hdmi->encoder); - ret = devm_request_irq(dev->dev, hdmi->irq, msm_hdmi_irq, IRQF_TRIGGER_HIGH, "hdmi_isr", hdmi); diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c index 7f07ae24e0dc..f5bb59cd5028 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_drv.c +++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c @@ -99,12 +99,6 @@ static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif) return dev_err_probe(dev, PTR_ERR(connector), "Failed to init bridge_connector for endpoint%u\n", of_ep.id); - - ret = drm_connector_attach_encoder(connector, encoder); - if (ret) - return dev_err_probe(dev, ret, - "Failed to attach connector for endpoint%u\n", - of_ep.id); } return 0; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index b983f4f9bcd7..364227f5456f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -2474,7 +2474,7 @@ nv50_disp_atomic_commit(struct drm_device *dev, pm_runtime_get_noresume(dev->dev); if (nonblock) - queue_work(system_unbound_wq, &state->commit_work); + queue_work(system_dfl_wq, &state->commit_work); else nv50_disp_atomic_commit_tail(state); diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index c8335f5b49db..38032bb95826 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2085,10 +2085,27 @@ nouveau_bios_init(struct drm_device *dev) int ret; /* only relevant for PCI devices */ - if (!dev_is_pci(dev->dev) || - nvkm_gsp_rm(nvxx_device(drm)->gsp)) + if (!dev_is_pci(dev->dev)) return 0; + if (nvkm_gsp_rm(nvxx_device(drm)->gsp)) { + struct nvkm_bios *nvkm_bios = nvxx_bios(drm); + + /* + * If this GPU has an nvkm_device_chip.bios entry, then the VBIOS + * data was already read by the nvkm layer during nvkm_bios_new(). + * Point the legacy DRM-level VBIOS structure at the same buffer + * so that any remaining legacy code can access it. This exposes + * the VBIOS via the DRM debugfs entries. + */ + if (nvkm_bios) { + bios->data = nvkm_bios->data; + bios->length = nvkm_bios->size; + } + + return 0; + } + if (!NVInitVBIOS(dev)) return -ENODEV; diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 517ff2c31dce..e16f59b00f6f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -632,7 +632,7 @@ nouveau_drm_device_init(struct nouveau_drm *drm) struct drm_device *dev = drm->dev; int ret; - drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", 0, + drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", WQ_PERCPU, WQ_MAX_ACTIVE); if (!drm->sched_wq) return -ENOMEM; diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c index 179edf90a531..2cbae003d6de 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sched.c +++ b/drivers/gpu/drm/nouveau/nouveau_sched.c @@ -413,7 +413,8 @@ nouveau_sched_init(struct nouveau_sched *sched, struct nouveau_drm *drm, int ret; if (!wq) { - wq = alloc_workqueue("nouveau_sched_wq_%d", 0, WQ_MAX_ACTIVE, + wq = alloc_workqueue("nouveau_sched_wq_%d", WQ_PERCPU, + WQ_MAX_ACTIVE, current->pid); if (!wq) return -ENOMEM; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 2042a633835f..92d6a1f9c0a1 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -507,8 +507,6 @@ static int omap_modeset_init(struct drm_device *dev) return PTR_ERR(pipe->connector); } - drm_connector_attach_encoder(pipe->connector, encoder); - crtc = omap_crtc_init(dev, pipe, priv->planes[i]); if (IS_ERR(crtc)) return PTR_ERR(crtc); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 979109c27b9b..b0e9b92e2c84 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -105,8 +105,20 @@ config DRM_PANEL_BOE_TV101WUM_LL2 Say Y here if you want to support for BOE TV101WUM-LL2 WUXGA PANEL DSI Video Mode panel +config DRM_PANEL_CHIPWEALTH_CH13726A + tristate "CHIPWEALTH CH13726A-based DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER + help + Say Y here if you want to enable support for ChipWealth + CH13726A-based display panels. + config DRM_PANEL_EBBG_FT8719 tristate "EBBG FT8719 panel driver" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE @@ -357,7 +369,8 @@ config DRM_PANEL_JADARD_JD9365DA_H3 config DRM_PANEL_JDI_LPM102A188A tristate "JDI LPM102A188A DSI panel" - depends on OF && GPIOLIB + depends on GPIOLIB + depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE help @@ -472,6 +485,7 @@ config DRM_PANEL_LG_LG4573 config DRM_PANEL_LG_SW43408 tristate "LG SW43408 panel" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE @@ -562,6 +576,16 @@ config DRM_PANEL_NOVATEK_NT35510 around the Novatek NT35510 display controller, such as some Hydis panels. +config DRM_PANEL_NOVATEK_NT35532 + tristate "Novatek NT35532-based DSI video mode panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_KMS_HELPER + help + Say Y or M here if you want to enable support for Novatek + NT35532-based 1080p video mode DSI panels. + config DRM_PANEL_NOVATEK_NT35560 tristate "Novatek NT35560 DSI command mode panel" depends on OF @@ -597,6 +621,7 @@ config DRM_PANEL_NOVATEK_NT36523 config DRM_PANEL_NOVATEK_NT36672A tristate "Novatek NT36672A DSI panel" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE @@ -607,6 +632,7 @@ config DRM_PANEL_NOVATEK_NT36672A config DRM_PANEL_NOVATEK_NT36672E tristate "Novatek NT36672E DSI panel" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE @@ -744,7 +770,8 @@ config DRM_PANEL_RAYDIUM_RM692E5 config DRM_PANEL_RAYDIUM_RM69380 tristate "Raydium RM69380-based DSI panel" - depends on OF && GPIOLIB + depends on GPIOLIB + depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE help @@ -902,10 +929,10 @@ config DRM_PANEL_SAMSUNG_S6D7AA0 config DRM_PANEL_SAMSUNG_S6E3FC2X01 tristate "Samsung S6E3FC2X01 DSI panel controller" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE - select VIDEOMODE_HELPERS help Say Y or M here if you want to enable support for the Samsung S6E3FC2 DDIC and connected MIPI DSI panel. @@ -922,11 +949,18 @@ config DRM_PANEL_SAMSUNG_S6E3HA2 config DRM_PANEL_SAMSUNG_S6E3HA8 tristate "Samsung S6E3HA8 DSI video mode panel" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE select DRM_DISPLAY_DSC_HELPER - select VIDEOMODE_HELPERS + help + Say Y or M here if you want to enable support for the + Samsung S6E3HA8 DDIC and connected MIPI DSI panel. + Currently supported panels: + + Samsung AMB577PX01 (found in the Samsung S9 smartphone) + config DRM_PANEL_SAMSUNG_S6E63J0X03 tristate "Samsung S6E63J0X03 DSI command mode panel" @@ -994,10 +1028,10 @@ config DRM_PANEL_SAMSUNG_S6E8FC0 config DRM_PANEL_SAMSUNG_SOFEF00 tristate "Samsung SOFEF00 DSI panel controller" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE - select VIDEOMODE_HELPERS help Say Y or M here if you want to enable support for the Samsung AMOLED panel SOFEF00 DDIC and connected panel. @@ -1262,6 +1296,7 @@ config DRM_PANEL_VISIONOX_R66451 config DRM_PANEL_VISIONOX_RM69299 tristate "Visionox RM69299" + depends on GPIOLIB depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 0d694acbfbb6..c2c5cf817116 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o +obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o @@ -55,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3052C) += panel-newvision-nv3052c.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35532) += panel-novatek-nt35532.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o diff --git a/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c new file mode 100644 index 000000000000..be76bc825c3f --- /dev/null +++ b/drivers/gpu/drm/panel/panel-chipwealth-ch13726a.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ChipWealth CH13726A MIPI-DSI panel driver + * Copyright (c) 2024, Teguh Sobirin . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include